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: stable/10/sys/net/if_fddisubr.c 332160 2018-04-07 00:04:28Z brooks $
377055Sdg */
387055Sdg
3932356Seivind#include "opt_atalk.h"
4032350Seivind#include "opt_inet.h"
4154263Sshin#include "opt_inet6.h"
4231742Seivind#include "opt_ipx.h"
4331742Seivind
447055Sdg#include <sys/param.h>
457055Sdg#include <sys/systm.h>
4693375Smdodd#include <sys/kernel.h>
4793375Smdodd#include <sys/malloc.h>
487055Sdg#include <sys/mbuf.h>
4993375Smdodd#include <sys/module.h>
507055Sdg#include <sys/socket.h>
5193375Smdodd#include <sys/sockio.h>
527055Sdg
537055Sdg#include <net/if.h>
54112271Smdodd#include <net/if_dl.h>
557055Sdg#include <net/if_llc.h>
567055Sdg#include <net/if_types.h>
57186119Sqingli#include <net/if_llatbl.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 *);
99249925Sglebiusstatic int fddi_output(struct ifnet *, struct mbuf *, const struct sockaddr *,
100191148Skmacy		       struct route *);
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
113249925Sglebiusfddi_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
114249925Sglebius	struct route *ro)
1157055Sdg{
11621830Sjoerg	u_int16_t type;
11769152Sjlemon	int loop_copy = 0, error = 0, hdrcmplt = 0;
11893373Smdodd 	u_char esrc[FDDI_ADDR_LEN], edst[FDDI_ADDR_LEN];
11993367Smdodd	struct fddi_header *fh;
120193891Sbz#if defined(INET) || defined(INET6)
121186119Sqingli	struct llentry *lle;
122193891Sbz#endif
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: {
140193891Sbz		struct rtentry *rt0 = NULL;
141193891Sbz
142193891Sbz		if (ro != NULL)
143193891Sbz			rt0 = ro->ro_rt;
144186119Sqingli		error = arpresolve(ifp, rt0, m, dst, edst, &lle);
145128636Sluigi		if (error)
146128636Sluigi			return (error == EWOULDBLOCK ? 0 : error);
14721830Sjoerg		type = htons(ETHERTYPE_IP);
1487055Sdg		break;
14921830Sjoerg	}
150126951Smdodd	case AF_ARP:
151126951Smdodd	{
152126951Smdodd		struct arphdr *ah;
153126951Smdodd		ah = mtod(m, struct arphdr *);
154126951Smdodd		ah->ar_hrd = htons(ARPHRD_ETHER);
155126951Smdodd
156126951Smdodd		loop_copy = -1; /* if this is for us, don't do it */
157126951Smdodd
158126951Smdodd		switch (ntohs(ah->ar_op)) {
159126951Smdodd		case ARPOP_REVREQUEST:
160126951Smdodd		case ARPOP_REVREPLY:
161126951Smdodd			type = htons(ETHERTYPE_REVARP);
162126951Smdodd			break;
163126951Smdodd		case ARPOP_REQUEST:
164126951Smdodd		case ARPOP_REPLY:
165126951Smdodd		default:
166126951Smdodd			type = htons(ETHERTYPE_ARP);
167126951Smdodd			break;
168126951Smdodd		}
169126951Smdodd
170126951Smdodd		if (m->m_flags & M_BCAST)
171126951Smdodd			bcopy(ifp->if_broadcastaddr, edst, FDDI_ADDR_LEN);
172126951Smdodd                else
173126951Smdodd			bcopy(ar_tha(ah), edst, FDDI_ADDR_LEN);
174126951Smdodd
175126951Smdodd	}
176126951Smdodd	break;
177112266Smdodd#endif /* INET */
17854263Sshin#ifdef INET6
17954263Sshin	case AF_INET6:
180186217Sqingli		error = nd6_storelladdr(ifp, m, dst, (u_char *)edst, &lle);
181128636Sluigi		if (error)
182128636Sluigi			return (error); /* Something bad happened */
18354263Sshin		type = htons(ETHERTYPE_IPV6);
18454263Sshin		break;
185112266Smdodd#endif /* INET6 */
18611819Sjulian#ifdef IPX
18711819Sjulian	case AF_IPX:
18821830Sjoerg		type = htons(ETHERTYPE_IPX);
189249925Sglebius 		bcopy(&((const struct sockaddr_ipx *)dst)->sipx_addr.x_host,
190249925Sglebius		    edst, FDDI_ADDR_LEN);
19111819Sjulian		break;
192112266Smdodd#endif /* IPX */
19321830Sjoerg#ifdef NETATALK
19421830Sjoerg	case AF_APPLETALK: {
19521830Sjoerg	    struct at_ifaddr *aa;
196249925Sglebius            if (!aarpresolve(ifp, m, (const struct sockaddr_at *)dst, edst))
19721830Sjoerg                return (0);
19821830Sjoerg	    /*
19921830Sjoerg	     * ifaddr is the first thing in at_ifaddr
20021830Sjoerg	     */
201249925Sglebius	    if ((aa = at_ifawithnet((const struct sockaddr_at *)dst)) == 0)
20221830Sjoerg		goto bad;
20321830Sjoerg
20421830Sjoerg	    /*
20521830Sjoerg	     * In the phase 2 case, we need to prepend an mbuf for the llc header.
20621830Sjoerg	     * Since we must preserve the value of m, which is passed to us by
20721830Sjoerg	     * value, we m_copy() the first mbuf, and use it for our llc header.
20821830Sjoerg	     */
20921830Sjoerg	    if (aa->aa_flags & AFA_PHASE2) {
21021830Sjoerg		struct llc llc;
21121830Sjoerg
212243882Sglebius		M_PREPEND(m, LLC_SNAPFRAMELEN, M_WAITOK);
21321830Sjoerg		llc.llc_dsap = llc.llc_ssap = LLC_SNAP_LSAP;
21421830Sjoerg		llc.llc_control = LLC_UI;
21593371Smdodd		bcopy(at_org_code, llc.llc_snap.org_code, sizeof(at_org_code));
21693371Smdodd		llc.llc_snap.ether_type = htons(ETHERTYPE_AT);
21793373Smdodd		bcopy(&llc, mtod(m, caddr_t), LLC_SNAPFRAMELEN);
21821830Sjoerg		type = 0;
21921830Sjoerg	    } else {
22021830Sjoerg		type = htons(ETHERTYPE_AT);
22121830Sjoerg	    }
222194819Srwatson	    ifa_free(&aa->aa_ifa);
22321830Sjoerg	    break;
22421830Sjoerg	}
22521830Sjoerg#endif /* NETATALK */
2267055Sdg
22752248Smsmith	case pseudo_AF_HDRCMPLT:
22852248Smsmith	{
229249925Sglebius		const struct ether_header *eh;
230249925Sglebius
23152248Smsmith		hdrcmplt = 1;
232249925Sglebius		eh = (const struct ether_header *)dst->sa_data;
233249925Sglebius		bcopy(eh->ether_shost, esrc, FDDI_ADDR_LEN);
23452248Smsmith		/* FALLTHROUGH */
23552248Smsmith	}
23652248Smsmith
2377055Sdg	case AF_UNSPEC:
2387055Sdg	{
239249925Sglebius		const struct ether_header *eh;
240249925Sglebius
24136992Sjulian		loop_copy = -1;
242249925Sglebius		eh = (const struct ether_header *)dst->sa_data;
243249925Sglebius		bcopy(eh->ether_dhost, edst, FDDI_ADDR_LEN);
2447055Sdg		if (*edst & 1)
2457055Sdg			m->m_flags |= (M_BCAST|M_MCAST);
2467055Sdg		type = eh->ether_type;
2477055Sdg		break;
2487055Sdg	}
2497055Sdg
2507055Sdg	case AF_IMPLINK:
2517055Sdg	{
2527055Sdg		fh = mtod(m, struct fddi_header *);
2537055Sdg		error = EPROTONOSUPPORT;
2547055Sdg		switch (fh->fddi_fc & (FDDIFC_C|FDDIFC_L|FDDIFC_F)) {
2557055Sdg			case FDDIFC_LLC_ASYNC: {
2567055Sdg				/* legal priorities are 0 through 7 */
2577055Sdg				if ((fh->fddi_fc & FDDIFC_Z) > 7)
2587055Sdg			        	goto bad;
2597055Sdg				break;
2607055Sdg			}
2617055Sdg			case FDDIFC_LLC_SYNC: {
2627055Sdg				/* FDDIFC_Z bits reserved, must be zero */
2637055Sdg				if (fh->fddi_fc & FDDIFC_Z)
2647055Sdg					goto bad;
2657055Sdg				break;
2667055Sdg			}
2677055Sdg			case FDDIFC_SMT: {
2687055Sdg				/* FDDIFC_Z bits must be non zero */
2697055Sdg				if ((fh->fddi_fc & FDDIFC_Z) == 0)
2707055Sdg					goto bad;
2717055Sdg				break;
2727055Sdg			}
2737055Sdg			default: {
2747055Sdg				/* anything else is too dangerous */
2757055Sdg               	 		goto bad;
2767055Sdg			}
2777055Sdg		}
2787055Sdg		error = 0;
2797055Sdg		if (fh->fddi_dhost[0] & 1)
2807055Sdg			m->m_flags |= (M_BCAST|M_MCAST);
2817055Sdg		goto queue_it;
2827055Sdg	}
2837055Sdg	default:
284105598Sbrooks		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
2857055Sdg		senderr(EAFNOSUPPORT);
2867055Sdg	}
2877055Sdg
28893380Smdodd	/*
28993380Smdodd	 * Add LLC header.
29093380Smdodd	 */
2917055Sdg	if (type != 0) {
29293367Smdodd		struct llc *l;
293243882Sglebius		M_PREPEND(m, LLC_SNAPFRAMELEN, M_NOWAIT);
2947055Sdg		if (m == 0)
2957055Sdg			senderr(ENOBUFS);
2967055Sdg		l = mtod(m, struct llc *);
2977055Sdg		l->llc_control = LLC_UI;
2987055Sdg		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
299112266Smdodd		l->llc_snap.org_code[0] =
300112266Smdodd			l->llc_snap.org_code[1] =
301112266Smdodd			l->llc_snap.org_code[2] = 0;
302112266Smdodd		l->llc_snap.ether_type = htons(type);
3037055Sdg	}
30436908Sjulian
3057055Sdg	/*
3067055Sdg	 * Add local net header.  If no space in first mbuf,
3077055Sdg	 * allocate another.
3087055Sdg	 */
309243882Sglebius	M_PREPEND(m, FDDI_HDR_LEN, M_NOWAIT);
3107055Sdg	if (m == 0)
3117055Sdg		senderr(ENOBUFS);
3127055Sdg	fh = mtod(m, struct fddi_header *);
3137055Sdg	fh->fddi_fc = FDDIFC_LLC_ASYNC|FDDIFC_LLC_PRIO4;
31493375Smdodd	bcopy((caddr_t)edst, (caddr_t)fh->fddi_dhost, FDDI_ADDR_LEN);
3157055Sdg  queue_it:
31652248Smsmith	if (hdrcmplt)
31793375Smdodd		bcopy((caddr_t)esrc, (caddr_t)fh->fddi_shost, FDDI_ADDR_LEN);
31852248Smsmith	else
319152315Sru		bcopy(IF_LLADDR(ifp), (caddr_t)fh->fddi_shost,
32093373Smdodd			FDDI_ADDR_LEN);
32193377Smdodd
32236908Sjulian	/*
32336908Sjulian	 * If a simplex interface, and the packet is being sent to our
32436908Sjulian	 * Ethernet address or a broadcast address, loopback a copy.
32536908Sjulian	 * XXX To make a simplex device behave exactly like a duplex
32636908Sjulian	 * device, we should copy in the case of sending to our own
32736908Sjulian	 * ethernet address (thus letting the original actually appear
32836908Sjulian	 * on the wire). However, we don't do that here for security
32936908Sjulian	 * reasons and compatibility with the original behavior.
33036908Sjulian	 */
33193377Smdodd	if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
332112279Smdodd		if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
333112279Smdodd			struct mbuf *n;
334112279Smdodd			n = m_copy(m, 0, (int)M_COPYALL);
335112279Smdodd			(void) if_simloop(ifp, n, dst->sa_family,
336112279Smdodd					  FDDI_HDR_LEN);
337112279Smdodd	     	} else if (bcmp(fh->fddi_dhost, fh->fddi_shost,
338112279Smdodd				FDDI_ADDR_LEN) == 0) {
339112279Smdodd			(void) if_simloop(ifp, m, dst->sa_family,
340112279Smdodd					  FDDI_HDR_LEN);
34193369Smdodd			return (0);	/* XXX */
34236908Sjulian		}
34336908Sjulian	}
34436908Sjulian
345185164Skmacy	error = (ifp->if_transmit)(ifp, m);
346130549Smlaier	if (error)
347130549Smlaier		ifp->if_oerrors++;
348130549Smlaier
3497055Sdg	return (error);
3507055Sdg
3517055Sdgbad:
35293379Smdodd	ifp->if_oerrors++;
3537055Sdg	if (m)
3547055Sdg		m_freem(m);
3557055Sdg	return (error);
3567055Sdg}
3577055Sdg
3587055Sdg/*
359112308Smdodd * Process a received FDDI packet.
3607055Sdg */
361106939Ssamstatic void
362106939Ssamfddi_input(ifp, m)
3637055Sdg	struct ifnet *ifp;
3647055Sdg	struct mbuf *m;
3657055Sdg{
366111888Sjlemon	int isr;
36793367Smdodd	struct llc *l;
368106939Ssam	struct fddi_header *fh;
3697055Sdg
370112308Smdodd	/*
371112308Smdodd	 * Do consistency checks to verify assumptions
372112308Smdodd	 * made by code past this point.
373112308Smdodd	 */
374112308Smdodd	if ((m->m_flags & M_PKTHDR) == 0) {
375112308Smdodd		if_printf(ifp, "discard frame w/o packet header\n");
376112308Smdodd		ifp->if_ierrors++;
377112308Smdodd		m_freem(m);
378112308Smdodd		return;
379112308Smdodd	}
380112308Smdodd	if (m->m_pkthdr.rcvif == NULL) {
381112308Smdodd		if_printf(ifp, "discard frame w/o interface pointer\n");
382112308Smdodd		ifp->if_ierrors++;
383112308Smdodd		m_freem(m);
384112308Smdodd		return;
385112308Smdodd        }
386112308Smdodd
387112308Smdodd	m = m_pullup(m, FDDI_HDR_LEN);
388112308Smdodd	if (m == NULL) {
389112308Smdodd		ifp->if_ierrors++;
390112308Smdodd		goto dropanyway;
391112308Smdodd	}
392106939Ssam	fh = mtod(m, struct fddi_header *);
393106939Ssam
39493379Smdodd	/*
39593379Smdodd	 * Discard packet if interface is not up.
39693379Smdodd	 */
397148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
398148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
39993379Smdodd		goto dropanyway;
40093379Smdodd
401112308Smdodd	/*
402112308Smdodd	 * Give bpf a chance at the packet.
403112308Smdodd	 */
404112308Smdodd	BPF_MTAP(ifp, m);
405112308Smdodd
406112308Smdodd	/*
407112308Smdodd	 * Interface marked for monitoring; discard packet.
408112308Smdodd	 */
409112308Smdodd	if (ifp->if_flags & IFF_MONITOR) {
410112308Smdodd		m_freem(m);
411112308Smdodd		return;
412112308Smdodd	}
413112308Smdodd
414105577Srwatson#ifdef MAC
415172930Srwatson	mac_ifnet_create_mbuf(ifp, m);
416105577Srwatson#endif
417105577Srwatson
41893379Smdodd	/*
419112287Smdodd	 * Update interface statistics.
420112287Smdodd	 */
421112287Smdodd	ifp->if_ibytes += m->m_pkthdr.len;
422112287Smdodd	getmicrotime(&ifp->if_lastchange);
423112287Smdodd
424112287Smdodd	/*
42593379Smdodd	 * Discard non local unicast packets when interface
42693379Smdodd	 * is in promiscuous mode.
42793379Smdodd	 */
42893379Smdodd	if ((ifp->if_flags & IFF_PROMISC) && ((fh->fddi_dhost[0] & 1) == 0) &&
429152315Sru	    (bcmp(IF_LLADDR(ifp), (caddr_t)fh->fddi_dhost,
43093379Smdodd	     FDDI_ADDR_LEN) != 0))
43193379Smdodd		goto dropanyway;
43293379Smdodd
43393379Smdodd	/*
43493379Smdodd	 * Set mbuf flags for bcast/mcast.
43593379Smdodd	 */
43621830Sjoerg	if (fh->fddi_dhost[0] & 1) {
437121436Simp		if (bcmp(ifp->if_broadcastaddr, fh->fddi_dhost,
438121436Simp		    FDDI_ADDR_LEN) == 0)
43921830Sjoerg			m->m_flags |= M_BCAST;
44021830Sjoerg		else
44121830Sjoerg			m->m_flags |= M_MCAST;
4427055Sdg		ifp->if_imcasts++;
44321830Sjoerg	}
4447055Sdg
44521830Sjoerg#ifdef M_LINK0
44621830Sjoerg	/*
44721830Sjoerg	 * If this has a LLC priority of 0, then mark it so upper
44821830Sjoerg	 * layers have a hint that it really came via a FDDI/Ethernet
44921830Sjoerg	 * bridge.
45021830Sjoerg	 */
45121830Sjoerg	if ((fh->fddi_fc & FDDIFC_LLC_PRIO7) == FDDIFC_LLC_PRIO0)
45221830Sjoerg		m->m_flags |= M_LINK0;
45321830Sjoerg#endif
45421830Sjoerg
455106939Ssam	/* Strip off FDDI header. */
456111790Smdodd	m_adj(m, FDDI_HDR_LEN);
457106939Ssam
458111790Smdodd	m = m_pullup(m, LLC_SNAPFRAMELEN);
45993379Smdodd	if (m == 0) {
46093379Smdodd		ifp->if_ierrors++;
46193379Smdodd		goto dropanyway;
46293379Smdodd	}
4637055Sdg	l = mtod(m, struct llc *);
46493377Smdodd
4657055Sdg	switch (l->llc_dsap) {
4667055Sdg	case LLC_SNAP_LSAP:
4677055Sdg	{
46821830Sjoerg		u_int16_t type;
469112266Smdodd		if ((l->llc_control != LLC_UI) ||
470112266Smdodd		    (l->llc_ssap != LLC_SNAP_LSAP)) {
47193379Smdodd			ifp->if_noproto++;
4727055Sdg			goto dropanyway;
47393379Smdodd		}
47421830Sjoerg#ifdef NETATALK
475128396Sluigi		if (bcmp(&(l->llc_snap.org_code)[0], at_org_code,
476111888Sjlemon		    sizeof(at_org_code)) == 0 &&
477111888Sjlemon		    ntohs(l->llc_snap.ether_type) == ETHERTYPE_AT) {
478111888Sjlemon			isr = NETISR_ATALK2;
479111888Sjlemon			m_adj(m, LLC_SNAPFRAMELEN);
480111888Sjlemon			break;
48121830Sjoerg		}
48221830Sjoerg
483128396Sluigi		if (bcmp(&(l->llc_snap.org_code)[0], aarp_org_code,
484111888Sjlemon		    sizeof(aarp_org_code)) == 0 &&
485111888Sjlemon		    ntohs(l->llc_snap.ether_type) == ETHERTYPE_AARP) {
486111888Sjlemon			m_adj(m, LLC_SNAPFRAMELEN);
487111888Sjlemon			isr = NETISR_AARP;
488111888Sjlemon			break;
48921830Sjoerg		}
49021830Sjoerg#endif /* NETATALK */
49193377Smdodd		if (l->llc_snap.org_code[0] != 0 ||
49293377Smdodd		    l->llc_snap.org_code[1] != 0 ||
49393379Smdodd		    l->llc_snap.org_code[2] != 0) {
49493379Smdodd			ifp->if_noproto++;
4957055Sdg			goto dropanyway;
49693379Smdodd		}
49793377Smdodd
49821830Sjoerg		type = ntohs(l->llc_snap.ether_type);
49993377Smdodd		m_adj(m, LLC_SNAPFRAMELEN);
50093377Smdodd
50121830Sjoerg		switch (type) {
5027055Sdg#ifdef INET
5037055Sdg		case ETHERTYPE_IP:
504295896Sgnn			if ((m = ip_fastforward(m)) == NULL)
505295896Sgnn				return;
506111888Sjlemon			isr = NETISR_IP;
5077055Sdg			break;
5087055Sdg
5097055Sdg		case ETHERTYPE_ARP:
51078295Sjlemon			if (ifp->if_flags & IFF_NOARP)
51178295Sjlemon				goto dropanyway;
512111888Sjlemon			isr = NETISR_ARP;
5137055Sdg			break;
5147055Sdg#endif
51554263Sshin#ifdef INET6
51654263Sshin		case ETHERTYPE_IPV6:
517111888Sjlemon			isr = NETISR_IPV6;
51854263Sshin			break;
51954263Sshin#endif
52021830Sjoerg#ifdef IPX
52121830Sjoerg		case ETHERTYPE_IPX:
522111888Sjlemon			isr = NETISR_IPX;
52321830Sjoerg			break;
52421830Sjoerg#endif
5257055Sdg#ifdef DECNET
52621830Sjoerg		case ETHERTYPE_DECNET:
527111888Sjlemon			isr = NETISR_DECNET;
5287055Sdg			break;
5297055Sdg#endif
53021830Sjoerg#ifdef NETATALK
53121830Sjoerg		case ETHERTYPE_AT:
532111888Sjlemon	                isr = NETISR_ATALK1;
53321830Sjoerg			break;
53421830Sjoerg	        case ETHERTYPE_AARP:
535111888Sjlemon			isr = NETISR_AARP;
536111888Sjlemon			break;
53721830Sjoerg#endif /* NETATALK */
5387055Sdg		default:
53921830Sjoerg			/* printf("fddi_input: unknown protocol 0x%x\n", type); */
5407055Sdg			ifp->if_noproto++;
5417055Sdg			goto dropanyway;
5427055Sdg		}
5437055Sdg		break;
5447055Sdg	}
54521830Sjoerg
5467055Sdg	default:
54721830Sjoerg		/* printf("fddi_input: unknown dsap 0x%x\n", l->llc_dsap); */
5487055Sdg		ifp->if_noproto++;
54993377Smdodd		goto dropanyway;
5507055Sdg	}
551223741Sbz	M_SETFIB(m, ifp->if_fib);
552111888Sjlemon	netisr_dispatch(isr, m);
55393377Smdodd	return;
55493377Smdodd
55593377Smdodddropanyway:
55693377Smdodd	ifp->if_iqdrops++;
55793377Smdodd	if (m)
55893377Smdodd		m_freem(m);
55993377Smdodd	return;
5607055Sdg}
56193380Smdodd
5627055Sdg/*
5637055Sdg * Perform common duties while attaching to interface list
5647055Sdg */
5657055Sdgvoid
566152296Srufddi_ifattach(ifp, lla, bpf)
56793367Smdodd	struct ifnet *ifp;
568152296Sru	const u_int8_t *lla;
56993383Smdodd	int bpf;
5707055Sdg{
57193367Smdodd	struct ifaddr *ifa;
57293367Smdodd	struct sockaddr_dl *sdl;
5737055Sdg
5747055Sdg	ifp->if_type = IFT_FDDI;
57593373Smdodd	ifp->if_addrlen = FDDI_ADDR_LEN;
5767055Sdg	ifp->if_hdrlen = 21;
57793379Smdodd
57893379Smdodd	if_attach(ifp);         /* Must be called before additional assignments */
57993379Smdodd
5807055Sdg	ifp->if_mtu = FDDIMTU;
58193379Smdodd	ifp->if_output = fddi_output;
582106939Ssam	ifp->if_input = fddi_input;
58368180Sume	ifp->if_resolvemulti = fddi_resolvemulti;
58493379Smdodd	ifp->if_broadcastaddr = fddibroadcastaddr;
58516063Sgpalmer	ifp->if_baudrate = 100000000;
58621830Sjoerg#ifdef IFF_NOTRAILERS
58721830Sjoerg	ifp->if_flags |= IFF_NOTRAILERS;
58821830Sjoerg#endif
589152315Sru	ifa = ifp->if_addr;
590152315Sru	KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
59193379Smdodd
59221831Sjoerg	sdl = (struct sockaddr_dl *)ifa->ifa_addr;
59321831Sjoerg	sdl->sdl_type = IFT_FDDI;
59421831Sjoerg	sdl->sdl_alen = ifp->if_addrlen;
595152296Sru	bcopy(lla, LLADDR(sdl), ifp->if_addrlen);
59693379Smdodd
59793383Smdodd	if (bpf)
59893383Smdodd		bpfattach(ifp, DLT_FDDI, FDDI_HDR_LEN);
59993383Smdodd
60093379Smdodd	return;
6017055Sdg}
60268180Sume
60393382Smdoddvoid
60493382Smdoddfddi_ifdetach(ifp, bpf)
60593382Smdodd	struct ifnet *ifp;
60693382Smdodd	int bpf;
60793382Smdodd{
60893382Smdodd
60993382Smdodd	if (bpf)
61093382Smdodd		bpfdetach(ifp);
61193382Smdodd
61293382Smdodd	if_detach(ifp);
61393382Smdodd
61493382Smdodd	return;
61593382Smdodd}
61693382Smdodd
61793382Smdoddint
61893382Smdoddfddi_ioctl (ifp, command, data)
61993382Smdodd	struct ifnet *ifp;
620194581Srdivacky	u_long command;
62193382Smdodd	caddr_t data;
62293382Smdodd{
62393382Smdodd	struct ifaddr *ifa;
62493382Smdodd	struct ifreq *ifr;
62593382Smdodd	int error;
62693382Smdodd
62793382Smdodd	ifa = (struct ifaddr *) data;
62893382Smdodd	ifr = (struct ifreq *) data;
62993382Smdodd	error = 0;
63093382Smdodd
63193382Smdodd	switch (command) {
63293382Smdodd	case SIOCSIFADDR:
63393382Smdodd		ifp->if_flags |= IFF_UP;
63493382Smdodd
63593382Smdodd		switch (ifa->ifa_addr->sa_family) {
63693382Smdodd#ifdef INET
63793382Smdodd		case AF_INET:	/* before arpwhohas */
63893382Smdodd			ifp->if_init(ifp->if_softc);
63993382Smdodd			arp_ifinit(ifp, ifa);
64093382Smdodd			break;
64193382Smdodd#endif
64293382Smdodd#ifdef IPX
64393382Smdodd		/*
64493382Smdodd		 * XXX - This code is probably wrong
64593382Smdodd		 */
64693382Smdodd		case AF_IPX: {
64793382Smdodd				struct ipx_addr *ina;
64893382Smdodd
64993382Smdodd				ina = &(IA_SIPX(ifa)->sipx_addr);
65093382Smdodd
65193382Smdodd				if (ipx_nullhost(*ina)) {
65293382Smdodd					ina->x_host = *(union ipx_host *)
653152315Sru							IF_LLADDR(ifp);
65493382Smdodd				} else {
65593382Smdodd					bcopy((caddr_t) ina->x_host.c_host,
656152315Sru					      (caddr_t) IF_LLADDR(ifp),
657147256Sbrooks					      ETHER_ADDR_LEN);
65893382Smdodd				}
65993382Smdodd
66093382Smdodd				/*
66193382Smdodd				 * Set new address
66293382Smdodd				 */
66393382Smdodd				ifp->if_init(ifp->if_softc);
66493382Smdodd			}
66593382Smdodd			break;
66693382Smdodd#endif
66793382Smdodd		default:
66893382Smdodd			ifp->if_init(ifp->if_softc);
66993382Smdodd			break;
670104302Sphk		}
671144045Smdodd		break;
672332160Sbrooks	case SIOCGIFADDR:
673332160Sbrooks		bcopy(IF_LLADDR(ifp), &ifr->ifr_addr.sa_data[0],
674332160Sbrooks		    FDDI_ADDR_LEN);
67593382Smdodd		break;
67693382Smdodd	case SIOCSIFMTU:
67793382Smdodd		/*
67893382Smdodd		 * Set the interface MTU.
67993382Smdodd		 */
68093382Smdodd		if (ifr->ifr_mtu > FDDIMTU) {
68193382Smdodd			error = EINVAL;
68293382Smdodd		} else {
68393382Smdodd			ifp->if_mtu = ifr->ifr_mtu;
68493382Smdodd		}
68593382Smdodd		break;
68693382Smdodd	default:
687144045Smdodd		error = EINVAL;
68893382Smdodd		break;
68993382Smdodd	}
69093382Smdodd
69193382Smdodd	return (error);
69293382Smdodd}
69393382Smdodd
69468180Sumestatic int
69568180Sumefddi_resolvemulti(ifp, llsa, sa)
69668180Sume	struct ifnet *ifp;
69768180Sume	struct sockaddr **llsa;
69868180Sume	struct sockaddr *sa;
69968180Sume{
70068180Sume	struct sockaddr_dl *sdl;
701184709Sbz#ifdef INET
70268180Sume	struct sockaddr_in *sin;
703184709Sbz#endif
70468180Sume#ifdef INET6
70568180Sume	struct sockaddr_in6 *sin6;
70668180Sume#endif
70768180Sume	u_char *e_addr;
70868180Sume
70968180Sume	switch(sa->sa_family) {
71068180Sume	case AF_LINK:
71168180Sume		/*
71268180Sume		 * No mapping needed. Just check that it's a valid MC address.
71368180Sume		 */
71468180Sume		sdl = (struct sockaddr_dl *)sa;
71568180Sume		e_addr = LLADDR(sdl);
71668180Sume		if ((e_addr[0] & 1) != 1)
71793369Smdodd			return (EADDRNOTAVAIL);
71868180Sume		*llsa = 0;
71993369Smdodd		return (0);
72068180Sume
72168180Sume#ifdef INET
72268180Sume	case AF_INET:
72368180Sume		sin = (struct sockaddr_in *)sa;
72468180Sume		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
72593369Smdodd			return (EADDRNOTAVAIL);
726184205Sdes		sdl = malloc(sizeof *sdl, M_IFMADDR,
727148641Srwatson		       M_NOWAIT | M_ZERO);
728148641Srwatson		if (sdl == NULL)
729148641Srwatson			return (ENOMEM);
73068180Sume		sdl->sdl_len = sizeof *sdl;
73168180Sume		sdl->sdl_family = AF_LINK;
73268180Sume		sdl->sdl_index = ifp->if_index;
73368180Sume		sdl->sdl_type = IFT_FDDI;
73468180Sume		sdl->sdl_nlen = 0;
73593375Smdodd		sdl->sdl_alen = FDDI_ADDR_LEN;
73668180Sume		sdl->sdl_slen = 0;
73768180Sume		e_addr = LLADDR(sdl);
73868180Sume		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
73968180Sume		*llsa = (struct sockaddr *)sdl;
74093369Smdodd		return (0);
74168180Sume#endif
74268180Sume#ifdef INET6
74368180Sume	case AF_INET6:
74468180Sume		sin6 = (struct sockaddr_in6 *)sa;
74568180Sume		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
74668180Sume			/*
74768180Sume			 * An IP6 address of 0 means listen to all
74868180Sume			 * of the Ethernet multicast address used for IP6.
74968180Sume			 * (This is used for multicast routers.)
75068180Sume			 */
75168180Sume			ifp->if_flags |= IFF_ALLMULTI;
75268180Sume			*llsa = 0;
75393369Smdodd			return (0);
75468180Sume		}
75568180Sume		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
75693369Smdodd			return (EADDRNOTAVAIL);
757184205Sdes		sdl = malloc(sizeof *sdl, M_IFMADDR,
758148641Srwatson		       M_NOWAIT | M_ZERO);
759148641Srwatson		if (sdl == NULL)
760148641Srwatson			return (ENOMEM);
76168180Sume		sdl->sdl_len = sizeof *sdl;
76268180Sume		sdl->sdl_family = AF_LINK;
76368180Sume		sdl->sdl_index = ifp->if_index;
76468180Sume		sdl->sdl_type = IFT_FDDI;
76568180Sume		sdl->sdl_nlen = 0;
76693375Smdodd		sdl->sdl_alen = FDDI_ADDR_LEN;
76768180Sume		sdl->sdl_slen = 0;
76868180Sume		e_addr = LLADDR(sdl);
76968180Sume		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
77068180Sume		*llsa = (struct sockaddr *)sdl;
77193369Smdodd		return (0);
77268180Sume#endif
77368180Sume
77468180Sume	default:
77568180Sume		/*
77668180Sume		 * Well, the text isn't quite right, but it's the name
77768180Sume		 * that counts...
77868180Sume		 */
77993369Smdodd		return (EAFNOSUPPORT);
78068180Sume	}
78193375Smdodd
78293375Smdodd	return (0);
78368180Sume}
78493375Smdodd
78593375Smdoddstatic moduledata_t fddi_mod = {
78693375Smdodd	"fddi",	/* module name */
78793375Smdodd	NULL,	/* event handler */
788241394Skevlo	0	/* extra data */
78993375Smdodd};
79093375Smdodd
79193375SmdoddDECLARE_MODULE(fddi, fddi_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
79293375SmdoddMODULE_VERSION(fddi, 1);
793