xref: /illumos-gate/usr/src/uts/common/io/bpf/bpf_mod.c (revision b7ea883b48e925772db7fa37388112c84c6d5f5b)
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 bpf_provider_head_t 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
_init()115 _init()
116 {
117 	int bpfinst;
118 
119 	bpfinst = mod_install(&modlink1);
120 	return (bpfinst);
121 }
122 
123 int
_fini(void)124 _fini(void)
125 {
126 	int bpfinst;
127 
128 	bpfinst = mod_remove(&modlink1);
129 	return (bpfinst);
130 }
131 
132 int
_info(struct modinfo * modinfop)133 _info(struct modinfo *modinfop)
134 {
135 	int bpfinst;
136 
137 	bpfinst = mod_info(&modlink1, modinfop);
138 	return (bpfinst);
139 }
140 
141 static int
bpf_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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 		ipnet_set_itap(bpf_itap);
172 		VERIFY(bpf_provider_add(&bpf_ipnet) == 0);
173 		VERIFY(bpf_provider_add(&bpf_mac) == 0);
174 
175 		/*
176 		 * Set up to be notified about zones coming and going
177 		 * so that proper interaction with ipnet is possible.
178 		 */
179 		bpf_inst = net_instance_alloc(NETINFO_VERSION);
180 		if (bpf_inst == NULL)
181 			goto attach_failed;
182 		bpf_inst->nin_name = "bpf";
183 		bpf_inst->nin_create = bpf_create_inst;
184 		bpf_inst->nin_destroy = bpf_destroy_inst;
185 		bpf_inst->nin_shutdown = bpf_shutdown_inst;
186 		if (net_instance_register(bpf_inst) != 0) {
187 			net_instance_free(bpf_inst);
188 			goto attach_failed;
189 		}
190 
191 		return (DDI_SUCCESS);
192 		/* NOTREACHED */
193 	case DDI_RESUME:
194 		return (DDI_SUCCESS);
195 		/* NOTREACHED */
196 	default:
197 		break;
198 	}
199 
200 attach_failed:
201 
202 	/*
203 	 * Use our own detach routine to toss
204 	 * away any stuff we allocated above.
205 	 */
206 	(void) bpfilterdetach();
207 	(void) bpf_detach(dip, DDI_DETACH);
208 	return (DDI_FAILURE);
209 }
210 
211 static int
bpf_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)212 bpf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
213 {
214 	int error;
215 
216 	switch (cmd) {
217 	case DDI_DETACH:
218 		if (net_instance_unregister(bpf_inst) != 0)
219 			return (DDI_FAILURE);
220 		net_instance_free(bpf_inst);
221 
222 		ipnet_set_itap(NULL);
223 		error = bpfilterdetach();
224 		if (error != 0)
225 			return (DDI_FAILURE);
226 		VERIFY(bpf_provider_remove(&bpf_ipnet) == 0);
227 		VERIFY(bpf_provider_remove(&bpf_mac) == 0);
228 
229 		ASSERT(LIST_EMPTY(&bpf_providers));
230 
231 		ddi_prop_remove_all(dip);
232 
233 		return (DDI_SUCCESS);
234 		/* NOTREACHED */
235 	case DDI_SUSPEND:
236 	case DDI_PM_SUSPEND:
237 		return (DDI_SUCCESS);
238 		/* NOTREACHED */
239 	default:
240 		break;
241 	}
242 	return (DDI_FAILURE);
243 }
244 
245 /*ARGSUSED*/
246 static int
bpf_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)247 bpf_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
248 {
249 	int error = DDI_FAILURE;
250 
251 	switch (infocmd) {
252 	case DDI_INFO_DEVT2DEVINFO:
253 		*result = bpf_dev_info;
254 		error = DDI_SUCCESS;
255 		break;
256 	case DDI_INFO_DEVT2INSTANCE:
257 		*result = (void *)0;
258 		error = DDI_SUCCESS;
259 		break;
260 	default:
261 		break;
262 	}
263 	return (error);
264 }
265 
266 /*
267  * The two functions below work with and manage a list of providers that
268  * supply BPF with packets. Their addition and removal is only happens
269  * when the bpf module is attaching/detaching, thus there is no race
270  * condition to guard against with using locks as the kernel module system
271  * takes care of this for us. Similarly, bpf_provider_tickle() is called
272  * from bpf_setif, which implies an open file descriptor that would get
273  * in the way of detach being active.
274  */
275 static int
bpf_provider_add(bpf_provider_t * provider)276 bpf_provider_add(bpf_provider_t *provider)
277 {
278 	bpf_provider_list_t *bp;
279 
280 	LIST_FOREACH(bp, &bpf_providers, bpl_next) {
281 		if (bp->bpl_what == provider)
282 			return (EEXIST);
283 	}
284 
285 
286 	bp = kmem_alloc(sizeof (*bp), KM_SLEEP);
287 	bp->bpl_what = provider;
288 	LIST_INSERT_HEAD(&bpf_providers, bp, bpl_next);
289 
290 	return (0);
291 }
292 
293 static int
bpf_provider_remove(bpf_provider_t * provider)294 bpf_provider_remove(bpf_provider_t *provider)
295 {
296 	bpf_provider_list_t *bp;
297 
298 	LIST_FOREACH(bp, &bpf_providers, bpl_next) {
299 		if (bp->bpl_what == provider)
300 			break;
301 	}
302 
303 	if (bp == NULL)
304 		return (ESRCH);
305 
306 	LIST_REMOVE(bp, bpl_next);
307 
308 	kmem_free(bp, sizeof (*bp));
309 
310 	return (0);
311 }
312 
313 /*
314  * return a pointer to the structure that holds all of the functions
315  * available to be used to support a particular packet provider.
316  */
317 bpf_provider_t *
bpf_find_provider_by_id(int who)318 bpf_find_provider_by_id(int who)
319 {
320 	bpf_provider_list_t *b;
321 
322 	LIST_FOREACH(b, &bpf_providers, bpl_next) {
323 		if (b->bpl_what->bpr_unit == who)
324 			return (b->bpl_what);
325 	}
326 
327 	return (NULL);
328 }
329 
330 /*
331  * This function is used by bpf_setif() to force an open() to be called on
332  * a given device name. If a device has been unloaded by the kernel, but it
333  * is still recognised, then calling this function will hopefully cause it
334  * to be loaded back into the kernel. When this function is called, it is
335  * not known which packet provider the name belongs to so all are tried.
336  */
337 int
bpf_provider_tickle(char * name,zoneid_t zone)338 bpf_provider_tickle(char *name, zoneid_t zone)
339 {
340 	bpf_provider_list_t *bp;
341 	uintptr_t handle;
342 	int tickled = 0;
343 
344 	LIST_FOREACH(bp, &bpf_providers, bpl_next) {
345 		handle = 0;
346 		if (bp->bpl_what->bpr_open(name, &handle, zone) == 0) {
347 			bp->bpl_what->bpr_close(handle);
348 			tickled++;
349 		} else if (bp->bpl_what->bpr_unit == BPR_MAC) {
350 			/*
351 			 * For mac devices, sometimes the open/close is not
352 			 * enough. In that case, further provocation is
353 			 * attempted by fetching the linkid and trying to
354 			 * use that as the key for open, rather than the
355 			 * name.
356 			 */
357 			datalink_id_t id;
358 
359 			if (bp->bpl_what->bpr_getlinkid(name, &id,
360 			    zone) == 0) {
361 				if (bp->bpl_what->bpr_open(name, &handle,
362 				    zone) == 0) {
363 					bp->bpl_what->bpr_close(handle);
364 					tickled++;
365 				} else {
366 					mac_handle_t mh;
367 
368 					if (mac_open_by_linkid(id, &mh) == 0) {
369 						mac_close(mh);
370 						tickled++;
371 					}
372 				}
373 			}
374 		}
375 
376 	}
377 
378 	if (tickled != 0)
379 		return (EWOULDBLOCK);
380 
381 	return (ENXIO);
382 }
383 
384 /*
385  * The following three functions provide the necessary callbacks into
386  * the netinfo API. This API is primarily used to trigger awareness of
387  * when a zone is being torn down, allowing BPF to drive IPNET to
388  * tell it which interfaces need to go away.
389  */
390 /*ARGSUSED*/
391 static void *
bpf_create_inst(const netid_t netid)392 bpf_create_inst(const netid_t netid)
393 {
394 	/*
395 	 * BPF does not keep any per-instance state, its list of
396 	 * interfaces is global, as is its device hash table.
397 	 */
398 	return ((void *)bpf_itap);
399 }
400 
401 /*ARGSUSED*/
402 static void
bpf_shutdown_inst(const netid_t netid,void * arg)403 bpf_shutdown_inst(const netid_t netid, void *arg)
404 {
405 }
406 
407 /*ARGSUSED*/
408 static void
bpf_destroy_inst(const netid_t netid,void * arg)409 bpf_destroy_inst(const netid_t netid, void *arg)
410 {
411 }
412