1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /* XXX KEBE ASKS --> Is this affected instead by the IPFILTER.LICENCE? */
  13 
  14 /*
  15  * Copyright 2019, Joyent, Inc.
  16  */
  17 
  18 #if defined(KERNEL) || defined(_KERNEL)
  19 # undef KERNEL
  20 # undef _KERNEL
  21 # define        KERNEL  1
  22 # define        _KERNEL 1
  23 #endif
  24 #include <sys/errno.h>
  25 #include <sys/types.h>
  26 #include <sys/param.h>
  27 #include <sys/time.h>
  28 #include <sys/socket.h>
  29 #include <net/if.h>
  30 #include <net/route.h>
  31 #include <netinet/in.h>
  32 #include <netinet/in_systm.h>
  33 #include <netinet/ip.h>
  34 #include <netinet/ip_var.h>
  35 #include <netinet/tcp.h>
  36 #include "netinet/ip_compat.h"
  37 #ifdef  USE_INET6
  38 #include <netinet/icmp6.h>
  39 #endif
  40 #include <netinet/tcpip.h>
  41 #include "netinet/ip_fil.h"
  42 #include "netinet/ip_nat.h"
  43 #include "netinet/ip_frag.h"
  44 #include "netinet/ip_state.h"
  45 #include "netinet/ip_proxy.h"
  46 #include "netinet/ip_auth.h"
  47 #include "netinet/ipf_stack.h"
  48 #ifdef IPFILTER_SCAN
  49 # include "netinet/ip_scan.h"
  50 #endif
  51 #ifdef IPFILTER_SYNC
  52 # include "netinet/ip_sync.h"
  53 #endif
  54 #include "netinet/ip_pool.h"
  55 #include "netinet/ip_htable.h"
  56 #ifdef IPFILTER_COMPILED
  57 # include "netinet/ip_rules.h"
  58 #endif
  59 #if defined(_KERNEL)
  60 #include <sys/sunddi.h>
  61 #endif
  62 
  63 /* Extra includes outside normal ipf things. */
  64 #include <sys/types.h>
  65 #include <inet/ip6.h>
  66 /* Because ipf compiles this kernel file in userland testing... */
  67 #ifndef ASSERT3U
  68 #define ASSERT3U(a, b, c) ASSERT(a ## b ## c);
  69 #endif  /* ASSERT3U */
  70 
  71 /*
  72  * cfw == Cloud Firewall ==> routines for a global-zone data collector about
  73  * ipf events for SmartOS.
  74  *
  75  * The variable below is mdb-hackable to experiment with turning it on and
  76  * off. Eventually this will tie into a new ipf (GZ-only) device that flips
  77  * this on when there is an open instance.
  78  */
  79 boolean_t ipf_cfwlog_enabled;
  80 
  81 /*
  82  * XXX KEBE ASKS MOVE THIS TO A HEADER FILE?
  83  */
  84 #define CFWEV_BLOCK     1
  85 #define CFWEV_BEGIN     2
  86 #define CFWEV_END       3
  87 #define CFWDIR_IN       1
  88 #define CFWDIR_OUT      2
  89 typedef struct cfwev_s {
  90         uint16_t cfwev_type;    /* BEGIN, END, BLOCK */
  91         uint8_t cfwev_protocol; /* IPPROTO_* */
  92         uint8_t cfwev_direction;
  93         /*
  94          * The above "direction" informs if src/dst are local/remote or
  95          * remote/local.
  96          */
  97         uint16_t cfwev_sport;   /* Source port */
  98         uint16_t cfwev_dport;   /* Dest. port */
  99         in6_addr_t cfwev_saddr; /* Can be clever later with unions, w/not. */
 100         in6_addr_t cfwev_daddr;
 101         /* XXX KEBE ASKS hrtime for relative time from some start instead? */
 102         struct timeval cfwev_tstamp;
 103         zoneid_t cfwev_zonedid; /* Pullable from ipf_stack_t. */
 104         uint32_t cfwev_ruleid;  /* Pullable from fr_info_t. */
 105 } cfwev_t;
 106 
 107 #ifdef _KERNEL
 108 static inline zoneid_t
 109 ifs_to_did(ipf_stack_t *ifs)
 110 {
 111         if (ifs->ifs_zone_did == 0) {
 112                 zone_t *zone;
 113 
 114                 /*
 115                  * Because we can't get the zone_did at initialization time
 116                  * because most zone data isn't readily available then,
 117                  * cement the did in place now.
 118                  */
 119                 ASSERT(ifs->ifs_zone != GLOBAL_ZONEID);
 120                 zone = zone_find_by_id(ifs->ifs_zone);
 121                 if (zone != NULL) {
 122                         ifs->ifs_zone_did = zone->zone_did;
 123                         zone_rele(zone);
 124                 }
 125                 /* Else we are either in shutdown or something weirder. */
 126         }
 127         return (ifs->ifs_zone_did);
 128 }
 129 
 130 /*
 131  * ipf_block_cfwlog()
 132  *
 133  * Called by fr_check().  Record drop events for a global-zone data collector.
 134  * Use rest-of-ipf-style names for the parameters.
 135  */
 136 void
 137 ipf_block_cfwlog(frentry_t *fr, fr_info_t *fin, ipf_stack_t *ifs)
 138 {
 139         cfwev_t event = {0};
 140 
 141         /*
 142          * We need a rule.
 143          * Capture failure by using dtrace on this function's entry.
 144          * 'ipf_block_cfwlog:entry /arg0 == NULL/ { printf("GOTCHA!\n"); }'
 145          */
 146         if (fr == NULL)
 147                 return;
 148 
 149         event.cfwev_type = CFWEV_BLOCK;
 150         /*
 151          * IPF code elsewhere does the cheesy single-flag check, even thogh
 152          * there are two flags in a rule (one for in, one for out).
 153          */
 154         event.cfwev_direction = (fr->fr_flags & FR_INQUE) ?
 155             CFWDIR_IN : CFWDIR_OUT;
 156 
 157         event.cfwev_protocol = fin->fin_p;
 158         /* XXX KEBE SAYS ICMP stuff should fall in here too. */
 159         event.cfwev_sport = fin->fin_sport;
 160         event.cfwev_dport = fin->fin_dport;
 161 
 162         if (fin->fin_v == IPV4_VERSION) {
 163                 IN6_INADDR_TO_V4MAPPED(&fin->fin_src, &event.cfwev_saddr);
 164                 IN6_INADDR_TO_V4MAPPED(&fin->fin_dst, &event.cfwev_daddr);
 165         } else {
 166                 ASSERT3U(fin->fin_v, ==, IPV6_VERSION);
 167                 event.cfwev_saddr = fin->fin_src6.in6;
 168                 event.cfwev_daddr = fin->fin_dst6.in6;
 169         }
 170 
 171         /*
 172          * XXX KEBE ASKS -> something better instead?!?
 173          * uniqtime() is what ipf's GETKTIME() uses. It does give us tv_usec,
 174          * but I'm not sure if it's suitable for what we need.
 175          */
 176         uniqtime(&event.cfwev_tstamp);
 177         event.cfwev_zonedid = ifs_to_did(ifs);
 178         event.cfwev_ruleid = fin->fin_rule;
 179 
 180         DTRACE_PROBE1(ipf__cfw__block, cfwev_t *, &event);
 181 }
 182 
 183 /*
 184  * ipf_log_cfwlog()
 185  *
 186  * Twin of ipstate_log(), but records state events for a global-zone data
 187  * collector.
 188  */
 189 void
 190 ipf_log_cfwlog(struct ipstate *is, uint_t type, ipf_stack_t *ifs)
 191 {
 192         cfwev_t event = {0};
 193 
 194         switch (type) {
 195         case ISL_NEW:
 196         case ISL_CLONE:
 197                 event.cfwev_type = CFWEV_BEGIN;
 198                 break;
 199         case ISL_EXPIRE:
 200         case ISL_FLUSH:
 201         case ISL_REMOVE:
 202         case ISL_KILLED:
 203         case ISL_ORPHAN:
 204 #if 0
 205                 event.cfwev_type = CFWEV_END;
 206                 break;
 207 #else
 208                 /* We don't care about disappearances in CFW logging for now. */
 209                 return;
 210 #endif
 211         default:
 212                 event.cfwev_type = CFWEV_BLOCK;
 213                 break;
 214         }
 215 
 216         /*
 217          * IPF code elsewhere does the cheesy single-flag check, even thogh
 218          * there are two flags in a rule (one for in, one for out).
 219          */
 220         event.cfwev_direction = (is->is_rule->fr_flags & FR_INQUE) ?
 221             CFWDIR_IN : CFWDIR_OUT;
 222         event.cfwev_protocol = is->is_p;
 223         switch (is->is_p) {
 224         case IPPROTO_TCP:
 225         case IPPROTO_UDP:
 226                 event.cfwev_sport = is->is_sport;
 227                 event.cfwev_dport = is->is_dport;
 228                 break;
 229         case IPPROTO_ICMP:
 230         case IPPROTO_ICMPV6:
 231                 /* Scribble the ICMP type in sport... */
 232                 event.cfwev_sport = is->is_icmp.ici_type;
 233                 break;
 234         }
 235 
 236         if (is->is_v == IPV4_VERSION) {
 237                 IN6_INADDR_TO_V4MAPPED(&is->is_src.in4, &event.cfwev_saddr);
 238                 IN6_INADDR_TO_V4MAPPED(&is->is_dst.in4, &event.cfwev_daddr);
 239         } else {
 240                 ASSERT3U(is->is_v, ==, IPV6_VERSION);
 241                 event.cfwev_saddr = is->is_src.in6;
 242                 event.cfwev_daddr = is->is_dst.in6;
 243         }
 244 
 245         /*
 246          * XXX KEBE ASKS -> something better instead?!?
 247          * uniqtime() is what ipf's GETKTIME() uses. It does give us tv_usec,
 248          * but I'm not sure if it's suitable for what we need.
 249          */
 250         uniqtime(&event.cfwev_tstamp);
 251         event.cfwev_zonedid = ifs_to_did(ifs);
 252         /* XXX KEBE ASKS -> good enough? */
 253         event.cfwev_ruleid = is->is_rulen;
 254 
 255         /* XXX KEBE SAYS Then we do something with it. */
 256         DTRACE_PROBE1(ipf__cfw__state, cfwev_t *, &event);
 257 }
 258 
 259 #else
 260 /* Blank stubs to satisfy userland's test stuff. */
 261 
 262 void
 263 ipf_log_cfwlog(struct ipstate *a, uint_t b, ipf_stack_t *c)
 264 {
 265 }
 266 
 267 void
 268 ipf_block_cfwlog(frentry_t *a, fr_info_t *b, ipf_stack_t *c)
 269 {
 270 }
 271 
 272 #endif  /* _KERNEL */