if_fddisubr.c revision 163606
1139823Simp/*-
221830Sjoerg * Copyright (c) 1995, 1996
321830Sjoerg *	Matt Thomas <matt@3am-software.com>.  All rights reserved.
47055Sdg * Copyright (c) 1982, 1989, 1993
57055Sdg *	The Regents of the University of California.  All rights reserved.
67055Sdg *
77055Sdg * Redistribution and use in source and binary forms, with or without
87055Sdg * modification, are permitted provided that the following conditions
97055Sdg * are met:
107055Sdg * 1. Redistributions of source code must retain the above copyright
117055Sdg *    notice, this list of conditions and the following disclaimer.
127055Sdg * 2. Redistributions in binary form must reproduce the above copyright
137055Sdg *    notice, this list of conditions and the following disclaimer in the
147055Sdg *    documentation and/or other materials provided with the distribution.
157055Sdg * 3. All advertising materials mentioning features or use of this software
167055Sdg *    must display the following acknowledgement:
177055Sdg *	This product includes software developed by the University of
187055Sdg *	California, Berkeley and its contributors.
197055Sdg * 4. Neither the name of the University nor the names of its contributors
207055Sdg *    may be used to endorse or promote products derived from this software
217055Sdg *    without specific prior written permission.
227055Sdg *
237055Sdg * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
247055Sdg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
257055Sdg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
267055Sdg * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
277055Sdg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
287055Sdg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
297055Sdg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
307055Sdg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
317055Sdg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
327055Sdg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
337055Sdg * SUCH DAMAGE.
347055Sdg *
357061Sdg *	from: if_ethersubr.c,v 1.5 1994/12/13 22:31:45 wollman Exp
3650477Speter * $FreeBSD: head/sys/net/if_fddisubr.c 163606 2006-10-22 11:52:19Z rwatson $
377055Sdg */
387055Sdg
3932356Seivind#include "opt_atalk.h"
4032350Seivind#include "opt_inet.h"
4154263Sshin#include "opt_inet6.h"
4231742Seivind#include "opt_ipx.h"
43105577Srwatson#include "opt_mac.h"
4431742Seivind
457055Sdg#include <sys/param.h>
467055Sdg#include <sys/systm.h>
4793375Smdodd#include <sys/kernel.h>
4893375Smdodd#include <sys/malloc.h>
497055Sdg#include <sys/mbuf.h>
5093375Smdodd#include <sys/module.h>
517055Sdg#include <sys/socket.h>
5293375Smdodd#include <sys/sockio.h>
537055Sdg
547055Sdg#include <net/if.h>
55112271Smdodd#include <net/if_dl.h>
567055Sdg#include <net/if_llc.h>
577055Sdg#include <net/if_types.h>
58112271Smdodd
5993375Smdodd#include <net/netisr.h>
6093375Smdodd#include <net/route.h>
6193375Smdodd#include <net/bpf.h>
6293373Smdodd#include <net/fddi.h>
637055Sdg
6454263Sshin#if defined(INET) || defined(INET6)
657055Sdg#include <netinet/in.h>
667055Sdg#include <netinet/in_var.h>
6732350Seivind#include <netinet/if_ether.h>
687055Sdg#endif
6954263Sshin#ifdef INET6
7054263Sshin#include <netinet6/nd6.h>
7154263Sshin#endif
727055Sdg
7311819Sjulian#ifdef IPX
7421830Sjoerg#include <netipx/ipx.h>
7511819Sjulian#include <netipx/ipx_if.h>
7611819Sjulian#endif
7711819Sjulian
787055Sdg#ifdef DECNET
797055Sdg#include <netdnet/dn.h>
807055Sdg#endif
817055Sdg
8221830Sjoerg#ifdef NETATALK
8321830Sjoerg#include <netatalk/at.h>
8421830Sjoerg#include <netatalk/at_var.h>
8521830Sjoerg#include <netatalk/at_extern.h>
8621830Sjoerg
8721830Sjoergextern u_char	at_org_code[ 3 ];
8821830Sjoergextern u_char	aarp_org_code[ 3 ];
8921830Sjoerg#endif /* NETATALK */
9021830Sjoerg
91163606Srwatson#include <security/mac/mac_framework.h>
92163606Srwatson
93126788Srwatsonstatic const u_char fddibroadcastaddr[FDDI_ADDR_LEN] =
9493382Smdodd			{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
9593382Smdodd
9693383Smdoddstatic int fddi_resolvemulti(struct ifnet *, struct sockaddr **,
9793084Sbde			      struct sockaddr *);
9893383Smdoddstatic int fddi_output(struct ifnet *, struct mbuf *, struct sockaddr *,
9993383Smdodd		       struct rtentry *);
100106939Ssamstatic void fddi_input(struct ifnet *ifp, struct mbuf *m);
10168180Sume
102112276Smdodd#define	senderr(e)	do { error = (e); goto bad; } while (0)
10393369Smdodd
1047055Sdg/*
1057055Sdg * FDDI output routine.
1067055Sdg * Encapsulate a packet of type family for the local net.
1077055Sdg * Use trailer local net encapsulation if enough data in first
1087055Sdg * packet leaves a multiple of 512 bytes of data in remainder.
1097055Sdg * Assumes that ifp is actually pointer to arpcom structure.
1107055Sdg */
11193383Smdoddstatic int
11254799Sgreenfddi_output(ifp, m, dst, rt0)
11393367Smdodd	struct ifnet *ifp;
11454799Sgreen	struct mbuf *m;
1157055Sdg	struct sockaddr *dst;
1167055Sdg	struct rtentry *rt0;
1177055Sdg{
11821830Sjoerg	u_int16_t type;
11969152Sjlemon	int loop_copy = 0, error = 0, hdrcmplt = 0;
12093373Smdodd 	u_char esrc[FDDI_ADDR_LEN], edst[FDDI_ADDR_LEN];
12193367Smdodd	struct fddi_header *fh;
1227055Sdg
123105577Srwatson#ifdef MAC
124105577Srwatson	error = mac_check_ifnet_transmit(ifp, m);
125105577Srwatson	if (error)
126105577Srwatson		senderr(error);
127105577Srwatson#endif
128105577Srwatson
129112308Smdodd	if (ifp->if_flags & IFF_MONITOR)
130112308Smdodd		senderr(ENETDOWN);
131148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
132148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
1337055Sdg		senderr(ENETDOWN);
13434961Sphk	getmicrotime(&ifp->if_lastchange);
135111767Smdodd
1367055Sdg	switch (dst->sa_family) {
1377055Sdg#ifdef INET
13821830Sjoerg	case AF_INET: {
139128636Sluigi		error = arpresolve(ifp, rt0, m, dst, edst);
140128636Sluigi		if (error)
141128636Sluigi			return (error == EWOULDBLOCK ? 0 : error);
14221830Sjoerg		type = htons(ETHERTYPE_IP);
1437055Sdg		break;
14421830Sjoerg	}
145126951Smdodd	case AF_ARP:
146126951Smdodd	{
147126951Smdodd		struct arphdr *ah;
148126951Smdodd		ah = mtod(m, struct arphdr *);
149126951Smdodd		ah->ar_hrd = htons(ARPHRD_ETHER);
150126951Smdodd
151126951Smdodd		loop_copy = -1; /* if this is for us, don't do it */
152126951Smdodd
153126951Smdodd		switch (ntohs(ah->ar_op)) {
154126951Smdodd		case ARPOP_REVREQUEST:
155126951Smdodd		case ARPOP_REVREPLY:
156126951Smdodd			type = htons(ETHERTYPE_REVARP);
157126951Smdodd			break;
158126951Smdodd		case ARPOP_REQUEST:
159126951Smdodd		case ARPOP_REPLY:
160126951Smdodd		default:
161126951Smdodd			type = htons(ETHERTYPE_ARP);
162126951Smdodd			break;
163126951Smdodd		}
164126951Smdodd
165126951Smdodd		if (m->m_flags & M_BCAST)
166126951Smdodd			bcopy(ifp->if_broadcastaddr, edst, FDDI_ADDR_LEN);
167126951Smdodd                else
168126951Smdodd			bcopy(ar_tha(ah), edst, FDDI_ADDR_LEN);
169126951Smdodd
170126951Smdodd	}
171126951Smdodd	break;
172112266Smdodd#endif /* INET */
17354263Sshin#ifdef INET6
17454263Sshin	case AF_INET6:
175128636Sluigi		error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)edst);
176128636Sluigi		if (error)
177128636Sluigi			return (error); /* Something bad happened */
17854263Sshin		type = htons(ETHERTYPE_IPV6);
17954263Sshin		break;
180112266Smdodd#endif /* INET6 */
18111819Sjulian#ifdef IPX
18211819Sjulian	case AF_IPX:
18321830Sjoerg		type = htons(ETHERTYPE_IPX);
18411819Sjulian 		bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host),
18593373Smdodd		    (caddr_t)edst, FDDI_ADDR_LEN);
18611819Sjulian		break;
187112266Smdodd#endif /* IPX */
18821830Sjoerg#ifdef NETATALK
18921830Sjoerg	case AF_APPLETALK: {
19021830Sjoerg	    struct at_ifaddr *aa;
191128636Sluigi            if (!aarpresolve(ifp, m, (struct sockaddr_at *)dst, edst))
19221830Sjoerg                return (0);
19321830Sjoerg	    /*
19421830Sjoerg	     * ifaddr is the first thing in at_ifaddr
19521830Sjoerg	     */
19630834Sjulian	    if ((aa = at_ifawithnet( (struct sockaddr_at *)dst)) == 0)
19721830Sjoerg		goto bad;
19821830Sjoerg
19921830Sjoerg	    /*
20021830Sjoerg	     * In the phase 2 case, we need to prepend an mbuf for the llc header.
20121830Sjoerg	     * Since we must preserve the value of m, which is passed to us by
20221830Sjoerg	     * value, we m_copy() the first mbuf, and use it for our llc header.
20321830Sjoerg	     */
20421830Sjoerg	    if (aa->aa_flags & AFA_PHASE2) {
20521830Sjoerg		struct llc llc;
20621830Sjoerg
207111119Simp		M_PREPEND(m, LLC_SNAPFRAMELEN, M_TRYWAIT);
20821830Sjoerg		if (m == 0)
20921830Sjoerg			senderr(ENOBUFS);
21021830Sjoerg		llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP;
21121830Sjoerg		llc.llc_control = LLC_UI;
21293371Smdodd		bcopy(at_org_code, llc.llc_snap.org_code, sizeof(at_org_code));
21393371Smdodd		llc.llc_snap.ether_type = htons(ETHERTYPE_AT);
21493373Smdodd		bcopy(&llc, mtod(m, caddr_t), LLC_SNAPFRAMELEN);
21521830Sjoerg		type = 0;
21621830Sjoerg	    } else {
21721830Sjoerg		type = htons(ETHERTYPE_AT);
21821830Sjoerg	    }
21921830Sjoerg	    break;
22021830Sjoerg	}
22121830Sjoerg#endif /* NETATALK */
2227055Sdg
22352248Smsmith	case pseudo_AF_HDRCMPLT:
22452248Smsmith	{
22593376Smdodd		struct ether_header *eh;
22652248Smsmith		hdrcmplt = 1;
22793376Smdodd		eh = (struct ether_header *)dst->sa_data;
22893376Smdodd		bcopy((caddr_t)eh->ether_shost, (caddr_t)esrc, FDDI_ADDR_LEN);
22952248Smsmith		/* FALLTHROUGH */
23052248Smsmith	}
23152248Smsmith
2327055Sdg	case AF_UNSPEC:
2337055Sdg	{
2347055Sdg		struct ether_header *eh;
23536992Sjulian		loop_copy = -1;
2367055Sdg		eh = (struct ether_header *)dst->sa_data;
23793375Smdodd		bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, FDDI_ADDR_LEN);
2387055Sdg		if (*edst & 1)
2397055Sdg			m->m_flags |= (M_BCAST|M_MCAST);
2407055Sdg		type = eh->ether_type;
2417055Sdg		break;
2427055Sdg	}
2437055Sdg
2447055Sdg	case AF_IMPLINK:
2457055Sdg	{
2467055Sdg		fh = mtod(m, struct fddi_header *);
2477055Sdg		error = EPROTONOSUPPORT;
2487055Sdg		switch (fh->fddi_fc & (FDDIFC_C|FDDIFC_L|FDDIFC_F)) {
2497055Sdg			case FDDIFC_LLC_ASYNC: {
2507055Sdg				/* legal priorities are 0 through 7 */
2517055Sdg				if ((fh->fddi_fc & FDDIFC_Z) > 7)
2527055Sdg			        	goto bad;
2537055Sdg				break;
2547055Sdg			}
2557055Sdg			case FDDIFC_LLC_SYNC: {
2567055Sdg				/* FDDIFC_Z bits reserved, must be zero */
2577055Sdg				if (fh->fddi_fc & FDDIFC_Z)
2587055Sdg					goto bad;
2597055Sdg				break;
2607055Sdg			}
2617055Sdg			case FDDIFC_SMT: {
2627055Sdg				/* FDDIFC_Z bits must be non zero */
2637055Sdg				if ((fh->fddi_fc & FDDIFC_Z) == 0)
2647055Sdg					goto bad;
2657055Sdg				break;
2667055Sdg			}
2677055Sdg			default: {
2687055Sdg				/* anything else is too dangerous */
2697055Sdg               	 		goto bad;
2707055Sdg			}
2717055Sdg		}
2727055Sdg		error = 0;
2737055Sdg		if (fh->fddi_dhost[0] & 1)
2747055Sdg			m->m_flags |= (M_BCAST|M_MCAST);
2757055Sdg		goto queue_it;
2767055Sdg	}
2777055Sdg	default:
278105598Sbrooks		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
2797055Sdg		senderr(EAFNOSUPPORT);
2807055Sdg	}
2817055Sdg
28293380Smdodd	/*
28393380Smdodd	 * Add LLC header.
28493380Smdodd	 */
2857055Sdg	if (type != 0) {
28693367Smdodd		struct llc *l;
287111119Simp		M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
2887055Sdg		if (m == 0)
2897055Sdg			senderr(ENOBUFS);
2907055Sdg		l = mtod(m, struct llc *);
2917055Sdg		l->llc_control = LLC_UI;
2927055Sdg		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
293112266Smdodd		l->llc_snap.org_code[0] =
294112266Smdodd			l->llc_snap.org_code[1] =
295112266Smdodd			l->llc_snap.org_code[2] = 0;
296112266Smdodd		l->llc_snap.ether_type = htons(type);
2977055Sdg	}
29836908Sjulian
2997055Sdg	/*
3007055Sdg	 * Add local net header.  If no space in first mbuf,
3017055Sdg	 * allocate another.
3027055Sdg	 */
303111119Simp	M_PREPEND(m, FDDI_HDR_LEN, M_DONTWAIT);
3047055Sdg	if (m == 0)
3057055Sdg		senderr(ENOBUFS);
3067055Sdg	fh = mtod(m, struct fddi_header *);
3077055Sdg	fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4;
30893375Smdodd	bcopy((caddr_t)edst, (caddr_t)fh->fddi_dhost, FDDI_ADDR_LEN);
3097055Sdg  queue_it:
31052248Smsmith	if (hdrcmplt)
31193375Smdodd		bcopy((caddr_t)esrc, (caddr_t)fh->fddi_shost, FDDI_ADDR_LEN);
31252248Smsmith	else
313152315Sru		bcopy(IF_LLADDR(ifp), (caddr_t)fh->fddi_shost,
31493373Smdodd			FDDI_ADDR_LEN);
31593377Smdodd
31636908Sjulian	/*
31736908Sjulian	 * If a simplex interface, and the packet is being sent to our
31836908Sjulian	 * Ethernet address or a broadcast address, loopback a copy.
31936908Sjulian	 * XXX To make a simplex device behave exactly like a duplex
32036908Sjulian	 * device, we should copy in the case of sending to our own
32136908Sjulian	 * ethernet address (thus letting the original actually appear
32236908Sjulian	 * on the wire). However, we don't do that here for security
32336908Sjulian	 * reasons and compatibility with the original behavior.
32436908Sjulian	 */
32593377Smdodd	if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
326112279Smdodd		if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
327112279Smdodd			struct mbuf *n;
328112279Smdodd			n = m_copy(m, 0, (int)M_COPYALL);
329112279Smdodd			(void) if_simloop(ifp, n, dst->sa_family,
330112279Smdodd					  FDDI_HDR_LEN);
331112279Smdodd	     	} else if (bcmp(fh->fddi_dhost, fh->fddi_shost,
332112279Smdodd				FDDI_ADDR_LEN) == 0) {
333112279Smdodd			(void) if_simloop(ifp, m, dst->sa_family,
334112279Smdodd					  FDDI_HDR_LEN);
33593369Smdodd			return (0);	/* XXX */
33636908Sjulian		}
33736908Sjulian	}
33836908Sjulian
339130549Smlaier	IFQ_HANDOFF(ifp, m, error);
340130549Smlaier	if (error)
341130549Smlaier		ifp->if_oerrors++;
342130549Smlaier
3437055Sdg	return (error);
3447055Sdg
3457055Sdgbad:
34693379Smdodd	ifp->if_oerrors++;
3477055Sdg	if (m)
3487055Sdg		m_freem(m);
3497055Sdg	return (error);
3507055Sdg}
3517055Sdg
3527055Sdg/*
353112308Smdodd * Process a received FDDI packet.
3547055Sdg */
355106939Ssamstatic void
356106939Ssamfddi_input(ifp, m)
3577055Sdg	struct ifnet *ifp;
3587055Sdg	struct mbuf *m;
3597055Sdg{
360111888Sjlemon	int isr;
36193367Smdodd	struct llc *l;
362106939Ssam	struct fddi_header *fh;
3637055Sdg
364112308Smdodd	/*
365112308Smdodd	 * Do consistency checks to verify assumptions
366112308Smdodd	 * made by code past this point.
367112308Smdodd	 */
368112308Smdodd	if ((m->m_flags & M_PKTHDR) == 0) {
369112308Smdodd		if_printf(ifp, "discard frame w/o packet header\n");
370112308Smdodd		ifp->if_ierrors++;
371112308Smdodd		m_freem(m);
372112308Smdodd		return;
373112308Smdodd	}
374112308Smdodd	if (m->m_pkthdr.rcvif == NULL) {
375112308Smdodd		if_printf(ifp, "discard frame w/o interface pointer\n");
376112308Smdodd		ifp->if_ierrors++;
377112308Smdodd		m_freem(m);
378112308Smdodd		return;
379112308Smdodd        }
380112308Smdodd
381112308Smdodd	m = m_pullup(m, FDDI_HDR_LEN);
382112308Smdodd	if (m == NULL) {
383112308Smdodd		ifp->if_ierrors++;
384112308Smdodd		goto dropanyway;
385112308Smdodd	}
386106939Ssam	fh = mtod(m, struct fddi_header *);
387112308Smdodd	m->m_pkthdr.header = (void *)fh;
388106939Ssam
38993379Smdodd	/*
39093379Smdodd	 * Discard packet if interface is not up.
39193379Smdodd	 */
392148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
393148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
39493379Smdodd		goto dropanyway;
39593379Smdodd
396112308Smdodd	/*
397112308Smdodd	 * Give bpf a chance at the packet.
398112308Smdodd	 */
399112308Smdodd	BPF_MTAP(ifp, m);
400112308Smdodd
401112308Smdodd	/*
402112308Smdodd	 * Interface marked for monitoring; discard packet.
403112308Smdodd	 */
404112308Smdodd	if (ifp->if_flags & IFF_MONITOR) {
405112308Smdodd		m_freem(m);
406112308Smdodd		return;
407112308Smdodd	}
408112308Smdodd
409105577Srwatson#ifdef MAC
410105577Srwatson	mac_create_mbuf_from_ifnet(ifp, m);
411105577Srwatson#endif
412105577Srwatson
41393379Smdodd	/*
414112287Smdodd	 * Update interface statistics.
415112287Smdodd	 */
416112287Smdodd	ifp->if_ibytes += m->m_pkthdr.len;
417112287Smdodd	getmicrotime(&ifp->if_lastchange);
418112287Smdodd
419112287Smdodd	/*
42093379Smdodd	 * Discard non local unicast packets when interface
42193379Smdodd	 * is in promiscuous mode.
42293379Smdodd	 */
42393379Smdodd	if ((ifp->if_flags & IFF_PROMISC) && ((fh->fddi_dhost[0] & 1) == 0) &&
424152315Sru	    (bcmp(IF_LLADDR(ifp), (caddr_t)fh->fddi_dhost,
42593379Smdodd	     FDDI_ADDR_LEN) != 0))
42693379Smdodd		goto dropanyway;
42793379Smdodd
42893379Smdodd	/*
42993379Smdodd	 * Set mbuf flags for bcast/mcast.
43093379Smdodd	 */
43121830Sjoerg	if (fh->fddi_dhost[0] & 1) {
432121436Simp		if (bcmp(ifp->if_broadcastaddr, fh->fddi_dhost,
433121436Simp		    FDDI_ADDR_LEN) == 0)
43421830Sjoerg			m->m_flags |= M_BCAST;
43521830Sjoerg		else
43621830Sjoerg			m->m_flags |= M_MCAST;
4377055Sdg		ifp->if_imcasts++;
43821830Sjoerg	}
4397055Sdg
44021830Sjoerg#ifdef M_LINK0
44121830Sjoerg	/*
44221830Sjoerg	 * If this has a LLC priority of 0, then mark it so upper
44321830Sjoerg	 * layers have a hint that it really came via a FDDI/Ethernet
44421830Sjoerg	 * bridge.
44521830Sjoerg	 */
44621830Sjoerg	if ((fh->fddi_fc & FDDIFC_LLC_PRIO7) == FDDIFC_LLC_PRIO0)
44721830Sjoerg		m->m_flags |= M_LINK0;
44821830Sjoerg#endif
44921830Sjoerg
450106939Ssam	/* Strip off FDDI header. */
451111790Smdodd	m_adj(m, FDDI_HDR_LEN);
452106939Ssam
453111790Smdodd	m = m_pullup(m, LLC_SNAPFRAMELEN);
45493379Smdodd	if (m == 0) {
45593379Smdodd		ifp->if_ierrors++;
45693379Smdodd		goto dropanyway;
45793379Smdodd	}
4587055Sdg	l = mtod(m, struct llc *);
45993377Smdodd
4607055Sdg	switch (l->llc_dsap) {
4617055Sdg	case LLC_SNAP_LSAP:
4627055Sdg	{
46321830Sjoerg		u_int16_t type;
464112266Smdodd		if ((l->llc_control != LLC_UI) ||
465112266Smdodd		    (l->llc_ssap != LLC_SNAP_LSAP)) {
46693379Smdodd			ifp->if_noproto++;
4677055Sdg			goto dropanyway;
46893379Smdodd		}
46921830Sjoerg#ifdef NETATALK
470128396Sluigi		if (bcmp(&(l->llc_snap.org_code)[0], at_org_code,
471111888Sjlemon		    sizeof(at_org_code)) == 0 &&
472111888Sjlemon		    ntohs(l->llc_snap.ether_type) == ETHERTYPE_AT) {
473111888Sjlemon			isr = NETISR_ATALK2;
474111888Sjlemon			m_adj(m, LLC_SNAPFRAMELEN);
475111888Sjlemon			break;
47621830Sjoerg		}
47721830Sjoerg
478128396Sluigi		if (bcmp(&(l->llc_snap.org_code)[0], aarp_org_code,
479111888Sjlemon		    sizeof(aarp_org_code)) == 0 &&
480111888Sjlemon		    ntohs(l->llc_snap.ether_type) == ETHERTYPE_AARP) {
481111888Sjlemon			m_adj(m, LLC_SNAPFRAMELEN);
482111888Sjlemon			isr = NETISR_AARP;
483111888Sjlemon			break;
48421830Sjoerg		}
48521830Sjoerg#endif /* NETATALK */
48693377Smdodd		if (l->llc_snap.org_code[0] != 0 ||
48793377Smdodd		    l->llc_snap.org_code[1] != 0 ||
48893379Smdodd		    l->llc_snap.org_code[2] != 0) {
48993379Smdodd			ifp->if_noproto++;
4907055Sdg			goto dropanyway;
49193379Smdodd		}
49293377Smdodd
49321830Sjoerg		type = ntohs(l->llc_snap.ether_type);
49493377Smdodd		m_adj(m, LLC_SNAPFRAMELEN);
49593377Smdodd
49621830Sjoerg		switch (type) {
4977055Sdg#ifdef INET
4987055Sdg		case ETHERTYPE_IP:
499154518Sandre			if ((m = ip_fastforward(m)) == NULL)
50036192Sdg				return;
501111888Sjlemon			isr = NETISR_IP;
5027055Sdg			break;
5037055Sdg
5047055Sdg		case ETHERTYPE_ARP:
50578295Sjlemon			if (ifp->if_flags & IFF_NOARP)
50678295Sjlemon				goto dropanyway;
507111888Sjlemon			isr = NETISR_ARP;
5087055Sdg			break;
5097055Sdg#endif
51054263Sshin#ifdef INET6
51154263Sshin		case ETHERTYPE_IPV6:
512111888Sjlemon			isr = NETISR_IPV6;
51354263Sshin			break;
51454263Sshin#endif
51521830Sjoerg#ifdef IPX
51621830Sjoerg		case ETHERTYPE_IPX:
517111888Sjlemon			isr = NETISR_IPX;
51821830Sjoerg			break;
51921830Sjoerg#endif
5207055Sdg#ifdef DECNET
52121830Sjoerg		case ETHERTYPE_DECNET:
522111888Sjlemon			isr = NETISR_DECNET;
5237055Sdg			break;
5247055Sdg#endif
52521830Sjoerg#ifdef NETATALK
52621830Sjoerg		case ETHERTYPE_AT:
527111888Sjlemon	                isr = NETISR_ATALK1;
52821830Sjoerg			break;
52921830Sjoerg	        case ETHERTYPE_AARP:
530111888Sjlemon			isr = NETISR_AARP;
531111888Sjlemon			break;
53221830Sjoerg#endif /* NETATALK */
5337055Sdg		default:
53421830Sjoerg			/* printf("fddi_input: unknown protocol 0x%x\n", type); */
5357055Sdg			ifp->if_noproto++;
5367055Sdg			goto dropanyway;
5377055Sdg		}
5387055Sdg		break;
5397055Sdg	}
54021830Sjoerg
5417055Sdg	default:
54221830Sjoerg		/* printf("fddi_input: unknown dsap 0x%x\n", l->llc_dsap); */
5437055Sdg		ifp->if_noproto++;
54493377Smdodd		goto dropanyway;
5457055Sdg	}
546111888Sjlemon	netisr_dispatch(isr, m);
54793377Smdodd	return;
54893377Smdodd
54993377Smdodddropanyway:
55093377Smdodd	ifp->if_iqdrops++;
55193377Smdodd	if (m)
55293377Smdodd		m_freem(m);
55393377Smdodd	return;
5547055Sdg}
55593380Smdodd
5567055Sdg/*
5577055Sdg * Perform common duties while attaching to interface list
5587055Sdg */
5597055Sdgvoid
560152296Srufddi_ifattach(ifp, lla, bpf)
56193367Smdodd	struct ifnet *ifp;
562152296Sru	const u_int8_t *lla;
56393383Smdodd	int bpf;
5647055Sdg{
56593367Smdodd	struct ifaddr *ifa;
56693367Smdodd	struct sockaddr_dl *sdl;
5677055Sdg
5687055Sdg	ifp->if_type = IFT_FDDI;
56993373Smdodd	ifp->if_addrlen = FDDI_ADDR_LEN;
5707055Sdg	ifp->if_hdrlen = 21;
57193379Smdodd
57293379Smdodd	if_attach(ifp);         /* Must be called before additional assignments */
57393379Smdodd
5747055Sdg	ifp->if_mtu = FDDIMTU;
57593379Smdodd	ifp->if_output = fddi_output;
576106939Ssam	ifp->if_input = fddi_input;
57768180Sume	ifp->if_resolvemulti = fddi_resolvemulti;
57893379Smdodd	ifp->if_broadcastaddr = fddibroadcastaddr;
57916063Sgpalmer	ifp->if_baudrate = 100000000;
58021830Sjoerg#ifdef IFF_NOTRAILERS
58121830Sjoerg	ifp->if_flags |= IFF_NOTRAILERS;
58221830Sjoerg#endif
583152315Sru	ifa = ifp->if_addr;
584152315Sru	KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
58593379Smdodd
58621831Sjoerg	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
58721831Sjoerg	sdl->sdl_type = IFT_FDDI;
58821831Sjoerg	sdl->sdl_alen = ifp->if_addrlen;
589152296Sru	bcopy(lla, LLADDR(sdl), ifp->if_addrlen);
59093379Smdodd
59193383Smdodd	if (bpf)
59293383Smdodd		bpfattach(ifp, DLT_FDDI, FDDI_HDR_LEN);
59393383Smdodd
59493379Smdodd	return;
5957055Sdg}
59668180Sume
59793382Smdoddvoid
59893382Smdoddfddi_ifdetach(ifp, bpf)
59993382Smdodd	struct ifnet *ifp;
60093382Smdodd	int bpf;
60193382Smdodd{
60293382Smdodd
60393382Smdodd	if (bpf)
60493382Smdodd		bpfdetach(ifp);
60593382Smdodd
60693382Smdodd	if_detach(ifp);
60793382Smdodd
60893382Smdodd	return;
60993382Smdodd}
61093382Smdodd
61193382Smdoddint
61293382Smdoddfddi_ioctl (ifp, command, data)
61393382Smdodd	struct ifnet *ifp;
61493382Smdodd	int command;
61593382Smdodd	caddr_t data;
61693382Smdodd{
61793382Smdodd	struct ifaddr *ifa;
61893382Smdodd	struct ifreq *ifr;
61993382Smdodd	int error;
62093382Smdodd
62193382Smdodd	ifa = (struct ifaddr *) data;
62293382Smdodd	ifr = (struct ifreq *) data;
62393382Smdodd	error = 0;
62493382Smdodd
62593382Smdodd	switch (command) {
62693382Smdodd	case SIOCSIFADDR:
62793382Smdodd		ifp->if_flags |= IFF_UP;
62893382Smdodd
62993382Smdodd		switch (ifa->ifa_addr->sa_family) {
63093382Smdodd#ifdef INET
63193382Smdodd		case AF_INET:	/* before arpwhohas */
63293382Smdodd			ifp->if_init(ifp->if_softc);
63393382Smdodd			arp_ifinit(ifp, ifa);
63493382Smdodd			break;
63593382Smdodd#endif
63693382Smdodd#ifdef IPX
63793382Smdodd		/*
63893382Smdodd		 * XXX - This code is probably wrong
63993382Smdodd		 */
64093382Smdodd		case AF_IPX: {
64193382Smdodd				struct ipx_addr *ina;
64293382Smdodd
64393382Smdodd				ina = &(IA_SIPX(ifa)->sipx_addr);
64493382Smdodd
64593382Smdodd				if (ipx_nullhost(*ina)) {
64693382Smdodd					ina->x_host = *(union ipx_host *)
647152315Sru							IF_LLADDR(ifp);
64893382Smdodd				} else {
64993382Smdodd					bcopy((caddr_t) ina->x_host.c_host,
650152315Sru					      (caddr_t) IF_LLADDR(ifp),
651147256Sbrooks					      ETHER_ADDR_LEN);
65293382Smdodd				}
65393382Smdodd
65493382Smdodd				/*
65593382Smdodd				 * Set new address
65693382Smdodd				 */
65793382Smdodd				ifp->if_init(ifp->if_softc);
65893382Smdodd			}
65993382Smdodd			break;
66093382Smdodd#endif
66193382Smdodd		default:
66293382Smdodd			ifp->if_init(ifp->if_softc);
66393382Smdodd			break;
664104302Sphk		}
665144045Smdodd		break;
66693382Smdodd	case SIOCGIFADDR: {
66793382Smdodd			struct sockaddr *sa;
66893382Smdodd
66993382Smdodd			sa = (struct sockaddr *) & ifr->ifr_data;
670152315Sru			bcopy(IF_LLADDR(ifp),
67193382Smdodd			      (caddr_t) sa->sa_data, FDDI_ADDR_LEN);
67293382Smdodd
67393382Smdodd		}
67493382Smdodd		break;
67593382Smdodd	case SIOCSIFMTU:
67693382Smdodd		/*
67793382Smdodd		 * Set the interface MTU.
67893382Smdodd		 */
67993382Smdodd		if (ifr->ifr_mtu > FDDIMTU) {
68093382Smdodd			error = EINVAL;
68193382Smdodd		} else {
68293382Smdodd			ifp->if_mtu = ifr->ifr_mtu;
68393382Smdodd		}
68493382Smdodd		break;
68593382Smdodd	default:
686144045Smdodd		error = EINVAL;
68793382Smdodd		break;
68893382Smdodd	}
68993382Smdodd
69093382Smdodd	return (error);
69193382Smdodd}
69293382Smdodd
69368180Sumestatic int
69468180Sumefddi_resolvemulti(ifp, llsa, sa)
69568180Sume	struct ifnet *ifp;
69668180Sume	struct sockaddr **llsa;
69768180Sume	struct sockaddr *sa;
69868180Sume{
69968180Sume	struct sockaddr_dl *sdl;
70068180Sume	struct sockaddr_in *sin;
70168180Sume#ifdef INET6
70268180Sume	struct sockaddr_in6 *sin6;
70368180Sume#endif
70468180Sume	u_char *e_addr;
70568180Sume
70668180Sume	switch(sa->sa_family) {
70768180Sume	case AF_LINK:
70868180Sume		/*
70968180Sume		 * No mapping needed. Just check that it's a valid MC address.
71068180Sume		 */
71168180Sume		sdl = (struct sockaddr_dl *)sa;
71268180Sume		e_addr = LLADDR(sdl);
71368180Sume		if ((e_addr[0] & 1) != 1)
71493369Smdodd			return (EADDRNOTAVAIL);
71568180Sume		*llsa = 0;
71693369Smdodd		return (0);
71768180Sume
71868180Sume#ifdef INET
71968180Sume	case AF_INET:
72068180Sume		sin = (struct sockaddr_in *)sa;
72168180Sume		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
72293369Smdodd			return (EADDRNOTAVAIL);
72368180Sume		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
724148641Srwatson		       M_NOWAIT | M_ZERO);
725148641Srwatson		if (sdl == NULL)
726148641Srwatson			return (ENOMEM);
72768180Sume		sdl->sdl_len = sizeof *sdl;
72868180Sume		sdl->sdl_family = AF_LINK;
72968180Sume		sdl->sdl_index = ifp->if_index;
73068180Sume		sdl->sdl_type = IFT_FDDI;
73168180Sume		sdl->sdl_nlen = 0;
73293375Smdodd		sdl->sdl_alen = FDDI_ADDR_LEN;
73368180Sume		sdl->sdl_slen = 0;
73468180Sume		e_addr = LLADDR(sdl);
73568180Sume		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
73668180Sume		*llsa = (struct sockaddr *)sdl;
73793369Smdodd		return (0);
73868180Sume#endif
73968180Sume#ifdef INET6
74068180Sume	case AF_INET6:
74168180Sume		sin6 = (struct sockaddr_in6 *)sa;
74268180Sume		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
74368180Sume			/*
74468180Sume			 * An IP6 address of 0 means listen to all
74568180Sume			 * of the Ethernet multicast address used for IP6.
74668180Sume			 * (This is used for multicast routers.)
74768180Sume			 */
74868180Sume			ifp->if_flags |= IFF_ALLMULTI;
74968180Sume			*llsa = 0;
75093369Smdodd			return (0);
75168180Sume		}
75268180Sume		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
75393369Smdodd			return (EADDRNOTAVAIL);
75468180Sume		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
755148641Srwatson		       M_NOWAIT | M_ZERO);
756148641Srwatson		if (sdl == NULL)
757148641Srwatson			return (ENOMEM);
75868180Sume		sdl->sdl_len = sizeof *sdl;
75968180Sume		sdl->sdl_family = AF_LINK;
76068180Sume		sdl->sdl_index = ifp->if_index;
76168180Sume		sdl->sdl_type = IFT_FDDI;
76268180Sume		sdl->sdl_nlen = 0;
76393375Smdodd		sdl->sdl_alen = FDDI_ADDR_LEN;
76468180Sume		sdl->sdl_slen = 0;
76568180Sume		e_addr = LLADDR(sdl);
76668180Sume		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
76768180Sume		*llsa = (struct sockaddr *)sdl;
76893369Smdodd		return (0);
76968180Sume#endif
77068180Sume
77168180Sume	default:
77268180Sume		/*
77368180Sume		 * Well, the text isn't quite right, but it's the name
77468180Sume		 * that counts...
77568180Sume		 */
77693369Smdodd		return (EAFNOSUPPORT);
77768180Sume	}
77893375Smdodd
77993375Smdodd	return (0);
78068180Sume}
78193375Smdodd
78293375Smdoddstatic moduledata_t fddi_mod = {
78393375Smdodd	"fddi",	/* module name */
78493375Smdodd	NULL,	/* event handler */
78593375Smdodd	0	/* extra data */
78693375Smdodd};
78793375Smdodd
78893375SmdoddDECLARE_MODULE(fddi, fddi_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
78993375SmdoddMODULE_VERSION(fddi, 1);
790