if_fddisubr.c revision 184709
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 184709 2008-11-06 09:07:56Z bz $
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
59184709Sbz#include <net/ethernet.h>
6093375Smdodd#include <net/netisr.h>
6193375Smdodd#include <net/route.h>
6293375Smdodd#include <net/bpf.h>
6393373Smdodd#include <net/fddi.h>
647055Sdg
6554263Sshin#if defined(INET) || defined(INET6)
667055Sdg#include <netinet/in.h>
677055Sdg#include <netinet/in_var.h>
6832350Seivind#include <netinet/if_ether.h>
697055Sdg#endif
7054263Sshin#ifdef INET6
7154263Sshin#include <netinet6/nd6.h>
7254263Sshin#endif
737055Sdg
7411819Sjulian#ifdef IPX
7521830Sjoerg#include <netipx/ipx.h>
7611819Sjulian#include <netipx/ipx_if.h>
7711819Sjulian#endif
7811819Sjulian
797055Sdg#ifdef DECNET
807055Sdg#include <netdnet/dn.h>
817055Sdg#endif
827055Sdg
8321830Sjoerg#ifdef NETATALK
8421830Sjoerg#include <netatalk/at.h>
8521830Sjoerg#include <netatalk/at_var.h>
8621830Sjoerg#include <netatalk/at_extern.h>
8721830Sjoerg
8821830Sjoergextern u_char	at_org_code[ 3 ];
8921830Sjoergextern u_char	aarp_org_code[ 3 ];
9021830Sjoerg#endif /* NETATALK */
9121830Sjoerg
92163606Srwatson#include <security/mac/mac_framework.h>
93163606Srwatson
94126788Srwatsonstatic const u_char fddibroadcastaddr[FDDI_ADDR_LEN] =
9593382Smdodd			{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
9693382Smdodd
9793383Smdoddstatic int fddi_resolvemulti(struct ifnet *, struct sockaddr **,
9893084Sbde			      struct sockaddr *);
9993383Smdoddstatic int fddi_output(struct ifnet *, struct mbuf *, struct sockaddr *,
10093383Smdodd		       struct rtentry *);
101106939Ssamstatic void fddi_input(struct ifnet *ifp, struct mbuf *m);
10268180Sume
103112276Smdodd#define	senderr(e)	do { error = (e); goto bad; } while (0)
10493369Smdodd
1057055Sdg/*
1067055Sdg * FDDI output routine.
1077055Sdg * Encapsulate a packet of type family for the local net.
1087055Sdg * Use trailer local net encapsulation if enough data in first
1097055Sdg * packet leaves a multiple of 512 bytes of data in remainder.
1107055Sdg * Assumes that ifp is actually pointer to arpcom structure.
1117055Sdg */
11293383Smdoddstatic int
11354799Sgreenfddi_output(ifp, m, dst, rt0)
11493367Smdodd	struct ifnet *ifp;
11554799Sgreen	struct mbuf *m;
1167055Sdg	struct sockaddr *dst;
1177055Sdg	struct rtentry *rt0;
1187055Sdg{
11921830Sjoerg	u_int16_t type;
12069152Sjlemon	int loop_copy = 0, error = 0, hdrcmplt = 0;
12193373Smdodd 	u_char esrc[FDDI_ADDR_LEN], edst[FDDI_ADDR_LEN];
12293367Smdodd	struct fddi_header *fh;
1237055Sdg
124105577Srwatson#ifdef MAC
125172930Srwatson	error = mac_ifnet_check_transmit(ifp, m);
126105577Srwatson	if (error)
127105577Srwatson		senderr(error);
128105577Srwatson#endif
129105577Srwatson
130112308Smdodd	if (ifp->if_flags & IFF_MONITOR)
131112308Smdodd		senderr(ENETDOWN);
132148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
133148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
1347055Sdg		senderr(ENETDOWN);
13534961Sphk	getmicrotime(&ifp->if_lastchange);
136111767Smdodd
1377055Sdg	switch (dst->sa_family) {
1387055Sdg#ifdef INET
13921830Sjoerg	case AF_INET: {
140128636Sluigi		error = arpresolve(ifp, rt0, m, dst, edst);
141128636Sluigi		if (error)
142128636Sluigi			return (error == EWOULDBLOCK ? 0 : error);
14321830Sjoerg		type = htons(ETHERTYPE_IP);
1447055Sdg		break;
14521830Sjoerg	}
146126951Smdodd	case AF_ARP:
147126951Smdodd	{
148126951Smdodd		struct arphdr *ah;
149126951Smdodd		ah = mtod(m, struct arphdr *);
150126951Smdodd		ah->ar_hrd = htons(ARPHRD_ETHER);
151126951Smdodd
152126951Smdodd		loop_copy = -1; /* if this is for us, don't do it */
153126951Smdodd
154126951Smdodd		switch (ntohs(ah->ar_op)) {
155126951Smdodd		case ARPOP_REVREQUEST:
156126951Smdodd		case ARPOP_REVREPLY:
157126951Smdodd			type = htons(ETHERTYPE_REVARP);
158126951Smdodd			break;
159126951Smdodd		case ARPOP_REQUEST:
160126951Smdodd		case ARPOP_REPLY:
161126951Smdodd		default:
162126951Smdodd			type = htons(ETHERTYPE_ARP);
163126951Smdodd			break;
164126951Smdodd		}
165126951Smdodd
166126951Smdodd		if (m->m_flags & M_BCAST)
167126951Smdodd			bcopy(ifp->if_broadcastaddr, edst, FDDI_ADDR_LEN);
168126951Smdodd                else
169126951Smdodd			bcopy(ar_tha(ah), edst, FDDI_ADDR_LEN);
170126951Smdodd
171126951Smdodd	}
172126951Smdodd	break;
173112266Smdodd#endif /* INET */
17454263Sshin#ifdef INET6
17554263Sshin	case AF_INET6:
176128636Sluigi		error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)edst);
177128636Sluigi		if (error)
178128636Sluigi			return (error); /* Something bad happened */
17954263Sshin		type = htons(ETHERTYPE_IPV6);
18054263Sshin		break;
181112266Smdodd#endif /* INET6 */
18211819Sjulian#ifdef IPX
18311819Sjulian	case AF_IPX:
18421830Sjoerg		type = htons(ETHERTYPE_IPX);
18511819Sjulian 		bcopy((caddr_t)&(((struct sockaddr_ipx *)dst)->sipx_addr.x_host),
18693373Smdodd		    (caddr_t)edst, FDDI_ADDR_LEN);
18711819Sjulian		break;
188112266Smdodd#endif /* IPX */
18921830Sjoerg#ifdef NETATALK
19021830Sjoerg	case AF_APPLETALK: {
19121830Sjoerg	    struct at_ifaddr *aa;
192128636Sluigi            if (!aarpresolve(ifp, m, (struct sockaddr_at *)dst, edst))
19321830Sjoerg                return (0);
19421830Sjoerg	    /*
19521830Sjoerg	     * ifaddr is the first thing in at_ifaddr
19621830Sjoerg	     */
19730834Sjulian	    if ((aa = at_ifawithnet( (struct sockaddr_at *)dst)) == 0)
19821830Sjoerg		goto bad;
19921830Sjoerg
20021830Sjoerg	    /*
20121830Sjoerg	     * In the phase 2 case, we need to prepend an mbuf for the llc header.
20221830Sjoerg	     * Since we must preserve the value of m, which is passed to us by
20321830Sjoerg	     * value, we m_copy() the first mbuf, and use it for our llc header.
20421830Sjoerg	     */
20521830Sjoerg	    if (aa->aa_flags & AFA_PHASE2) {
20621830Sjoerg		struct llc llc;
20721830Sjoerg
208177599Sru		M_PREPEND(m, LLC_SNAPFRAMELEN, M_WAIT);
20921830Sjoerg		llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP;
21021830Sjoerg		llc.llc_control = LLC_UI;
21193371Smdodd		bcopy(at_org_code, llc.llc_snap.org_code, sizeof(at_org_code));
21293371Smdodd		llc.llc_snap.ether_type = htons(ETHERTYPE_AT);
21393373Smdodd		bcopy(&llc, mtod(m, caddr_t), LLC_SNAPFRAMELEN);
21421830Sjoerg		type = 0;
21521830Sjoerg	    } else {
21621830Sjoerg		type = htons(ETHERTYPE_AT);
21721830Sjoerg	    }
21821830Sjoerg	    break;
21921830Sjoerg	}
22021830Sjoerg#endif /* NETATALK */
2217055Sdg
22252248Smsmith	case pseudo_AF_HDRCMPLT:
22352248Smsmith	{
22493376Smdodd		struct ether_header *eh;
22552248Smsmith		hdrcmplt = 1;
22693376Smdodd		eh = (struct ether_header *)dst->sa_data;
22793376Smdodd		bcopy((caddr_t)eh->ether_shost, (caddr_t)esrc, FDDI_ADDR_LEN);
22852248Smsmith		/* FALLTHROUGH */
22952248Smsmith	}
23052248Smsmith
2317055Sdg	case AF_UNSPEC:
2327055Sdg	{
2337055Sdg		struct ether_header *eh;
23436992Sjulian		loop_copy = -1;
2357055Sdg		eh = (struct ether_header *)dst->sa_data;
23693375Smdodd		bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, FDDI_ADDR_LEN);
2377055Sdg		if (*edst & 1)
2387055Sdg			m->m_flags |= (M_BCAST|M_MCAST);
2397055Sdg		type = eh->ether_type;
2407055Sdg		break;
2417055Sdg	}
2427055Sdg
2437055Sdg	case AF_IMPLINK:
2447055Sdg	{
2457055Sdg		fh = mtod(m, struct fddi_header *);
2467055Sdg		error = EPROTONOSUPPORT;
2477055Sdg		switch (fh->fddi_fc & (FDDIFC_C|FDDIFC_L|FDDIFC_F)) {
2487055Sdg			case FDDIFC_LLC_ASYNC: {
2497055Sdg				/* legal priorities are 0 through 7 */
2507055Sdg				if ((fh->fddi_fc & FDDIFC_Z) > 7)
2517055Sdg			        	goto bad;
2527055Sdg				break;
2537055Sdg			}
2547055Sdg			case FDDIFC_LLC_SYNC: {
2557055Sdg				/* FDDIFC_Z bits reserved, must be zero */
2567055Sdg				if (fh->fddi_fc & FDDIFC_Z)
2577055Sdg					goto bad;
2587055Sdg				break;
2597055Sdg			}
2607055Sdg			case FDDIFC_SMT: {
2617055Sdg				/* FDDIFC_Z bits must be non zero */
2627055Sdg				if ((fh->fddi_fc & FDDIFC_Z) == 0)
2637055Sdg					goto bad;
2647055Sdg				break;
2657055Sdg			}
2667055Sdg			default: {
2677055Sdg				/* anything else is too dangerous */
2687055Sdg               	 		goto bad;
2697055Sdg			}
2707055Sdg		}
2717055Sdg		error = 0;
2727055Sdg		if (fh->fddi_dhost[0] & 1)
2737055Sdg			m->m_flags |= (M_BCAST|M_MCAST);
2747055Sdg		goto queue_it;
2757055Sdg	}
2767055Sdg	default:
277105598Sbrooks		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
2787055Sdg		senderr(EAFNOSUPPORT);
2797055Sdg	}
2807055Sdg
28193380Smdodd	/*
28293380Smdodd	 * Add LLC header.
28393380Smdodd	 */
2847055Sdg	if (type != 0) {
28593367Smdodd		struct llc *l;
286111119Simp		M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
2877055Sdg		if (m == 0)
2887055Sdg			senderr(ENOBUFS);
2897055Sdg		l = mtod(m, struct llc *);
2907055Sdg		l->llc_control = LLC_UI;
2917055Sdg		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
292112266Smdodd		l->llc_snap.org_code[0] =
293112266Smdodd			l->llc_snap.org_code[1] =
294112266Smdodd			l->llc_snap.org_code[2] = 0;
295112266Smdodd		l->llc_snap.ether_type = htons(type);
2967055Sdg	}
29736908Sjulian
2987055Sdg	/*
2997055Sdg	 * Add local net header.  If no space in first mbuf,
3007055Sdg	 * allocate another.
3017055Sdg	 */
302111119Simp	M_PREPEND(m, FDDI_HDR_LEN, M_DONTWAIT);
3037055Sdg	if (m == 0)
3047055Sdg		senderr(ENOBUFS);
3057055Sdg	fh = mtod(m, struct fddi_header *);
3067055Sdg	fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4;
30793375Smdodd	bcopy((caddr_t)edst, (caddr_t)fh->fddi_dhost, FDDI_ADDR_LEN);
3087055Sdg  queue_it:
30952248Smsmith	if (hdrcmplt)
31093375Smdodd		bcopy((caddr_t)esrc, (caddr_t)fh->fddi_shost, FDDI_ADDR_LEN);
31152248Smsmith	else
312152315Sru		bcopy(IF_LLADDR(ifp), (caddr_t)fh->fddi_shost,
31393373Smdodd			FDDI_ADDR_LEN);
31493377Smdodd
31536908Sjulian	/*
31636908Sjulian	 * If a simplex interface, and the packet is being sent to our
31736908Sjulian	 * Ethernet address or a broadcast address, loopback a copy.
31836908Sjulian	 * XXX To make a simplex device behave exactly like a duplex
31936908Sjulian	 * device, we should copy in the case of sending to our own
32036908Sjulian	 * ethernet address (thus letting the original actually appear
32136908Sjulian	 * on the wire). However, we don't do that here for security
32236908Sjulian	 * reasons and compatibility with the original behavior.
32336908Sjulian	 */
32493377Smdodd	if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
325112279Smdodd		if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
326112279Smdodd			struct mbuf *n;
327112279Smdodd			n = m_copy(m, 0, (int)M_COPYALL);
328112279Smdodd			(void) if_simloop(ifp, n, dst->sa_family,
329112279Smdodd					  FDDI_HDR_LEN);
330112279Smdodd	     	} else if (bcmp(fh->fddi_dhost, fh->fddi_shost,
331112279Smdodd				FDDI_ADDR_LEN) == 0) {
332112279Smdodd			(void) if_simloop(ifp, m, dst->sa_family,
333112279Smdodd					  FDDI_HDR_LEN);
33493369Smdodd			return (0);	/* XXX */
33536908Sjulian		}
33636908Sjulian	}
33736908Sjulian
338130549Smlaier	IFQ_HANDOFF(ifp, m, error);
339130549Smlaier	if (error)
340130549Smlaier		ifp->if_oerrors++;
341130549Smlaier
3427055Sdg	return (error);
3437055Sdg
3447055Sdgbad:
34593379Smdodd	ifp->if_oerrors++;
3467055Sdg	if (m)
3477055Sdg		m_freem(m);
3487055Sdg	return (error);
3497055Sdg}
3507055Sdg
3517055Sdg/*
352112308Smdodd * Process a received FDDI packet.
3537055Sdg */
354106939Ssamstatic void
355106939Ssamfddi_input(ifp, m)
3567055Sdg	struct ifnet *ifp;
3577055Sdg	struct mbuf *m;
3587055Sdg{
359111888Sjlemon	int isr;
36093367Smdodd	struct llc *l;
361106939Ssam	struct fddi_header *fh;
3627055Sdg
363112308Smdodd	/*
364112308Smdodd	 * Do consistency checks to verify assumptions
365112308Smdodd	 * made by code past this point.
366112308Smdodd	 */
367112308Smdodd	if ((m->m_flags & M_PKTHDR) == 0) {
368112308Smdodd		if_printf(ifp, "discard frame w/o packet header\n");
369112308Smdodd		ifp->if_ierrors++;
370112308Smdodd		m_freem(m);
371112308Smdodd		return;
372112308Smdodd	}
373112308Smdodd	if (m->m_pkthdr.rcvif == NULL) {
374112308Smdodd		if_printf(ifp, "discard frame w/o interface pointer\n");
375112308Smdodd		ifp->if_ierrors++;
376112308Smdodd		m_freem(m);
377112308Smdodd		return;
378112308Smdodd        }
379112308Smdodd
380112308Smdodd	m = m_pullup(m, FDDI_HDR_LEN);
381112308Smdodd	if (m == NULL) {
382112308Smdodd		ifp->if_ierrors++;
383112308Smdodd		goto dropanyway;
384112308Smdodd	}
385106939Ssam	fh = mtod(m, struct fddi_header *);
386112308Smdodd	m->m_pkthdr.header = (void *)fh;
387106939Ssam
38893379Smdodd	/*
38993379Smdodd	 * Discard packet if interface is not up.
39093379Smdodd	 */
391148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
392148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
39393379Smdodd		goto dropanyway;
39493379Smdodd
395112308Smdodd	/*
396112308Smdodd	 * Give bpf a chance at the packet.
397112308Smdodd	 */
398112308Smdodd	BPF_MTAP(ifp, m);
399112308Smdodd
400112308Smdodd	/*
401112308Smdodd	 * Interface marked for monitoring; discard packet.
402112308Smdodd	 */
403112308Smdodd	if (ifp->if_flags & IFF_MONITOR) {
404112308Smdodd		m_freem(m);
405112308Smdodd		return;
406112308Smdodd	}
407112308Smdodd
408105577Srwatson#ifdef MAC
409172930Srwatson	mac_ifnet_create_mbuf(ifp, m);
410105577Srwatson#endif
411105577Srwatson
41293379Smdodd	/*
413112287Smdodd	 * Update interface statistics.
414112287Smdodd	 */
415112287Smdodd	ifp->if_ibytes += m->m_pkthdr.len;
416112287Smdodd	getmicrotime(&ifp->if_lastchange);
417112287Smdodd
418112287Smdodd	/*
41993379Smdodd	 * Discard non local unicast packets when interface
42093379Smdodd	 * is in promiscuous mode.
42193379Smdodd	 */
42293379Smdodd	if ((ifp->if_flags & IFF_PROMISC) && ((fh->fddi_dhost[0] & 1) == 0) &&
423152315Sru	    (bcmp(IF_LLADDR(ifp), (caddr_t)fh->fddi_dhost,
42493379Smdodd	     FDDI_ADDR_LEN) != 0))
42593379Smdodd		goto dropanyway;
42693379Smdodd
42793379Smdodd	/*
42893379Smdodd	 * Set mbuf flags for bcast/mcast.
42993379Smdodd	 */
43021830Sjoerg	if (fh->fddi_dhost[0] & 1) {
431121436Simp		if (bcmp(ifp->if_broadcastaddr, fh->fddi_dhost,
432121436Simp		    FDDI_ADDR_LEN) == 0)
43321830Sjoerg			m->m_flags |= M_BCAST;
43421830Sjoerg		else
43521830Sjoerg			m->m_flags |= M_MCAST;
4367055Sdg		ifp->if_imcasts++;
43721830Sjoerg	}
4387055Sdg
43921830Sjoerg#ifdef M_LINK0
44021830Sjoerg	/*
44121830Sjoerg	 * If this has a LLC priority of 0, then mark it so upper
44221830Sjoerg	 * layers have a hint that it really came via a FDDI/Ethernet
44321830Sjoerg	 * bridge.
44421830Sjoerg	 */
44521830Sjoerg	if ((fh->fddi_fc & FDDIFC_LLC_PRIO7) == FDDIFC_LLC_PRIO0)
44621830Sjoerg		m->m_flags |= M_LINK0;
44721830Sjoerg#endif
44821830Sjoerg
449106939Ssam	/* Strip off FDDI header. */
450111790Smdodd	m_adj(m, FDDI_HDR_LEN);
451106939Ssam
452111790Smdodd	m = m_pullup(m, LLC_SNAPFRAMELEN);
45393379Smdodd	if (m == 0) {
45493379Smdodd		ifp->if_ierrors++;
45593379Smdodd		goto dropanyway;
45693379Smdodd	}
4577055Sdg	l = mtod(m, struct llc *);
45893377Smdodd
4597055Sdg	switch (l->llc_dsap) {
4607055Sdg	case LLC_SNAP_LSAP:
4617055Sdg	{
46221830Sjoerg		u_int16_t type;
463112266Smdodd		if ((l->llc_control != LLC_UI) ||
464112266Smdodd		    (l->llc_ssap != LLC_SNAP_LSAP)) {
46593379Smdodd			ifp->if_noproto++;
4667055Sdg			goto dropanyway;
46793379Smdodd		}
46821830Sjoerg#ifdef NETATALK
469128396Sluigi		if (bcmp(&(l->llc_snap.org_code)[0], at_org_code,
470111888Sjlemon		    sizeof(at_org_code)) == 0 &&
471111888Sjlemon		    ntohs(l->llc_snap.ether_type) == ETHERTYPE_AT) {
472111888Sjlemon			isr = NETISR_ATALK2;
473111888Sjlemon			m_adj(m, LLC_SNAPFRAMELEN);
474111888Sjlemon			break;
47521830Sjoerg		}
47621830Sjoerg
477128396Sluigi		if (bcmp(&(l->llc_snap.org_code)[0], aarp_org_code,
478111888Sjlemon		    sizeof(aarp_org_code)) == 0 &&
479111888Sjlemon		    ntohs(l->llc_snap.ether_type) == ETHERTYPE_AARP) {
480111888Sjlemon			m_adj(m, LLC_SNAPFRAMELEN);
481111888Sjlemon			isr = NETISR_AARP;
482111888Sjlemon			break;
48321830Sjoerg		}
48421830Sjoerg#endif /* NETATALK */
48593377Smdodd		if (l->llc_snap.org_code[0] != 0 ||
48693377Smdodd		    l->llc_snap.org_code[1] != 0 ||
48793379Smdodd		    l->llc_snap.org_code[2] != 0) {
48893379Smdodd			ifp->if_noproto++;
4897055Sdg			goto dropanyway;
49093379Smdodd		}
49193377Smdodd
49221830Sjoerg		type = ntohs(l->llc_snap.ether_type);
49393377Smdodd		m_adj(m, LLC_SNAPFRAMELEN);
49493377Smdodd
49521830Sjoerg		switch (type) {
4967055Sdg#ifdef INET
4977055Sdg		case ETHERTYPE_IP:
498154518Sandre			if ((m = ip_fastforward(m)) == NULL)
49936192Sdg				return;
500111888Sjlemon			isr = NETISR_IP;
5017055Sdg			break;
5027055Sdg
5037055Sdg		case ETHERTYPE_ARP:
50478295Sjlemon			if (ifp->if_flags & IFF_NOARP)
50578295Sjlemon				goto dropanyway;
506111888Sjlemon			isr = NETISR_ARP;
5077055Sdg			break;
5087055Sdg#endif
50954263Sshin#ifdef INET6
51054263Sshin		case ETHERTYPE_IPV6:
511111888Sjlemon			isr = NETISR_IPV6;
51254263Sshin			break;
51354263Sshin#endif
51421830Sjoerg#ifdef IPX
51521830Sjoerg		case ETHERTYPE_IPX:
516111888Sjlemon			isr = NETISR_IPX;
51721830Sjoerg			break;
51821830Sjoerg#endif
5197055Sdg#ifdef DECNET
52021830Sjoerg		case ETHERTYPE_DECNET:
521111888Sjlemon			isr = NETISR_DECNET;
5227055Sdg			break;
5237055Sdg#endif
52421830Sjoerg#ifdef NETATALK
52521830Sjoerg		case ETHERTYPE_AT:
526111888Sjlemon	                isr = NETISR_ATALK1;
52721830Sjoerg			break;
52821830Sjoerg	        case ETHERTYPE_AARP:
529111888Sjlemon			isr = NETISR_AARP;
530111888Sjlemon			break;
53121830Sjoerg#endif /* NETATALK */
5327055Sdg		default:
53321830Sjoerg			/* printf("fddi_input: unknown protocol 0x%x\n", type); */
5347055Sdg			ifp->if_noproto++;
5357055Sdg			goto dropanyway;
5367055Sdg		}
5377055Sdg		break;
5387055Sdg	}
53921830Sjoerg
5407055Sdg	default:
54121830Sjoerg		/* printf("fddi_input: unknown dsap 0x%x\n", l->llc_dsap); */
5427055Sdg		ifp->if_noproto++;
54393377Smdodd		goto dropanyway;
5447055Sdg	}
545111888Sjlemon	netisr_dispatch(isr, m);
54693377Smdodd	return;
54793377Smdodd
54893377Smdodddropanyway:
54993377Smdodd	ifp->if_iqdrops++;
55093377Smdodd	if (m)
55193377Smdodd		m_freem(m);
55293377Smdodd	return;
5537055Sdg}
55493380Smdodd
5557055Sdg/*
5567055Sdg * Perform common duties while attaching to interface list
5577055Sdg */
5587055Sdgvoid
559152296Srufddi_ifattach(ifp, lla, bpf)
56093367Smdodd	struct ifnet *ifp;
561152296Sru	const u_int8_t *lla;
56293383Smdodd	int bpf;
5637055Sdg{
56493367Smdodd	struct ifaddr *ifa;
56593367Smdodd	struct sockaddr_dl *sdl;
5667055Sdg
5677055Sdg	ifp->if_type = IFT_FDDI;
56893373Smdodd	ifp->if_addrlen = FDDI_ADDR_LEN;
5697055Sdg	ifp->if_hdrlen = 21;
57093379Smdodd
57193379Smdodd	if_attach(ifp);         /* Must be called before additional assignments */
57293379Smdodd
5737055Sdg	ifp->if_mtu = FDDIMTU;
57493379Smdodd	ifp->if_output = fddi_output;
575106939Ssam	ifp->if_input = fddi_input;
57668180Sume	ifp->if_resolvemulti = fddi_resolvemulti;
57793379Smdodd	ifp->if_broadcastaddr = fddibroadcastaddr;
57816063Sgpalmer	ifp->if_baudrate = 100000000;
57921830Sjoerg#ifdef IFF_NOTRAILERS
58021830Sjoerg	ifp->if_flags |= IFF_NOTRAILERS;
58121830Sjoerg#endif
582152315Sru	ifa = ifp->if_addr;
583152315Sru	KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
58493379Smdodd
58521831Sjoerg	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
58621831Sjoerg	sdl->sdl_type = IFT_FDDI;
58721831Sjoerg	sdl->sdl_alen = ifp->if_addrlen;
588152296Sru	bcopy(lla, LLADDR(sdl), ifp->if_addrlen);
58993379Smdodd
59093383Smdodd	if (bpf)
59193383Smdodd		bpfattach(ifp, DLT_FDDI, FDDI_HDR_LEN);
59293383Smdodd
59393379Smdodd	return;
5947055Sdg}
59568180Sume
59693382Smdoddvoid
59793382Smdoddfddi_ifdetach(ifp, bpf)
59893382Smdodd	struct ifnet *ifp;
59993382Smdodd	int bpf;
60093382Smdodd{
60193382Smdodd
60293382Smdodd	if (bpf)
60393382Smdodd		bpfdetach(ifp);
60493382Smdodd
60593382Smdodd	if_detach(ifp);
60693382Smdodd
60793382Smdodd	return;
60893382Smdodd}
60993382Smdodd
61093382Smdoddint
61193382Smdoddfddi_ioctl (ifp, command, data)
61293382Smdodd	struct ifnet *ifp;
61393382Smdodd	int command;
61493382Smdodd	caddr_t data;
61593382Smdodd{
61693382Smdodd	struct ifaddr *ifa;
61793382Smdodd	struct ifreq *ifr;
61893382Smdodd	int error;
61993382Smdodd
62093382Smdodd	ifa = (struct ifaddr *) data;
62193382Smdodd	ifr = (struct ifreq *) data;
62293382Smdodd	error = 0;
62393382Smdodd
62493382Smdodd	switch (command) {
62593382Smdodd	case SIOCSIFADDR:
62693382Smdodd		ifp->if_flags |= IFF_UP;
62793382Smdodd
62893382Smdodd		switch (ifa->ifa_addr->sa_family) {
62993382Smdodd#ifdef INET
63093382Smdodd		case AF_INET:	/* before arpwhohas */
63193382Smdodd			ifp->if_init(ifp->if_softc);
63293382Smdodd			arp_ifinit(ifp, ifa);
63393382Smdodd			break;
63493382Smdodd#endif
63593382Smdodd#ifdef IPX
63693382Smdodd		/*
63793382Smdodd		 * XXX - This code is probably wrong
63893382Smdodd		 */
63993382Smdodd		case AF_IPX: {
64093382Smdodd				struct ipx_addr *ina;
64193382Smdodd
64293382Smdodd				ina = &(IA_SIPX(ifa)->sipx_addr);
64393382Smdodd
64493382Smdodd				if (ipx_nullhost(*ina)) {
64593382Smdodd					ina->x_host = *(union ipx_host *)
646152315Sru							IF_LLADDR(ifp);
64793382Smdodd				} else {
64893382Smdodd					bcopy((caddr_t) ina->x_host.c_host,
649152315Sru					      (caddr_t) IF_LLADDR(ifp),
650147256Sbrooks					      ETHER_ADDR_LEN);
65193382Smdodd				}
65293382Smdodd
65393382Smdodd				/*
65493382Smdodd				 * Set new address
65593382Smdodd				 */
65693382Smdodd				ifp->if_init(ifp->if_softc);
65793382Smdodd			}
65893382Smdodd			break;
65993382Smdodd#endif
66093382Smdodd		default:
66193382Smdodd			ifp->if_init(ifp->if_softc);
66293382Smdodd			break;
663104302Sphk		}
664144045Smdodd		break;
66593382Smdodd	case SIOCGIFADDR: {
66693382Smdodd			struct sockaddr *sa;
66793382Smdodd
66893382Smdodd			sa = (struct sockaddr *) & ifr->ifr_data;
669152315Sru			bcopy(IF_LLADDR(ifp),
67093382Smdodd			      (caddr_t) sa->sa_data, FDDI_ADDR_LEN);
67193382Smdodd
67293382Smdodd		}
67393382Smdodd		break;
67493382Smdodd	case SIOCSIFMTU:
67593382Smdodd		/*
67693382Smdodd		 * Set the interface MTU.
67793382Smdodd		 */
67893382Smdodd		if (ifr->ifr_mtu > FDDIMTU) {
67993382Smdodd			error = EINVAL;
68093382Smdodd		} else {
68193382Smdodd			ifp->if_mtu = ifr->ifr_mtu;
68293382Smdodd		}
68393382Smdodd		break;
68493382Smdodd	default:
685144045Smdodd		error = EINVAL;
68693382Smdodd		break;
68793382Smdodd	}
68893382Smdodd
68993382Smdodd	return (error);
69093382Smdodd}
69193382Smdodd
69268180Sumestatic int
69368180Sumefddi_resolvemulti(ifp, llsa, sa)
69468180Sume	struct ifnet *ifp;
69568180Sume	struct sockaddr **llsa;
69668180Sume	struct sockaddr *sa;
69768180Sume{
69868180Sume	struct sockaddr_dl *sdl;
699184709Sbz#ifdef INET
70068180Sume	struct sockaddr_in *sin;
701184709Sbz#endif
70268180Sume#ifdef INET6
70368180Sume	struct sockaddr_in6 *sin6;
70468180Sume#endif
70568180Sume	u_char *e_addr;
70668180Sume
70768180Sume	switch(sa->sa_family) {
70868180Sume	case AF_LINK:
70968180Sume		/*
71068180Sume		 * No mapping needed. Just check that it's a valid MC address.
71168180Sume		 */
71268180Sume		sdl = (struct sockaddr_dl *)sa;
71368180Sume		e_addr = LLADDR(sdl);
71468180Sume		if ((e_addr[0] & 1) != 1)
71593369Smdodd			return (EADDRNOTAVAIL);
71668180Sume		*llsa = 0;
71793369Smdodd		return (0);
71868180Sume
71968180Sume#ifdef INET
72068180Sume	case AF_INET:
72168180Sume		sin = (struct sockaddr_in *)sa;
72268180Sume		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
72393369Smdodd			return (EADDRNOTAVAIL);
724184205Sdes		sdl = malloc(sizeof *sdl, M_IFMADDR,
725148641Srwatson		       M_NOWAIT | M_ZERO);
726148641Srwatson		if (sdl == NULL)
727148641Srwatson			return (ENOMEM);
72868180Sume		sdl->sdl_len = sizeof *sdl;
72968180Sume		sdl->sdl_family = AF_LINK;
73068180Sume		sdl->sdl_index = ifp->if_index;
73168180Sume		sdl->sdl_type = IFT_FDDI;
73268180Sume		sdl->sdl_nlen = 0;
73393375Smdodd		sdl->sdl_alen = FDDI_ADDR_LEN;
73468180Sume		sdl->sdl_slen = 0;
73568180Sume		e_addr = LLADDR(sdl);
73668180Sume		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
73768180Sume		*llsa = (struct sockaddr *)sdl;
73893369Smdodd		return (0);
73968180Sume#endif
74068180Sume#ifdef INET6
74168180Sume	case AF_INET6:
74268180Sume		sin6 = (struct sockaddr_in6 *)sa;
74368180Sume		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
74468180Sume			/*
74568180Sume			 * An IP6 address of 0 means listen to all
74668180Sume			 * of the Ethernet multicast address used for IP6.
74768180Sume			 * (This is used for multicast routers.)
74868180Sume			 */
74968180Sume			ifp->if_flags |= IFF_ALLMULTI;
75068180Sume			*llsa = 0;
75193369Smdodd			return (0);
75268180Sume		}
75368180Sume		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
75493369Smdodd			return (EADDRNOTAVAIL);
755184205Sdes		sdl = malloc(sizeof *sdl, M_IFMADDR,
756148641Srwatson		       M_NOWAIT | M_ZERO);
757148641Srwatson		if (sdl == NULL)
758148641Srwatson			return (ENOMEM);
75968180Sume		sdl->sdl_len = sizeof *sdl;
76068180Sume		sdl->sdl_family = AF_LINK;
76168180Sume		sdl->sdl_index = ifp->if_index;
76268180Sume		sdl->sdl_type = IFT_FDDI;
76368180Sume		sdl->sdl_nlen = 0;
76493375Smdodd		sdl->sdl_alen = FDDI_ADDR_LEN;
76568180Sume		sdl->sdl_slen = 0;
76668180Sume		e_addr = LLADDR(sdl);
76768180Sume		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
76868180Sume		*llsa = (struct sockaddr *)sdl;
76993369Smdodd		return (0);
77068180Sume#endif
77168180Sume
77268180Sume	default:
77368180Sume		/*
77468180Sume		 * Well, the text isn't quite right, but it's the name
77568180Sume		 * that counts...
77668180Sume		 */
77793369Smdodd		return (EAFNOSUPPORT);
77868180Sume	}
77993375Smdodd
78093375Smdodd	return (0);
78168180Sume}
78293375Smdodd
78393375Smdoddstatic moduledata_t fddi_mod = {
78493375Smdodd	"fddi",	/* module name */
78593375Smdodd	NULL,	/* event handler */
78693375Smdodd	0	/* extra data */
78793375Smdodd};
78893375Smdodd
78993375SmdoddDECLARE_MODULE(fddi, fddi_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
79093375SmdoddMODULE_VERSION(fddi, 1);
791