xref: /titanic_52/usr/src/uts/common/inet/ipd/ipd.c (revision fe77cc0407fb667ddc04e1a8f2e203bb7b9c80e1)
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