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