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