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