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