1139823Simp/*-
244165Sjulian * Copyright (c) 1998, Larry Lile
344165Sjulian * All rights reserved.
444165Sjulian *
544165Sjulian * For latest sources and information on this driver, please
644165Sjulian * go to http://anarchy.stdio.com.
744165Sjulian *
844165Sjulian * Questions, comments or suggestions should be directed to
944165Sjulian * Larry Lile <lile@stdio.com>.
1044165Sjulian *
1144165Sjulian * Redistribution and use in source and binary forms, with or without
1244165Sjulian * modification, are permitted provided that the following conditions
1344165Sjulian * are met:
1444165Sjulian * 1. Redistributions of source code must retain the above copyright
1544165Sjulian *    notice unmodified, this list of conditions, and the following
1644165Sjulian *    disclaimer.
1744165Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1844165Sjulian *    notice, this list of conditions and the following disclaimer in the
1944165Sjulian *    documentation and/or other materials provided with the distribution.
2044165Sjulian *
2144165Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2244165Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2344165Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2444165Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2544165Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2644165Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2744165Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2844165Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2944165Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3044165Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3144165Sjulian * SUCH DAMAGE.
3244165Sjulian *
3350477Speter * $FreeBSD$
3444165Sjulian *
3544165Sjulian */
3644165Sjulian
3744165Sjulian/*
3844165Sjulian *
3944165Sjulian * General ISO 802.5 (Token Ring) support routines
4044165Sjulian *
4144165Sjulian */
4244165Sjulian
4344165Sjulian#include "opt_inet.h"
4474408Smdodd#include "opt_inet6.h"
4574408Smdodd#include "opt_ipx.h"
4644165Sjulian
4744165Sjulian#include <sys/param.h>
4844165Sjulian#include <sys/systm.h>
49112271Smdodd#include <sys/kernel.h>
50112271Smdodd#include <sys/malloc.h>
5144165Sjulian#include <sys/mbuf.h>
52112271Smdodd#include <sys/module.h>
5344165Sjulian#include <sys/socket.h>
5444165Sjulian#include <sys/sockio.h>
5544165Sjulian
5644165Sjulian#include <net/if.h>
57184710Sbz#include <net/if_arp.h>
58112271Smdodd#include <net/if_dl.h>
5944165Sjulian#include <net/if_llc.h>
6044165Sjulian#include <net/if_types.h>
61186119Sqingli#include <net/if_llatbl.h>
6244165Sjulian
63184710Sbz#include <net/ethernet.h>
64112271Smdodd#include <net/netisr.h>
65112271Smdodd#include <net/route.h>
66112271Smdodd#include <net/bpf.h>
6744165Sjulian#include <net/iso88025.h>
6844165Sjulian
6974408Smdodd#if defined(INET) || defined(INET6)
7044165Sjulian#include <netinet/in.h>
7144165Sjulian#include <netinet/in_var.h>
7244165Sjulian#include <netinet/if_ether.h>
7344165Sjulian#endif
7474408Smdodd#ifdef INET6
7574408Smdodd#include <netinet6/nd6.h>
7674408Smdodd#endif
7744165Sjulian
7874408Smdodd#ifdef IPX
7974408Smdodd#include <netipx/ipx.h>
8074408Smdodd#include <netipx/ipx_if.h>
8174408Smdodd#endif
8274408Smdodd
83163606Srwatson#include <security/mac/mac_framework.h>
84163606Srwatson
85126907Srwatsonstatic const u_char iso88025_broadcastaddr[ISO88025_ADDR_LEN] =
86112277Smdodd			{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
87112277Smdodd
88112273Smdoddstatic int iso88025_resolvemulti (struct ifnet *, struct sockaddr **,
89112294Smdodd				  struct sockaddr *);
90112273Smdodd
91112276Smdodd#define	senderr(e)	do { error = (e); goto bad; } while (0)
9274408Smdodd
93112297Smdodd/*
94112297Smdodd * Perform common duties while attaching to interface list
95112297Smdodd */
9644165Sjulianvoid
97152296Sruiso88025_ifattach(struct ifnet *ifp, const u_int8_t *lla, int bpf)
9844165Sjulian{
99112296Smdodd    struct ifaddr *ifa;
100111774Smdodd    struct sockaddr_dl *sdl;
10144165Sjulian
102112296Smdodd    ifa = NULL;
103112296Smdodd
10444165Sjulian    ifp->if_type = IFT_ISO88025;
10558313Slile    ifp->if_addrlen = ISO88025_ADDR_LEN;
10658313Slile    ifp->if_hdrlen = ISO88025_HDR_LEN;
107112297Smdodd
108112297Smdodd    if_attach(ifp);	/* Must be called before additional assignments */
109112297Smdodd
110112297Smdodd    ifp->if_output = iso88025_output;
111112297Smdodd    ifp->if_input = iso88025_input;
112112297Smdodd    ifp->if_resolvemulti = iso88025_resolvemulti;
113112297Smdodd    ifp->if_broadcastaddr = iso88025_broadcastaddr;
114112297Smdodd
11544165Sjulian    if (ifp->if_baudrate == 0)
11658313Slile        ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */
11744165Sjulian    if (ifp->if_mtu == 0)
11844165Sjulian        ifp->if_mtu = ISO88025_DEFAULT_MTU;
11944165Sjulian
120152315Sru    ifa = ifp->if_addr;
121152315Sru    KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
122112297Smdodd
123112272Smdodd    sdl = (struct sockaddr_dl *)ifa->ifa_addr;
124112272Smdodd    sdl->sdl_type = IFT_ISO88025;
125112272Smdodd    sdl->sdl_alen = ifp->if_addrlen;
126152296Sru    bcopy(lla, LLADDR(sdl), ifp->if_addrlen);
127112297Smdodd
128112297Smdodd    if (bpf)
129112297Smdodd        bpfattach(ifp, DLT_IEEE802, ISO88025_HDR_LEN);
130112297Smdodd
131112297Smdodd    return;
13244165Sjulian}
13344165Sjulian
13474408Smdodd/*
13574408Smdodd * Perform common duties while detaching a Token Ring interface
13674408Smdodd */
13774408Smdoddvoid
13874408Smdoddiso88025_ifdetach(ifp, bpf)
13974408Smdodd        struct ifnet *ifp;
14074408Smdodd        int bpf;
14174408Smdodd{
142112274Smdodd
14374408Smdodd	if (bpf)
14474408Smdodd                bpfdetach(ifp);
145112274Smdodd
14674408Smdodd	if_detach(ifp);
147112274Smdodd
148112274Smdodd	return;
14974408Smdodd}
15074408Smdodd
15144165Sjulianint
152194581Srdivackyiso88025_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
15344165Sjulian{
154112274Smdodd        struct ifaddr *ifa;
155112274Smdodd        struct ifreq *ifr;
156112274Smdodd        int error;
15744165Sjulian
158112274Smdodd	ifa = (struct ifaddr *) data;
159112274Smdodd	ifr = (struct ifreq *) data;
160112274Smdodd	error = 0;
161112274Smdodd
16244165Sjulian        switch (command) {
16344165Sjulian        case SIOCSIFADDR:
16444165Sjulian                ifp->if_flags |= IFF_UP;
16544165Sjulian
16644165Sjulian                switch (ifa->ifa_addr->sa_family) {
16744165Sjulian#ifdef INET
16844165Sjulian                case AF_INET:
16944165Sjulian                        ifp->if_init(ifp->if_softc);    /* before arpwhohas */
17084931Sfjoe                        arp_ifinit(ifp, ifa);
17144165Sjulian                        break;
17274408Smdodd#endif	/* INET */
17374408Smdodd#ifdef IPX
17474408Smdodd                /*
17574408Smdodd                 * XXX - This code is probably wrong
17674408Smdodd                 */
177120048Smdodd                case AF_IPX: {
178120048Smdodd				struct ipx_addr *ina;
17974408Smdodd
180120048Smdodd				ina = &(IA_SIPX(ifa)->sipx_addr);
18174408Smdodd
182120048Smdodd				if (ipx_nullhost(*ina))
183120048Smdodd					ina->x_host = *(union ipx_host *)
184152315Sru							IF_LLADDR(ifp);
185120048Smdodd				else
186120048Smdodd					bcopy((caddr_t) ina->x_host.c_host,
187152315Sru					      (caddr_t) IF_LLADDR(ifp),
188120048Smdodd					      ISO88025_ADDR_LEN);
189120048Smdodd
190120048Smdodd				/*
191120048Smdodd				 * Set new address
192120048Smdodd				 */
193120048Smdodd				ifp->if_init(ifp->if_softc);
194120048Smdodd			}
195120048Smdodd			break;
19674408Smdodd#endif	/* IPX */
19744165Sjulian                default:
19844165Sjulian                        ifp->if_init(ifp->if_softc);
19944165Sjulian                        break;
20044165Sjulian                }
20144165Sjulian                break;
20244165Sjulian
203120047Smdodd        case SIOCGIFADDR: {
20444165Sjulian                        struct sockaddr *sa;
20544165Sjulian
20644165Sjulian                        sa = (struct sockaddr *) & ifr->ifr_data;
207152315Sru                        bcopy(IF_LLADDR(ifp),
20844165Sjulian                              (caddr_t) sa->sa_data, ISO88025_ADDR_LEN);
20944165Sjulian                }
21044165Sjulian                break;
21144165Sjulian
21244165Sjulian        case SIOCSIFMTU:
21344165Sjulian                /*
21444165Sjulian                 * Set the interface MTU.
21544165Sjulian                 */
21658313Slile                if (ifr->ifr_mtu > ISO88025_MAX_MTU) {
21744165Sjulian                        error = EINVAL;
21844165Sjulian                } else {
21944165Sjulian                        ifp->if_mtu = ifr->ifr_mtu;
22044165Sjulian                }
22144165Sjulian                break;
222112274Smdodd	default:
223112274Smdodd		error = EINVAL;			/* XXX netbsd has ENOTTY??? */
224112274Smdodd		break;
22544165Sjulian        }
226112274Smdodd
22744165Sjulian        return (error);
22844165Sjulian}
22944165Sjulian
23044165Sjulian/*
23144165Sjulian * ISO88025 encapsulation
23244165Sjulian */
23344165Sjulianint
234249925Sglebiusiso88025_output(struct ifnet *ifp, struct mbuf *m, const struct sockaddr *dst,
235249925Sglebius	struct route *ro)
23644165Sjulian{
23774408Smdodd	u_int16_t snap_type = 0;
23887914Sjlemon	int loop_copy = 0, error = 0, rif_len = 0;
23987914Sjlemon	u_char edst[ISO88025_ADDR_LEN];
24074408Smdodd	struct iso88025_header *th;
24144627Sjulian	struct iso88025_header gen_th;
24274408Smdodd	struct sockaddr_dl *sdl = NULL;
243193891Sbz	struct rtentry *rt0 = NULL;
244193891Sbz#if defined(INET) || defined(INET6)
245186119Sqingli	struct llentry *lle;
246193891Sbz#endif
24744165Sjulian
248191148Skmacy	if (ro != NULL)
249191148Skmacy		rt0 = ro->ro_rt;
250191148Skmacy
251112285Smdodd#ifdef MAC
252172930Srwatson	error = mac_ifnet_check_transmit(ifp, m);
253112285Smdodd	if (error)
254112285Smdodd		senderr(error);
255112285Smdodd#endif
256112285Smdodd
257112308Smdodd	if (ifp->if_flags & IFF_MONITOR)
258112308Smdodd		senderr(ENETDOWN);
259148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
260148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
26144165Sjulian		senderr(ENETDOWN);
26274408Smdodd	getmicrotime(&ifp->if_lastchange);
26374408Smdodd
264128636Sluigi	/* Calculate routing info length based on arp table entry */
265128636Sluigi	/* XXX any better way to do this ? */
26644627Sjulian
267186119Sqingli	if (rt0 && (sdl = (struct sockaddr_dl *)rt0->rt_gateway))
268102291Sarchie		if (SDL_ISO88025(sdl)->trld_rcf != 0)
26996184Skbyanc			rif_len = TR_RCF_RIFLEN(SDL_ISO88025(sdl)->trld_rcf);
27044627Sjulian
27144627Sjulian	/* Generate a generic 802.5 header for the packet */
27258313Slile	gen_th.ac = TR_AC;
27358313Slile	gen_th.fc = TR_LLC_FRAME;
274152315Sru	(void)memcpy((caddr_t)gen_th.iso88025_shost, IF_LLADDR(ifp),
275112278Smdodd		     ISO88025_ADDR_LEN);
27644627Sjulian	if (rif_len) {
27758313Slile		gen_th.iso88025_shost[0] |= TR_RII;
27844627Sjulian		if (rif_len > 2) {
27996184Skbyanc			gen_th.rcf = SDL_ISO88025(sdl)->trld_rcf;
28074408Smdodd			(void)memcpy((caddr_t)gen_th.rd,
28196184Skbyanc				(caddr_t)SDL_ISO88025(sdl)->trld_route,
28296184Skbyanc				rif_len - 2);
28344627Sjulian		}
28444627Sjulian	}
28544627Sjulian
28644165Sjulian	switch (dst->sa_family) {
28744165Sjulian#ifdef INET
28844165Sjulian	case AF_INET:
289186119Sqingli		error = arpresolve(ifp, rt0, m, dst, edst, &lle);
290128636Sluigi		if (error)
291128636Sluigi			return (error == EWOULDBLOCK ? 0 : error);
29274408Smdodd		snap_type = ETHERTYPE_IP;
29374408Smdodd		break;
294126951Smdodd	case AF_ARP:
295126951Smdodd	{
296126951Smdodd		struct arphdr *ah;
297126951Smdodd		ah = mtod(m, struct arphdr *);
298126951Smdodd		ah->ar_hrd = htons(ARPHRD_IEEE802);
299126951Smdodd
300126951Smdodd		loop_copy = -1; /* if this is for us, don't do it */
301126951Smdodd
302126951Smdodd		switch(ntohs(ah->ar_op)) {
303126951Smdodd		case ARPOP_REVREQUEST:
304126951Smdodd		case ARPOP_REVREPLY:
305126951Smdodd			snap_type = ETHERTYPE_REVARP;
306126951Smdodd			break;
307126951Smdodd		case ARPOP_REQUEST:
308126951Smdodd		case ARPOP_REPLY:
309126951Smdodd		default:
310126951Smdodd			snap_type = ETHERTYPE_ARP;
311126951Smdodd			break;
312126951Smdodd		}
313126951Smdodd
314126951Smdodd		if (m->m_flags & M_BCAST)
315126951Smdodd			bcopy(ifp->if_broadcastaddr, edst, ISO88025_ADDR_LEN);
316126951Smdodd		else
317126951Smdodd			bcopy(ar_tha(ah), edst, ISO88025_ADDR_LEN);
318126951Smdodd
319126951Smdodd	}
320126951Smdodd	break;
32174408Smdodd#endif	/* INET */
32274408Smdodd#ifdef INET6
32374408Smdodd	case AF_INET6:
324186217Sqingli		error = nd6_storelladdr(ifp, m, dst, (u_char *)edst, &lle);
325128636Sluigi		if (error)
326128636Sluigi			return (error);
32774408Smdodd		snap_type = ETHERTYPE_IPV6;
32874408Smdodd		break;
32974408Smdodd#endif	/* INET6 */
33074408Smdodd#ifdef IPX
33174408Smdodd	case AF_IPX:
33274408Smdodd	{
33374408Smdodd		u_int8_t	*cp;
33474408Smdodd
33574408Smdodd		bcopy((caddr_t)&(satoipx_addr(dst).x_host), (caddr_t)edst,
336112278Smdodd		      ISO88025_ADDR_LEN);
33774408Smdodd
338243882Sglebius		M_PREPEND(m, 3, M_WAITOK);
33974408Smdodd		m = m_pullup(m, 3);
34074408Smdodd		if (m == 0)
34174408Smdodd			senderr(ENOBUFS);
34274408Smdodd		cp = mtod(m, u_int8_t *);
34374408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
34474408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
34574408Smdodd		*cp++ = LLC_UI;
34674408Smdodd	}
34774408Smdodd	break;
34874408Smdodd#endif	/* IPX */
34944165Sjulian	case AF_UNSPEC:
35074408Smdodd	{
351249925Sglebius		const struct iso88025_sockaddr_data *sd;
35244627Sjulian		/*
35344627Sjulian		 * For AF_UNSPEC sockaddr.sa_data must contain all of the
35444627Sjulian		 * mac information needed to send the packet.  This allows
35544627Sjulian		 * full mac, llc, and source routing function to be controlled.
35644627Sjulian		 * llc and source routing information must already be in the
35744627Sjulian		 * mbuf provided, ac/fc are set in sa_data.  sockaddr.sa_data
358108533Sschweikh		 * should be an iso88025_sockaddr_data structure see iso88025.h
35944627Sjulian		 */
36044165Sjulian                loop_copy = -1;
361249925Sglebius		sd = (const struct iso88025_sockaddr_data *)dst->sa_data;
36244627Sjulian		gen_th.ac = sd->ac;
36344627Sjulian		gen_th.fc = sd->fc;
364249925Sglebius		(void)memcpy(edst, sd->ether_dhost, ISO88025_ADDR_LEN);
365249925Sglebius		(void)memcpy(gen_th.iso88025_shost, sd->ether_shost,
366249925Sglebius		    ISO88025_ADDR_LEN);
36744627Sjulian		rif_len = 0;
36844165Sjulian		break;
36974408Smdodd	}
37044165Sjulian	default:
371105598Sbrooks		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
37244165Sjulian		senderr(EAFNOSUPPORT);
37374408Smdodd		break;
37444165Sjulian	}
37544165Sjulian
376112274Smdodd	/*
377112274Smdodd	 * Add LLC header.
378112274Smdodd	 */
37974408Smdodd	if (snap_type != 0) {
38074408Smdodd        	struct llc *l;
381243882Sglebius		M_PREPEND(m, LLC_SNAPFRAMELEN, M_NOWAIT);
38274408Smdodd		if (m == 0)
38374408Smdodd			senderr(ENOBUFS);
38474408Smdodd		l = mtod(m, struct llc *);
385112281Smdodd		l->llc_control = LLC_UI;
38674408Smdodd		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
387112268Smdodd		l->llc_snap.org_code[0] =
388112268Smdodd			l->llc_snap.org_code[1] =
389112268Smdodd			l->llc_snap.org_code[2] = 0;
390112274Smdodd		l->llc_snap.ether_type = htons(snap_type);
39174408Smdodd	}
39274408Smdodd
39344165Sjulian	/*
39444165Sjulian	 * Add local net header.  If no space in first mbuf,
39544165Sjulian	 * allocate another.
39644165Sjulian	 */
397243882Sglebius	M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_NOWAIT);
39844165Sjulian	if (m == 0)
39944165Sjulian		senderr(ENOBUFS);
400112274Smdodd	th = mtod(m, struct iso88025_header *);
401112291Smdodd	bcopy((caddr_t)edst, (caddr_t)&gen_th.iso88025_dhost, ISO88025_ADDR_LEN);
40244627Sjulian
40344627Sjulian	/* Copy as much of the generic header as is needed into the mbuf */
40444627Sjulian	memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len);
40544627Sjulian
40644165Sjulian        /*
40744165Sjulian         * If a simplex interface, and the packet is being sent to our
40844165Sjulian         * Ethernet address or a broadcast address, loopback a copy.
40944165Sjulian         * XXX To make a simplex device behave exactly like a duplex
41044165Sjulian         * device, we should copy in the case of sending to our own
41144165Sjulian         * ethernet address (thus letting the original actually appear
41244165Sjulian         * on the wire). However, we don't do that here for security
41344165Sjulian         * reasons and compatibility with the original behavior.
41444165Sjulian         */
41574408Smdodd        if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
41644165Sjulian                if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
41774408Smdodd                        struct mbuf *n;
41874408Smdodd			n = m_copy(m, 0, (int)M_COPYALL);
419112279Smdodd                        (void) if_simloop(ifp, n, dst->sa_family,
42074408Smdodd					  ISO88025_HDR_LEN);
421112279Smdodd                } else if (bcmp(th->iso88025_dhost, th->iso88025_shost,
42274408Smdodd				 ETHER_ADDR_LEN) == 0) {
423112279Smdodd			(void) if_simloop(ifp, m, dst->sa_family,
424112279Smdodd					  ISO88025_HDR_LEN);
425112279Smdodd                       	return(0);      /* XXX */
426112279Smdodd		}
42744165Sjulian        }
42844165Sjulian
429130549Smlaier	IFQ_HANDOFF_ADJ(ifp, m, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN, error);
430130549Smlaier	if (error) {
43169152Sjlemon		printf("iso88025_output: packet dropped QFULL.\n");
432130549Smlaier		ifp->if_oerrors++;
43344165Sjulian	}
43444165Sjulian	return (error);
43544165Sjulian
43644165Sjulianbad:
437112296Smdodd	ifp->if_oerrors++;
43844165Sjulian	if (m)
43944165Sjulian		m_freem(m);
44044165Sjulian	return (error);
44144165Sjulian}
44244165Sjulian
44344165Sjulian/*
44444165Sjulian * ISO 88025 de-encapsulation
44544165Sjulian */
44644165Sjulianvoid
447112299Smdoddiso88025_input(ifp, m)
44874408Smdodd	struct ifnet *ifp;
44974408Smdodd	struct mbuf *m;
45044165Sjulian{
451112299Smdodd	struct iso88025_header *th;
452112299Smdodd	struct llc *l;
453111888Sjlemon	int isr;
454112299Smdodd	int mac_hdr_len;
45544165Sjulian
456112308Smdodd	/*
457112308Smdodd	 * Do consistency checks to verify assumptions
458112308Smdodd	 * made by code past this point.
459112308Smdodd	 */
460112308Smdodd	if ((m->m_flags & M_PKTHDR) == 0) {
461112308Smdodd		if_printf(ifp, "discard frame w/o packet header\n");
462112308Smdodd		ifp->if_ierrors++;
463112308Smdodd		m_freem(m);
464112308Smdodd		return;
465112308Smdodd	}
466112308Smdodd	if (m->m_pkthdr.rcvif == NULL) {
467112308Smdodd		if_printf(ifp, "discard frame w/o interface pointer\n");
468112308Smdodd		ifp->if_ierrors++;
469112308Smdodd 		m_freem(m);
470112308Smdodd		return;
471112308Smdodd	}
472112308Smdodd
473112299Smdodd	m = m_pullup(m, ISO88025_HDR_LEN);
474112299Smdodd	if (m == NULL) {
475112299Smdodd		ifp->if_ierrors++;
476112299Smdodd		goto dropanyway;
477112299Smdodd	}
478112299Smdodd	th = mtod(m, struct iso88025_header *);
479112299Smdodd
480112286Smdodd	/*
481112286Smdodd	 * Discard packet if interface is not up.
482112286Smdodd	 */
483148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
484148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
485112286Smdodd		goto dropanyway;
48644165Sjulian
487112308Smdodd	/*
488112308Smdodd	 * Give bpf a chance at the packet.
489112308Smdodd	 */
490112308Smdodd	BPF_MTAP(ifp, m);
491112308Smdodd
492112308Smdodd	/*
493112308Smdodd	 * Interface marked for monitoring; discard packet.
494112308Smdodd	 */
495112308Smdodd	if (ifp->if_flags & IFF_MONITOR) {
496112308Smdodd		m_freem(m);
497112308Smdodd		return;
498112308Smdodd	}
499112308Smdodd
500112285Smdodd#ifdef MAC
501172930Srwatson	mac_ifnet_create_mbuf(ifp, m);
502112285Smdodd#endif
503112285Smdodd
504112286Smdodd	/*
505112286Smdodd	 * Update interface statistics.
506112286Smdodd	 */
507112299Smdodd	ifp->if_ibytes += m->m_pkthdr.len;
50874408Smdodd	getmicrotime(&ifp->if_lastchange);
50958313Slile
510112280Smdodd	/*
511112286Smdodd	 * Discard non local unicast packets when interface
512112286Smdodd	 * is in promiscuous mode.
513112286Smdodd	 */
514112286Smdodd	if ((ifp->if_flags & IFF_PROMISC) &&
515112286Smdodd	    ((th->iso88025_dhost[0] & 1) == 0) &&
516152315Sru	     (bcmp(IF_LLADDR(ifp), (caddr_t) th->iso88025_dhost,
517112286Smdodd	     ISO88025_ADDR_LEN) != 0))
518112286Smdodd		goto dropanyway;
519112286Smdodd
520112286Smdodd	/*
521112280Smdodd	 * Set mbuf flags for bcast/mcast.
522112280Smdodd	 */
52344165Sjulian	if (th->iso88025_dhost[0] & 1) {
524126907Srwatson		if (bcmp(iso88025_broadcastaddr, th->iso88025_dhost,
525126907Srwatson		    ISO88025_ADDR_LEN) == 0)
52644165Sjulian			m->m_flags |= M_BCAST;
52744165Sjulian		else
52844165Sjulian			m->m_flags |= M_MCAST;
52974408Smdodd		ifp->if_imcasts++;
530112274Smdodd	}
53144165Sjulian
532112299Smdodd	mac_hdr_len = ISO88025_HDR_LEN;
533112299Smdodd	/* Check for source routing info */
534112299Smdodd	if (th->iso88025_shost[0] & TR_RII)
535112299Smdodd		mac_hdr_len += TR_RCF_RIFLEN(th->rcf);
536112299Smdodd
537112299Smdodd	/* Strip off ISO88025 header. */
538112299Smdodd	m_adj(m, mac_hdr_len);
539112299Smdodd
540112299Smdodd	m = m_pullup(m, LLC_SNAPFRAMELEN);
541112299Smdodd	if (m == 0) {
542112299Smdodd		ifp->if_ierrors++;
543112299Smdodd		goto dropanyway;
544112299Smdodd	}
54574408Smdodd	l = mtod(m, struct llc *);
54644165Sjulian
54774408Smdodd	switch (l->llc_dsap) {
54874408Smdodd#ifdef IPX
54974408Smdodd	case ETHERTYPE_IPX_8022:	/* Thanks a bunch Novell */
55074408Smdodd		if ((l->llc_control != LLC_UI) ||
551112289Smdodd		    (l->llc_ssap != ETHERTYPE_IPX_8022)) {
552112289Smdodd			ifp->if_noproto++;
55374408Smdodd			goto dropanyway;
554112289Smdodd		}
55574408Smdodd
55674408Smdodd		th->iso88025_shost[0] &= ~(TR_RII);
55774408Smdodd		m_adj(m, 3);
558111888Sjlemon		isr = NETISR_IPX;
55974408Smdodd		break;
56074408Smdodd#endif	/* IPX */
56174408Smdodd	case LLC_SNAP_LSAP: {
56274408Smdodd		u_int16_t type;
56374408Smdodd		if ((l->llc_control != LLC_UI) ||
564112289Smdodd		    (l->llc_ssap != LLC_SNAP_LSAP)) {
565112289Smdodd			ifp->if_noproto++;
56674408Smdodd			goto dropanyway;
567112289Smdodd		}
56874408Smdodd
569112268Smdodd		if (l->llc_snap.org_code[0] != 0 ||
570112268Smdodd		    l->llc_snap.org_code[1] != 0 ||
571112294Smdodd		    l->llc_snap.org_code[2] != 0) {
572112294Smdodd			ifp->if_noproto++;
57374408Smdodd			goto dropanyway;
574112294Smdodd		}
57574408Smdodd
576112268Smdodd		type = ntohs(l->llc_snap.ether_type);
577111790Smdodd		m_adj(m, LLC_SNAPFRAMELEN);
57874408Smdodd		switch (type) {
57944165Sjulian#ifdef INET
58074408Smdodd		case ETHERTYPE_IP:
58174408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
582154518Sandre			if ((m = ip_fastforward(m)) == NULL)
58374408Smdodd				return;
584111888Sjlemon			isr = NETISR_IP;
58574408Smdodd			break;
58674408Smdodd
58774408Smdodd		case ETHERTYPE_ARP:
58878295Sjlemon			if (ifp->if_flags & IFF_NOARP)
58978295Sjlemon				goto dropanyway;
590111888Sjlemon			isr = NETISR_ARP;
59174408Smdodd			break;
59274408Smdodd#endif	/* INET */
59374408Smdodd#ifdef IPX_SNAP	/* XXX: Not supported! */
59474408Smdodd		case ETHERTYPE_IPX:
59574408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
596111888Sjlemon			isr = NETISR_IPX;
59774408Smdodd			break;
59874408Smdodd#endif	/* IPX_SNAP */
59974408Smdodd#ifdef INET6
60074408Smdodd		case ETHERTYPE_IPV6:
60174408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
602111888Sjlemon			isr = NETISR_IPV6;
60374408Smdodd			break;
60474408Smdodd#endif	/* INET6 */
60574408Smdodd		default:
60674408Smdodd			printf("iso88025_input: unexpected llc_snap ether_type  0x%02x\n", type);
607112289Smdodd			ifp->if_noproto++;
608112289Smdodd			goto dropanyway;
60974408Smdodd		}
61044165Sjulian		break;
61174408Smdodd	}
612112296Smdodd#ifdef ISO
61374408Smdodd	case LLC_ISO_LSAP:
61474408Smdodd		switch (l->llc_control) {
61574408Smdodd		case LLC_UI:
616112289Smdodd			ifp->if_noproto++;
61774408Smdodd			goto dropanyway;
61874408Smdodd			break;
61974408Smdodd                case LLC_XID:
62074408Smdodd                case LLC_XID_P:
62174408Smdodd			if(m->m_len < ISO88025_ADDR_LEN)
62274408Smdodd				goto dropanyway;
62374408Smdodd			l->llc_window = 0;
62474408Smdodd			l->llc_fid = 9;
62574408Smdodd			l->llc_class = 1;
62674408Smdodd			l->llc_dsap = l->llc_ssap = 0;
62774408Smdodd			/* Fall through to */
62874408Smdodd		case LLC_TEST:
62974408Smdodd		case LLC_TEST_P:
63074408Smdodd		{
63174408Smdodd			struct sockaddr sa;
632112296Smdodd			struct arpcom *ac;
63374408Smdodd			struct iso88025_sockaddr_data *th2;
63474408Smdodd			int i;
635112296Smdodd			u_char c;
63644165Sjulian
637112296Smdodd			c = l->llc_dsap;
638112296Smdodd
63974408Smdodd			if (th->iso88025_shost[0] & TR_RII) { /* XXX */
64074408Smdodd				printf("iso88025_input: dropping source routed LLC_TEST\n");
641112289Smdodd				goto dropanyway;
64274408Smdodd			}
64374408Smdodd			l->llc_dsap = l->llc_ssap;
64474408Smdodd			l->llc_ssap = c;
64574408Smdodd			if (m->m_flags & (M_BCAST | M_MCAST))
646152315Sru				bcopy((caddr_t)IF_LLADDR(ifp),
647112280Smdodd				      (caddr_t)th->iso88025_dhost,
64874408Smdodd					ISO88025_ADDR_LEN);
64974408Smdodd			sa.sa_family = AF_UNSPEC;
65074408Smdodd			sa.sa_len = sizeof(sa);
65174408Smdodd			th2 = (struct iso88025_sockaddr_data *)sa.sa_data;
65274408Smdodd			for (i = 0; i < ISO88025_ADDR_LEN; i++) {
65374408Smdodd				th2->ether_shost[i] = c = th->iso88025_dhost[i];
65474408Smdodd				th2->ether_dhost[i] = th->iso88025_dhost[i] =
65574408Smdodd					th->iso88025_shost[i];
65674408Smdodd				th->iso88025_shost[i] = c;
65774408Smdodd			}
65874408Smdodd			th2->ac = TR_AC;
65974408Smdodd			th2->fc = TR_LLC_FRAME;
66074408Smdodd			ifp->if_output(ifp, m, &sa, NULL);
66174408Smdodd			return;
66274408Smdodd		}
66374408Smdodd		default:
66474408Smdodd			printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control);
665112289Smdodd			ifp->if_noproto++;
666112289Smdodd			goto dropanyway;
667112294Smdodd			break;
66874408Smdodd		}
66974408Smdodd		break;
670112296Smdodd#endif	/* ISO */
67144165Sjulian	default:
67274408Smdodd		printf("iso88025_input: unknown dsap 0x%x\n", l->llc_dsap);
67374408Smdodd		ifp->if_noproto++;
674112289Smdodd		goto dropanyway;
675112294Smdodd		break;
67644165Sjulian	}
677112274Smdodd
678223741Sbz	M_SETFIB(m, ifp->if_fib);
679111888Sjlemon	netisr_dispatch(isr, m);
680112274Smdodd	return;
681112289Smdodd
682112289Smdodddropanyway:
683112289Smdodd	ifp->if_iqdrops++;
684112289Smdodd	if (m)
685112289Smdodd		m_freem(m);
686112289Smdodd	return;
68744165Sjulian}
688112269Smdodd
689112273Smdoddstatic int
690112273Smdoddiso88025_resolvemulti (ifp, llsa, sa)
691112273Smdodd	struct ifnet *ifp;
692112273Smdodd	struct sockaddr **llsa;
693112273Smdodd	struct sockaddr *sa;
694112273Smdodd{
695112273Smdodd	struct sockaddr_dl *sdl;
696184710Sbz#ifdef INET
697112273Smdodd	struct sockaddr_in *sin;
698184710Sbz#endif
699112273Smdodd#ifdef INET6
700112273Smdodd	struct sockaddr_in6 *sin6;
701112273Smdodd#endif
702112273Smdodd	u_char *e_addr;
703112273Smdodd
704112273Smdodd	switch(sa->sa_family) {
705112273Smdodd	case AF_LINK:
706112273Smdodd		/*
707112273Smdodd		 * No mapping needed. Just check that it's a valid MC address.
708112273Smdodd		 */
709112273Smdodd		sdl = (struct sockaddr_dl *)sa;
710112273Smdodd		e_addr = LLADDR(sdl);
711112273Smdodd		if ((e_addr[0] & 1) != 1) {
712112273Smdodd			return (EADDRNOTAVAIL);
713112273Smdodd		}
714112273Smdodd		*llsa = 0;
715112273Smdodd		return (0);
716112273Smdodd
717112273Smdodd#ifdef INET
718112273Smdodd	case AF_INET:
719112273Smdodd		sin = (struct sockaddr_in *)sa;
720112273Smdodd		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
721112273Smdodd			return (EADDRNOTAVAIL);
722112273Smdodd		}
723184205Sdes		sdl = malloc(sizeof *sdl, M_IFMADDR,
724148641Srwatson		       M_NOWAIT|M_ZERO);
725148641Srwatson		if (sdl == NULL)
726148641Srwatson			return (ENOMEM);
727112273Smdodd		sdl->sdl_len = sizeof *sdl;
728112273Smdodd		sdl->sdl_family = AF_LINK;
729112273Smdodd		sdl->sdl_index = ifp->if_index;
730112273Smdodd		sdl->sdl_type = IFT_ISO88025;
731112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
732112273Smdodd		e_addr = LLADDR(sdl);
733112273Smdodd		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
734112273Smdodd		*llsa = (struct sockaddr *)sdl;
735112273Smdodd		return (0);
736112273Smdodd#endif
737112273Smdodd#ifdef INET6
738112273Smdodd	case AF_INET6:
739112273Smdodd		sin6 = (struct sockaddr_in6 *)sa;
740112273Smdodd		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
741112273Smdodd			/*
742112273Smdodd			 * An IP6 address of 0 means listen to all
743112273Smdodd			 * of the Ethernet multicast address used for IP6.
744112273Smdodd			 * (This is used for multicast routers.)
745112273Smdodd			 */
746112273Smdodd			ifp->if_flags |= IFF_ALLMULTI;
747112273Smdodd			*llsa = 0;
748112273Smdodd			return (0);
749112273Smdodd		}
750112273Smdodd		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
751112273Smdodd			return (EADDRNOTAVAIL);
752112273Smdodd		}
753184205Sdes		sdl = malloc(sizeof *sdl, M_IFMADDR,
754148641Srwatson		       M_NOWAIT|M_ZERO);
755148641Srwatson		if (sdl == NULL)
756148641Srwatson			return (ENOMEM);
757112273Smdodd		sdl->sdl_len = sizeof *sdl;
758112273Smdodd		sdl->sdl_family = AF_LINK;
759112273Smdodd		sdl->sdl_index = ifp->if_index;
760112273Smdodd		sdl->sdl_type = IFT_ISO88025;
761112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
762112273Smdodd		e_addr = LLADDR(sdl);
763112273Smdodd		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
764112273Smdodd		*llsa = (struct sockaddr *)sdl;
765112273Smdodd		return (0);
766112273Smdodd#endif
767112273Smdodd
768112273Smdodd	default:
769112273Smdodd		/*
770112273Smdodd		 * Well, the text isn't quite right, but it's the name
771112273Smdodd		 * that counts...
772112273Smdodd		 */
773112273Smdodd		return (EAFNOSUPPORT);
774112273Smdodd	}
775112273Smdodd
776112273Smdodd	return (0);
777112273Smdodd}
778112273Smdodd
779227293Sedstatic MALLOC_DEFINE(M_ISO88025, "arpcom", "802.5 interface internals");
780147256Sbrooks
781147256Sbrooksstatic void*
782147256Sbrooksiso88025_alloc(u_char type, struct ifnet *ifp)
783147256Sbrooks{
784147256Sbrooks	struct arpcom	*ac;
785147256Sbrooks
786147256Sbrooks        ac = malloc(sizeof(struct arpcom), M_ISO88025, M_WAITOK | M_ZERO);
787147256Sbrooks	ac->ac_ifp = ifp;
788147256Sbrooks
789147256Sbrooks	return (ac);
790147256Sbrooks}
791147256Sbrooks
792147256Sbrooksstatic void
793147256Sbrooksiso88025_free(void *com, u_char type)
794147256Sbrooks{
795147256Sbrooks
796147256Sbrooks        free(com, M_ISO88025);
797147256Sbrooks}
798147256Sbrooks
799147256Sbrooksstatic int
800147256Sbrooksiso88025_modevent(module_t mod, int type, void *data)
801147256Sbrooks{
802147256Sbrooks
803147256Sbrooks        switch (type) {
804147256Sbrooks        case MOD_LOAD:
805147256Sbrooks                if_register_com_alloc(IFT_ISO88025, iso88025_alloc,
806147256Sbrooks                    iso88025_free);
807147256Sbrooks                break;
808147256Sbrooks        case MOD_UNLOAD:
809147256Sbrooks                if_deregister_com_alloc(IFT_ISO88025);
810147256Sbrooks                break;
811147256Sbrooks        default:
812147256Sbrooks                return EOPNOTSUPP;
813147256Sbrooks        }
814147256Sbrooks
815147256Sbrooks        return (0);
816147256Sbrooks}
817147256Sbrooks
818112269Smdoddstatic moduledata_t iso88025_mod = {
819112269Smdodd	"iso88025",
820147256Sbrooks	iso88025_modevent,
821241394Skevlo	0
822112269Smdodd};
823112269Smdodd
824112269SmdoddDECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
825112269SmdoddMODULE_VERSION(iso88025, 1);
826