xref: /freebsd/sys/netgraph/ng_eiface.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
1 /*-
2  *
3  * Copyright (c) 1999-2001, Vitaly V Belekhov
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice unmodified, this list of conditions, and the following
11  *    disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/errno.h>
34 #include <sys/kernel.h>
35 #include <sys/lock.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/mutex.h>
39 #include <sys/errno.h>
40 #include <sys/sockio.h>
41 #include <sys/socket.h>
42 #include <sys/syslog.h>
43 
44 #include <net/if.h>
45 #include <net/if_dl.h>
46 #include <net/if_types.h>
47 #include <net/netisr.h>
48 
49 #include <netgraph/ng_message.h>
50 #include <netgraph/netgraph.h>
51 #include <netgraph/ng_parse.h>
52 #include <netgraph/ng_eiface.h>
53 
54 #include <net/bpf.h>
55 #include <net/ethernet.h>
56 #include <net/if_arp.h>
57 
58 static const struct ng_cmdlist ng_eiface_cmdlist[] = {
59 	{
60 	  NGM_EIFACE_COOKIE,
61 	  NGM_EIFACE_GET_IFNAME,
62 	  "getifname",
63 	  NULL,
64 	  &ng_parse_string_type
65 	},
66 	{
67 	  NGM_EIFACE_COOKIE,
68 	  NGM_EIFACE_SET,
69 	  "set",
70 	  &ng_parse_enaddr_type,
71 	  NULL
72 	},
73 	{ 0 }
74 };
75 
76 /* Node private data */
77 struct ng_eiface_private {
78 	struct arpcom	arpcom;		/* per-interface network data */
79 	struct ifnet	*ifp;		/* This interface */
80 	int		unit;		/* Interface unit number */
81 	node_p		node;		/* Our netgraph node */
82 	hook_p		ether;		/* Hook for ethernet stream */
83 };
84 typedef struct ng_eiface_private *priv_p;
85 
86 /* Interface methods */
87 static void	ng_eiface_init(void *xsc);
88 static void	ng_eiface_start(struct ifnet *ifp);
89 static int	ng_eiface_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
90 #ifdef DEBUG
91 static void	ng_eiface_print_ioctl(struct ifnet *ifp, int cmd, caddr_t data);
92 #endif
93 
94 /* Netgraph methods */
95 static int		ng_eiface_mod_event(module_t, int, void *);
96 static ng_constructor_t	ng_eiface_constructor;
97 static ng_rcvmsg_t	ng_eiface_rcvmsg;
98 static ng_shutdown_t	ng_eiface_rmnode;
99 static ng_newhook_t	ng_eiface_newhook;
100 static ng_rcvdata_t	ng_eiface_rcvdata;
101 static ng_connect_t	ng_eiface_connect;
102 static ng_disconnect_t	ng_eiface_disconnect;
103 
104 /* Node type descriptor */
105 static struct ng_type typestruct = {
106 	.version =	NG_ABI_VERSION,
107 	.name =		NG_EIFACE_NODE_TYPE,
108 	.mod_event =	ng_eiface_mod_event,
109 	.constructor =	ng_eiface_constructor,
110 	.rcvmsg =	ng_eiface_rcvmsg,
111 	.shutdown =	ng_eiface_rmnode,
112 	.newhook =	ng_eiface_newhook,
113 	.connect =	ng_eiface_connect,
114 	.rcvdata =	ng_eiface_rcvdata,
115 	.disconnect =	ng_eiface_disconnect,
116 	.cmdlist =	ng_eiface_cmdlist
117 };
118 NETGRAPH_INIT(eiface, &typestruct);
119 
120 /* We keep a bitmap indicating which unit numbers are free.
121    One means the unit number is free, zero means it's taken. */
122 static int	*ng_eiface_units = NULL;
123 static int	ng_eiface_units_len = 0;
124 static int	ng_units_in_use = 0;
125 
126 #define UNITS_BITSPERWORD	(sizeof(*ng_eiface_units) * NBBY)
127 
128 static struct mtx	ng_eiface_mtx;
129 
130 /************************************************************************
131 			HELPER STUFF
132  ************************************************************************/
133 /*
134  * Find the first free unit number for a new interface.
135  * Increase the size of the unit bitmap as necessary.
136  */
137 static __inline int
138 ng_eiface_get_unit(int *unit)
139 {
140 	int index, bit;
141 
142 	mtx_lock(&ng_eiface_mtx);
143 	for (index = 0; index < ng_eiface_units_len
144 	    && ng_eiface_units[index] == 0; index++);
145 	if (index == ng_eiface_units_len) {		/* extend array */
146 		int i, *newarray, newlen;
147 
148 		newlen = (2 * ng_eiface_units_len) + 4;
149 		MALLOC(newarray, int *, newlen * sizeof(*ng_eiface_units),
150 		    M_NETGRAPH, M_NOWAIT);
151 		if (newarray == NULL) {
152 			mtx_unlock(&ng_eiface_mtx);
153 			return (ENOMEM);
154 		}
155 		bcopy(ng_eiface_units, newarray,
156 		    ng_eiface_units_len * sizeof(*ng_eiface_units));
157 		for (i = ng_eiface_units_len; i < newlen; i++)
158 			newarray[i] = ~0;
159 		if (ng_eiface_units != NULL)
160 			FREE(ng_eiface_units, M_NETGRAPH);
161 		ng_eiface_units = newarray;
162 		ng_eiface_units_len = newlen;
163 	}
164 	bit = ffs(ng_eiface_units[index]) - 1;
165 	KASSERT(bit >= 0 && bit <= UNITS_BITSPERWORD - 1,
166 	    ("%s: word=%d bit=%d", __func__, ng_eiface_units[index], bit));
167 	ng_eiface_units[index] &= ~(1 << bit);
168 	*unit = (index * UNITS_BITSPERWORD) + bit;
169 	ng_units_in_use++;
170 	mtx_unlock(&ng_eiface_mtx);
171 	return (0);
172 }
173 
174 /*
175  * Free a no longer needed unit number.
176  */
177 static __inline void
178 ng_eiface_free_unit(int unit)
179 {
180 	int index, bit;
181 
182 	index = unit / UNITS_BITSPERWORD;
183 	bit = unit % UNITS_BITSPERWORD;
184 	mtx_lock(&ng_eiface_mtx);
185 	KASSERT(index < ng_eiface_units_len,
186 	    ("%s: unit=%d len=%d", __func__, unit, ng_eiface_units_len));
187 	KASSERT((ng_eiface_units[index] & (1 << bit)) == 0,
188 	    ("%s: unit=%d is free", __func__, unit));
189 	ng_eiface_units[index] |= (1 << bit);
190 	/*
191 	 * XXX We could think about reducing the size of ng_eiface_units[]
192 	 * XXX here if the last portion is all ones
193 	 * XXX At least free it if no more units.
194 	 * Needed if we are to eventually be able to unload.
195 	 */
196 	ng_units_in_use--;
197 	if (ng_units_in_use == 0) { /* XXX make SMP safe */
198 		FREE(ng_eiface_units, M_NETGRAPH);
199 		ng_eiface_units_len = 0;
200 		ng_eiface_units = NULL;
201 	}
202 	mtx_unlock(&ng_eiface_mtx);
203 }
204 
205 /************************************************************************
206 			INTERFACE STUFF
207  ************************************************************************/
208 
209 /*
210  * Process an ioctl for the virtual interface
211  */
212 static int
213 ng_eiface_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
214 {
215 	struct ifreq *const ifr = (struct ifreq *)data;
216 	int s, error = 0;
217 
218 #ifdef DEBUG
219 	ng_eiface_print_ioctl(ifp, command, data);
220 #endif
221 	s = splimp();
222 	switch (command) {
223 
224 	/* These two are mostly handled at a higher layer */
225 	case SIOCSIFADDR:
226 		error = ether_ioctl(ifp, command, data);
227 		break;
228 	case SIOCGIFADDR:
229 		break;
230 
231 	/* Set flags */
232 	case SIOCSIFFLAGS:
233 		/*
234 		 * If the interface is marked up and stopped, then start it.
235 		 * If it is marked down and running, then stop it.
236 		 */
237 		if (ifr->ifr_flags & IFF_UP) {
238 			if (!(ifp->if_flags & IFF_RUNNING)) {
239 				ifp->if_flags &= ~(IFF_OACTIVE);
240 				ifp->if_flags |= IFF_RUNNING;
241 			}
242 		} else {
243 			if (ifp->if_flags & IFF_RUNNING)
244 				ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
245 		}
246 		break;
247 
248 	/* Set the interface MTU */
249 	case SIOCSIFMTU:
250 		if (ifr->ifr_mtu > NG_EIFACE_MTU_MAX ||
251 		    ifr->ifr_mtu < NG_EIFACE_MTU_MIN)
252 			error = EINVAL;
253 		else
254 			ifp->if_mtu = ifr->ifr_mtu;
255 		break;
256 
257 	/* Stuff that's not supported */
258 	case SIOCADDMULTI:
259 	case SIOCDELMULTI:
260 		error = 0;
261 		break;
262 	case SIOCSIFPHYS:
263 		error = EOPNOTSUPP;
264 		break;
265 
266 	default:
267 		error = EINVAL;
268 		break;
269 	}
270 	splx(s);
271 	return (error);
272 }
273 
274 static void
275 ng_eiface_init(void *xsc)
276 {
277 	priv_p sc = xsc;
278 	struct ifnet *ifp = sc->ifp;
279 	int s;
280 
281 	s = splimp();
282 
283 	ifp->if_flags |= IFF_RUNNING;
284 	ifp->if_flags &= ~IFF_OACTIVE;
285 
286 	splx(s);
287 }
288 
289 /*
290  * We simply relay the packet to the "ether" hook, if it is connected.
291  * We have been through the netgraph locking and are guaranteed to
292  * be the only code running in this node at this time.
293  */
294 static void
295 ng_eiface_start2(node_p node, hook_p hook, void *arg1, int arg2)
296 {
297 	struct ifnet *ifp = arg1;
298 	const priv_p priv = (priv_p)ifp->if_softc;
299 	int len, error = 0;
300 	struct mbuf *m;
301 
302 	/* Check interface flags */
303 	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
304 		return;
305 
306 	/* Don't do anything if output is active */
307 	if (ifp->if_flags & IFF_OACTIVE)
308 		return;
309 
310 	ifp->if_flags |= IFF_OACTIVE;
311 
312 	/*
313 	 * Grab a packet to transmit.
314 	 */
315 	IF_DEQUEUE(&ifp->if_snd, m);
316 
317 	/* If there's nothing to send, return. */
318 	if (m == NULL) {
319 		ifp->if_flags &= ~IFF_OACTIVE;
320 		return;
321 	}
322 
323 	/*
324 	 * Berkeley packet filter.
325 	 * Pass packet to bpf if there is a listener.
326 	 * XXX is this safe? locking?
327 	 */
328 	BPF_MTAP(ifp, m);
329 
330 	/* Copy length before the mbuf gets invalidated */
331 	len = m->m_pkthdr.len;
332 
333 	/*
334 	 * Send packet; if hook is not connected, mbuf will get
335 	 * freed.
336 	 */
337 	NG_SEND_DATA_ONLY(error, priv->ether, m);
338 
339 	/* Update stats */
340 	if (error == 0) {
341 		ifp->if_obytes += len;
342 		ifp->if_opackets++;
343 	}
344 
345 	ifp->if_flags &= ~IFF_OACTIVE;
346 
347 	return;
348 }
349 
350 /*
351  * This routine is called to deliver a packet out the interface.
352  * We simply queue the netgraph version to be called when netgraph locking
353  * allows it to happen.
354  * Until we know what the rest of the networking code is doing for
355  * locking, we don't know how we will interact with it.
356  * Take comfort from the fact that the ifnet struct is part of our
357  * private info and can't go away while we are queued.
358  * [Though we don't know it is still there now....]
359  * it is possible we don't gain anything from this because
360  * we would like to get the mbuf and queue it as data
361  * somehow, but we can't and if we did would we solve anything?
362  */
363 static void
364 ng_eiface_start(struct ifnet *ifp)
365 {
366 
367 	const priv_p priv = (priv_p)ifp->if_softc;
368 
369 	ng_send_fn(priv->node, NULL, &ng_eiface_start2, ifp, 0);
370 }
371 
372 #ifdef DEBUG
373 /*
374  * Display an ioctl to the virtual interface
375  */
376 
377 static void
378 ng_eiface_print_ioctl(struct ifnet *ifp, int command, caddr_t data)
379 {
380 	char *str;
381 
382 	switch (command & IOC_DIRMASK) {
383 	case IOC_VOID:
384 		str = "IO";
385 		break;
386 	case IOC_OUT:
387 		str = "IOR";
388 		break;
389 	case IOC_IN:
390 		str = "IOW";
391 		break;
392 	case IOC_INOUT:
393 		str = "IORW";
394 		break;
395 	default:
396 		str = "IO??";
397 	}
398 	log(LOG_DEBUG, "%s: %s('%c', %d, char[%d])\n",
399 	    ifp->if_xname,
400 	    str,
401 	    IOCGROUP(command),
402 	    command & 0xff,
403 	    IOCPARM_LEN(command));
404 }
405 #endif /* DEBUG */
406 
407 /************************************************************************
408 			NETGRAPH NODE STUFF
409  ************************************************************************/
410 
411 /*
412  * Constructor for a node
413  */
414 static int
415 ng_eiface_constructor(node_p node)
416 {
417 	struct ifnet *ifp;
418 	priv_p priv;
419 	int error = 0;
420 
421 	/* Allocate node and interface private structures */
422 	MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO);
423 	if (priv == NULL)
424 		return (ENOMEM);
425 
426 	ifp = &(priv->arpcom.ac_if);
427 
428 	/* Link them together */
429 	ifp->if_softc = priv;
430 	priv->ifp = ifp;
431 
432 	/* Get an interface unit number */
433 	if ((error = ng_eiface_get_unit(&priv->unit)) != 0) {
434 		FREE(priv, M_NETGRAPH);
435 		return (error);
436 	}
437 
438 	/* Link together node and private info */
439 	NG_NODE_SET_PRIVATE(node, priv);
440 	priv->node = node;
441 
442 	/* Initialize interface structure */
443 	if_initname(ifp, NG_EIFACE_EIFACE_NAME, priv->unit);
444 	ifp->if_init = ng_eiface_init;
445 	ifp->if_output = ether_output;
446 	ifp->if_start = ng_eiface_start;
447 	ifp->if_ioctl = ng_eiface_ioctl;
448 	ifp->if_watchdog = NULL;
449 	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
450 	ifp->if_flags = (IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST);
451 
452 #if 0
453 	/* Give this node name */
454 	bzero(ifname, sizeof(ifname));
455 	sprintf(ifname, "if%s", ifp->if_xname);
456 	(void)ng_name_node(node, ifname);
457 #endif
458 
459 	/* Attach the interface */
460 	ether_ifattach(ifp, priv->arpcom.ac_enaddr);
461 
462 	/* Done */
463 	return (0);
464 }
465 
466 /*
467  * Give our ok for a hook to be added
468  */
469 static int
470 ng_eiface_newhook(node_p node, hook_p hook, const char *name)
471 {
472 	priv_p priv = NG_NODE_PRIVATE(node);
473 
474 	if (strcmp(name, NG_EIFACE_HOOK_ETHER))
475 		return (EPFNOSUPPORT);
476 	if (priv->ether != NULL)
477 		return (EISCONN);
478 	priv->ether = hook;
479 	NG_HOOK_SET_PRIVATE(hook, &priv->ether);
480 
481 	return (0);
482 }
483 
484 /*
485  * Receive a control message
486  */
487 static int
488 ng_eiface_rcvmsg(node_p node, item_p item, hook_p lasthook)
489 {
490 	const priv_p priv = NG_NODE_PRIVATE(node);
491 	struct ifnet *const ifp = priv->ifp;
492 	struct ng_mesg *resp = NULL;
493 	int error = 0;
494 	struct ng_mesg *msg;
495 
496 	NGI_GET_MSG(item, msg);
497 	switch (msg->header.typecookie) {
498 	case NGM_EIFACE_COOKIE:
499 		switch (msg->header.cmd) {
500 
501 		case NGM_EIFACE_SET:
502 		    {
503 			struct ether_addr *eaddr;
504 			struct ifaddr *ifa;
505 			struct sockaddr_dl *sdl;
506 
507 			if (msg->header.arglen != sizeof(struct ether_addr)) {
508 				error = EINVAL;
509 				break;
510 			}
511 			eaddr = (struct ether_addr *)(msg->data);
512 			bcopy(eaddr, priv->arpcom.ac_enaddr, ETHER_ADDR_LEN);
513 
514 			/* And put it in the ifaddr list */
515 			TAILQ_FOREACH(ifa, &(ifp->if_addrhead), ifa_link) {
516 				sdl = (struct sockaddr_dl *)ifa->ifa_addr;
517 				if (sdl->sdl_type == IFT_ETHER) {
518 					bcopy((IFP2AC(ifp))->ac_enaddr,
519 						LLADDR(sdl), ifp->if_addrlen);
520 					break;
521 				}
522 			}
523 			break;
524 		    }
525 
526 		case NGM_EIFACE_GET_IFNAME:
527 			NG_MKRESPONSE(resp, msg, IFNAMSIZ, M_NOWAIT);
528 			if (resp == NULL) {
529 				error = ENOMEM;
530 				break;
531 			}
532 			strlcpy(resp->data, ifp->if_xname, IFNAMSIZ);
533 			break;
534 
535 		case NGM_EIFACE_GET_IFADDRS:
536 		    {
537 			struct ifaddr *ifa;
538 			caddr_t ptr;
539 			int buflen;
540 
541 #define SA_SIZE(s)	((s)->sa_len<sizeof(*(s))? sizeof(*(s)):(s)->sa_len)
542 
543 			/* Determine size of response and allocate it */
544 			buflen = 0;
545 			TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link)
546 				buflen += SA_SIZE(ifa->ifa_addr);
547 			NG_MKRESPONSE(resp, msg, buflen, M_NOWAIT);
548 			if (resp == NULL) {
549 				error = ENOMEM;
550 				break;
551 			}
552 
553 			/* Add addresses */
554 			ptr = resp->data;
555 			TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
556 				const int len = SA_SIZE(ifa->ifa_addr);
557 
558 				if (buflen < len) {
559 					log(LOG_ERR, "%s: len changed?\n",
560 					    ifp->if_xname);
561 					break;
562 				}
563 				bcopy(ifa->ifa_addr, ptr, len);
564 				ptr += len;
565 				buflen -= len;
566 			}
567 			break;
568 #undef SA_SIZE
569 		    }
570 
571 		default:
572 			error = EINVAL;
573 			break;
574 		} /* end of inner switch() */
575 		break;
576 	case NGM_FLOW_COOKIE:
577 		switch (msg->header.cmd) {
578 		case NGM_LINK_IS_UP:
579 			ifp->if_flags |= IFF_RUNNING;
580 			break;
581 		case NGM_LINK_IS_DOWN:
582 			ifp->if_flags &= ~IFF_RUNNING;
583 			break;
584 		default:
585 			break;
586 		}
587 		break;
588 	default:
589 		error = EINVAL;
590 		break;
591 	}
592 	NG_RESPOND_MSG(error, node, item, resp);
593 	NG_FREE_MSG(msg);
594 	return (error);
595 }
596 
597 /*
598  * Receive data from a hook. Pass the packet to the ether_input routine.
599  */
600 static int
601 ng_eiface_rcvdata(hook_p hook, item_p item)
602 {
603 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
604 	struct ifnet *const ifp = priv->ifp;
605 	struct mbuf *m;
606 
607 	NGI_GET_M(item, m);
608 	NG_FREE_ITEM(item);
609 
610 	if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) !=
611 	    (IFF_UP | IFF_RUNNING)) {
612 		NG_FREE_M(m);
613 		return (ENETDOWN);
614 	}
615 
616 	/* Note receiving interface */
617 	m->m_pkthdr.rcvif = ifp;
618 
619 	/* Update interface stats */
620 	ifp->if_ipackets++;
621 
622 	(*ifp->if_input)(ifp, m);
623 
624 	/* Done */
625 	return (0);
626 }
627 
628 /*
629  * Shutdown processing.
630  */
631 static int
632 ng_eiface_rmnode(node_p node)
633 {
634 	const priv_p priv = NG_NODE_PRIVATE(node);
635 	struct ifnet *const ifp = priv->ifp;
636 
637 	ether_ifdetach(ifp);
638 	ng_eiface_free_unit(priv->unit);
639 	FREE(priv, M_NETGRAPH);
640 	NG_NODE_SET_PRIVATE(node, NULL);
641 	NG_NODE_UNREF(node);
642 	return (0);
643 }
644 
645 
646 /*
647  * This is called once we've already connected a new hook to the other node.
648  * It gives us a chance to balk at the last minute.
649  */
650 static int
651 ng_eiface_connect(hook_p hook)
652 {
653 	/* be really amiable and just say "YUP that's OK by me! " */
654 	return (0);
655 }
656 
657 /*
658  * Hook disconnection
659  */
660 static int
661 ng_eiface_disconnect(hook_p hook)
662 {
663 	const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
664 
665 	priv->ether = NULL;
666 	return (0);
667 }
668 
669 /*
670  * Handle loading and unloading for this node type.
671  */
672 static int
673 ng_eiface_mod_event(module_t mod, int event, void *data)
674 {
675 	int error = 0;
676 
677 	switch (event) {
678 	case MOD_LOAD:
679 		mtx_init(&ng_eiface_mtx, "ng_eiface", NULL, MTX_DEF);
680 		break;
681 	case MOD_UNLOAD:
682 		mtx_destroy(&ng_eiface_mtx);
683 		break;
684 	default:
685 		error = EOPNOTSUPP;
686 		break;
687 	}
688 	return (error);
689 }
690