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