xref: /illumos-gate/usr/src/uts/common/io/bpf/bpf_mod.c (revision 9525b14bcdeb5b5f6f95ab27c2f48f18bd2ec829)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <sys/types.h>
28 #include <sys/param.h>
29 #include <sys/stat.h>
30 #include <sys/errno.h>
31 #include <sys/uio.h>
32 #include <sys/buf.h>
33 #include <sys/modctl.h>
34 #include <sys/open.h>
35 #include <sys/kmem.h>
36 #include <sys/conf.h>
37 #include <sys/cmn_err.h>
38 #include <sys/cred.h>
39 #include <sys/sunddi.h>
40 #include <sys/mac_provider.h>
41 #include <sys/dls_impl.h>
42 #include <inet/ipnet.h>
43 
44 extern	int	bpfopen(dev_t *devp, int flag, int otyp, cred_t *cred);
45 extern	int	bpfclose(dev_t dev, int flag, int otyp, cred_t *cred);
46 extern	int	bpfread(dev_t dev, struct uio *uio_p, cred_t *cred_p);
47 extern	int	bpfwrite(dev_t dev, struct uio *uio, cred_t *cred);
48 extern	int	bpfchpoll(dev_t, short, int, short *, struct pollhead **);
49 extern	int	bpfioctl(dev_t, int, intptr_t, int, cred_t *, int *);
50 extern	int	bpfilterattach(void);
51 extern	int	bpfilterdetach(void);
52 
53 extern	bpf_provider_t	bpf_mac;
54 extern	bpf_provider_t	bpf_ipnet;
55 
56 static	int	bpf_attach(dev_info_t *, ddi_attach_cmd_t);
57 static	void	*bpf_create_inst(const netid_t);
58 static	void	bpf_destroy_inst(const netid_t, void *);
59 static	int	bpf_detach(dev_info_t *, ddi_detach_cmd_t);
60 static	int	bpf_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
61 static	int	bpf_provider_add(bpf_provider_t *);
62 static	int	bpf_provider_remove(bpf_provider_t *);
63 static	void	bpf_shutdown_inst(const netid_t, void *);
64 
65 extern	void	bpfdetach(uintptr_t);
66 extern	int	bpf_bufsize;
67 extern	int	bpf_maxbufsize;
68 
69 static LIST_HEAD(, bpf_provider_list) bpf_providers;
70 
71 static struct cb_ops bpf_cb_ops = {
72 	bpfopen,
73 	bpfclose,
74 	nodev,		/* strategy */
75 	nodev,		/* print */
76 	nodev,		/* dump */
77 	bpfread,
78 	bpfwrite,	/* write */
79 	bpfioctl,	/* ioctl */
80 	nodev,		/* devmap */
81 	nodev,		/* mmap */
82 	nodev,		/* segmap */
83 	bpfchpoll,	/* poll */
84 	ddi_prop_op,
85 	NULL,
86 	D_MTSAFE,
87 	CB_REV,
88 	nodev,		/* aread */
89 	nodev,		/* awrite */
90 };
91 
92 static struct dev_ops bpf_ops = {
93 	DEVO_REV,
94 	0,
95 	bpf_getinfo,
96 	nulldev,
97 	nulldev,
98 	bpf_attach,
99 	bpf_detach,
100 	nodev,		/* reset */
101 	&bpf_cb_ops,
102 	(struct bus_ops *)0
103 };
104 
105 extern struct mod_ops mod_driverops;
106 static struct modldrv bpfmod = {
107 	&mod_driverops, "Berkely Packet Filter", &bpf_ops
108 };
109 static struct modlinkage modlink1 = { MODREV_1, &bpfmod, NULL };
110 
111 static dev_info_t *bpf_dev_info = NULL;
112 static net_instance_t *bpf_inst = NULL;
113 
114 int
115 _init()
116 {
117 	int bpfinst;
118 
119 	bpfinst = mod_install(&modlink1);
120 	return (bpfinst);
121 }
122 
123 int
124 _fini(void)
125 {
126 	int bpfinst;
127 
128 	bpfinst = mod_remove(&modlink1);
129 	return (bpfinst);
130 }
131 
132 int
133 _info(struct modinfo *modinfop)
134 {
135 	int bpfinst;
136 
137 	bpfinst = mod_info(&modlink1, modinfop);
138 	return (bpfinst);
139 }
140 
141 static int
142 bpf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
143 {
144 
145 	switch (cmd) {
146 	case DDI_ATTACH:
147 		/*
148 		 * Default buffer size from bpf's driver.conf file
149 		 */
150 		bpf_bufsize = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
151 		    "buf_size", 32 * 1024);
152 		/*
153 		 * Maximum buffer size from bpf's driver.conf file
154 		 */
155 		bpf_maxbufsize = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 0,
156 		    "max_buf_size", 16 * 1024 * 1024);
157 
158 		if (ddi_create_minor_node(dip, "bpf", S_IFCHR, 0,
159 		    DDI_PSEUDO, 0) == DDI_FAILURE) {
160 			ddi_remove_minor_node(dip, NULL);
161 			goto attach_failed;
162 		}
163 		bpf_dev_info = dip;
164 		ddi_report_dev(dip);
165 
166 		LIST_INIT(&bpf_providers);
167 
168 		if (bpfilterattach() != 0)
169 			goto attach_failed;
170 
171 		ASSERT(bpf_provider_add(&bpf_mac) == 0);
172 		dls_set_bpfattach(bpfattach, bpfdetach);
173 		ipnet_set_bpfattach(bpfattach, bpfdetach, GLOBAL_ZONEID,
174 		    bpf_itap, bpf_provider_add);
175 
176 		/*
177 		 * Set up to be notified about zones coming and going
178 		 * so that proper interaction with ipnet is possible.
179 		 */
180 		bpf_inst = net_instance_alloc(NETINFO_VERSION);
181 		if (bpf_inst == NULL)
182 			goto attach_failed;
183 		bpf_inst->nin_name = "bpf";
184 		bpf_inst->nin_create = bpf_create_inst;
185 		bpf_inst->nin_destroy = bpf_destroy_inst;
186 		bpf_inst->nin_shutdown = bpf_shutdown_inst;
187 		if (net_instance_register(bpf_inst) != 0) {
188 			net_instance_free(bpf_inst);
189 			goto attach_failed;
190 		}
191 
192 		return (DDI_SUCCESS);
193 		/* NOTREACHED */
194 	case DDI_RESUME:
195 		return (DDI_SUCCESS);
196 		/* NOTREACHED */
197 	default:
198 		break;
199 	}
200 
201 attach_failed:
202 
203 	/*
204 	 * Use our own detach routine to toss
205 	 * away any stuff we allocated above.
206 	 */
207 	(void) bpfilterdetach();
208 	(void) bpf_detach(dip, DDI_DETACH);
209 	return (DDI_FAILURE);
210 }
211 
212 static int
213 bpf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
214 {
215 	int error;
216 
217 	switch (cmd) {
218 	case DDI_DETACH:
219 		if (net_instance_unregister(bpf_inst) != 0)
220 			return (DDI_FAILURE);
221 		net_instance_free(bpf_inst);
222 
223 		ipnet_set_bpfattach(NULL, NULL, GLOBAL_ZONEID, NULL,
224 		    bpf_provider_remove);
225 		/*
226 		 * Whilst we don't want to be notified about new devices that
227 		 * are being detached, to set the bpf detach function to NULL
228 		 * introduces a race condition between this kernel module
229 		 * unloading and a network interface driver also unloading.
230 		 */
231 		dls_set_bpfattach(NULL, bpfdetach);
232 		error = bpfilterdetach();
233 		if (error != 0)
234 			return (DDI_FAILURE);
235 		/*
236 		 * Now everything is clean, set the detach to NULL too.
237 		 */
238 		dls_set_bpfattach(NULL, NULL);
239 		ASSERT(bpf_provider_remove(&bpf_mac) == 0);
240 
241 		ASSERT(LIST_EMPTY(&bpf_providers));
242 
243 		ddi_prop_remove_all(dip);
244 
245 		return (DDI_SUCCESS);
246 		/* NOTREACHED */
247 	case DDI_SUSPEND:
248 	case DDI_PM_SUSPEND:
249 		return (DDI_SUCCESS);
250 		/* NOTREACHED */
251 	default:
252 		break;
253 	}
254 	return (DDI_FAILURE);
255 }
256 
257 /*ARGSUSED*/
258 static int
259 bpf_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
260 {
261 	int error = DDI_FAILURE;
262 
263 	switch (infocmd) {
264 	case DDI_INFO_DEVT2DEVINFO:
265 		*result = bpf_dev_info;
266 		error = DDI_SUCCESS;
267 		break;
268 	case DDI_INFO_DEVT2INSTANCE:
269 		*result = (void *)0;
270 		error = DDI_SUCCESS;
271 		break;
272 	default:
273 		break;
274 	}
275 	return (error);
276 }
277 
278 /*
279  * The two functions below work with and manage a list of providers that
280  * supply BPF with packets. Their addition and removal is only happens
281  * when the bpf module is attaching/detaching, thus there is no race
282  * condition to guard against with using locks as the kernel module system
283  * takes care of this for us. Similarly, bpf_provider_tickle() is called
284  * from bpf_setif, which implies an open file descriptor that would get
285  * in the way of detach being active.
286  */
287 static int
288 bpf_provider_add(bpf_provider_t *provider)
289 {
290 	bpf_provider_list_t *bp;
291 
292 	LIST_FOREACH(bp, &bpf_providers, bpl_next) {
293 		if (bp->bpl_what == provider)
294 			return (EEXIST);
295 	}
296 
297 
298 	bp = kmem_alloc(sizeof (*bp), KM_SLEEP);
299 	bp->bpl_what = provider;
300 	LIST_INSERT_HEAD(&bpf_providers, bp, bpl_next);
301 
302 	return (0);
303 }
304 
305 static int
306 bpf_provider_remove(bpf_provider_t *provider)
307 {
308 	bpf_provider_list_t *bp;
309 
310 	LIST_FOREACH(bp, &bpf_providers, bpl_next) {
311 		if (bp->bpl_what == provider)
312 			break;
313 	}
314 
315 	if (bp == NULL)
316 		return (ESRCH);
317 
318 	LIST_REMOVE(bp, bpl_next);
319 
320 	kmem_free(bp, sizeof (*bp));
321 
322 	return (0);
323 }
324 
325 /*
326  * return a pointer to the structure that holds all of the functions
327  * available to be used to support a particular packet provider.
328  */
329 bpf_provider_t *
330 bpf_find_provider_by_id(int who)
331 {
332 	bpf_provider_list_t *b;
333 
334 	LIST_FOREACH(b, &bpf_providers, bpl_next) {
335 		if (b->bpl_what->bpr_unit == who)
336 			return (b->bpl_what);
337 	}
338 
339 	return (NULL);
340 }
341 
342 /*
343  * This function is used by bpf_setif() to force an open() to be called on
344  * a given device name. If a device has been unloaded by the kernel, but it
345  * is still recognised, then calling this function will hopefully cause it
346  * to be loaded back into the kernel. When this function is called, it is
347  * not known which packet provider the name belongs to so all are tried.
348  */
349 int
350 bpf_provider_tickle(char *name, zoneid_t zone)
351 {
352 	bpf_provider_list_t *bp;
353 	uintptr_t handle;
354 	int tickled = 0;
355 
356 	LIST_FOREACH(bp, &bpf_providers, bpl_next) {
357 		handle = 0;
358 		if (bp->bpl_what->bpr_open(name, &handle, zone) == 0) {
359 			bp->bpl_what->bpr_close(handle);
360 			tickled++;
361 		} else if (bp->bpl_what->bpr_unit == BPR_MAC) {
362 			/*
363 			 * For mac devices, sometimes the open/close is not
364 			 * enough. In that case, further provocation is
365 			 * attempted by fetching the linkid and trying to
366 			 * use that as the key for open, rather than the
367 			 * name.
368 			 */
369 			datalink_id_t id;
370 
371 			if (bp->bpl_what->bpr_getlinkid(name, &id,
372 			    zone) == 0) {
373 				if (bp->bpl_what->bpr_open(name, &handle,
374 				    zone) == 0) {
375 					bp->bpl_what->bpr_close(handle);
376 					tickled++;
377 				} else {
378 					mac_handle_t mh;
379 
380 					if (mac_open_by_linkid(id, &mh) == 0) {
381 						mac_close(mh);
382 						tickled++;
383 					}
384 				}
385 			}
386 		}
387 
388 	}
389 
390 	if (tickled != 0)
391 		return (EWOULDBLOCK);
392 
393 	return (ENXIO);
394 }
395 
396 /*
397  * The following three functions provide the necessary callbacks into
398  * the netinfo API. This API is primarily used to trigger awareness of
399  * when a zone is being torn down, allowing BPF to drive IPNET to
400  * tell it which interfaces need to go away.
401  */
402 /*ARGSUSED*/
403 static void *
404 bpf_create_inst(const netid_t netid)
405 {
406 	/*
407 	 * BPF does not keep any per-instance state, its list of
408 	 * interfaces is global, as is its device hash table.
409 	 */
410 	return ((void *)bpf_itap);
411 }
412 
413 /*ARGSUSED*/
414 static void
415 bpf_shutdown_inst(const netid_t netid, void *arg)
416 {
417 	zoneid_t zoneid;
418 
419 	zoneid = net_getzoneidbynetid(netid);
420 	if (zoneid != GLOBAL_ZONEID) {
421 		ipnet_set_bpfattach(NULL, NULL, zoneid, NULL, NULL);
422 	}
423 }
424 
425 /*ARGSUSED*/
426 static void
427 bpf_destroy_inst(const netid_t netid, void *arg)
428 {
429 }
430 
431 /*
432  * This function is required, and is called from bpfopen, rather than
433  * bpf_create_inst() for the simple reason that when bpf_create_inst()
434  * is called, the zone is not fully initialised yet. This leads fo
435  * functions that map the zoneid to pointers failing (when they should
436  * not be failing) and thus the system panic'ing.
437  */
438 void
439 bpf_open_zone(const zoneid_t zoneid)
440 {
441 	ipnet_set_bpfattach(bpfattach, bpfdetach,
442 	    zoneid, bpf_itap, bpf_provider_add);
443 }
444