if_ef.c revision 69781
1/*-
2 * Copyright (c) 1999, 2000 Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/net/if_ef.c 69781 2000-12-08 21:51:06Z dwmalone $
27 */
28
29#include "opt_inet.h"
30#include "opt_ipx.h"
31#include "opt_ef.h"
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/sockio.h>
36#include <sys/malloc.h>
37#include <sys/mbuf.h>
38#include <sys/socket.h>
39#include <sys/syslog.h>
40#include <sys/kernel.h>
41#include <sys/module.h>
42
43#include <net/ethernet.h>
44#include <net/if_llc.h>
45#include <net/if.h>
46#include <net/if_arp.h>
47#include <net/if_dl.h>
48#include <net/if_types.h>
49#include <net/netisr.h>
50#include <net/route.h>
51#include <net/bpf.h>
52
53#ifdef INET
54#include <netinet/in.h>
55#include <netinet/in_var.h>
56#include <netinet/if_ether.h>
57#endif
58
59#ifdef IPX
60#include <netipx/ipx.h>
61#include <netipx/ipx_if.h>
62#endif
63
64/* internal frame types */
65#define ETHER_FT_EII		0	/* Ethernet_II - default */
66#define	ETHER_FT_8023		1	/* 802.3 (Novell) */
67#define	ETHER_FT_8022		2	/* 802.2 */
68#define	ETHER_FT_SNAP		3	/* SNAP */
69#define	EF_NFT			4	/* total number of frame types */
70
71#ifdef EF_DEBUG
72#define EFDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args)
73#else
74#define EFDEBUG(format, args...)
75#endif
76
77#define EFERROR(format, args...) printf("%s: "format, __FUNCTION__ ,## args)
78
79struct efnet {
80	struct arpcom	ef_ac;
81	struct ifnet *  ef_ifp;
82};
83
84struct ef_link {
85	SLIST_ENTRY(ef_link) el_next;
86	struct ifnet	*el_ifp;		/* raw device for this clones */
87	struct efnet	*el_units[EF_NFT];	/* our clones */
88};
89
90static SLIST_HEAD(ef_link_head, ef_link) efdev = {NULL};
91static int efcount;
92
93extern int (*ef_inputp)(struct ifnet*, struct ether_header *eh, struct mbuf *m);
94extern int (*ef_outputp)(struct ifnet *ifp, struct mbuf **mp,
95		struct sockaddr *dst, short *tp, int *hlen);
96
97/*
98static void ef_reset (struct ifnet *);
99*/
100static int ef_attach(struct efnet *sc);
101static int ef_detach(struct efnet *sc);
102static void ef_init(void *);
103static int ef_ioctl(struct ifnet *, u_long, caddr_t);
104static void ef_start(struct ifnet *);
105static int ef_input(struct ifnet*, struct ether_header *, struct mbuf *);
106static int ef_output(struct ifnet *ifp, struct mbuf **mp,
107		struct sockaddr *dst, short *tp, int *hlen);
108
109static int ef_load(void);
110static int ef_unload(void);
111
112/*
113 * Install the interface, most of structure initialization done in ef_clone()
114 */
115static int
116ef_attach(struct efnet *sc)
117{
118	struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if;
119	struct ifaddr *ifa1, *ifa2;
120	struct sockaddr_dl *sdl1, *sdl2;
121
122	ifp->if_output = ether_output;
123	ifp->if_start = ef_start;
124	ifp->if_watchdog = NULL;
125	ifp->if_init = ef_init;
126	ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
127	ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
128	/*
129	 * Attach the interface
130	 */
131	ether_ifattach(ifp, ETHER_BPF_SUPPORTED);
132
133	ifp->if_resolvemulti = 0;
134	ifp->if_type = IFT_XETHER;
135	ifp->if_flags |= IFF_RUNNING;
136
137	ifa1 = ifnet_addrs[ifp->if_index - 1];
138	ifa2 = ifnet_addrs[sc->ef_ifp->if_index - 1];
139	sdl1 = (struct sockaddr_dl *)ifa1->ifa_addr;
140	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
141	sdl1->sdl_type = IFT_ETHER;
142	sdl1->sdl_alen = ETHER_ADDR_LEN;
143	bcopy(LLADDR(sdl2), LLADDR(sdl1), ETHER_ADDR_LEN);
144	bcopy(LLADDR(sdl2), sc->ef_ac.ac_enaddr, ETHER_ADDR_LEN);
145
146	EFDEBUG("%s%d: attached\n", ifp->if_name, ifp->if_unit);
147	return 1;
148}
149
150/*
151 * This is for _testing_only_, just removes interface from interfaces list
152 */
153static int
154ef_detach(struct efnet *sc)
155{
156	struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if;
157	int s;
158
159	s = splimp();
160
161	if (ifp->if_flags & IFF_UP) {
162		if_down(ifp);
163		if (ifp->if_flags & IFF_RUNNING) {
164		    /* find internet addresses and delete routes */
165		    register struct ifaddr *ifa;
166		    for (ifa = ifp->if_addrhead.tqh_first; ifa;
167			 ifa = ifa->ifa_link.tqe_next) {
168			    rtinit(ifa, (int)RTM_DELETE, 0);
169		    }
170		}
171	}
172
173	TAILQ_REMOVE(&ifnet, ifp, if_link);
174	splx(s);
175	return 0;
176}
177
178static void
179ef_init(void *foo) {
180	return;
181}
182
183static int
184ef_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
185{
186/*	struct ef_link *sc = (struct ef_link*)ifp->if_softc;*/
187	struct ifaddr *ifa = (struct ifaddr*)data;
188	int s, error;
189
190	EFDEBUG("IOCTL %ld for %s%d\n", cmd, ifp->if_name, ifp->if_unit);
191	error = 0;
192	s = splimp();
193	switch (cmd) {
194	    case SIOCSIFADDR:
195		if (ifp->if_unit == ETHER_FT_8023 &&
196		    ifa->ifa_addr->sa_family != AF_IPX) {
197			error = EAFNOSUPPORT;
198			break;
199		}
200		ifp->if_flags |= IFF_UP;
201		/* FALL THROUGH */
202	    case SIOCGIFADDR:
203	    case SIOCSIFMTU:
204		error = ether_ioctl(ifp, cmd, data);
205		break;
206	    case SIOCSIFFLAGS:
207		error = 0;
208		break;
209	    default:
210		error = EINVAL;
211	}
212	splx(s);
213	return error;
214}
215
216/*
217 * Currently packet prepared in the ether_output(), but this can be a better
218 * place.
219 */
220static void
221ef_start(struct ifnet *ifp)
222{
223	struct efnet *sc = (struct efnet*)ifp->if_softc;
224	struct ifnet *p;
225	struct mbuf *m;
226
227	ifp->if_flags |= IFF_OACTIVE;
228	p = sc->ef_ifp;
229
230	EFDEBUG("\n");
231	for (;;) {
232		IF_DEQUEUE(&ifp->if_snd, m);
233		if (m == 0)
234			break;
235		if (ifp->if_bpf)
236			bpf_mtap(ifp, m);
237		if (! IF_HANDOFF(&p->if_snd, m, NULL)) {
238			ifp->if_oerrors++;
239			continue;
240		}
241		ifp->if_opackets++;
242	}
243	ifp->if_flags &= ~IFF_OACTIVE;
244	return;
245}
246
247/*
248 * Inline functions do not put additional overhead to procedure call or
249 * parameter passing but simplify the code
250 */
251static int __inline
252ef_inputEII(struct mbuf *m, struct ether_header *eh, struct llc* l,
253	u_short ether_type, struct ifqueue **inq)
254{
255	switch(ether_type) {
256#ifdef IPX
257	    case ETHERTYPE_IPX:
258		schednetisr(NETISR_IPX);
259		*inq = &ipxintrq;
260		break;
261#endif
262#ifdef INET
263	    case ETHERTYPE_IP:
264		if (ipflow_fastforward(m))
265			return 1;
266		schednetisr(NETISR_IP);
267		*inq = &ipintrq;
268		break;
269
270	    case ETHERTYPE_ARP:
271		schednetisr(NETISR_ARP);
272		*inq = &arpintrq;
273		break;
274#endif
275	    default:
276		return EPROTONOSUPPORT;
277	}
278	return 0;
279}
280
281static int __inline
282ef_inputSNAP(struct mbuf *m, struct ether_header *eh, struct llc* l,
283	u_short ether_type, struct ifqueue **inq)
284{
285	switch(ether_type) {
286#ifdef IPX
287	    case ETHERTYPE_IPX:
288		m_adj(m, 8);
289		schednetisr(NETISR_IPX);
290		*inq = &ipxintrq;
291		break;
292#endif
293	    default:
294		return EPROTONOSUPPORT;
295	}
296	return 0;
297}
298
299static int __inline
300ef_input8022(struct mbuf *m, struct ether_header *eh, struct llc* l,
301	u_short ether_type, struct ifqueue **inq)
302{
303	switch(ether_type) {
304#ifdef IPX
305	    case 0xe0:
306		m_adj(m, 3);
307		schednetisr(NETISR_IPX);
308		*inq = &ipxintrq;
309		break;
310#endif
311	    default:
312		return EPROTONOSUPPORT;
313	}
314	return 0;
315}
316/*
317 * Called from ether_input()
318 */
319static int
320ef_input(struct ifnet *ifp, struct ether_header *eh, struct mbuf *m)
321{
322	u_short ether_type;
323	int s, ft = -1;
324	struct ifqueue *inq;
325	struct efnet *efp;
326	struct ifnet *eifp;
327	struct llc *l;
328	struct ef_link *efl;
329
330	ether_type = ntohs(eh->ether_type);
331	if (ether_type < ETHERMTU) {
332		l = mtod(m, struct llc*);
333		if (l->llc_dsap == 0xff && l->llc_ssap == 0xff) {
334			/*
335			 * Novell's "802.3" frame
336			 */
337			ft = ETHER_FT_8023;
338		} else if (l->llc_dsap == 0xaa && l->llc_ssap == 0xaa) {
339			/*
340			 * 802.2/SNAP
341			 */
342			ft = ETHER_FT_SNAP;
343			ether_type = ntohs(l->llc_un.type_snap.ether_type);
344		} else if (l->llc_dsap == l->llc_ssap) {
345			/*
346			 * 802.3/802.2
347			 */
348			ft = ETHER_FT_8022;
349			ether_type = l->llc_ssap;
350		}
351	} else
352		ft = ETHER_FT_EII;
353
354	if (ft == -1) {
355		EFDEBUG("Unrecognised ether_type %x\n", ether_type);
356		return EPROTONOSUPPORT;
357	}
358
359	/*
360	 * Check if interface configured for the given frame
361	 */
362	efp = NULL;
363	SLIST_FOREACH(efl, &efdev, el_next) {
364		if (efl->el_ifp == ifp) {
365			efp = efl->el_units[ft];
366			break;
367		}
368	}
369	if (efp == NULL) {
370		EFDEBUG("Can't find if for %d\n", ft);
371		return EPROTONOSUPPORT;
372	}
373	eifp = &efp->ef_ac.ac_if;
374	if ((eifp->if_flags & IFF_UP) == 0)
375		return EPROTONOSUPPORT;
376	eifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
377	m->m_pkthdr.rcvif = eifp;
378
379	if (eifp->if_bpf) {
380		struct mbuf m0;
381		m0.m_next = m;
382		m0.m_len = sizeof(struct ether_header);
383		m0.m_data = (char *)eh;
384		bpf_mtap(eifp, &m0);
385	}
386	/*
387	 * Now we ready to adjust mbufs and pass them to protocol intr's
388	 */
389	inq = NULL;
390	switch(ft) {
391	    case ETHER_FT_EII:
392		if (ef_inputEII(m, eh, l, ether_type, &inq) != 0)
393			return EPROTONOSUPPORT;
394		break;
395#ifdef IPX
396	    case ETHER_FT_8023:		/* only IPX can be here */
397		schednetisr(NETISR_IPX);
398		inq = &ipxintrq;
399		break;
400#endif
401	    case ETHER_FT_SNAP:
402		if (ef_inputSNAP(m, eh, l, ether_type, &inq) != 0)
403			return EPROTONOSUPPORT;
404		break;
405	    case ETHER_FT_8022:
406		if (ef_input8022(m, eh, l, ether_type, &inq) != 0)
407			return EPROTONOSUPPORT;
408		break;
409	}
410
411	if (inq == NULL) {
412		EFDEBUG("No support for frame %d and proto %04x\n",
413			ft, ether_type);
414		return EPROTONOSUPPORT;
415	}
416	(void) IF_HANDOFF(inq, m, NULL);
417	return 0;
418}
419
420static int
421ef_output(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp,
422	int *hlen)
423{
424	struct mbuf *m = *mp;
425	u_char *cp;
426	short type;
427
428	if (ifp->if_type != IFT_XETHER)
429		return ENETDOWN;
430	switch (ifp->if_unit) {
431	    case ETHER_FT_EII:
432#ifdef IPX
433		type = htons(ETHERTYPE_IPX);
434#else
435		return EPFNOSUPPORT;
436#endif
437		break;
438	    case ETHER_FT_8023:
439		type = htons(m->m_pkthdr.len);
440		break;
441	    case ETHER_FT_8022:
442		M_PREPEND(m, ETHER_HDR_LEN + 3, M_WAIT);
443		if (m == NULL) {
444			*mp = NULL;
445			return ENOBUFS;
446		}
447		/*
448		 * Ensure that ethernet header and next three bytes
449		 * will fit into single mbuf
450		 */
451		m = m_pullup(m, ETHER_HDR_LEN + 3);
452		if (m == NULL) {
453			*mp = NULL;
454			return ENOBUFS;
455		}
456		m_adj(m, ETHER_HDR_LEN);
457		type = htons(m->m_pkthdr.len);
458		cp = mtod(m, u_char *);
459		*cp++ = 0xE0;
460		*cp++ = 0xE0;
461		*cp++ = 0x03;
462		*hlen += 3;
463		break;
464	    case ETHER_FT_SNAP:
465		M_PREPEND(m, 8, M_WAIT);
466		if (m == NULL) {
467			*mp = NULL;
468			return ENOBUFS;
469		}
470		type = htons(m->m_pkthdr.len);
471		cp = mtod(m, u_char *);
472		bcopy("\xAA\xAA\x03\x00\x00\x00\x81\x37", cp, 8);
473		*hlen += 8;
474		break;
475	    default:
476		return EPFNOSUPPORT;
477	}
478	*mp = m;
479	*tp = type;
480	return 0;
481}
482
483/*
484 * Create clone from the given interface
485 */
486static int
487ef_clone(struct ef_link *efl, int ft)
488{
489	struct efnet *efp;
490	struct ifnet *eifp;
491	struct ifnet *ifp = efl->el_ifp;
492	char cbuf[IFNAMSIZ], *ifname;
493	int ifnlen;
494
495	efp = (struct efnet*)malloc(sizeof(struct efnet), M_IFADDR,
496	    M_WAITOK | M_ZERO);
497	if (efp == NULL)
498		return ENOMEM;
499	efp->ef_ifp = ifp;
500	eifp = &efp->ef_ac.ac_if;
501	ifnlen = 1 + snprintf(cbuf, sizeof(cbuf), "%s%df", ifp->if_name,
502	    ifp->if_unit);
503	ifname = (char*)malloc(ifnlen, M_IFADDR, M_WAITOK);
504	eifp->if_name = strcpy(ifname, cbuf);
505	eifp->if_unit = ft;
506	eifp->if_softc = efp;
507	if (ifp->if_ioctl)
508		eifp->if_ioctl = ef_ioctl;
509	efl->el_units[ft] = efp;
510	return 0;
511}
512
513static int
514ef_load(void)
515{
516	struct ifnet *ifp;
517	struct efnet *efp;
518	struct ef_link *efl = NULL;
519	int error = 0, d;
520
521	for (ifp = ifnet.tqh_first; ifp; ifp = ifp->if_link.tqe_next) {
522		if (ifp->if_type != IFT_ETHER) continue;
523		EFDEBUG("Found interface %s%d\n", ifp->if_name, ifp->if_unit);
524		efl = (struct ef_link*)malloc(sizeof(struct ef_link),
525		    M_IFADDR, M_WAITOK);
526		if (efl == NULL) {
527			error = ENOMEM;
528			break;
529		}
530		bzero(efl, sizeof(*efl));
531
532		efl->el_ifp = ifp;
533#ifdef ETHER_II
534		error = ef_clone(efl, ETHER_FT_EII);
535		if (error) break;
536#endif
537#ifdef ETHER_8023
538		error = ef_clone(efl, ETHER_FT_8023);
539		if (error) break;
540#endif
541#ifdef ETHER_8022
542		error = ef_clone(efl, ETHER_FT_8022);
543		if (error) break;
544#endif
545#ifdef ETHER_SNAP
546		error = ef_clone(efl, ETHER_FT_SNAP);
547		if (error) break;
548#endif
549		efcount++;
550		SLIST_INSERT_HEAD(&efdev, efl, el_next);
551	}
552	if (error) {
553		if (efl)
554			SLIST_INSERT_HEAD(&efdev, efl, el_next);
555		SLIST_FOREACH(efl, &efdev, el_next) {
556			for (d = 0; d < EF_NFT; d++)
557				if (efl->el_units[d])
558					free(efl->el_units[d], M_IFADDR);
559			free(efl, M_IFADDR);
560		}
561		return error;
562	}
563	SLIST_FOREACH(efl, &efdev, el_next) {
564		for (d = 0; d < EF_NFT; d++) {
565			efp = efl->el_units[d];
566			if (efp)
567				ef_attach(efp);
568		}
569	}
570	ef_inputp = ef_input;
571	ef_outputp = ef_output;
572	EFDEBUG("Loaded\n");
573	return 0;
574}
575
576static int
577ef_unload(void)
578{
579	struct efnet *efp;
580	struct ef_link *efl;
581	int d;
582
583	ef_inputp = NULL;
584	ef_outputp = NULL;
585	SLIST_FOREACH(efl, &efdev, el_next) {
586		for (d = 0; d < EF_NFT; d++) {
587			efp = efl->el_units[d];
588			if (efp) {
589				ef_detach(efp);
590			}
591		}
592	}
593	EFDEBUG("Unloaded\n");
594	return 0;
595}
596
597static int
598if_ef_modevent(module_t mod, int type, void *data)
599{
600	switch ((modeventtype_t)type) {
601	    case MOD_LOAD:
602		return ef_load();
603	    case MOD_UNLOAD:
604		return ef_unload();
605	    default:
606		break;
607	}
608	return 0;
609}
610
611static moduledata_t if_ef_mod = {
612	"if_ef", if_ef_modevent, NULL
613};
614
615DECLARE_MODULE(if_ef, if_ef_mod, SI_SUB_PSEUDO, SI_ORDER_MIDDLE);
616