if_ef.c revision 132199
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 132199 2004-07-15 08:26:07Z phk $
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, __func__ ,## args)
73#else
74#define EFDEBUG(format, args...)
75#endif
76
77#define EFERROR(format, args...) printf("%s: "format, __func__ ,## args)
78
79struct efnet {
80	struct arpcom	ef_ac;
81	struct ifnet *  ef_ifp;
82	int		ef_frametype;
83};
84
85struct ef_link {
86	SLIST_ENTRY(ef_link) el_next;
87	struct ifnet	*el_ifp;		/* raw device for this clones */
88	struct efnet	*el_units[EF_NFT];	/* our clones */
89};
90
91static SLIST_HEAD(ef_link_head, ef_link) efdev = {NULL};
92static int efcount;
93
94extern int (*ef_inputp)(struct ifnet*, struct ether_header *eh, struct mbuf *m);
95extern int (*ef_outputp)(struct ifnet *ifp, struct mbuf **mp,
96		struct sockaddr *dst, short *tp, int *hlen);
97
98/*
99static void ef_reset (struct ifnet *);
100*/
101static int ef_attach(struct efnet *sc);
102static int ef_detach(struct efnet *sc);
103static void ef_init(void *);
104static int ef_ioctl(struct ifnet *, u_long, caddr_t);
105static void ef_start(struct ifnet *);
106static int ef_input(struct ifnet*, struct ether_header *, struct mbuf *);
107static int ef_output(struct ifnet *ifp, struct mbuf **mp,
108		struct sockaddr *dst, short *tp, int *hlen);
109
110static int ef_load(void);
111static int ef_unload(void);
112
113/*
114 * Install the interface, most of structure initialization done in ef_clone()
115 */
116static int
117ef_attach(struct efnet *sc)
118{
119	struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if;
120	struct ifaddr *ifa2;
121	struct sockaddr_dl *sdl2;
122
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	ifa2 = ifaddr_byindex(sc->ef_ifp->if_index);
132	sdl2 = (struct sockaddr_dl *)ifa2->ifa_addr;
133	ether_ifattach(ifp, LLADDR(sdl2));
134
135	ifp->if_resolvemulti = 0;
136	ifp->if_type = IFT_XETHER;
137	ifp->if_flags |= IFF_RUNNING;
138
139	bcopy(LLADDR(sdl2), sc->ef_ac.ac_enaddr, ETHER_ADDR_LEN);
140
141	EFDEBUG("%s: attached\n", ifp->if_xname);
142	return 1;
143}
144
145/*
146 * This is for _testing_only_, just removes interface from interfaces list
147 */
148static int
149ef_detach(struct efnet *sc)
150{
151	struct ifnet *ifp = (struct ifnet*)&sc->ef_ac.ac_if;
152	int s;
153
154	s = splimp();
155
156	if (ifp->if_flags & IFF_UP) {
157		if_down(ifp);
158		if (ifp->if_flags & IFF_RUNNING) {
159		    /* find internet addresses and delete routes */
160		    register struct ifaddr *ifa;
161		    TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
162			    rtinit(ifa, (int)RTM_DELETE, 0);
163		    }
164		}
165	}
166	IFNET_WLOCK();
167	TAILQ_REMOVE(&ifnet, ifp, if_link);
168	IFNET_WUNLOCK();
169	splx(s);
170	return 0;
171}
172
173static void
174ef_init(void *foo) {
175	return;
176}
177
178static int
179ef_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
180{
181	struct efnet *sc = ifp->if_softc;
182	struct ifaddr *ifa = (struct ifaddr*)data;
183	int s, error;
184
185	EFDEBUG("IOCTL %ld for %s\n", cmd, ifp->if_xname);
186	error = 0;
187	s = splimp();
188	switch (cmd) {
189	    case SIOCSIFFLAGS:
190		error = 0;
191		break;
192	    case SIOCSIFADDR:
193		if (sc->ef_frametype == ETHER_FT_8023 &&
194		    ifa->ifa_addr->sa_family != AF_IPX) {
195			error = EAFNOSUPPORT;
196			break;
197		}
198		ifp->if_flags |= IFF_UP;
199		/* FALL THROUGH */
200	    default:
201		error = ether_ioctl(ifp, cmd, data);
202		break;
203	}
204	splx(s);
205	return error;
206}
207
208/*
209 * Currently packet prepared in the ether_output(), but this can be a better
210 * place.
211 */
212static void
213ef_start(struct ifnet *ifp)
214{
215	struct efnet *sc = (struct efnet*)ifp->if_softc;
216	struct ifnet *p;
217	struct mbuf *m;
218	int error;
219
220	ifp->if_flags |= IFF_OACTIVE;
221	p = sc->ef_ifp;
222
223	EFDEBUG("\n");
224	for (;;) {
225		IF_DEQUEUE(&ifp->if_snd, m);
226		if (m == 0)
227			break;
228		BPF_MTAP(ifp, m);
229		IFQ_HANDOFF(p, m, error);
230		if (error) {
231			ifp->if_oerrors++;
232			continue;
233		}
234		ifp->if_opackets++;
235	}
236	ifp->if_flags &= ~IFF_OACTIVE;
237	return;
238}
239
240/*
241 * Inline functions do not put additional overhead to procedure call or
242 * parameter passing but simplify the code
243 */
244static int __inline
245ef_inputEII(struct mbuf *m, struct ether_header *eh, u_short ether_type)
246{
247	int isr;
248
249	switch(ether_type) {
250#ifdef IPX
251	case ETHERTYPE_IPX:
252		isr = NETISR_IPX;
253		break;
254#endif
255#ifdef INET
256	case ETHERTYPE_IP:
257		if (ip_fastforward(m))
258			return (0);
259		isr = NETISR_IP;
260		break;
261
262	case ETHERTYPE_ARP:
263		isr = NETISR_ARP;
264		break;
265#endif
266	default:
267		return (EPROTONOSUPPORT);
268	}
269	netisr_dispatch(isr, m);
270	return (0);
271}
272
273static int __inline
274ef_inputSNAP(struct mbuf *m, struct ether_header *eh, struct llc* l,
275	u_short ether_type)
276{
277	int isr;
278
279	switch(ether_type) {
280#ifdef IPX
281	case ETHERTYPE_IPX:
282		m_adj(m, 8);
283		isr = NETISR_IPX;
284		break;
285#endif
286	default:
287		return (EPROTONOSUPPORT);
288	}
289	netisr_dispatch(isr, m);
290	return (0);
291}
292
293static int __inline
294ef_input8022(struct mbuf *m, struct ether_header *eh, struct llc* l,
295	u_short ether_type)
296{
297	int isr;
298
299	switch(ether_type) {
300#ifdef IPX
301	case 0xe0:
302		m_adj(m, 3);
303		isr = NETISR_IPX;
304		break;
305#endif
306	default:
307		return (EPROTONOSUPPORT);
308	}
309	netisr_dispatch(isr, m);
310	return (0);
311}
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 ft = -1;
321	struct efnet *efp;
322	struct ifnet *eifp;
323	struct llc *l;
324	struct ef_link *efl;
325	int isr;
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 EPROTONOSUPPORT;
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 EPROTONOSUPPORT;
369	}
370	eifp = &efp->ef_ac.ac_if;
371	if ((eifp->if_flags & IFF_UP) == 0)
372		return EPROTONOSUPPORT;
373	eifp->if_ibytes += m->m_pkthdr.len + sizeof (*eh);
374	m->m_pkthdr.rcvif = eifp;
375
376	BPF_MTAP2(eifp, eh, ETHER_HDR_LEN, m);
377	/*
378	 * Now we ready to adjust mbufs and pass them to protocol intr's
379	 */
380	switch(ft) {
381	case ETHER_FT_EII:
382		return (ef_inputEII(m, eh, ether_type));
383#ifdef IPX
384	case ETHER_FT_8023:		/* only IPX can be here */
385		isr = NETISR_IPX;
386		break;
387#endif
388	case ETHER_FT_SNAP:
389		return (ef_inputSNAP(m, eh, l, ether_type));
390	case ETHER_FT_8022:
391		return (ef_input8022(m, eh, l, ether_type));
392	default:
393		EFDEBUG("No support for frame %d and proto %04x\n",
394			ft, ether_type);
395		return (EPROTONOSUPPORT);
396	}
397	netisr_dispatch(isr, m);
398	return (0);
399}
400
401static int
402ef_output(struct ifnet *ifp, struct mbuf **mp, struct sockaddr *dst, short *tp,
403	int *hlen)
404{
405	struct efnet *sc = (struct efnet*)ifp->if_softc;
406	struct mbuf *m = *mp;
407	u_char *cp;
408	short type;
409
410	if (ifp->if_type != IFT_XETHER)
411		return ENETDOWN;
412	switch (sc->ef_frametype) {
413	    case ETHER_FT_EII:
414#ifdef IPX
415		type = htons(ETHERTYPE_IPX);
416#else
417		return EPFNOSUPPORT;
418#endif
419		break;
420	    case ETHER_FT_8023:
421		type = htons(m->m_pkthdr.len);
422		break;
423	    case ETHER_FT_8022:
424		M_PREPEND(m, ETHER_HDR_LEN + 3, M_TRYWAIT);
425		if (m == NULL) {
426			*mp = NULL;
427			return ENOBUFS;
428		}
429		/*
430		 * Ensure that ethernet header and next three bytes
431		 * will fit into single mbuf
432		 */
433		m = m_pullup(m, ETHER_HDR_LEN + 3);
434		if (m == NULL) {
435			*mp = NULL;
436			return ENOBUFS;
437		}
438		m_adj(m, ETHER_HDR_LEN);
439		type = htons(m->m_pkthdr.len);
440		cp = mtod(m, u_char *);
441		*cp++ = 0xE0;
442		*cp++ = 0xE0;
443		*cp++ = 0x03;
444		*hlen += 3;
445		break;
446	    case ETHER_FT_SNAP:
447		M_PREPEND(m, 8, M_TRYWAIT);
448		if (m == NULL) {
449			*mp = NULL;
450			return ENOBUFS;
451		}
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		*hlen += 8;
456		break;
457	    default:
458		return EPFNOSUPPORT;
459	}
460	*mp = m;
461	*tp = type;
462	return 0;
463}
464
465/*
466 * Create clone from the given interface
467 */
468static int
469ef_clone(struct ef_link *efl, int ft)
470{
471	struct efnet *efp;
472	struct ifnet *eifp;
473	struct ifnet *ifp = efl->el_ifp;
474
475	efp = (struct efnet*)malloc(sizeof(struct efnet), M_IFADDR,
476	    M_WAITOK | M_ZERO);
477	if (efp == NULL)
478		return ENOMEM;
479	efp->ef_ifp = ifp;
480	efp->ef_frametype = ft;
481	eifp = &efp->ef_ac.ac_if;
482	snprintf(eifp->if_xname, IFNAMSIZ,
483	    "%sf%d", ifp->if_xname, efp->ef_frametype);
484	eifp->if_dname = "ef";
485	eifp->if_dunit = IF_DUNIT_NONE;
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	IFNET_RLOCK();
502	TAILQ_FOREACH(ifp, &ifnet, if_link) {
503		if (ifp->if_type != IFT_ETHER) continue;
504		EFDEBUG("Found interface %s\n", ifp->if_xname);
505		efl = (struct ef_link*)malloc(sizeof(struct ef_link),
506		    M_IFADDR, M_WAITOK | M_ZERO);
507		if (efl == NULL) {
508			error = ENOMEM;
509			break;
510		}
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	IFNET_RUNLOCK();
533	if (error) {
534		if (efl)
535			SLIST_INSERT_HEAD(&efdev, efl, el_next);
536		SLIST_FOREACH(efl, &efdev, el_next) {
537			for (d = 0; d < EF_NFT; d++)
538				if (efl->el_units[d])
539					free(efl->el_units[d], M_IFADDR);
540			free(efl, M_IFADDR);
541		}
542		return error;
543	}
544	SLIST_FOREACH(efl, &efdev, el_next) {
545		for (d = 0; d < EF_NFT; d++) {
546			efp = efl->el_units[d];
547			if (efp)
548				ef_attach(efp);
549		}
550	}
551	ef_inputp = ef_input;
552	ef_outputp = ef_output;
553	EFDEBUG("Loaded\n");
554	return 0;
555}
556
557static int
558ef_unload(void)
559{
560	struct efnet *efp;
561	struct ef_link *efl;
562	int d;
563
564	ef_inputp = NULL;
565	ef_outputp = NULL;
566	SLIST_FOREACH(efl, &efdev, el_next) {
567		for (d = 0; d < EF_NFT; d++) {
568			efp = efl->el_units[d];
569			if (efp) {
570				ef_detach(efp);
571			}
572		}
573	}
574	EFDEBUG("Unloaded\n");
575	return 0;
576}
577
578static int
579if_ef_modevent(module_t mod, int type, void *data)
580{
581	switch ((modeventtype_t)type) {
582	    case MOD_LOAD:
583		return ef_load();
584	    case MOD_UNLOAD:
585		return ef_unload();
586	    default:
587		return EOPNOTSUPP;
588	}
589	return 0;
590}
591
592static moduledata_t if_ef_mod = {
593	"if_ef", if_ef_modevent, NULL
594};
595
596DECLARE_MODULE(if_ef, if_ef_mod, SI_SUB_PSEUDO, SI_ORDER_MIDDLE);
597