if_iso88025subr.c revision 172930
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: head/sys/net/if_iso88025subr.c 172930 2007-10-24 19:04:04Z rwatson $
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"
46112285Smdodd#include "opt_mac.h"
4744165Sjulian
4844165Sjulian#include <sys/param.h>
4944165Sjulian#include <sys/systm.h>
50112271Smdodd#include <sys/kernel.h>
51112271Smdodd#include <sys/malloc.h>
5244165Sjulian#include <sys/mbuf.h>
53112271Smdodd#include <sys/module.h>
5444165Sjulian#include <sys/socket.h>
5544165Sjulian#include <sys/sockio.h>
5644165Sjulian
5744165Sjulian#include <net/if.h>
58112271Smdodd#include <net/if_dl.h>
5944165Sjulian#include <net/if_llc.h>
6044165Sjulian#include <net/if_types.h>
6144165Sjulian
62112271Smdodd#include <net/netisr.h>
63112271Smdodd#include <net/route.h>
64112271Smdodd#include <net/bpf.h>
6544165Sjulian#include <net/iso88025.h>
6644165Sjulian
6774408Smdodd#if defined(INET) || defined(INET6)
6844165Sjulian#include <netinet/in.h>
6944165Sjulian#include <netinet/in_var.h>
7044165Sjulian#include <netinet/if_ether.h>
7144165Sjulian#endif
7274408Smdodd#ifdef INET6
7374408Smdodd#include <netinet6/nd6.h>
7474408Smdodd#endif
7544165Sjulian
7674408Smdodd#ifdef IPX
7774408Smdodd#include <netipx/ipx.h>
7874408Smdodd#include <netipx/ipx_if.h>
7974408Smdodd#endif
8074408Smdodd
81163606Srwatson#include <security/mac/mac_framework.h>
82163606Srwatson
83126907Srwatsonstatic const u_char iso88025_broadcastaddr[ISO88025_ADDR_LEN] =
84112277Smdodd			{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
85112277Smdodd
86112273Smdoddstatic int iso88025_resolvemulti (struct ifnet *, struct sockaddr **,
87112294Smdodd				  struct sockaddr *);
88112273Smdodd
89112276Smdodd#define	senderr(e)	do { error = (e); goto bad; } while (0)
9074408Smdodd
91112297Smdodd/*
92112297Smdodd * Perform common duties while attaching to interface list
93112297Smdodd */
9444165Sjulianvoid
95152296Sruiso88025_ifattach(struct ifnet *ifp, const u_int8_t *lla, int bpf)
9644165Sjulian{
97112296Smdodd    struct ifaddr *ifa;
98111774Smdodd    struct sockaddr_dl *sdl;
9944165Sjulian
100112296Smdodd    ifa = NULL;
101112296Smdodd
10244165Sjulian    ifp->if_type = IFT_ISO88025;
10358313Slile    ifp->if_addrlen = ISO88025_ADDR_LEN;
10458313Slile    ifp->if_hdrlen = ISO88025_HDR_LEN;
105112297Smdodd
106112297Smdodd    if_attach(ifp);	/* Must be called before additional assignments */
107112297Smdodd
108112297Smdodd    ifp->if_output = iso88025_output;
109112297Smdodd    ifp->if_input = iso88025_input;
110112297Smdodd    ifp->if_resolvemulti = iso88025_resolvemulti;
111112297Smdodd    ifp->if_broadcastaddr = iso88025_broadcastaddr;
112112297Smdodd
11344165Sjulian    if (ifp->if_baudrate == 0)
11458313Slile        ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */
11544165Sjulian    if (ifp->if_mtu == 0)
11644165Sjulian        ifp->if_mtu = ISO88025_DEFAULT_MTU;
11744165Sjulian
118152315Sru    ifa = ifp->if_addr;
119152315Sru    KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
120112297Smdodd
121112272Smdodd    sdl = (struct sockaddr_dl *)ifa->ifa_addr;
122112272Smdodd    sdl->sdl_type = IFT_ISO88025;
123112272Smdodd    sdl->sdl_alen = ifp->if_addrlen;
124152296Sru    bcopy(lla, LLADDR(sdl), ifp->if_addrlen);
125112297Smdodd
126112297Smdodd    if (bpf)
127112297Smdodd        bpfattach(ifp, DLT_IEEE802, ISO88025_HDR_LEN);
128112297Smdodd
129112297Smdodd    return;
13044165Sjulian}
13144165Sjulian
13274408Smdodd/*
13374408Smdodd * Perform common duties while detaching a Token Ring interface
13474408Smdodd */
13574408Smdoddvoid
13674408Smdoddiso88025_ifdetach(ifp, bpf)
13774408Smdodd        struct ifnet *ifp;
13874408Smdodd        int bpf;
13974408Smdodd{
140112274Smdodd
14174408Smdodd	if (bpf)
14274408Smdodd                bpfdetach(ifp);
143112274Smdodd
14474408Smdodd	if_detach(ifp);
145112274Smdodd
146112274Smdodd	return;
14774408Smdodd}
14874408Smdodd
14944165Sjulianint
15044165Sjulianiso88025_ioctl(struct ifnet *ifp, int command, caddr_t data)
15144165Sjulian{
152112274Smdodd        struct ifaddr *ifa;
153112274Smdodd        struct ifreq *ifr;
154112274Smdodd        int error;
15544165Sjulian
156112274Smdodd	ifa = (struct ifaddr *) data;
157112274Smdodd	ifr = (struct ifreq *) data;
158112274Smdodd	error = 0;
159112274Smdodd
16044165Sjulian        switch (command) {
16144165Sjulian        case SIOCSIFADDR:
16244165Sjulian                ifp->if_flags |= IFF_UP;
16344165Sjulian
16444165Sjulian                switch (ifa->ifa_addr->sa_family) {
16544165Sjulian#ifdef INET
16644165Sjulian                case AF_INET:
16744165Sjulian                        ifp->if_init(ifp->if_softc);    /* before arpwhohas */
16884931Sfjoe                        arp_ifinit(ifp, ifa);
16944165Sjulian                        break;
17074408Smdodd#endif	/* INET */
17174408Smdodd#ifdef IPX
17274408Smdodd                /*
17374408Smdodd                 * XXX - This code is probably wrong
17474408Smdodd                 */
175120048Smdodd                case AF_IPX: {
176120048Smdodd				struct ipx_addr *ina;
17774408Smdodd
178120048Smdodd				ina = &(IA_SIPX(ifa)->sipx_addr);
17974408Smdodd
180120048Smdodd				if (ipx_nullhost(*ina))
181120048Smdodd					ina->x_host = *(union ipx_host *)
182152315Sru							IF_LLADDR(ifp);
183120048Smdodd				else
184120048Smdodd					bcopy((caddr_t) ina->x_host.c_host,
185152315Sru					      (caddr_t) IF_LLADDR(ifp),
186120048Smdodd					      ISO88025_ADDR_LEN);
187120048Smdodd
188120048Smdodd				/*
189120048Smdodd				 * Set new address
190120048Smdodd				 */
191120048Smdodd				ifp->if_init(ifp->if_softc);
192120048Smdodd			}
193120048Smdodd			break;
19474408Smdodd#endif	/* IPX */
19544165Sjulian                default:
19644165Sjulian                        ifp->if_init(ifp->if_softc);
19744165Sjulian                        break;
19844165Sjulian                }
19944165Sjulian                break;
20044165Sjulian
201120047Smdodd        case SIOCGIFADDR: {
20244165Sjulian                        struct sockaddr *sa;
20344165Sjulian
20444165Sjulian                        sa = (struct sockaddr *) & ifr->ifr_data;
205152315Sru                        bcopy(IF_LLADDR(ifp),
20644165Sjulian                              (caddr_t) sa->sa_data, ISO88025_ADDR_LEN);
20744165Sjulian                }
20844165Sjulian                break;
20944165Sjulian
21044165Sjulian        case SIOCSIFMTU:
21144165Sjulian                /*
21244165Sjulian                 * Set the interface MTU.
21344165Sjulian                 */
21458313Slile                if (ifr->ifr_mtu > ISO88025_MAX_MTU) {
21544165Sjulian                        error = EINVAL;
21644165Sjulian                } else {
21744165Sjulian                        ifp->if_mtu = ifr->ifr_mtu;
21844165Sjulian                }
21944165Sjulian                break;
220112274Smdodd	default:
221112274Smdodd		error = EINVAL;			/* XXX netbsd has ENOTTY??? */
222112274Smdodd		break;
22344165Sjulian        }
224112274Smdodd
22544165Sjulian        return (error);
22644165Sjulian}
22744165Sjulian
22844165Sjulian/*
22944165Sjulian * ISO88025 encapsulation
23044165Sjulian */
23144165Sjulianint
23274408Smdoddiso88025_output(ifp, m, dst, rt0)
23374408Smdodd	struct ifnet *ifp;
23474408Smdodd	struct mbuf *m;
23574408Smdodd	struct sockaddr *dst;
23674408Smdodd	struct rtentry *rt0;
23744165Sjulian{
23874408Smdodd	u_int16_t snap_type = 0;
23987914Sjlemon	int loop_copy = 0, error = 0, rif_len = 0;
24087914Sjlemon	u_char edst[ISO88025_ADDR_LEN];
24174408Smdodd	struct iso88025_header *th;
24244627Sjulian	struct iso88025_header gen_th;
24374408Smdodd	struct sockaddr_dl *sdl = NULL;
244148954Sglebius	struct rtentry *rt = NULL;
24544165Sjulian
246112285Smdodd#ifdef MAC
247172930Srwatson	error = mac_ifnet_check_transmit(ifp, m);
248112285Smdodd	if (error)
249112285Smdodd		senderr(error);
250112285Smdodd#endif
251112285Smdodd
252112308Smdodd	if (ifp->if_flags & IFF_MONITOR)
253112308Smdodd		senderr(ENETDOWN);
254148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
255148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
25644165Sjulian		senderr(ENETDOWN);
25774408Smdodd	getmicrotime(&ifp->if_lastchange);
25874408Smdodd
259128636Sluigi	/* Calculate routing info length based on arp table entry */
260128636Sluigi	/* XXX any better way to do this ? */
261148954Sglebius	if (rt0 != NULL) {
262148954Sglebius		error = rt_check(&rt, &rt0, dst);
263148954Sglebius		if (error)
264148954Sglebius			goto bad;
265148954Sglebius		RT_UNLOCK(rt);
266148954Sglebius	}
26744627Sjulian
26844627Sjulian	if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway))
269102291Sarchie		if (SDL_ISO88025(sdl)->trld_rcf != 0)
27096184Skbyanc			rif_len = TR_RCF_RIFLEN(SDL_ISO88025(sdl)->trld_rcf);
27144627Sjulian
27244627Sjulian	/* Generate a generic 802.5 header for the packet */
27358313Slile	gen_th.ac = TR_AC;
27458313Slile	gen_th.fc = TR_LLC_FRAME;
275152315Sru	(void)memcpy((caddr_t)gen_th.iso88025_shost, IF_LLADDR(ifp),
276112278Smdodd		     ISO88025_ADDR_LEN);
27744627Sjulian	if (rif_len) {
27858313Slile		gen_th.iso88025_shost[0] |= TR_RII;
27944627Sjulian		if (rif_len > 2) {
28096184Skbyanc			gen_th.rcf = SDL_ISO88025(sdl)->trld_rcf;
28174408Smdodd			(void)memcpy((caddr_t)gen_th.rd,
28296184Skbyanc				(caddr_t)SDL_ISO88025(sdl)->trld_route,
28396184Skbyanc				rif_len - 2);
28444627Sjulian		}
28544627Sjulian	}
28644627Sjulian
28744165Sjulian	switch (dst->sa_family) {
28844165Sjulian#ifdef INET
28944165Sjulian	case AF_INET:
290128636Sluigi		error = arpresolve(ifp, rt0, m, dst, edst);
291128636Sluigi		if (error)
292128636Sluigi			return (error == EWOULDBLOCK ? 0 : error);
29374408Smdodd		snap_type = ETHERTYPE_IP;
29474408Smdodd		break;
295126951Smdodd	case AF_ARP:
296126951Smdodd	{
297126951Smdodd		struct arphdr *ah;
298126951Smdodd		ah = mtod(m, struct arphdr *);
299126951Smdodd		ah->ar_hrd = htons(ARPHRD_IEEE802);
300126951Smdodd
301126951Smdodd		loop_copy = -1; /* if this is for us, don't do it */
302126951Smdodd
303126951Smdodd		switch(ntohs(ah->ar_op)) {
304126951Smdodd		case ARPOP_REVREQUEST:
305126951Smdodd		case ARPOP_REVREPLY:
306126951Smdodd			snap_type = ETHERTYPE_REVARP;
307126951Smdodd			break;
308126951Smdodd		case ARPOP_REQUEST:
309126951Smdodd		case ARPOP_REPLY:
310126951Smdodd		default:
311126951Smdodd			snap_type = ETHERTYPE_ARP;
312126951Smdodd			break;
313126951Smdodd		}
314126951Smdodd
315126951Smdodd		if (m->m_flags & M_BCAST)
316126951Smdodd			bcopy(ifp->if_broadcastaddr, edst, ISO88025_ADDR_LEN);
317126951Smdodd		else
318126951Smdodd			bcopy(ar_tha(ah), edst, ISO88025_ADDR_LEN);
319126951Smdodd
320126951Smdodd	}
321126951Smdodd	break;
32274408Smdodd#endif	/* INET */
32374408Smdodd#ifdef INET6
32474408Smdodd	case AF_INET6:
325128636Sluigi		error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)edst);
326128636Sluigi		if (error)
327128636Sluigi			return (error);
32874408Smdodd		snap_type = ETHERTYPE_IPV6;
32974408Smdodd		break;
33074408Smdodd#endif	/* INET6 */
33174408Smdodd#ifdef IPX
33274408Smdodd	case AF_IPX:
33374408Smdodd	{
33474408Smdodd		u_int8_t	*cp;
33574408Smdodd
33674408Smdodd		bcopy((caddr_t)&(satoipx_addr(dst).x_host), (caddr_t)edst,
337112278Smdodd		      ISO88025_ADDR_LEN);
33874408Smdodd
339111119Simp		M_PREPEND(m, 3, M_TRYWAIT);
34044627Sjulian		if (m == 0)
34144627Sjulian			senderr(ENOBUFS);
34274408Smdodd		m = m_pullup(m, 3);
34374408Smdodd		if (m == 0)
34474408Smdodd			senderr(ENOBUFS);
34574408Smdodd		cp = mtod(m, u_int8_t *);
34674408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
34774408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
34874408Smdodd		*cp++ = LLC_UI;
34974408Smdodd	}
35074408Smdodd	break;
35174408Smdodd#endif	/* IPX */
35244165Sjulian	case AF_UNSPEC:
35374408Smdodd	{
35474408Smdodd		struct iso88025_sockaddr_data *sd;
35544627Sjulian		/*
35644627Sjulian		 * For AF_UNSPEC sockaddr.sa_data must contain all of the
35744627Sjulian		 * mac information needed to send the packet.  This allows
35844627Sjulian		 * full mac, llc, and source routing function to be controlled.
35944627Sjulian		 * llc and source routing information must already be in the
36044627Sjulian		 * mbuf provided, ac/fc are set in sa_data.  sockaddr.sa_data
361108533Sschweikh		 * should be an iso88025_sockaddr_data structure see iso88025.h
36244627Sjulian		 */
36344165Sjulian                loop_copy = -1;
36444627Sjulian		sd = (struct iso88025_sockaddr_data *)dst->sa_data;
36544627Sjulian		gen_th.ac = sd->ac;
36644627Sjulian		gen_th.fc = sd->fc;
36774408Smdodd		(void)memcpy((caddr_t)edst, (caddr_t)sd->ether_dhost,
368112278Smdodd			     ISO88025_ADDR_LEN);
36974408Smdodd		(void)memcpy((caddr_t)gen_th.iso88025_shost,
370112280Smdodd			     (caddr_t)sd->ether_shost, ISO88025_ADDR_LEN);
37144627Sjulian		rif_len = 0;
37244165Sjulian		break;
37374408Smdodd	}
37444165Sjulian	default:
375105598Sbrooks		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
37644165Sjulian		senderr(EAFNOSUPPORT);
37774408Smdodd		break;
37844165Sjulian	}
37944165Sjulian
380112274Smdodd	/*
381112274Smdodd	 * Add LLC header.
382112274Smdodd	 */
38374408Smdodd	if (snap_type != 0) {
38474408Smdodd        	struct llc *l;
385111790Smdodd		M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
38674408Smdodd		if (m == 0)
38774408Smdodd			senderr(ENOBUFS);
38874408Smdodd		l = mtod(m, struct llc *);
389112281Smdodd		l->llc_control = LLC_UI;
39074408Smdodd		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
391112268Smdodd		l->llc_snap.org_code[0] =
392112268Smdodd			l->llc_snap.org_code[1] =
393112268Smdodd			l->llc_snap.org_code[2] = 0;
394112274Smdodd		l->llc_snap.ether_type = htons(snap_type);
39574408Smdodd	}
39674408Smdodd
39744165Sjulian	/*
39844165Sjulian	 * Add local net header.  If no space in first mbuf,
39944165Sjulian	 * allocate another.
40044165Sjulian	 */
401111119Simp	M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT);
40244165Sjulian	if (m == 0)
40344165Sjulian		senderr(ENOBUFS);
404112274Smdodd	th = mtod(m, struct iso88025_header *);
405112291Smdodd	bcopy((caddr_t)edst, (caddr_t)&gen_th.iso88025_dhost, ISO88025_ADDR_LEN);
40644627Sjulian
40744627Sjulian	/* Copy as much of the generic header as is needed into the mbuf */
40844627Sjulian	memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len);
40944627Sjulian
41044165Sjulian        /*
41144165Sjulian         * If a simplex interface, and the packet is being sent to our
41244165Sjulian         * Ethernet address or a broadcast address, loopback a copy.
41344165Sjulian         * XXX To make a simplex device behave exactly like a duplex
41444165Sjulian         * device, we should copy in the case of sending to our own
41544165Sjulian         * ethernet address (thus letting the original actually appear
41644165Sjulian         * on the wire). However, we don't do that here for security
41744165Sjulian         * reasons and compatibility with the original behavior.
41844165Sjulian         */
41974408Smdodd        if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
42044165Sjulian                if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
42174408Smdodd                        struct mbuf *n;
42274408Smdodd			n = m_copy(m, 0, (int)M_COPYALL);
423112279Smdodd                        (void) if_simloop(ifp, n, dst->sa_family,
42474408Smdodd					  ISO88025_HDR_LEN);
425112279Smdodd                } else if (bcmp(th->iso88025_dhost, th->iso88025_shost,
42674408Smdodd				 ETHER_ADDR_LEN) == 0) {
427112279Smdodd			(void) if_simloop(ifp, m, dst->sa_family,
428112279Smdodd					  ISO88025_HDR_LEN);
429112279Smdodd                       	return(0);      /* XXX */
430112279Smdodd		}
43144165Sjulian        }
43244165Sjulian
433130549Smlaier	IFQ_HANDOFF_ADJ(ifp, m, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN, error);
434130549Smlaier	if (error) {
43569152Sjlemon		printf("iso88025_output: packet dropped QFULL.\n");
436130549Smlaier		ifp->if_oerrors++;
43744165Sjulian	}
43844165Sjulian	return (error);
43944165Sjulian
44044165Sjulianbad:
441112296Smdodd	ifp->if_oerrors++;
44244165Sjulian	if (m)
44344165Sjulian		m_freem(m);
44444165Sjulian	return (error);
44544165Sjulian}
44644165Sjulian
44744165Sjulian/*
44844165Sjulian * ISO 88025 de-encapsulation
44944165Sjulian */
45044165Sjulianvoid
451112299Smdoddiso88025_input(ifp, m)
45274408Smdodd	struct ifnet *ifp;
45374408Smdodd	struct mbuf *m;
45444165Sjulian{
455112299Smdodd	struct iso88025_header *th;
456112299Smdodd	struct llc *l;
457111888Sjlemon	int isr;
458112299Smdodd	int mac_hdr_len;
45944165Sjulian
460112308Smdodd	/*
461112308Smdodd	 * Do consistency checks to verify assumptions
462112308Smdodd	 * made by code past this point.
463112308Smdodd	 */
464112308Smdodd	if ((m->m_flags & M_PKTHDR) == 0) {
465112308Smdodd		if_printf(ifp, "discard frame w/o packet header\n");
466112308Smdodd		ifp->if_ierrors++;
467112308Smdodd		m_freem(m);
468112308Smdodd		return;
469112308Smdodd	}
470112308Smdodd	if (m->m_pkthdr.rcvif == NULL) {
471112308Smdodd		if_printf(ifp, "discard frame w/o interface pointer\n");
472112308Smdodd		ifp->if_ierrors++;
473112308Smdodd 		m_freem(m);
474112308Smdodd		return;
475112308Smdodd	}
476112308Smdodd
477112299Smdodd	m = m_pullup(m, ISO88025_HDR_LEN);
478112299Smdodd	if (m == NULL) {
479112299Smdodd		ifp->if_ierrors++;
480112299Smdodd		goto dropanyway;
481112299Smdodd	}
482112299Smdodd	th = mtod(m, struct iso88025_header *);
483112299Smdodd	m->m_pkthdr.header = (void *)th;
484112299Smdodd
485112286Smdodd	/*
486112286Smdodd	 * Discard packet if interface is not up.
487112286Smdodd	 */
488148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
489148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
490112286Smdodd		goto dropanyway;
49144165Sjulian
492112308Smdodd	/*
493112308Smdodd	 * Give bpf a chance at the packet.
494112308Smdodd	 */
495112308Smdodd	BPF_MTAP(ifp, m);
496112308Smdodd
497112308Smdodd	/*
498112308Smdodd	 * Interface marked for monitoring; discard packet.
499112308Smdodd	 */
500112308Smdodd	if (ifp->if_flags & IFF_MONITOR) {
501112308Smdodd		m_freem(m);
502112308Smdodd		return;
503112308Smdodd	}
504112308Smdodd
505112285Smdodd#ifdef MAC
506172930Srwatson	mac_ifnet_create_mbuf(ifp, m);
507112285Smdodd#endif
508112285Smdodd
509112286Smdodd	/*
510112286Smdodd	 * Update interface statistics.
511112286Smdodd	 */
512112299Smdodd	ifp->if_ibytes += m->m_pkthdr.len;
51374408Smdodd	getmicrotime(&ifp->if_lastchange);
51458313Slile
515112280Smdodd	/*
516112286Smdodd	 * Discard non local unicast packets when interface
517112286Smdodd	 * is in promiscuous mode.
518112286Smdodd	 */
519112286Smdodd	if ((ifp->if_flags & IFF_PROMISC) &&
520112286Smdodd	    ((th->iso88025_dhost[0] & 1) == 0) &&
521152315Sru	     (bcmp(IF_LLADDR(ifp), (caddr_t) th->iso88025_dhost,
522112286Smdodd	     ISO88025_ADDR_LEN) != 0))
523112286Smdodd		goto dropanyway;
524112286Smdodd
525112286Smdodd	/*
526112280Smdodd	 * Set mbuf flags for bcast/mcast.
527112280Smdodd	 */
52844165Sjulian	if (th->iso88025_dhost[0] & 1) {
529126907Srwatson		if (bcmp(iso88025_broadcastaddr, th->iso88025_dhost,
530126907Srwatson		    ISO88025_ADDR_LEN) == 0)
53144165Sjulian			m->m_flags |= M_BCAST;
53244165Sjulian		else
53344165Sjulian			m->m_flags |= M_MCAST;
53474408Smdodd		ifp->if_imcasts++;
535112274Smdodd	}
53644165Sjulian
537112299Smdodd	mac_hdr_len = ISO88025_HDR_LEN;
538112299Smdodd	/* Check for source routing info */
539112299Smdodd	if (th->iso88025_shost[0] & TR_RII)
540112299Smdodd		mac_hdr_len += TR_RCF_RIFLEN(th->rcf);
541112299Smdodd
542112299Smdodd	/* Strip off ISO88025 header. */
543112299Smdodd	m_adj(m, mac_hdr_len);
544112299Smdodd
545112299Smdodd	m = m_pullup(m, LLC_SNAPFRAMELEN);
546112299Smdodd	if (m == 0) {
547112299Smdodd		ifp->if_ierrors++;
548112299Smdodd		goto dropanyway;
549112299Smdodd	}
55074408Smdodd	l = mtod(m, struct llc *);
55144165Sjulian
55274408Smdodd	switch (l->llc_dsap) {
55374408Smdodd#ifdef IPX
55474408Smdodd	case ETHERTYPE_IPX_8022:	/* Thanks a bunch Novell */
55574408Smdodd		if ((l->llc_control != LLC_UI) ||
556112289Smdodd		    (l->llc_ssap != ETHERTYPE_IPX_8022)) {
557112289Smdodd			ifp->if_noproto++;
55874408Smdodd			goto dropanyway;
559112289Smdodd		}
56074408Smdodd
56174408Smdodd		th->iso88025_shost[0] &= ~(TR_RII);
56274408Smdodd		m_adj(m, 3);
563111888Sjlemon		isr = NETISR_IPX;
56474408Smdodd		break;
56574408Smdodd#endif	/* IPX */
56674408Smdodd	case LLC_SNAP_LSAP: {
56774408Smdodd		u_int16_t type;
56874408Smdodd		if ((l->llc_control != LLC_UI) ||
569112289Smdodd		    (l->llc_ssap != LLC_SNAP_LSAP)) {
570112289Smdodd			ifp->if_noproto++;
57174408Smdodd			goto dropanyway;
572112289Smdodd		}
57374408Smdodd
574112268Smdodd		if (l->llc_snap.org_code[0] != 0 ||
575112268Smdodd		    l->llc_snap.org_code[1] != 0 ||
576112294Smdodd		    l->llc_snap.org_code[2] != 0) {
577112294Smdodd			ifp->if_noproto++;
57874408Smdodd			goto dropanyway;
579112294Smdodd		}
58074408Smdodd
581112268Smdodd		type = ntohs(l->llc_snap.ether_type);
582111790Smdodd		m_adj(m, LLC_SNAPFRAMELEN);
58374408Smdodd		switch (type) {
58444165Sjulian#ifdef INET
58574408Smdodd		case ETHERTYPE_IP:
58674408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
587154518Sandre			if ((m = ip_fastforward(m)) == NULL)
58874408Smdodd				return;
589111888Sjlemon			isr = NETISR_IP;
59074408Smdodd			break;
59174408Smdodd
59274408Smdodd		case ETHERTYPE_ARP:
59378295Sjlemon			if (ifp->if_flags & IFF_NOARP)
59478295Sjlemon				goto dropanyway;
595111888Sjlemon			isr = NETISR_ARP;
59674408Smdodd			break;
59774408Smdodd#endif	/* INET */
59874408Smdodd#ifdef IPX_SNAP	/* XXX: Not supported! */
59974408Smdodd		case ETHERTYPE_IPX:
60074408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
601111888Sjlemon			isr = NETISR_IPX;
60274408Smdodd			break;
60374408Smdodd#endif	/* IPX_SNAP */
60474408Smdodd#ifdef INET6
60574408Smdodd		case ETHERTYPE_IPV6:
60674408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
607111888Sjlemon			isr = NETISR_IPV6;
60874408Smdodd			break;
60974408Smdodd#endif	/* INET6 */
61074408Smdodd		default:
61174408Smdodd			printf("iso88025_input: unexpected llc_snap ether_type  0x%02x\n", type);
612112289Smdodd			ifp->if_noproto++;
613112289Smdodd			goto dropanyway;
61474408Smdodd		}
61544165Sjulian		break;
61674408Smdodd	}
617112296Smdodd#ifdef ISO
61874408Smdodd	case LLC_ISO_LSAP:
61974408Smdodd		switch (l->llc_control) {
62074408Smdodd		case LLC_UI:
621112289Smdodd			ifp->if_noproto++;
62274408Smdodd			goto dropanyway;
62374408Smdodd			break;
62474408Smdodd                case LLC_XID:
62574408Smdodd                case LLC_XID_P:
62674408Smdodd			if(m->m_len < ISO88025_ADDR_LEN)
62774408Smdodd				goto dropanyway;
62874408Smdodd			l->llc_window = 0;
62974408Smdodd			l->llc_fid = 9;
63074408Smdodd			l->llc_class = 1;
63174408Smdodd			l->llc_dsap = l->llc_ssap = 0;
63274408Smdodd			/* Fall through to */
63374408Smdodd		case LLC_TEST:
63474408Smdodd		case LLC_TEST_P:
63574408Smdodd		{
63674408Smdodd			struct sockaddr sa;
637112296Smdodd			struct arpcom *ac;
63874408Smdodd			struct iso88025_sockaddr_data *th2;
63974408Smdodd			int i;
640112296Smdodd			u_char c;
64144165Sjulian
642112296Smdodd			c = l->llc_dsap;
643112296Smdodd
64474408Smdodd			if (th->iso88025_shost[0] & TR_RII) { /* XXX */
64574408Smdodd				printf("iso88025_input: dropping source routed LLC_TEST\n");
646112289Smdodd				goto dropanyway;
64774408Smdodd			}
64874408Smdodd			l->llc_dsap = l->llc_ssap;
64974408Smdodd			l->llc_ssap = c;
65074408Smdodd			if (m->m_flags & (M_BCAST | M_MCAST))
651152315Sru				bcopy((caddr_t)IF_LLADDR(ifp),
652112280Smdodd				      (caddr_t)th->iso88025_dhost,
65374408Smdodd					ISO88025_ADDR_LEN);
65474408Smdodd			sa.sa_family = AF_UNSPEC;
65574408Smdodd			sa.sa_len = sizeof(sa);
65674408Smdodd			th2 = (struct iso88025_sockaddr_data *)sa.sa_data;
65774408Smdodd			for (i = 0; i < ISO88025_ADDR_LEN; i++) {
65874408Smdodd				th2->ether_shost[i] = c = th->iso88025_dhost[i];
65974408Smdodd				th2->ether_dhost[i] = th->iso88025_dhost[i] =
66074408Smdodd					th->iso88025_shost[i];
66174408Smdodd				th->iso88025_shost[i] = c;
66274408Smdodd			}
66374408Smdodd			th2->ac = TR_AC;
66474408Smdodd			th2->fc = TR_LLC_FRAME;
66574408Smdodd			ifp->if_output(ifp, m, &sa, NULL);
66674408Smdodd			return;
66774408Smdodd		}
66874408Smdodd		default:
66974408Smdodd			printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control);
670112289Smdodd			ifp->if_noproto++;
671112289Smdodd			goto dropanyway;
672112294Smdodd			break;
67374408Smdodd		}
67474408Smdodd		break;
675112296Smdodd#endif	/* ISO */
67644165Sjulian	default:
67774408Smdodd		printf("iso88025_input: unknown dsap 0x%x\n", l->llc_dsap);
67874408Smdodd		ifp->if_noproto++;
679112289Smdodd		goto dropanyway;
680112294Smdodd		break;
68144165Sjulian	}
682112274Smdodd
683111888Sjlemon	netisr_dispatch(isr, m);
684112274Smdodd	return;
685112289Smdodd
686112289Smdodddropanyway:
687112289Smdodd	ifp->if_iqdrops++;
688112289Smdodd	if (m)
689112289Smdodd		m_freem(m);
690112289Smdodd	return;
69144165Sjulian}
692112269Smdodd
693112273Smdoddstatic int
694112273Smdoddiso88025_resolvemulti (ifp, llsa, sa)
695112273Smdodd	struct ifnet *ifp;
696112273Smdodd	struct sockaddr **llsa;
697112273Smdodd	struct sockaddr *sa;
698112273Smdodd{
699112273Smdodd	struct sockaddr_dl *sdl;
700112273Smdodd	struct sockaddr_in *sin;
701112273Smdodd#ifdef INET6
702112273Smdodd	struct sockaddr_in6 *sin6;
703112273Smdodd#endif
704112273Smdodd	u_char *e_addr;
705112273Smdodd
706112273Smdodd	switch(sa->sa_family) {
707112273Smdodd	case AF_LINK:
708112273Smdodd		/*
709112273Smdodd		 * No mapping needed. Just check that it's a valid MC address.
710112273Smdodd		 */
711112273Smdodd		sdl = (struct sockaddr_dl *)sa;
712112273Smdodd		e_addr = LLADDR(sdl);
713112273Smdodd		if ((e_addr[0] & 1) != 1) {
714112273Smdodd			return (EADDRNOTAVAIL);
715112273Smdodd		}
716112273Smdodd		*llsa = 0;
717112273Smdodd		return (0);
718112273Smdodd
719112273Smdodd#ifdef INET
720112273Smdodd	case AF_INET:
721112273Smdodd		sin = (struct sockaddr_in *)sa;
722112273Smdodd		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
723112273Smdodd			return (EADDRNOTAVAIL);
724112273Smdodd		}
725112273Smdodd		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
726148641Srwatson		       M_NOWAIT|M_ZERO);
727148641Srwatson		if (sdl == NULL)
728148641Srwatson			return (ENOMEM);
729112273Smdodd		sdl->sdl_len = sizeof *sdl;
730112273Smdodd		sdl->sdl_family = AF_LINK;
731112273Smdodd		sdl->sdl_index = ifp->if_index;
732112273Smdodd		sdl->sdl_type = IFT_ISO88025;
733112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
734112273Smdodd		e_addr = LLADDR(sdl);
735112273Smdodd		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
736112273Smdodd		*llsa = (struct sockaddr *)sdl;
737112273Smdodd		return (0);
738112273Smdodd#endif
739112273Smdodd#ifdef INET6
740112273Smdodd	case AF_INET6:
741112273Smdodd		sin6 = (struct sockaddr_in6 *)sa;
742112273Smdodd		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
743112273Smdodd			/*
744112273Smdodd			 * An IP6 address of 0 means listen to all
745112273Smdodd			 * of the Ethernet multicast address used for IP6.
746112273Smdodd			 * (This is used for multicast routers.)
747112273Smdodd			 */
748112273Smdodd			ifp->if_flags |= IFF_ALLMULTI;
749112273Smdodd			*llsa = 0;
750112273Smdodd			return (0);
751112273Smdodd		}
752112273Smdodd		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
753112273Smdodd			return (EADDRNOTAVAIL);
754112273Smdodd		}
755112273Smdodd		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
756148641Srwatson		       M_NOWAIT|M_ZERO);
757148641Srwatson		if (sdl == NULL)
758148641Srwatson			return (ENOMEM);
759112273Smdodd		sdl->sdl_len = sizeof *sdl;
760112273Smdodd		sdl->sdl_family = AF_LINK;
761112273Smdodd		sdl->sdl_index = ifp->if_index;
762112273Smdodd		sdl->sdl_type = IFT_ISO88025;
763112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
764112273Smdodd		e_addr = LLADDR(sdl);
765112273Smdodd		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
766112273Smdodd		*llsa = (struct sockaddr *)sdl;
767112273Smdodd		return (0);
768112273Smdodd#endif
769112273Smdodd
770112273Smdodd	default:
771112273Smdodd		/*
772112273Smdodd		 * Well, the text isn't quite right, but it's the name
773112273Smdodd		 * that counts...
774112273Smdodd		 */
775112273Smdodd		return (EAFNOSUPPORT);
776112273Smdodd	}
777112273Smdodd
778112273Smdodd	return (0);
779112273Smdodd}
780112273Smdodd
781147256SbrooksMALLOC_DEFINE(M_ISO88025, "arpcom", "802.5 interface internals");
782147256Sbrooks
783147256Sbrooksstatic void*
784147256Sbrooksiso88025_alloc(u_char type, struct ifnet *ifp)
785147256Sbrooks{
786147256Sbrooks	struct arpcom	*ac;
787147256Sbrooks
788147256Sbrooks        ac = malloc(sizeof(struct arpcom), M_ISO88025, M_WAITOK | M_ZERO);
789147256Sbrooks	ac->ac_ifp = ifp;
790147256Sbrooks
791147256Sbrooks	return (ac);
792147256Sbrooks}
793147256Sbrooks
794147256Sbrooksstatic void
795147256Sbrooksiso88025_free(void *com, u_char type)
796147256Sbrooks{
797147256Sbrooks
798147256Sbrooks        free(com, M_ISO88025);
799147256Sbrooks}
800147256Sbrooks
801147256Sbrooksstatic int
802147256Sbrooksiso88025_modevent(module_t mod, int type, void *data)
803147256Sbrooks{
804147256Sbrooks
805147256Sbrooks        switch (type) {
806147256Sbrooks        case MOD_LOAD:
807147256Sbrooks                if_register_com_alloc(IFT_ISO88025, iso88025_alloc,
808147256Sbrooks                    iso88025_free);
809147256Sbrooks                break;
810147256Sbrooks        case MOD_UNLOAD:
811147256Sbrooks                if_deregister_com_alloc(IFT_ISO88025);
812147256Sbrooks                break;
813147256Sbrooks        default:
814147256Sbrooks                return EOPNOTSUPP;
815147256Sbrooks        }
816147256Sbrooks
817147256Sbrooks        return (0);
818147256Sbrooks}
819147256Sbrooks
820112269Smdoddstatic moduledata_t iso88025_mod = {
821112269Smdodd	"iso88025",
822147256Sbrooks	iso88025_modevent,
823112269Smdodd	0
824112269Smdodd};
825112269Smdodd
826112269SmdoddDECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
827112269SmdoddMODULE_VERSION(iso88025, 1);
828