1*fe77cc04SRobert Mustacchi /* 2*fe77cc04SRobert Mustacchi * This file and its contents are supplied under the terms of the 3*fe77cc04SRobert Mustacchi * Common Development and Distribution License ("CDDL"), version 1.0. 4*fe77cc04SRobert Mustacchi * You may only use this file in accordance with the terms of version 5*fe77cc04SRobert Mustacchi * 1.0 of the CDDL. 6*fe77cc04SRobert Mustacchi * 7*fe77cc04SRobert Mustacchi * A full copy of the text of the CDDL should have accompanied this 8*fe77cc04SRobert Mustacchi * source. A copy of the CDDL is also available via the Internet at 9*fe77cc04SRobert Mustacchi * http://www.illumos.org/license/CDDL. 10*fe77cc04SRobert Mustacchi */ 11*fe77cc04SRobert Mustacchi /* 12*fe77cc04SRobert Mustacchi * Copyright (c) 2012, Joyent, Inc. All rights reserved. 13*fe77cc04SRobert Mustacchi */ 14*fe77cc04SRobert Mustacchi 15*fe77cc04SRobert Mustacchi /* 16*fe77cc04SRobert Mustacchi * ipd: Internet packet disturber 17*fe77cc04SRobert Mustacchi * 18*fe77cc04SRobert Mustacchi * The purpose of ipd is to simulate congested and lossy networks when they 19*fe77cc04SRobert Mustacchi * don't actually exist. The features of these congested and lossy networks are 20*fe77cc04SRobert Mustacchi * events that end up leading to retransmits and thus kicking us out of the 21*fe77cc04SRobert Mustacchi * TCP/IP fastpath. Since normally this would require us to have an actually 22*fe77cc04SRobert Mustacchi * congested network, which can be problematic, we instead simulate this 23*fe77cc04SRobert Mustacchi * behavior. 24*fe77cc04SRobert Mustacchi * 25*fe77cc04SRobert Mustacchi * 1. ipd's operations and restrictions 26*fe77cc04SRobert Mustacchi * 27*fe77cc04SRobert Mustacchi * ipd currently has facilities to cause IP traffic to be: 28*fe77cc04SRobert Mustacchi * 29*fe77cc04SRobert Mustacchi * - Corrupted with some probability. 30*fe77cc04SRobert Mustacchi * - Delayed for a set number of microseconds. 31*fe77cc04SRobert Mustacchi * - Dropped with some probability. 32*fe77cc04SRobert Mustacchi * 33*fe77cc04SRobert Mustacchi * Each of these features are enabled on a per-zone basic. The current 34*fe77cc04SRobert Mustacchi * implementation restricts this specifically to exclusive stack zones. 35*fe77cc04SRobert Mustacchi * Enabling ipd on a given zone causes pfhooks to be installed for that zone's 36*fe77cc04SRobert Mustacchi * netstack. Because of the nature of ipd, it currently only supports exclusive 37*fe77cc04SRobert Mustacchi * stack zones and as a further restriction, it only allows the global zone 38*fe77cc04SRobert Mustacchi * administrative access. ipd can be enabled for the global zone, but doing so 39*fe77cc04SRobert Mustacchi * will cause all shared-stack zones to also be affected. 40*fe77cc04SRobert Mustacchi * 41*fe77cc04SRobert Mustacchi * 2. General architecture and Locking 42*fe77cc04SRobert Mustacchi * 43*fe77cc04SRobert Mustacchi * ipd consists of a few components. There is a per netstack data structure that 44*fe77cc04SRobert Mustacchi * is created and destroyed with the creation and destruction of each exclusive 45*fe77cc04SRobert Mustacchi * stack zone. Each of these netstacks is stored in a global list which is 46*fe77cc04SRobert Mustacchi * accessed for control of ipd via ioctls. The following diagram touches on the 47*fe77cc04SRobert Mustacchi * data structures that are used throughout ipd. 48*fe77cc04SRobert Mustacchi * 49*fe77cc04SRobert Mustacchi * ADMINISTRATIVE DATA PATH 50*fe77cc04SRobert Mustacchi * 51*fe77cc04SRobert Mustacchi * +--------+ +------+ +------+ 52*fe77cc04SRobert Mustacchi * | ipdadm | | ip | | nics | 53*fe77cc04SRobert Mustacchi * +--------+ +------+ +------+ 54*fe77cc04SRobert Mustacchi * | ^ | | 55*fe77cc04SRobert Mustacchi * | | ioctl(2) | | 56*fe77cc04SRobert Mustacchi * V | V V 57*fe77cc04SRobert Mustacchi * +----------+ +-------------------------+ 58*fe77cc04SRobert Mustacchi * | /dev/ipd | | pfhooks packet callback | == ipd_hook() 59*fe77cc04SRobert Mustacchi * +----------+ +-------------------------+ 60*fe77cc04SRobert Mustacchi * | | 61*fe77cc04SRobert Mustacchi * | | 62*fe77cc04SRobert Mustacchi * V | 63*fe77cc04SRobert Mustacchi * +----------------+ | 64*fe77cc04SRobert Mustacchi * | list_t ipd_nsl |------+ | 65*fe77cc04SRobert Mustacchi * +----------------+ | | 66*fe77cc04SRobert Mustacchi * | | 67*fe77cc04SRobert Mustacchi * V per netstack V 68*fe77cc04SRobert Mustacchi * +----------------------------+ 69*fe77cc04SRobert Mustacchi * | ipd_nestack_t | 70*fe77cc04SRobert Mustacchi * +----------------------------+ 71*fe77cc04SRobert Mustacchi * 72*fe77cc04SRobert Mustacchi * ipd has two different entry points, one is administrative, the other is the 73*fe77cc04SRobert Mustacchi * data path. The administrative path is accessed by a userland component called 74*fe77cc04SRobert Mustacchi * ipdadm(1M). It communicates to the kernel component via ioctls to /dev/ipd. 75*fe77cc04SRobert Mustacchi * If the administrative path enables a specific zone, then the data path will 76*fe77cc04SRobert Mustacchi * become active for that zone. Any packet that leaves that zone's IP stack or 77*fe77cc04SRobert Mustacchi * is going to enter it, comes through the callback specified in the hook_t(9S) 78*fe77cc04SRobert Mustacchi * structure. This will cause each packet to go through ipd_hook(). 79*fe77cc04SRobert Mustacchi * 80*fe77cc04SRobert Mustacchi * While the locking inside of ipd should be straightforward, unfortunately, the 81*fe77cc04SRobert Mustacchi * pfhooks subsystem necessarily complicates this a little bit. There are 82*fe77cc04SRobert Mustacchi * currently three different sets of locks in ipd. 83*fe77cc04SRobert Mustacchi * 84*fe77cc04SRobert Mustacchi * - Global lock N on the netstack list. 85*fe77cc04SRobert Mustacchi * - Global lock A on the active count. 86*fe77cc04SRobert Mustacchi * - Per-netstack data structure lock Z. 87*fe77cc04SRobert Mustacchi * 88*fe77cc04SRobert Mustacchi * # Locking rules 89*fe77cc04SRobert Mustacchi * 90*fe77cc04SRobert Mustacchi * L.1a N must always be acquired first and released last 91*fe77cc04SRobert Mustacchi * 92*fe77cc04SRobert Mustacchi * If you need to acquire the netstack list lock, either for reading or writing, 93*fe77cc04SRobert Mustacchi * then N must be acquired first and before any other locks. It may not be 94*fe77cc04SRobert Mustacchi * dropped before any other lock. 95*fe77cc04SRobert Mustacchi * 96*fe77cc04SRobert Mustacchi * L.1b N must only be acquired from the administrative path and zone creation, 97*fe77cc04SRobert Mustacchi * shutdown, and destruct callbacks. 98*fe77cc04SRobert Mustacchi * 99*fe77cc04SRobert Mustacchi * The data path, e.g. receiving the per-packet callbacks, should never be 100*fe77cc04SRobert Mustacchi * grabbing the list lock. If it is, then the architecture here needs to be 101*fe77cc04SRobert Mustacchi * reconsidered. 102*fe77cc04SRobert Mustacchi * 103*fe77cc04SRobert Mustacchi * L.2 Z cannot be held across calls to the pfhooks subsystem if packet hooks 104*fe77cc04SRobert Mustacchi * are active. 105*fe77cc04SRobert Mustacchi * 106*fe77cc04SRobert Mustacchi * The way the pfhooks subsystem is designed is that a reference count is 107*fe77cc04SRobert Mustacchi * present on the hook_t while it is active. As long as that reference count is 108*fe77cc04SRobert Mustacchi * non-zero, a call to net_hook_unregister will block until it is lowered. 109*fe77cc04SRobert Mustacchi * Because the callbacks want the same lock for the netstack that is held by the 110*fe77cc04SRobert Mustacchi * administrative path calling into net_hook_unregister, we deadlock. 111*fe77cc04SRobert Mustacchi * 112*fe77cc04SRobert Mustacchi * ioctl from ipdadm remove hook_t cb (from nic) hook_t cb (from IP) 113*fe77cc04SRobert Mustacchi * ----------------------- -------------------- ------------------- 114*fe77cc04SRobert Mustacchi * | | | 115*fe77cc04SRobert Mustacchi * | bump hook_t refcount | 116*fe77cc04SRobert Mustacchi * mutex_enter(ipd_nsl_lock); enter ipd_hook() bump hook_t refcount 117*fe77cc04SRobert Mustacchi * mutex acquired mutex_enter(ins->ipdn_lock); | 118*fe77cc04SRobert Mustacchi * | mutex acquired enter ipd_hook() 119*fe77cc04SRobert Mustacchi * mutex_enter(ins->ipdn_lock); | mutex_enter(ins->ipdn_lock); 120*fe77cc04SRobert Mustacchi * | | | 121*fe77cc04SRobert Mustacchi * | | | 122*fe77cc04SRobert Mustacchi * | mutex_exit(ins->ipdn_lock); | 123*fe77cc04SRobert Mustacchi * | | | 124*fe77cc04SRobert Mustacchi * mutex acquired leave ipd_hook() | 125*fe77cc04SRobert Mustacchi * | decrement hook_t refcount | 126*fe77cc04SRobert Mustacchi * | | | 127*fe77cc04SRobert Mustacchi * ipd_teardown_hooks() | | 128*fe77cc04SRobert Mustacchi * net_hook_unregister() | | 129*fe77cc04SRobert Mustacchi * cv_wait() if recount | | 130*fe77cc04SRobert Mustacchi * | | | 131*fe77cc04SRobert Mustacchi * --------------------------------------------------------------------------- 132*fe77cc04SRobert Mustacchi * 133*fe77cc04SRobert Mustacchi * At this point, we can see that the second hook callback still doesn't have 134*fe77cc04SRobert Mustacchi * the mutex, but it has bumped the hook_t refcount. However, it will never 135*fe77cc04SRobert Mustacchi * acquire the mutex that it needs to finish its operation and decrement the 136*fe77cc04SRobert Mustacchi * refcount. 137*fe77cc04SRobert Mustacchi * 138*fe77cc04SRobert Mustacchi * Obviously, deadlocking is not acceptable, thus the following corollary to the 139*fe77cc04SRobert Mustacchi * second locking rule: 140*fe77cc04SRobert Mustacchi * 141*fe77cc04SRobert Mustacchi * L.2 Corollary: If Z is being released across a call to the pfhooks subsystem, 142*fe77cc04SRobert Mustacchi * N must be held. 143*fe77cc04SRobert Mustacchi * 144*fe77cc04SRobert Mustacchi * There is currently only one path where we have to worry about this. That is 145*fe77cc04SRobert Mustacchi * when we are removing a hook, but the zone is not being shutdown, then hooks 146*fe77cc04SRobert Mustacchi * are currently active. The only place that this currently happens is in 147*fe77cc04SRobert Mustacchi * ipd_check_hooks(). 148*fe77cc04SRobert Mustacchi * 149*fe77cc04SRobert Mustacchi */ 150*fe77cc04SRobert Mustacchi 151*fe77cc04SRobert Mustacchi #include <sys/types.h> 152*fe77cc04SRobert Mustacchi #include <sys/file.h> 153*fe77cc04SRobert Mustacchi #include <sys/errno.h> 154*fe77cc04SRobert Mustacchi #include <sys/open.h> 155*fe77cc04SRobert Mustacchi #include <sys/cred.h> 156*fe77cc04SRobert Mustacchi #include <sys/ddi.h> 157*fe77cc04SRobert Mustacchi #include <sys/sunddi.h> 158*fe77cc04SRobert Mustacchi #include <sys/kmem.h> 159*fe77cc04SRobert Mustacchi #include <sys/conf.h> 160*fe77cc04SRobert Mustacchi #include <sys/stat.h> 161*fe77cc04SRobert Mustacchi #include <sys/cmn_err.h> 162*fe77cc04SRobert Mustacchi #include <sys/ddi.h> 163*fe77cc04SRobert Mustacchi #include <sys/sunddi.h> 164*fe77cc04SRobert Mustacchi #include <sys/modctl.h> 165*fe77cc04SRobert Mustacchi #include <sys/kstat.h> 166*fe77cc04SRobert Mustacchi #include <sys/neti.h> 167*fe77cc04SRobert Mustacchi #include <sys/list.h> 168*fe77cc04SRobert Mustacchi #include <sys/ksynch.h> 169*fe77cc04SRobert Mustacchi #include <sys/sysmacros.h> 170*fe77cc04SRobert Mustacchi #include <sys/policy.h> 171*fe77cc04SRobert Mustacchi #include <sys/atomic.h> 172*fe77cc04SRobert Mustacchi #include <sys/model.h> 173*fe77cc04SRobert Mustacchi #include <sys/strsun.h> 174*fe77cc04SRobert Mustacchi 175*fe77cc04SRobert Mustacchi #include <sys/netstack.h> 176*fe77cc04SRobert Mustacchi #include <sys/hook.h> 177*fe77cc04SRobert Mustacchi #include <sys/hook_event.h> 178*fe77cc04SRobert Mustacchi 179*fe77cc04SRobert Mustacchi #include <sys/ipd.h> 180*fe77cc04SRobert Mustacchi 181*fe77cc04SRobert Mustacchi #define IPDN_STATUS_DISABLED 0x1 182*fe77cc04SRobert Mustacchi #define IPDN_STATUS_ENABLED 0x2 183*fe77cc04SRobert Mustacchi #define IPDN_STATUS_CONDEMNED 0x4 184*fe77cc04SRobert Mustacchi 185*fe77cc04SRobert Mustacchi /* 186*fe77cc04SRobert Mustacchi * These flags are used to determine whether or not the hooks are registered. 187*fe77cc04SRobert Mustacchi */ 188*fe77cc04SRobert Mustacchi #define IPDN_HOOK_NONE 0x0 189*fe77cc04SRobert Mustacchi #define IPDN_HOOK_V4IN 0x1 190*fe77cc04SRobert Mustacchi #define IPDN_HOOK_V4OUT 0x2 191*fe77cc04SRobert Mustacchi #define IPDN_HOOK_V6IN 0x4 192*fe77cc04SRobert Mustacchi #define IPDN_HOOK_V6OUT 0x8 193*fe77cc04SRobert Mustacchi #define IPDN_HOOK_ALL 0xf 194*fe77cc04SRobert Mustacchi 195*fe77cc04SRobert Mustacchi /* 196*fe77cc04SRobert Mustacchi * Per-netstack kstats. 197*fe77cc04SRobert Mustacchi */ 198*fe77cc04SRobert Mustacchi typedef struct ipd_nskstat { 199*fe77cc04SRobert Mustacchi kstat_named_t ink_ndrops; 200*fe77cc04SRobert Mustacchi kstat_named_t ink_ncorrupts; 201*fe77cc04SRobert Mustacchi kstat_named_t ink_ndelays; 202*fe77cc04SRobert Mustacchi } ipd_nskstat_t; 203*fe77cc04SRobert Mustacchi 204*fe77cc04SRobert Mustacchi /* 205*fe77cc04SRobert Mustacchi * Different parts of this structure have different locking semantics. The list 206*fe77cc04SRobert Mustacchi * node is not normally referenced, if it is, one has to hold the ipd_nsl_lock. 207*fe77cc04SRobert Mustacchi * The following members are read only: ipdn_netid and ipdn_zoneid. The members 208*fe77cc04SRobert Mustacchi * of the kstat structure are always accessible in the data path, but the 209*fe77cc04SRobert Mustacchi * counters must be bumped with atomic operations. The ipdn_lock protects every 210*fe77cc04SRobert Mustacchi * other aspect of this structure. Please see the big theory statement on the 211*fe77cc04SRobert Mustacchi * requirements for lock ordering. 212*fe77cc04SRobert Mustacchi */ 213*fe77cc04SRobert Mustacchi typedef struct ipd_netstack { 214*fe77cc04SRobert Mustacchi list_node_t ipdn_link; /* link on ipd_nsl */ 215*fe77cc04SRobert Mustacchi netid_t ipdn_netid; /* netstack id */ 216*fe77cc04SRobert Mustacchi zoneid_t ipdn_zoneid; /* zone id */ 217*fe77cc04SRobert Mustacchi kstat_t *ipdn_kstat; /* kstat_t ptr */ 218*fe77cc04SRobert Mustacchi ipd_nskstat_t ipdn_ksdata; /* kstat data */ 219*fe77cc04SRobert Mustacchi kmutex_t ipdn_lock; /* protects following members */ 220*fe77cc04SRobert Mustacchi int ipdn_status; /* status flags */ 221*fe77cc04SRobert Mustacchi net_handle_t ipdn_v4hdl; /* IPv4 net handle */ 222*fe77cc04SRobert Mustacchi net_handle_t ipdn_v6hdl; /* IPv4 net handle */ 223*fe77cc04SRobert Mustacchi int ipdn_hooked; /* are hooks registered */ 224*fe77cc04SRobert Mustacchi hook_t *ipdn_v4in; /* IPv4 traffic in hook */ 225*fe77cc04SRobert Mustacchi hook_t *ipdn_v4out; /* IPv4 traffice out hook */ 226*fe77cc04SRobert Mustacchi hook_t *ipdn_v6in; /* IPv6 traffic in hook */ 227*fe77cc04SRobert Mustacchi hook_t *ipdn_v6out; /* IPv6 traffic out hook */ 228*fe77cc04SRobert Mustacchi int ipdn_enabled; /* which perturbs are on */ 229*fe77cc04SRobert Mustacchi int ipdn_corrupt; /* corrupt percentage */ 230*fe77cc04SRobert Mustacchi int ipdn_drop; /* drop percentage */ 231*fe77cc04SRobert Mustacchi uint_t ipdn_delay; /* delay us */ 232*fe77cc04SRobert Mustacchi long ipdn_rand; /* random seed */ 233*fe77cc04SRobert Mustacchi } ipd_netstack_t; 234*fe77cc04SRobert Mustacchi 235*fe77cc04SRobert Mustacchi /* 236*fe77cc04SRobert Mustacchi * ipd internal variables 237*fe77cc04SRobert Mustacchi */ 238*fe77cc04SRobert Mustacchi static dev_info_t *ipd_devi; /* device info */ 239*fe77cc04SRobert Mustacchi static net_instance_t *ipd_neti; /* net_instance for hooks */ 240*fe77cc04SRobert Mustacchi static unsigned int ipd_max_delay = IPD_MAX_DELAY; /* max delay in us */ 241*fe77cc04SRobert Mustacchi static kmutex_t ipd_nsl_lock; /* lock for the nestack list */ 242*fe77cc04SRobert Mustacchi static list_t ipd_nsl; /* list of netstacks */ 243*fe77cc04SRobert Mustacchi static kmutex_t ipd_nactive_lock; /* lock for nactive */ 244*fe77cc04SRobert Mustacchi static unsigned int ipd_nactive; /* number of active netstacks */ 245*fe77cc04SRobert Mustacchi static int ipd_nactive_fudge = 4; /* amount to fudge by in list */ 246*fe77cc04SRobert Mustacchi 247*fe77cc04SRobert Mustacchi /* 248*fe77cc04SRobert Mustacchi * Note that this random number implementation is based upon the old BSD 4.1 249*fe77cc04SRobert Mustacchi * rand. It's good enough for us! 250*fe77cc04SRobert Mustacchi */ 251*fe77cc04SRobert Mustacchi static int 252*fe77cc04SRobert Mustacchi ipd_nextrand(ipd_netstack_t *ins) 253*fe77cc04SRobert Mustacchi { 254*fe77cc04SRobert Mustacchi ins->ipdn_rand = ins->ipdn_rand * 1103515245L + 12345; 255*fe77cc04SRobert Mustacchi return (ins->ipdn_rand & 0x7fffffff); 256*fe77cc04SRobert Mustacchi } 257*fe77cc04SRobert Mustacchi 258*fe77cc04SRobert Mustacchi static void 259*fe77cc04SRobert Mustacchi ipd_ksbump(kstat_named_t *nkp) 260*fe77cc04SRobert Mustacchi { 261*fe77cc04SRobert Mustacchi atomic_inc_64(&nkp->value.ui64); 262*fe77cc04SRobert Mustacchi } 263*fe77cc04SRobert Mustacchi 264*fe77cc04SRobert Mustacchi /* 265*fe77cc04SRobert Mustacchi * This is where all the magic actually happens. The way that this works is we 266*fe77cc04SRobert Mustacchi * grab the ins lock to basically get a copy of all the data that we need to do 267*fe77cc04SRobert Mustacchi * our job and then let it go to minimize contention. In terms of actual work on 268*fe77cc04SRobert Mustacchi * the packet we do them in the following order: 269*fe77cc04SRobert Mustacchi * 270*fe77cc04SRobert Mustacchi * - drop 271*fe77cc04SRobert Mustacchi * - delay 272*fe77cc04SRobert Mustacchi * - corrupt 273*fe77cc04SRobert Mustacchi */ 274*fe77cc04SRobert Mustacchi /*ARGSUSED*/ 275*fe77cc04SRobert Mustacchi static int 276*fe77cc04SRobert Mustacchi ipd_hook(hook_event_token_t event, hook_data_t data, void *arg) 277*fe77cc04SRobert Mustacchi { 278*fe77cc04SRobert Mustacchi unsigned char *crp; 279*fe77cc04SRobert Mustacchi int dwait, corrupt, drop, rand, off, status; 280*fe77cc04SRobert Mustacchi mblk_t *mbp; 281*fe77cc04SRobert Mustacchi ipd_netstack_t *ins = arg; 282*fe77cc04SRobert Mustacchi hook_pkt_event_t *pkt = (hook_pkt_event_t *)data; 283*fe77cc04SRobert Mustacchi 284*fe77cc04SRobert Mustacchi mutex_enter(&ins->ipdn_lock); 285*fe77cc04SRobert Mustacchi status = ins->ipdn_status; 286*fe77cc04SRobert Mustacchi dwait = ins->ipdn_delay; 287*fe77cc04SRobert Mustacchi corrupt = ins->ipdn_corrupt; 288*fe77cc04SRobert Mustacchi drop = ins->ipdn_drop; 289*fe77cc04SRobert Mustacchi rand = ipd_nextrand(ins); 290*fe77cc04SRobert Mustacchi mutex_exit(&ins->ipdn_lock); 291*fe77cc04SRobert Mustacchi 292*fe77cc04SRobert Mustacchi /* 293*fe77cc04SRobert Mustacchi * This probably cannot happen, but we'll do an extra guard just in 294*fe77cc04SRobert Mustacchi * case. 295*fe77cc04SRobert Mustacchi */ 296*fe77cc04SRobert Mustacchi if (status & IPDN_STATUS_CONDEMNED) 297*fe77cc04SRobert Mustacchi return (0); 298*fe77cc04SRobert Mustacchi 299*fe77cc04SRobert Mustacchi if (drop != 0 && rand % 100 < drop) { 300*fe77cc04SRobert Mustacchi freemsg(*pkt->hpe_mp); 301*fe77cc04SRobert Mustacchi *pkt->hpe_mp = NULL; 302*fe77cc04SRobert Mustacchi pkt->hpe_mb = NULL; 303*fe77cc04SRobert Mustacchi pkt->hpe_hdr = NULL; 304*fe77cc04SRobert Mustacchi ipd_ksbump(&ins->ipdn_ksdata.ink_ndrops); 305*fe77cc04SRobert Mustacchi 306*fe77cc04SRobert Mustacchi return (1); 307*fe77cc04SRobert Mustacchi } 308*fe77cc04SRobert Mustacchi 309*fe77cc04SRobert Mustacchi if (dwait != 0) { 310*fe77cc04SRobert Mustacchi if (dwait < TICK_TO_USEC(1)) 311*fe77cc04SRobert Mustacchi drv_usecwait(dwait); 312*fe77cc04SRobert Mustacchi else 313*fe77cc04SRobert Mustacchi delay(drv_usectohz(dwait)); 314*fe77cc04SRobert Mustacchi ipd_ksbump(&ins->ipdn_ksdata.ink_ndelays); 315*fe77cc04SRobert Mustacchi } 316*fe77cc04SRobert Mustacchi 317*fe77cc04SRobert Mustacchi if (corrupt != 0 && rand % 100 < corrupt) { 318*fe77cc04SRobert Mustacchi /* 319*fe77cc04SRobert Mustacchi * Since we're corrupting the mblk, just corrupt everything in 320*fe77cc04SRobert Mustacchi * the chain. While we could corrupt the entire packet, that's a 321*fe77cc04SRobert Mustacchi * little strong. Instead we're going to just change one of the 322*fe77cc04SRobert Mustacchi * bytes in each mblock. 323*fe77cc04SRobert Mustacchi */ 324*fe77cc04SRobert Mustacchi mbp = *pkt->hpe_mp; 325*fe77cc04SRobert Mustacchi while (mbp != NULL) { 326*fe77cc04SRobert Mustacchi if (mbp->b_wptr == mbp->b_rptr) 327*fe77cc04SRobert Mustacchi continue; 328*fe77cc04SRobert Mustacchi 329*fe77cc04SRobert Mustacchi /* 330*fe77cc04SRobert Mustacchi * While pfhooks probably won't send us anything else, 331*fe77cc04SRobert Mustacchi * let's just be extra careful. The stack probably isn't 332*fe77cc04SRobert Mustacchi * as resiliant to corruption of control messages. 333*fe77cc04SRobert Mustacchi */ 334*fe77cc04SRobert Mustacchi if (DB_TYPE(mbp) != M_DATA) 335*fe77cc04SRobert Mustacchi continue; 336*fe77cc04SRobert Mustacchi 337*fe77cc04SRobert Mustacchi off = rand % ((uintptr_t)mbp->b_wptr - 338*fe77cc04SRobert Mustacchi (uintptr_t)mbp->b_rptr); 339*fe77cc04SRobert Mustacchi crp = mbp->b_rptr + off; 340*fe77cc04SRobert Mustacchi off = rand % 8; 341*fe77cc04SRobert Mustacchi *crp = *crp ^ (1 << off); 342*fe77cc04SRobert Mustacchi 343*fe77cc04SRobert Mustacchi mbp = mbp->b_cont; 344*fe77cc04SRobert Mustacchi } 345*fe77cc04SRobert Mustacchi ipd_ksbump(&ins->ipdn_ksdata.ink_ncorrupts); 346*fe77cc04SRobert Mustacchi } 347*fe77cc04SRobert Mustacchi 348*fe77cc04SRobert Mustacchi return (0); 349*fe77cc04SRobert Mustacchi } 350*fe77cc04SRobert Mustacchi 351*fe77cc04SRobert Mustacchi /* 352*fe77cc04SRobert Mustacchi * Sets up and registers all the proper hooks needed for the netstack to capture 353*fe77cc04SRobert Mustacchi * packets. Callers are assumed to already be holding the ipd_netstack_t's lock. 354*fe77cc04SRobert Mustacchi * If there is a failure in setting something up, it is the responsibility of 355*fe77cc04SRobert Mustacchi * this function to clean it up. Once this function has been called, it should 356*fe77cc04SRobert Mustacchi * not be called until a corresponding call to tear down the hooks has been 357*fe77cc04SRobert Mustacchi * done. 358*fe77cc04SRobert Mustacchi */ 359*fe77cc04SRobert Mustacchi static int 360*fe77cc04SRobert Mustacchi ipd_setup_hooks(ipd_netstack_t *ins) 361*fe77cc04SRobert Mustacchi { 362*fe77cc04SRobert Mustacchi ASSERT(MUTEX_HELD(&ins->ipdn_lock)); 363*fe77cc04SRobert Mustacchi ins->ipdn_v4hdl = net_protocol_lookup(ins->ipdn_netid, NHF_INET); 364*fe77cc04SRobert Mustacchi if (ins->ipdn_v4hdl == NULL) 365*fe77cc04SRobert Mustacchi goto cleanup; 366*fe77cc04SRobert Mustacchi 367*fe77cc04SRobert Mustacchi ins->ipdn_v6hdl = net_protocol_lookup(ins->ipdn_netid, NHF_INET6); 368*fe77cc04SRobert Mustacchi if (ins->ipdn_v6hdl == NULL) 369*fe77cc04SRobert Mustacchi goto cleanup; 370*fe77cc04SRobert Mustacchi 371*fe77cc04SRobert Mustacchi ins->ipdn_v4in = hook_alloc(HOOK_VERSION); 372*fe77cc04SRobert Mustacchi if (ins->ipdn_v4in == NULL) 373*fe77cc04SRobert Mustacchi goto cleanup; 374*fe77cc04SRobert Mustacchi 375*fe77cc04SRobert Mustacchi ins->ipdn_v4in->h_flags = 0; 376*fe77cc04SRobert Mustacchi ins->ipdn_v4in->h_hint = HH_NONE; 377*fe77cc04SRobert Mustacchi ins->ipdn_v4in->h_hintvalue = 0; 378*fe77cc04SRobert Mustacchi ins->ipdn_v4in->h_func = ipd_hook; 379*fe77cc04SRobert Mustacchi ins->ipdn_v4in->h_arg = ins; 380*fe77cc04SRobert Mustacchi ins->ipdn_v4in->h_name = "ipd IPv4 in"; 381*fe77cc04SRobert Mustacchi 382*fe77cc04SRobert Mustacchi if (net_hook_register(ins->ipdn_v4hdl, NH_PHYSICAL_IN, 383*fe77cc04SRobert Mustacchi ins->ipdn_v4in) != 0) 384*fe77cc04SRobert Mustacchi goto cleanup; 385*fe77cc04SRobert Mustacchi ins->ipdn_hooked |= IPDN_HOOK_V4IN; 386*fe77cc04SRobert Mustacchi 387*fe77cc04SRobert Mustacchi ins->ipdn_v4out = hook_alloc(HOOK_VERSION); 388*fe77cc04SRobert Mustacchi if (ins->ipdn_v4out == NULL) 389*fe77cc04SRobert Mustacchi goto cleanup; 390*fe77cc04SRobert Mustacchi ins->ipdn_v4out->h_flags = 0; 391*fe77cc04SRobert Mustacchi ins->ipdn_v4out->h_hint = HH_NONE; 392*fe77cc04SRobert Mustacchi ins->ipdn_v4out->h_hintvalue = 0; 393*fe77cc04SRobert Mustacchi ins->ipdn_v4out->h_func = ipd_hook; 394*fe77cc04SRobert Mustacchi ins->ipdn_v4out->h_arg = ins; 395*fe77cc04SRobert Mustacchi ins->ipdn_v4out->h_name = "ipd IPv4 out"; 396*fe77cc04SRobert Mustacchi 397*fe77cc04SRobert Mustacchi if (net_hook_register(ins->ipdn_v4hdl, NH_PHYSICAL_OUT, 398*fe77cc04SRobert Mustacchi ins->ipdn_v4out) != 0) 399*fe77cc04SRobert Mustacchi goto cleanup; 400*fe77cc04SRobert Mustacchi ins->ipdn_hooked |= IPDN_HOOK_V4OUT; 401*fe77cc04SRobert Mustacchi 402*fe77cc04SRobert Mustacchi ins->ipdn_v6in = hook_alloc(HOOK_VERSION); 403*fe77cc04SRobert Mustacchi if (ins->ipdn_v6in == NULL) 404*fe77cc04SRobert Mustacchi goto cleanup; 405*fe77cc04SRobert Mustacchi ins->ipdn_v6in->h_flags = 0; 406*fe77cc04SRobert Mustacchi ins->ipdn_v6in->h_hint = HH_NONE; 407*fe77cc04SRobert Mustacchi ins->ipdn_v6in->h_hintvalue = 0; 408*fe77cc04SRobert Mustacchi ins->ipdn_v6in->h_func = ipd_hook; 409*fe77cc04SRobert Mustacchi ins->ipdn_v6in->h_arg = ins; 410*fe77cc04SRobert Mustacchi ins->ipdn_v6in->h_name = "ipd IPv6 in"; 411*fe77cc04SRobert Mustacchi 412*fe77cc04SRobert Mustacchi if (net_hook_register(ins->ipdn_v6hdl, NH_PHYSICAL_IN, 413*fe77cc04SRobert Mustacchi ins->ipdn_v6in) != 0) 414*fe77cc04SRobert Mustacchi goto cleanup; 415*fe77cc04SRobert Mustacchi ins->ipdn_hooked |= IPDN_HOOK_V6IN; 416*fe77cc04SRobert Mustacchi 417*fe77cc04SRobert Mustacchi ins->ipdn_v6out = hook_alloc(HOOK_VERSION); 418*fe77cc04SRobert Mustacchi if (ins->ipdn_v6out == NULL) 419*fe77cc04SRobert Mustacchi goto cleanup; 420*fe77cc04SRobert Mustacchi ins->ipdn_v6out->h_flags = 0; 421*fe77cc04SRobert Mustacchi ins->ipdn_v6out->h_hint = HH_NONE; 422*fe77cc04SRobert Mustacchi ins->ipdn_v6out->h_hintvalue = 0; 423*fe77cc04SRobert Mustacchi ins->ipdn_v6out->h_func = ipd_hook; 424*fe77cc04SRobert Mustacchi ins->ipdn_v6out->h_arg = ins; 425*fe77cc04SRobert Mustacchi ins->ipdn_v6out->h_name = "ipd IPv6 out"; 426*fe77cc04SRobert Mustacchi 427*fe77cc04SRobert Mustacchi if (net_hook_register(ins->ipdn_v6hdl, NH_PHYSICAL_OUT, 428*fe77cc04SRobert Mustacchi ins->ipdn_v6out) != 0) 429*fe77cc04SRobert Mustacchi goto cleanup; 430*fe77cc04SRobert Mustacchi ins->ipdn_hooked |= IPDN_HOOK_V6OUT; 431*fe77cc04SRobert Mustacchi mutex_enter(&ipd_nactive_lock); 432*fe77cc04SRobert Mustacchi ipd_nactive++; 433*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nactive_lock); 434*fe77cc04SRobert Mustacchi 435*fe77cc04SRobert Mustacchi return (0); 436*fe77cc04SRobert Mustacchi 437*fe77cc04SRobert Mustacchi cleanup: 438*fe77cc04SRobert Mustacchi if (ins->ipdn_hooked & IPDN_HOOK_V6OUT) 439*fe77cc04SRobert Mustacchi (void) net_hook_unregister(ins->ipdn_v6hdl, NH_PHYSICAL_OUT, 440*fe77cc04SRobert Mustacchi ins->ipdn_v6out); 441*fe77cc04SRobert Mustacchi 442*fe77cc04SRobert Mustacchi if (ins->ipdn_hooked & IPDN_HOOK_V6IN) 443*fe77cc04SRobert Mustacchi (void) net_hook_unregister(ins->ipdn_v6hdl, NH_PHYSICAL_IN, 444*fe77cc04SRobert Mustacchi ins->ipdn_v6in); 445*fe77cc04SRobert Mustacchi 446*fe77cc04SRobert Mustacchi if (ins->ipdn_hooked & IPDN_HOOK_V4OUT) 447*fe77cc04SRobert Mustacchi (void) net_hook_unregister(ins->ipdn_v4hdl, NH_PHYSICAL_OUT, 448*fe77cc04SRobert Mustacchi ins->ipdn_v4out); 449*fe77cc04SRobert Mustacchi 450*fe77cc04SRobert Mustacchi if (ins->ipdn_hooked & IPDN_HOOK_V4IN) 451*fe77cc04SRobert Mustacchi (void) net_hook_unregister(ins->ipdn_v4hdl, NH_PHYSICAL_IN, 452*fe77cc04SRobert Mustacchi ins->ipdn_v4in); 453*fe77cc04SRobert Mustacchi 454*fe77cc04SRobert Mustacchi ins->ipdn_hooked = IPDN_HOOK_NONE; 455*fe77cc04SRobert Mustacchi 456*fe77cc04SRobert Mustacchi if (ins->ipdn_v6out != NULL) 457*fe77cc04SRobert Mustacchi hook_free(ins->ipdn_v6out); 458*fe77cc04SRobert Mustacchi 459*fe77cc04SRobert Mustacchi if (ins->ipdn_v6in != NULL) 460*fe77cc04SRobert Mustacchi hook_free(ins->ipdn_v6in); 461*fe77cc04SRobert Mustacchi 462*fe77cc04SRobert Mustacchi if (ins->ipdn_v4out != NULL) 463*fe77cc04SRobert Mustacchi hook_free(ins->ipdn_v4out); 464*fe77cc04SRobert Mustacchi 465*fe77cc04SRobert Mustacchi if (ins->ipdn_v4in != NULL) 466*fe77cc04SRobert Mustacchi hook_free(ins->ipdn_v4in); 467*fe77cc04SRobert Mustacchi 468*fe77cc04SRobert Mustacchi if (ins->ipdn_v6hdl != NULL) 469*fe77cc04SRobert Mustacchi (void) net_protocol_release(ins->ipdn_v6hdl); 470*fe77cc04SRobert Mustacchi 471*fe77cc04SRobert Mustacchi if (ins->ipdn_v4hdl != NULL) 472*fe77cc04SRobert Mustacchi (void) net_protocol_release(ins->ipdn_v4hdl); 473*fe77cc04SRobert Mustacchi 474*fe77cc04SRobert Mustacchi return (1); 475*fe77cc04SRobert Mustacchi } 476*fe77cc04SRobert Mustacchi 477*fe77cc04SRobert Mustacchi static void 478*fe77cc04SRobert Mustacchi ipd_teardown_hooks(ipd_netstack_t *ins) 479*fe77cc04SRobert Mustacchi { 480*fe77cc04SRobert Mustacchi ASSERT(ins->ipdn_hooked == IPDN_HOOK_ALL); 481*fe77cc04SRobert Mustacchi VERIFY(net_hook_unregister(ins->ipdn_v6hdl, NH_PHYSICAL_OUT, 482*fe77cc04SRobert Mustacchi ins->ipdn_v6out) == 0); 483*fe77cc04SRobert Mustacchi VERIFY(net_hook_unregister(ins->ipdn_v6hdl, NH_PHYSICAL_IN, 484*fe77cc04SRobert Mustacchi ins->ipdn_v6in) == 0); 485*fe77cc04SRobert Mustacchi VERIFY(net_hook_unregister(ins->ipdn_v4hdl, NH_PHYSICAL_OUT, 486*fe77cc04SRobert Mustacchi ins->ipdn_v4out) == 0); 487*fe77cc04SRobert Mustacchi VERIFY(net_hook_unregister(ins->ipdn_v4hdl, NH_PHYSICAL_IN, 488*fe77cc04SRobert Mustacchi ins->ipdn_v4in) == 0); 489*fe77cc04SRobert Mustacchi 490*fe77cc04SRobert Mustacchi ins->ipdn_hooked = IPDN_HOOK_NONE; 491*fe77cc04SRobert Mustacchi 492*fe77cc04SRobert Mustacchi hook_free(ins->ipdn_v6out); 493*fe77cc04SRobert Mustacchi hook_free(ins->ipdn_v6in); 494*fe77cc04SRobert Mustacchi hook_free(ins->ipdn_v4out); 495*fe77cc04SRobert Mustacchi hook_free(ins->ipdn_v4in); 496*fe77cc04SRobert Mustacchi 497*fe77cc04SRobert Mustacchi VERIFY(net_protocol_release(ins->ipdn_v6hdl) == 0); 498*fe77cc04SRobert Mustacchi VERIFY(net_protocol_release(ins->ipdn_v4hdl) == 0); 499*fe77cc04SRobert Mustacchi 500*fe77cc04SRobert Mustacchi mutex_enter(&ipd_nactive_lock); 501*fe77cc04SRobert Mustacchi ipd_nactive--; 502*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nactive_lock); 503*fe77cc04SRobert Mustacchi } 504*fe77cc04SRobert Mustacchi 505*fe77cc04SRobert Mustacchi static int 506*fe77cc04SRobert Mustacchi ipd_check_hooks(ipd_netstack_t *ins, int type, boolean_t enable) 507*fe77cc04SRobert Mustacchi { 508*fe77cc04SRobert Mustacchi int olden, rval; 509*fe77cc04SRobert Mustacchi olden = ins->ipdn_enabled; 510*fe77cc04SRobert Mustacchi 511*fe77cc04SRobert Mustacchi if (enable) 512*fe77cc04SRobert Mustacchi ins->ipdn_enabled |= type; 513*fe77cc04SRobert Mustacchi else 514*fe77cc04SRobert Mustacchi ins->ipdn_enabled &= ~type; 515*fe77cc04SRobert Mustacchi 516*fe77cc04SRobert Mustacchi /* 517*fe77cc04SRobert Mustacchi * If hooks were previously enabled. 518*fe77cc04SRobert Mustacchi */ 519*fe77cc04SRobert Mustacchi if (olden == 0 && ins->ipdn_enabled != 0) { 520*fe77cc04SRobert Mustacchi rval = ipd_setup_hooks(ins); 521*fe77cc04SRobert Mustacchi if (rval != 0) { 522*fe77cc04SRobert Mustacchi ins->ipdn_enabled &= ~type; 523*fe77cc04SRobert Mustacchi ASSERT(ins->ipdn_enabled == 0); 524*fe77cc04SRobert Mustacchi return (rval); 525*fe77cc04SRobert Mustacchi } 526*fe77cc04SRobert Mustacchi 527*fe77cc04SRobert Mustacchi return (0); 528*fe77cc04SRobert Mustacchi } 529*fe77cc04SRobert Mustacchi 530*fe77cc04SRobert Mustacchi if (olden != 0 && ins->ipdn_enabled == 0) { 531*fe77cc04SRobert Mustacchi ASSERT(olden != 0); 532*fe77cc04SRobert Mustacchi 533*fe77cc04SRobert Mustacchi /* 534*fe77cc04SRobert Mustacchi * We have to drop the lock here, lest we cause a deadlock. 535*fe77cc04SRobert Mustacchi * Unfortunately, there may be hooks that are running and are 536*fe77cc04SRobert Mustacchi * actively in flight and we have to call the unregister 537*fe77cc04SRobert Mustacchi * function. Due to the hooks framework, if there is an inflight 538*fe77cc04SRobert Mustacchi * hook (most likely right now), and we are holding the 539*fe77cc04SRobert Mustacchi * netstack's lock, those hooks will never return. This is 540*fe77cc04SRobert Mustacchi * unfortunate. 541*fe77cc04SRobert Mustacchi * 542*fe77cc04SRobert Mustacchi * Because we only come into this path holding the list lock, we 543*fe77cc04SRobert Mustacchi * know that only way that someone else can come in and get to 544*fe77cc04SRobert Mustacchi * this structure is via the hook callbacks which are going to 545*fe77cc04SRobert Mustacchi * only be doing reads. They'll also see that everything has 546*fe77cc04SRobert Mustacchi * been disabled and return. So while this is unfortunate, it 547*fe77cc04SRobert Mustacchi * should be relatively safe. 548*fe77cc04SRobert Mustacchi */ 549*fe77cc04SRobert Mustacchi mutex_exit(&ins->ipdn_lock); 550*fe77cc04SRobert Mustacchi ipd_teardown_hooks(ins); 551*fe77cc04SRobert Mustacchi mutex_enter(&ins->ipdn_lock); 552*fe77cc04SRobert Mustacchi return (0); 553*fe77cc04SRobert Mustacchi } 554*fe77cc04SRobert Mustacchi 555*fe77cc04SRobert Mustacchi /* 556*fe77cc04SRobert Mustacchi * Othwerise, nothing should have changed here. 557*fe77cc04SRobert Mustacchi */ 558*fe77cc04SRobert Mustacchi ASSERT((olden == 0) == (ins->ipdn_enabled == 0)); 559*fe77cc04SRobert Mustacchi return (0); 560*fe77cc04SRobert Mustacchi } 561*fe77cc04SRobert Mustacchi 562*fe77cc04SRobert Mustacchi static int 563*fe77cc04SRobert Mustacchi ipd_toggle_corrupt(ipd_netstack_t *ins, int percent) 564*fe77cc04SRobert Mustacchi { 565*fe77cc04SRobert Mustacchi int rval; 566*fe77cc04SRobert Mustacchi 567*fe77cc04SRobert Mustacchi ASSERT(MUTEX_HELD(&ins->ipdn_lock)); 568*fe77cc04SRobert Mustacchi 569*fe77cc04SRobert Mustacchi if (percent < 0 || percent > 100) 570*fe77cc04SRobert Mustacchi return (ERANGE); 571*fe77cc04SRobert Mustacchi 572*fe77cc04SRobert Mustacchi /* 573*fe77cc04SRobert Mustacchi * If we've been asked to set the value to a value that we already have, 574*fe77cc04SRobert Mustacchi * great, then we're done. 575*fe77cc04SRobert Mustacchi */ 576*fe77cc04SRobert Mustacchi if (percent == ins->ipdn_corrupt) 577*fe77cc04SRobert Mustacchi return (0); 578*fe77cc04SRobert Mustacchi 579*fe77cc04SRobert Mustacchi ins->ipdn_corrupt = percent; 580*fe77cc04SRobert Mustacchi rval = ipd_check_hooks(ins, IPD_CORRUPT, percent != 0); 581*fe77cc04SRobert Mustacchi 582*fe77cc04SRobert Mustacchi /* 583*fe77cc04SRobert Mustacchi * If ipd_check_hooks_failed, that must mean that we failed to set up 584*fe77cc04SRobert Mustacchi * the hooks, so we are going to effectively zero out and fail the 585*fe77cc04SRobert Mustacchi * request to enable corruption. 586*fe77cc04SRobert Mustacchi */ 587*fe77cc04SRobert Mustacchi if (rval != 0) 588*fe77cc04SRobert Mustacchi ins->ipdn_corrupt = 0; 589*fe77cc04SRobert Mustacchi 590*fe77cc04SRobert Mustacchi return (rval); 591*fe77cc04SRobert Mustacchi } 592*fe77cc04SRobert Mustacchi 593*fe77cc04SRobert Mustacchi static int 594*fe77cc04SRobert Mustacchi ipd_toggle_delay(ipd_netstack_t *ins, uint32_t delay) 595*fe77cc04SRobert Mustacchi { 596*fe77cc04SRobert Mustacchi int rval; 597*fe77cc04SRobert Mustacchi 598*fe77cc04SRobert Mustacchi ASSERT(MUTEX_HELD(&ins->ipdn_lock)); 599*fe77cc04SRobert Mustacchi 600*fe77cc04SRobert Mustacchi if (delay > ipd_max_delay) 601*fe77cc04SRobert Mustacchi return (ERANGE); 602*fe77cc04SRobert Mustacchi 603*fe77cc04SRobert Mustacchi /* 604*fe77cc04SRobert Mustacchi * If we've been asked to set the value to a value that we already have, 605*fe77cc04SRobert Mustacchi * great, then we're done. 606*fe77cc04SRobert Mustacchi */ 607*fe77cc04SRobert Mustacchi if (delay == ins->ipdn_delay) 608*fe77cc04SRobert Mustacchi return (0); 609*fe77cc04SRobert Mustacchi 610*fe77cc04SRobert Mustacchi ins->ipdn_delay = delay; 611*fe77cc04SRobert Mustacchi rval = ipd_check_hooks(ins, IPD_DELAY, delay != 0); 612*fe77cc04SRobert Mustacchi 613*fe77cc04SRobert Mustacchi /* 614*fe77cc04SRobert Mustacchi * If ipd_check_hooks_failed, that must mean that we failed to set up 615*fe77cc04SRobert Mustacchi * the hooks, so we are going to effectively zero out and fail the 616*fe77cc04SRobert Mustacchi * request to enable corruption. 617*fe77cc04SRobert Mustacchi */ 618*fe77cc04SRobert Mustacchi if (rval != 0) 619*fe77cc04SRobert Mustacchi ins->ipdn_delay = 0; 620*fe77cc04SRobert Mustacchi 621*fe77cc04SRobert Mustacchi return (rval); 622*fe77cc04SRobert Mustacchi } 623*fe77cc04SRobert Mustacchi static int 624*fe77cc04SRobert Mustacchi ipd_toggle_drop(ipd_netstack_t *ins, int percent) 625*fe77cc04SRobert Mustacchi { 626*fe77cc04SRobert Mustacchi int rval; 627*fe77cc04SRobert Mustacchi 628*fe77cc04SRobert Mustacchi ASSERT(MUTEX_HELD(&ins->ipdn_lock)); 629*fe77cc04SRobert Mustacchi 630*fe77cc04SRobert Mustacchi if (percent < 0 || percent > 100) 631*fe77cc04SRobert Mustacchi return (ERANGE); 632*fe77cc04SRobert Mustacchi 633*fe77cc04SRobert Mustacchi /* 634*fe77cc04SRobert Mustacchi * If we've been asked to set the value to a value that we already have, 635*fe77cc04SRobert Mustacchi * great, then we're done. 636*fe77cc04SRobert Mustacchi */ 637*fe77cc04SRobert Mustacchi if (percent == ins->ipdn_drop) 638*fe77cc04SRobert Mustacchi return (0); 639*fe77cc04SRobert Mustacchi 640*fe77cc04SRobert Mustacchi ins->ipdn_drop = percent; 641*fe77cc04SRobert Mustacchi rval = ipd_check_hooks(ins, IPD_DROP, percent != 0); 642*fe77cc04SRobert Mustacchi 643*fe77cc04SRobert Mustacchi /* 644*fe77cc04SRobert Mustacchi * If ipd_check_hooks_failed, that must mean that we failed to set up 645*fe77cc04SRobert Mustacchi * the hooks, so we are going to effectively zero out and fail the 646*fe77cc04SRobert Mustacchi * request to enable corruption. 647*fe77cc04SRobert Mustacchi */ 648*fe77cc04SRobert Mustacchi if (rval != 0) 649*fe77cc04SRobert Mustacchi ins->ipdn_drop = 0; 650*fe77cc04SRobert Mustacchi 651*fe77cc04SRobert Mustacchi return (rval); 652*fe77cc04SRobert Mustacchi } 653*fe77cc04SRobert Mustacchi 654*fe77cc04SRobert Mustacchi static int 655*fe77cc04SRobert Mustacchi ipd_ioctl_perturb(ipd_ioc_perturb_t *ipi, cred_t *cr, intptr_t cmd) 656*fe77cc04SRobert Mustacchi { 657*fe77cc04SRobert Mustacchi zoneid_t zid; 658*fe77cc04SRobert Mustacchi ipd_netstack_t *ins; 659*fe77cc04SRobert Mustacchi int rval = 0; 660*fe77cc04SRobert Mustacchi 661*fe77cc04SRobert Mustacchi /* 662*fe77cc04SRobert Mustacchi * If the zone that we're coming from is not the GZ, then we ignore it 663*fe77cc04SRobert Mustacchi * completely and then instead just set the zoneid to be that of the 664*fe77cc04SRobert Mustacchi * caller. If the zoneid is that of the GZ, then we don't touch this 665*fe77cc04SRobert Mustacchi * value. 666*fe77cc04SRobert Mustacchi */ 667*fe77cc04SRobert Mustacchi zid = crgetzoneid(cr); 668*fe77cc04SRobert Mustacchi if (zid != GLOBAL_ZONEID) 669*fe77cc04SRobert Mustacchi ipi->ipip_zoneid = zid; 670*fe77cc04SRobert Mustacchi 671*fe77cc04SRobert Mustacchi if (zoneid_to_netstackid(ipi->ipip_zoneid) == GLOBAL_NETSTACKID && 672*fe77cc04SRobert Mustacchi zid != GLOBAL_ZONEID) 673*fe77cc04SRobert Mustacchi return (EPERM); 674*fe77cc04SRobert Mustacchi 675*fe77cc04SRobert Mustacchi /* 676*fe77cc04SRobert Mustacchi * We need to hold the ipd_nsl_lock throughout the entire operation, 677*fe77cc04SRobert Mustacchi * otherwise someone else could come in and remove us from the list and 678*fe77cc04SRobert Mustacchi * free us, e.g. the netstack destroy handler. By holding the lock, we 679*fe77cc04SRobert Mustacchi * stop it from being able to do anything wrong. 680*fe77cc04SRobert Mustacchi */ 681*fe77cc04SRobert Mustacchi mutex_enter(&ipd_nsl_lock); 682*fe77cc04SRobert Mustacchi for (ins = list_head(&ipd_nsl); ins != NULL; 683*fe77cc04SRobert Mustacchi ins = list_next(&ipd_nsl, ins)) { 684*fe77cc04SRobert Mustacchi if (ins->ipdn_zoneid == ipi->ipip_zoneid) 685*fe77cc04SRobert Mustacchi break; 686*fe77cc04SRobert Mustacchi } 687*fe77cc04SRobert Mustacchi 688*fe77cc04SRobert Mustacchi if (ins == NULL) { 689*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nsl_lock); 690*fe77cc04SRobert Mustacchi return (EINVAL); 691*fe77cc04SRobert Mustacchi } 692*fe77cc04SRobert Mustacchi 693*fe77cc04SRobert Mustacchi mutex_enter(&ins->ipdn_lock); 694*fe77cc04SRobert Mustacchi 695*fe77cc04SRobert Mustacchi if (ins->ipdn_status & IPDN_STATUS_CONDEMNED) { 696*fe77cc04SRobert Mustacchi rval = ESHUTDOWN; 697*fe77cc04SRobert Mustacchi goto cleanup; 698*fe77cc04SRobert Mustacchi } 699*fe77cc04SRobert Mustacchi 700*fe77cc04SRobert Mustacchi switch (cmd) { 701*fe77cc04SRobert Mustacchi case IPDIOC_CORRUPT: 702*fe77cc04SRobert Mustacchi rval = ipd_toggle_corrupt(ins, ipi->ipip_arg); 703*fe77cc04SRobert Mustacchi break; 704*fe77cc04SRobert Mustacchi case IPDIOC_DELAY: 705*fe77cc04SRobert Mustacchi rval = ipd_toggle_delay(ins, ipi->ipip_arg); 706*fe77cc04SRobert Mustacchi break; 707*fe77cc04SRobert Mustacchi case IPDIOC_DROP: 708*fe77cc04SRobert Mustacchi rval = ipd_toggle_drop(ins, ipi->ipip_arg); 709*fe77cc04SRobert Mustacchi break; 710*fe77cc04SRobert Mustacchi } 711*fe77cc04SRobert Mustacchi 712*fe77cc04SRobert Mustacchi cleanup: 713*fe77cc04SRobert Mustacchi mutex_exit(&ins->ipdn_lock); 714*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nsl_lock); 715*fe77cc04SRobert Mustacchi return (rval); 716*fe77cc04SRobert Mustacchi } 717*fe77cc04SRobert Mustacchi 718*fe77cc04SRobert Mustacchi static int 719*fe77cc04SRobert Mustacchi ipd_ioctl_remove(ipd_ioc_perturb_t *ipi, cred_t *cr) 720*fe77cc04SRobert Mustacchi { 721*fe77cc04SRobert Mustacchi zoneid_t zid; 722*fe77cc04SRobert Mustacchi ipd_netstack_t *ins; 723*fe77cc04SRobert Mustacchi int rval = 0; 724*fe77cc04SRobert Mustacchi 725*fe77cc04SRobert Mustacchi /* 726*fe77cc04SRobert Mustacchi * See ipd_ioctl_perturb for the rational here. 727*fe77cc04SRobert Mustacchi */ 728*fe77cc04SRobert Mustacchi zid = crgetzoneid(cr); 729*fe77cc04SRobert Mustacchi if (zid != GLOBAL_ZONEID) 730*fe77cc04SRobert Mustacchi ipi->ipip_zoneid = zid; 731*fe77cc04SRobert Mustacchi 732*fe77cc04SRobert Mustacchi if (zoneid_to_netstackid(ipi->ipip_zoneid) == GLOBAL_NETSTACKID && 733*fe77cc04SRobert Mustacchi zid != GLOBAL_ZONEID) 734*fe77cc04SRobert Mustacchi return (EPERM); 735*fe77cc04SRobert Mustacchi 736*fe77cc04SRobert Mustacchi mutex_enter(&ipd_nsl_lock); 737*fe77cc04SRobert Mustacchi for (ins = list_head(&ipd_nsl); ins != NULL; 738*fe77cc04SRobert Mustacchi ins = list_next(&ipd_nsl, ins)) { 739*fe77cc04SRobert Mustacchi if (ins->ipdn_zoneid == ipi->ipip_zoneid) 740*fe77cc04SRobert Mustacchi break; 741*fe77cc04SRobert Mustacchi } 742*fe77cc04SRobert Mustacchi 743*fe77cc04SRobert Mustacchi if (ins == NULL) { 744*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nsl_lock); 745*fe77cc04SRobert Mustacchi return (EINVAL); 746*fe77cc04SRobert Mustacchi } 747*fe77cc04SRobert Mustacchi 748*fe77cc04SRobert Mustacchi mutex_enter(&ins->ipdn_lock); 749*fe77cc04SRobert Mustacchi 750*fe77cc04SRobert Mustacchi /* 751*fe77cc04SRobert Mustacchi * If this is condemned, that means it's very shortly going to be torn 752*fe77cc04SRobert Mustacchi * down. In that case, there's no reason to actually do anything here, 753*fe77cc04SRobert Mustacchi * as it will all be done rather shortly in the destroy function. 754*fe77cc04SRobert Mustacchi * Furthermore, because condemned corresponds with it having hit 755*fe77cc04SRobert Mustacchi * shutdown, we know that no more packets can be received by this 756*fe77cc04SRobert Mustacchi * netstack. All this translates to a no-op. 757*fe77cc04SRobert Mustacchi */ 758*fe77cc04SRobert Mustacchi if (ins->ipdn_status & IPDN_STATUS_CONDEMNED) { 759*fe77cc04SRobert Mustacchi rval = 0; 760*fe77cc04SRobert Mustacchi goto cleanup; 761*fe77cc04SRobert Mustacchi } 762*fe77cc04SRobert Mustacchi 763*fe77cc04SRobert Mustacchi rval = EINVAL; 764*fe77cc04SRobert Mustacchi /* 765*fe77cc04SRobert Mustacchi * Go through and disable the requested pieces. We can safely ignore the 766*fe77cc04SRobert Mustacchi * return value of ipd_check_hooks because the removal case should never 767*fe77cc04SRobert Mustacchi * fail, we verify that in the hook teardown case. 768*fe77cc04SRobert Mustacchi */ 769*fe77cc04SRobert Mustacchi if (ipi->ipip_arg & IPD_CORRUPT) { 770*fe77cc04SRobert Mustacchi ins->ipdn_corrupt = 0; 771*fe77cc04SRobert Mustacchi (void) ipd_check_hooks(ins, IPD_CORRUPT, B_FALSE); 772*fe77cc04SRobert Mustacchi rval = 0; 773*fe77cc04SRobert Mustacchi } 774*fe77cc04SRobert Mustacchi 775*fe77cc04SRobert Mustacchi if (ipi->ipip_arg & IPD_DELAY) { 776*fe77cc04SRobert Mustacchi ins->ipdn_delay = 0; 777*fe77cc04SRobert Mustacchi (void) ipd_check_hooks(ins, IPD_DELAY, B_FALSE); 778*fe77cc04SRobert Mustacchi rval = 0; 779*fe77cc04SRobert Mustacchi } 780*fe77cc04SRobert Mustacchi 781*fe77cc04SRobert Mustacchi if (ipi->ipip_arg & IPD_DROP) { 782*fe77cc04SRobert Mustacchi ins->ipdn_drop = 0; 783*fe77cc04SRobert Mustacchi (void) ipd_check_hooks(ins, IPD_DROP, B_FALSE); 784*fe77cc04SRobert Mustacchi rval = 0; 785*fe77cc04SRobert Mustacchi } 786*fe77cc04SRobert Mustacchi 787*fe77cc04SRobert Mustacchi cleanup: 788*fe77cc04SRobert Mustacchi mutex_exit(&ins->ipdn_lock); 789*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nsl_lock); 790*fe77cc04SRobert Mustacchi return (rval); 791*fe77cc04SRobert Mustacchi } 792*fe77cc04SRobert Mustacchi 793*fe77cc04SRobert Mustacchi /* 794*fe77cc04SRobert Mustacchi * When this function is called, the value of the ipil_nzones argument controls 795*fe77cc04SRobert Mustacchi * how this function works. When called with a value of zero, then we treat that 796*fe77cc04SRobert Mustacchi * as the caller asking us what's a reasonable number of entries for me to 797*fe77cc04SRobert Mustacchi * allocate memory for. If the zone is the global zone, then we tell them how 798*fe77cc04SRobert Mustacchi * many folks are currently active and add a fudge factor. Otherwise the answer 799*fe77cc04SRobert Mustacchi * is always one. 800*fe77cc04SRobert Mustacchi * 801*fe77cc04SRobert Mustacchi * In the non-zero case, we give them that number of zone ids. While this isn't 802*fe77cc04SRobert Mustacchi * quite ideal as it might mean that someone misses something, this generally 803*fe77cc04SRobert Mustacchi * won't be an issue, as it involves a rather tight race condition in the 804*fe77cc04SRobert Mustacchi * current ipdadm implementation. 805*fe77cc04SRobert Mustacchi */ 806*fe77cc04SRobert Mustacchi static int 807*fe77cc04SRobert Mustacchi ipd_ioctl_list(intptr_t arg, cred_t *cr) 808*fe77cc04SRobert Mustacchi { 809*fe77cc04SRobert Mustacchi zoneid_t zid; 810*fe77cc04SRobert Mustacchi ipd_ioc_info_t *configs; 811*fe77cc04SRobert Mustacchi ipd_netstack_t *ins; 812*fe77cc04SRobert Mustacchi uint_t azones, rzones, nzones, cur; 813*fe77cc04SRobert Mustacchi int rval = 0; 814*fe77cc04SRobert Mustacchi STRUCT_DECL(ipd_ioc_list, h); 815*fe77cc04SRobert Mustacchi 816*fe77cc04SRobert Mustacchi STRUCT_INIT(h, get_udatamodel()); 817*fe77cc04SRobert Mustacchi if (ddi_copyin((void *)arg, STRUCT_BUF(h), 818*fe77cc04SRobert Mustacchi STRUCT_SIZE(h), 0) != 0) 819*fe77cc04SRobert Mustacchi return (EFAULT); 820*fe77cc04SRobert Mustacchi 821*fe77cc04SRobert Mustacchi zid = crgetzoneid(cr); 822*fe77cc04SRobert Mustacchi 823*fe77cc04SRobert Mustacchi rzones = STRUCT_FGET(h, ipil_nzones); 824*fe77cc04SRobert Mustacchi if (rzones == 0) { 825*fe77cc04SRobert Mustacchi if (zid == GLOBAL_ZONEID) { 826*fe77cc04SRobert Mustacchi mutex_enter(&ipd_nactive_lock); 827*fe77cc04SRobert Mustacchi rzones = ipd_nactive + ipd_nactive_fudge; 828*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nactive_lock); 829*fe77cc04SRobert Mustacchi } else { 830*fe77cc04SRobert Mustacchi rzones = 1; 831*fe77cc04SRobert Mustacchi } 832*fe77cc04SRobert Mustacchi STRUCT_FSET(h, ipil_nzones, rzones); 833*fe77cc04SRobert Mustacchi if (ddi_copyout(STRUCT_BUF(h), (void *)arg, 834*fe77cc04SRobert Mustacchi STRUCT_SIZE(h), 0) != 0) 835*fe77cc04SRobert Mustacchi return (EFAULT); 836*fe77cc04SRobert Mustacchi 837*fe77cc04SRobert Mustacchi return (0); 838*fe77cc04SRobert Mustacchi } 839*fe77cc04SRobert Mustacchi 840*fe77cc04SRobert Mustacchi mutex_enter(&ipd_nsl_lock); 841*fe77cc04SRobert Mustacchi if (zid == GLOBAL_ZONEID) { 842*fe77cc04SRobert Mustacchi azones = ipd_nactive; 843*fe77cc04SRobert Mustacchi } else { 844*fe77cc04SRobert Mustacchi azones = 1; 845*fe77cc04SRobert Mustacchi } 846*fe77cc04SRobert Mustacchi 847*fe77cc04SRobert Mustacchi configs = kmem_alloc(sizeof (ipd_ioc_info_t) * azones, KM_SLEEP); 848*fe77cc04SRobert Mustacchi cur = 0; 849*fe77cc04SRobert Mustacchi for (ins = list_head(&ipd_nsl); ins != NULL; 850*fe77cc04SRobert Mustacchi ins = list_next(&ipd_nsl, ins)) { 851*fe77cc04SRobert Mustacchi if (ins->ipdn_enabled == 0) 852*fe77cc04SRobert Mustacchi continue; 853*fe77cc04SRobert Mustacchi 854*fe77cc04SRobert Mustacchi ASSERT(cur < azones); 855*fe77cc04SRobert Mustacchi 856*fe77cc04SRobert Mustacchi if (zid == GLOBAL_ZONEID || zid == ins->ipdn_zoneid) { 857*fe77cc04SRobert Mustacchi configs[cur].ipii_zoneid = ins->ipdn_zoneid; 858*fe77cc04SRobert Mustacchi 859*fe77cc04SRobert Mustacchi mutex_enter(&ins->ipdn_lock); 860*fe77cc04SRobert Mustacchi configs[cur].ipii_corrupt = ins->ipdn_corrupt; 861*fe77cc04SRobert Mustacchi configs[cur].ipii_delay = ins->ipdn_delay; 862*fe77cc04SRobert Mustacchi configs[cur].ipii_drop = ins->ipdn_drop; 863*fe77cc04SRobert Mustacchi mutex_exit(&ins->ipdn_lock); 864*fe77cc04SRobert Mustacchi 865*fe77cc04SRobert Mustacchi ++cur; 866*fe77cc04SRobert Mustacchi } 867*fe77cc04SRobert Mustacchi 868*fe77cc04SRobert Mustacchi if (zid != GLOBAL_ZONEID && zid == ins->ipdn_zoneid) 869*fe77cc04SRobert Mustacchi break; 870*fe77cc04SRobert Mustacchi } 871*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nsl_lock); 872*fe77cc04SRobert Mustacchi 873*fe77cc04SRobert Mustacchi ASSERT(zid != GLOBAL_ZONEID || cur == azones); 874*fe77cc04SRobert Mustacchi 875*fe77cc04SRobert Mustacchi if (cur == 0) 876*fe77cc04SRobert Mustacchi STRUCT_FSET(h, ipil_nzones, 0); 877*fe77cc04SRobert Mustacchi else 878*fe77cc04SRobert Mustacchi STRUCT_FSET(h, ipil_nzones, cur); 879*fe77cc04SRobert Mustacchi 880*fe77cc04SRobert Mustacchi nzones = MIN(cur, rzones); 881*fe77cc04SRobert Mustacchi if (nzones > 0) { 882*fe77cc04SRobert Mustacchi if (ddi_copyout(configs, STRUCT_FGETP(h, ipil_info), 883*fe77cc04SRobert Mustacchi nzones * sizeof (ipd_ioc_info_t), NULL) != 0) 884*fe77cc04SRobert Mustacchi rval = EFAULT; 885*fe77cc04SRobert Mustacchi } 886*fe77cc04SRobert Mustacchi 887*fe77cc04SRobert Mustacchi kmem_free(configs, sizeof (ipd_ioc_info_t) * azones); 888*fe77cc04SRobert Mustacchi if (ddi_copyout(STRUCT_BUF(h), (void *)arg, STRUCT_SIZE(h), 0) != 0) 889*fe77cc04SRobert Mustacchi return (EFAULT); 890*fe77cc04SRobert Mustacchi 891*fe77cc04SRobert Mustacchi return (rval); 892*fe77cc04SRobert Mustacchi } 893*fe77cc04SRobert Mustacchi 894*fe77cc04SRobert Mustacchi static void * 895*fe77cc04SRobert Mustacchi ipd_nin_create(const netid_t id) 896*fe77cc04SRobert Mustacchi { 897*fe77cc04SRobert Mustacchi ipd_netstack_t *ins; 898*fe77cc04SRobert Mustacchi ipd_nskstat_t *ink; 899*fe77cc04SRobert Mustacchi 900*fe77cc04SRobert Mustacchi ins = kmem_zalloc(sizeof (ipd_netstack_t), KM_SLEEP); 901*fe77cc04SRobert Mustacchi ins->ipdn_status = IPDN_STATUS_DISABLED; 902*fe77cc04SRobert Mustacchi ins->ipdn_netid = id; 903*fe77cc04SRobert Mustacchi ins->ipdn_zoneid = netstackid_to_zoneid(id); 904*fe77cc04SRobert Mustacchi ins->ipdn_rand = gethrtime(); 905*fe77cc04SRobert Mustacchi mutex_init(&ins->ipdn_lock, NULL, MUTEX_DRIVER, NULL); 906*fe77cc04SRobert Mustacchi 907*fe77cc04SRobert Mustacchi ins->ipdn_kstat = net_kstat_create(id, "ipd", ins->ipdn_zoneid, 908*fe77cc04SRobert Mustacchi "ipd", "net", KSTAT_TYPE_NAMED, 909*fe77cc04SRobert Mustacchi sizeof (ipd_nskstat_t) / sizeof (kstat_named_t), 910*fe77cc04SRobert Mustacchi KSTAT_FLAG_VIRTUAL); 911*fe77cc04SRobert Mustacchi 912*fe77cc04SRobert Mustacchi if (ins->ipdn_kstat != NULL) { 913*fe77cc04SRobert Mustacchi if (ins->ipdn_zoneid != GLOBAL_ZONEID) 914*fe77cc04SRobert Mustacchi kstat_zone_add(ins->ipdn_kstat, GLOBAL_ZONEID); 915*fe77cc04SRobert Mustacchi 916*fe77cc04SRobert Mustacchi ink = &ins->ipdn_ksdata; 917*fe77cc04SRobert Mustacchi ins->ipdn_kstat->ks_data = ink; 918*fe77cc04SRobert Mustacchi kstat_named_init(&ink->ink_ncorrupts, "corrupts", 919*fe77cc04SRobert Mustacchi KSTAT_DATA_UINT64); 920*fe77cc04SRobert Mustacchi kstat_named_init(&ink->ink_ndrops, "drops", KSTAT_DATA_UINT64); 921*fe77cc04SRobert Mustacchi kstat_named_init(&ink->ink_ndelays, "delays", 922*fe77cc04SRobert Mustacchi KSTAT_DATA_UINT64); 923*fe77cc04SRobert Mustacchi kstat_install(ins->ipdn_kstat); 924*fe77cc04SRobert Mustacchi } 925*fe77cc04SRobert Mustacchi 926*fe77cc04SRobert Mustacchi mutex_enter(&ipd_nsl_lock); 927*fe77cc04SRobert Mustacchi list_insert_tail(&ipd_nsl, ins); 928*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nsl_lock); 929*fe77cc04SRobert Mustacchi 930*fe77cc04SRobert Mustacchi return (ins); 931*fe77cc04SRobert Mustacchi } 932*fe77cc04SRobert Mustacchi 933*fe77cc04SRobert Mustacchi static void 934*fe77cc04SRobert Mustacchi ipd_nin_shutdown(const netid_t id, void *arg) 935*fe77cc04SRobert Mustacchi { 936*fe77cc04SRobert Mustacchi ipd_netstack_t *ins = arg; 937*fe77cc04SRobert Mustacchi 938*fe77cc04SRobert Mustacchi VERIFY(id == ins->ipdn_netid); 939*fe77cc04SRobert Mustacchi mutex_enter(&ins->ipdn_lock); 940*fe77cc04SRobert Mustacchi ASSERT(ins->ipdn_status == IPDN_STATUS_DISABLED || 941*fe77cc04SRobert Mustacchi ins->ipdn_status == IPDN_STATUS_ENABLED); 942*fe77cc04SRobert Mustacchi ins->ipdn_status |= IPDN_STATUS_CONDEMNED; 943*fe77cc04SRobert Mustacchi if (ins->ipdn_kstat != NULL) 944*fe77cc04SRobert Mustacchi net_kstat_delete(id, ins->ipdn_kstat); 945*fe77cc04SRobert Mustacchi mutex_exit(&ins->ipdn_lock); 946*fe77cc04SRobert Mustacchi } 947*fe77cc04SRobert Mustacchi 948*fe77cc04SRobert Mustacchi /*ARGSUSED*/ 949*fe77cc04SRobert Mustacchi static void 950*fe77cc04SRobert Mustacchi ipd_nin_destroy(const netid_t id, void *arg) 951*fe77cc04SRobert Mustacchi { 952*fe77cc04SRobert Mustacchi ipd_netstack_t *ins = arg; 953*fe77cc04SRobert Mustacchi 954*fe77cc04SRobert Mustacchi /* 955*fe77cc04SRobert Mustacchi * At this point none of the hooks should be able to fire because the 956*fe77cc04SRobert Mustacchi * zone has been shutdown and we are in the process of destroying it. 957*fe77cc04SRobert Mustacchi * Thus it should not be possible for someone else to come in and grab 958*fe77cc04SRobert Mustacchi * our ipd_netstack_t for this zone. Because of that, we know that we 959*fe77cc04SRobert Mustacchi * are the only ones who could be running here. 960*fe77cc04SRobert Mustacchi */ 961*fe77cc04SRobert Mustacchi mutex_enter(&ipd_nsl_lock); 962*fe77cc04SRobert Mustacchi list_remove(&ipd_nsl, ins); 963*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nsl_lock); 964*fe77cc04SRobert Mustacchi 965*fe77cc04SRobert Mustacchi if (ins->ipdn_hooked) 966*fe77cc04SRobert Mustacchi ipd_teardown_hooks(ins); 967*fe77cc04SRobert Mustacchi mutex_destroy(&ins->ipdn_lock); 968*fe77cc04SRobert Mustacchi kmem_free(ins, sizeof (ipd_netstack_t)); 969*fe77cc04SRobert Mustacchi } 970*fe77cc04SRobert Mustacchi 971*fe77cc04SRobert Mustacchi /*ARGSUSED*/ 972*fe77cc04SRobert Mustacchi static int 973*fe77cc04SRobert Mustacchi ipd_open(dev_t *devp, int flag, int otype, cred_t *credp) 974*fe77cc04SRobert Mustacchi { 975*fe77cc04SRobert Mustacchi if (flag & FEXCL || flag & FNDELAY) 976*fe77cc04SRobert Mustacchi return (EINVAL); 977*fe77cc04SRobert Mustacchi 978*fe77cc04SRobert Mustacchi if (otype != OTYP_CHR) 979*fe77cc04SRobert Mustacchi return (EINVAL); 980*fe77cc04SRobert Mustacchi 981*fe77cc04SRobert Mustacchi if (!(flag & FREAD && flag & FWRITE)) 982*fe77cc04SRobert Mustacchi return (EINVAL); 983*fe77cc04SRobert Mustacchi 984*fe77cc04SRobert Mustacchi if (secpolicy_ip_config(credp, B_FALSE) != 0) 985*fe77cc04SRobert Mustacchi return (EPERM); 986*fe77cc04SRobert Mustacchi 987*fe77cc04SRobert Mustacchi return (0); 988*fe77cc04SRobert Mustacchi } 989*fe77cc04SRobert Mustacchi 990*fe77cc04SRobert Mustacchi /*ARGSUSED*/ 991*fe77cc04SRobert Mustacchi static int 992*fe77cc04SRobert Mustacchi ipd_ioctl(dev_t dev, int cmd, intptr_t arg, int md, cred_t *cr, int *rv) 993*fe77cc04SRobert Mustacchi { 994*fe77cc04SRobert Mustacchi int rval; 995*fe77cc04SRobert Mustacchi ipd_ioc_perturb_t ipip; 996*fe77cc04SRobert Mustacchi 997*fe77cc04SRobert Mustacchi switch (cmd) { 998*fe77cc04SRobert Mustacchi case IPDIOC_CORRUPT: 999*fe77cc04SRobert Mustacchi case IPDIOC_DELAY: 1000*fe77cc04SRobert Mustacchi case IPDIOC_DROP: 1001*fe77cc04SRobert Mustacchi if (ddi_copyin((void *)arg, &ipip, sizeof (ipd_ioc_perturb_t), 1002*fe77cc04SRobert Mustacchi 0) != 0) 1003*fe77cc04SRobert Mustacchi return (EFAULT); 1004*fe77cc04SRobert Mustacchi rval = ipd_ioctl_perturb(&ipip, cr, cmd); 1005*fe77cc04SRobert Mustacchi return (rval); 1006*fe77cc04SRobert Mustacchi case IPDIOC_REMOVE: 1007*fe77cc04SRobert Mustacchi if (ddi_copyin((void *)arg, &ipip, sizeof (ipd_ioc_perturb_t), 1008*fe77cc04SRobert Mustacchi 0) != 0) 1009*fe77cc04SRobert Mustacchi return (EFAULT); 1010*fe77cc04SRobert Mustacchi rval = ipd_ioctl_remove(&ipip, cr); 1011*fe77cc04SRobert Mustacchi return (rval); 1012*fe77cc04SRobert Mustacchi case IPDIOC_LIST: 1013*fe77cc04SRobert Mustacchi /* 1014*fe77cc04SRobert Mustacchi * Because the list ioctl doesn't have a fixed-size struct due 1015*fe77cc04SRobert Mustacchi * to needing to pass around a pointer, we instead delegate the 1016*fe77cc04SRobert Mustacchi * copyin logic to the list code. 1017*fe77cc04SRobert Mustacchi */ 1018*fe77cc04SRobert Mustacchi return (ipd_ioctl_list(arg, cr)); 1019*fe77cc04SRobert Mustacchi default: 1020*fe77cc04SRobert Mustacchi break; 1021*fe77cc04SRobert Mustacchi } 1022*fe77cc04SRobert Mustacchi return (ENOTTY); 1023*fe77cc04SRobert Mustacchi } 1024*fe77cc04SRobert Mustacchi 1025*fe77cc04SRobert Mustacchi /*ARGSUSED*/ 1026*fe77cc04SRobert Mustacchi static int 1027*fe77cc04SRobert Mustacchi ipd_close(dev_t dev, int flag, int otype, cred_t *credp) 1028*fe77cc04SRobert Mustacchi { 1029*fe77cc04SRobert Mustacchi return (0); 1030*fe77cc04SRobert Mustacchi } 1031*fe77cc04SRobert Mustacchi 1032*fe77cc04SRobert Mustacchi static int 1033*fe77cc04SRobert Mustacchi ipd_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 1034*fe77cc04SRobert Mustacchi { 1035*fe77cc04SRobert Mustacchi minor_t instance; 1036*fe77cc04SRobert Mustacchi 1037*fe77cc04SRobert Mustacchi if (cmd != DDI_ATTACH) 1038*fe77cc04SRobert Mustacchi return (DDI_FAILURE); 1039*fe77cc04SRobert Mustacchi 1040*fe77cc04SRobert Mustacchi if (ipd_devi != NULL) 1041*fe77cc04SRobert Mustacchi return (DDI_FAILURE); 1042*fe77cc04SRobert Mustacchi 1043*fe77cc04SRobert Mustacchi instance = ddi_get_instance(dip); 1044*fe77cc04SRobert Mustacchi if (ddi_create_minor_node(dip, "ipd", S_IFCHR, instance, 1045*fe77cc04SRobert Mustacchi DDI_PSEUDO, 0) == DDI_FAILURE) 1046*fe77cc04SRobert Mustacchi return (DDI_FAILURE); 1047*fe77cc04SRobert Mustacchi 1048*fe77cc04SRobert Mustacchi ipd_neti = net_instance_alloc(NETINFO_VERSION); 1049*fe77cc04SRobert Mustacchi if (ipd_neti == NULL) { 1050*fe77cc04SRobert Mustacchi ddi_remove_minor_node(dip, NULL); 1051*fe77cc04SRobert Mustacchi return (DDI_FAILURE); 1052*fe77cc04SRobert Mustacchi } 1053*fe77cc04SRobert Mustacchi 1054*fe77cc04SRobert Mustacchi /* 1055*fe77cc04SRobert Mustacchi * Note that these global structures MUST be initialized before we call 1056*fe77cc04SRobert Mustacchi * net_instance_register, as that will instantly cause us to drive into 1057*fe77cc04SRobert Mustacchi * the ipd_nin_create callbacks. 1058*fe77cc04SRobert Mustacchi */ 1059*fe77cc04SRobert Mustacchi list_create(&ipd_nsl, sizeof (ipd_netstack_t), 1060*fe77cc04SRobert Mustacchi offsetof(ipd_netstack_t, ipdn_link)); 1061*fe77cc04SRobert Mustacchi mutex_init(&ipd_nsl_lock, NULL, MUTEX_DRIVER, NULL); 1062*fe77cc04SRobert Mustacchi mutex_init(&ipd_nactive_lock, NULL, MUTEX_DRIVER, NULL); 1063*fe77cc04SRobert Mustacchi 1064*fe77cc04SRobert Mustacchi /* Note, net_instance_alloc sets the version. */ 1065*fe77cc04SRobert Mustacchi ipd_neti->nin_name = "ipd"; 1066*fe77cc04SRobert Mustacchi ipd_neti->nin_create = ipd_nin_create; 1067*fe77cc04SRobert Mustacchi ipd_neti->nin_destroy = ipd_nin_destroy; 1068*fe77cc04SRobert Mustacchi ipd_neti->nin_shutdown = ipd_nin_shutdown; 1069*fe77cc04SRobert Mustacchi if (net_instance_register(ipd_neti) == DDI_FAILURE) { 1070*fe77cc04SRobert Mustacchi net_instance_free(ipd_neti); 1071*fe77cc04SRobert Mustacchi ddi_remove_minor_node(dip, NULL); 1072*fe77cc04SRobert Mustacchi } 1073*fe77cc04SRobert Mustacchi 1074*fe77cc04SRobert Mustacchi ddi_report_dev(dip); 1075*fe77cc04SRobert Mustacchi ipd_devi = dip; 1076*fe77cc04SRobert Mustacchi 1077*fe77cc04SRobert Mustacchi return (DDI_SUCCESS); 1078*fe77cc04SRobert Mustacchi } 1079*fe77cc04SRobert Mustacchi 1080*fe77cc04SRobert Mustacchi /*ARGSUSED*/ 1081*fe77cc04SRobert Mustacchi static int 1082*fe77cc04SRobert Mustacchi ipd_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 1083*fe77cc04SRobert Mustacchi { 1084*fe77cc04SRobert Mustacchi int error; 1085*fe77cc04SRobert Mustacchi 1086*fe77cc04SRobert Mustacchi switch (infocmd) { 1087*fe77cc04SRobert Mustacchi case DDI_INFO_DEVT2DEVINFO: 1088*fe77cc04SRobert Mustacchi *result = ipd_devi; 1089*fe77cc04SRobert Mustacchi error = DDI_SUCCESS; 1090*fe77cc04SRobert Mustacchi break; 1091*fe77cc04SRobert Mustacchi case DDI_INFO_DEVT2INSTANCE: 1092*fe77cc04SRobert Mustacchi *result = (void *)(uintptr_t)getminor((dev_t)arg); 1093*fe77cc04SRobert Mustacchi error = DDI_SUCCESS; 1094*fe77cc04SRobert Mustacchi default: 1095*fe77cc04SRobert Mustacchi error = DDI_FAILURE; 1096*fe77cc04SRobert Mustacchi break; 1097*fe77cc04SRobert Mustacchi } 1098*fe77cc04SRobert Mustacchi 1099*fe77cc04SRobert Mustacchi return (error); 1100*fe77cc04SRobert Mustacchi } 1101*fe77cc04SRobert Mustacchi 1102*fe77cc04SRobert Mustacchi static int 1103*fe77cc04SRobert Mustacchi ipd_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 1104*fe77cc04SRobert Mustacchi { 1105*fe77cc04SRobert Mustacchi if (cmd != DDI_DETACH) 1106*fe77cc04SRobert Mustacchi return (DDI_FAILURE); 1107*fe77cc04SRobert Mustacchi 1108*fe77cc04SRobert Mustacchi mutex_enter(&ipd_nactive_lock); 1109*fe77cc04SRobert Mustacchi if (ipd_nactive > 0) { 1110*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nactive_lock); 1111*fe77cc04SRobert Mustacchi return (EBUSY); 1112*fe77cc04SRobert Mustacchi } 1113*fe77cc04SRobert Mustacchi mutex_exit(&ipd_nactive_lock); 1114*fe77cc04SRobert Mustacchi ASSERT(dip == ipd_devi); 1115*fe77cc04SRobert Mustacchi ddi_remove_minor_node(dip, NULL); 1116*fe77cc04SRobert Mustacchi ipd_devi = NULL; 1117*fe77cc04SRobert Mustacchi 1118*fe77cc04SRobert Mustacchi if (ipd_neti != NULL) { 1119*fe77cc04SRobert Mustacchi VERIFY(net_instance_unregister(ipd_neti) == 0); 1120*fe77cc04SRobert Mustacchi net_instance_free(ipd_neti); 1121*fe77cc04SRobert Mustacchi } 1122*fe77cc04SRobert Mustacchi 1123*fe77cc04SRobert Mustacchi mutex_destroy(&ipd_nsl_lock); 1124*fe77cc04SRobert Mustacchi mutex_destroy(&ipd_nactive_lock); 1125*fe77cc04SRobert Mustacchi list_destroy(&ipd_nsl); 1126*fe77cc04SRobert Mustacchi 1127*fe77cc04SRobert Mustacchi return (DDI_SUCCESS); 1128*fe77cc04SRobert Mustacchi } 1129*fe77cc04SRobert Mustacchi 1130*fe77cc04SRobert Mustacchi static struct cb_ops ipd_cb_ops = { 1131*fe77cc04SRobert Mustacchi ipd_open, /* open */ 1132*fe77cc04SRobert Mustacchi ipd_close, /* close */ 1133*fe77cc04SRobert Mustacchi nodev, /* strategy */ 1134*fe77cc04SRobert Mustacchi nodev, /* print */ 1135*fe77cc04SRobert Mustacchi nodev, /* dump */ 1136*fe77cc04SRobert Mustacchi nodev, /* read */ 1137*fe77cc04SRobert Mustacchi nodev, /* write */ 1138*fe77cc04SRobert Mustacchi ipd_ioctl, /* ioctl */ 1139*fe77cc04SRobert Mustacchi nodev, /* devmap */ 1140*fe77cc04SRobert Mustacchi nodev, /* mmap */ 1141*fe77cc04SRobert Mustacchi nodev, /* segmap */ 1142*fe77cc04SRobert Mustacchi nochpoll, /* poll */ 1143*fe77cc04SRobert Mustacchi ddi_prop_op, /* cb_prop_op */ 1144*fe77cc04SRobert Mustacchi NULL, /* streamtab */ 1145*fe77cc04SRobert Mustacchi D_NEW | D_MP, /* Driver compatibility flag */ 1146*fe77cc04SRobert Mustacchi CB_REV, /* rev */ 1147*fe77cc04SRobert Mustacchi nodev, /* aread */ 1148*fe77cc04SRobert Mustacchi nodev /* awrite */ 1149*fe77cc04SRobert Mustacchi }; 1150*fe77cc04SRobert Mustacchi 1151*fe77cc04SRobert Mustacchi static struct dev_ops ipd_ops = { 1152*fe77cc04SRobert Mustacchi DEVO_REV, /* devo_rev */ 1153*fe77cc04SRobert Mustacchi 0, /* refcnt */ 1154*fe77cc04SRobert Mustacchi ipd_getinfo, /* get_dev_info */ 1155*fe77cc04SRobert Mustacchi nulldev, /* identify */ 1156*fe77cc04SRobert Mustacchi nulldev, /* probe */ 1157*fe77cc04SRobert Mustacchi ipd_attach, /* attach */ 1158*fe77cc04SRobert Mustacchi ipd_detach, /* detach */ 1159*fe77cc04SRobert Mustacchi nodev, /* reset */ 1160*fe77cc04SRobert Mustacchi &ipd_cb_ops, /* driver operations */ 1161*fe77cc04SRobert Mustacchi NULL, /* bus operations */ 1162*fe77cc04SRobert Mustacchi nodev, /* dev power */ 1163*fe77cc04SRobert Mustacchi ddi_quiesce_not_needed /* quiesce */ 1164*fe77cc04SRobert Mustacchi }; 1165*fe77cc04SRobert Mustacchi 1166*fe77cc04SRobert Mustacchi static struct modldrv modldrv = { 1167*fe77cc04SRobert Mustacchi &mod_driverops, 1168*fe77cc04SRobert Mustacchi "Internet packet disturber", 1169*fe77cc04SRobert Mustacchi &ipd_ops 1170*fe77cc04SRobert Mustacchi }; 1171*fe77cc04SRobert Mustacchi 1172*fe77cc04SRobert Mustacchi static struct modlinkage modlinkage = { 1173*fe77cc04SRobert Mustacchi MODREV_1, 1174*fe77cc04SRobert Mustacchi { (void *)&modldrv, NULL } 1175*fe77cc04SRobert Mustacchi }; 1176*fe77cc04SRobert Mustacchi 1177*fe77cc04SRobert Mustacchi int 1178*fe77cc04SRobert Mustacchi _init(void) 1179*fe77cc04SRobert Mustacchi { 1180*fe77cc04SRobert Mustacchi return (mod_install(&modlinkage)); 1181*fe77cc04SRobert Mustacchi } 1182*fe77cc04SRobert Mustacchi 1183*fe77cc04SRobert Mustacchi int 1184*fe77cc04SRobert Mustacchi _info(struct modinfo *modinfop) 1185*fe77cc04SRobert Mustacchi { 1186*fe77cc04SRobert Mustacchi return (mod_info(&modlinkage, modinfop)); 1187*fe77cc04SRobert Mustacchi } 1188*fe77cc04SRobert Mustacchi 1189*fe77cc04SRobert Mustacchi int 1190*fe77cc04SRobert Mustacchi _fini(void) 1191*fe77cc04SRobert Mustacchi { 1192*fe77cc04SRobert Mustacchi return (mod_remove(&modlinkage)); 1193*fe77cc04SRobert Mustacchi } 1194