if_iso88025subr.c revision 186217
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 186217 2008-12-17 10:27:34Z qingli $
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>
58184710Sbz#include <net/if_arp.h>
59112271Smdodd#include <net/if_dl.h>
6044165Sjulian#include <net/if_llc.h>
6144165Sjulian#include <net/if_types.h>
62186119Sqingli#include <net/if_llatbl.h>
6344165Sjulian
64184710Sbz#include <net/ethernet.h>
65112271Smdodd#include <net/netisr.h>
66112271Smdodd#include <net/route.h>
67112271Smdodd#include <net/bpf.h>
6844165Sjulian#include <net/iso88025.h>
6944165Sjulian
7074408Smdodd#if defined(INET) || defined(INET6)
7144165Sjulian#include <netinet/in.h>
7244165Sjulian#include <netinet/in_var.h>
7344165Sjulian#include <netinet/if_ether.h>
7444165Sjulian#endif
7574408Smdodd#ifdef INET6
7674408Smdodd#include <netinet6/nd6.h>
7774408Smdodd#endif
7844165Sjulian
7974408Smdodd#ifdef IPX
8074408Smdodd#include <netipx/ipx.h>
8174408Smdodd#include <netipx/ipx_if.h>
8274408Smdodd#endif
8374408Smdodd
84163606Srwatson#include <security/mac/mac_framework.h>
85163606Srwatson
86126907Srwatsonstatic const u_char iso88025_broadcastaddr[ISO88025_ADDR_LEN] =
87112277Smdodd			{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
88112277Smdodd
89112273Smdoddstatic int iso88025_resolvemulti (struct ifnet *, struct sockaddr **,
90112294Smdodd				  struct sockaddr *);
91112273Smdodd
92112276Smdodd#define	senderr(e)	do { error = (e); goto bad; } while (0)
9374408Smdodd
94112297Smdodd/*
95112297Smdodd * Perform common duties while attaching to interface list
96112297Smdodd */
9744165Sjulianvoid
98152296Sruiso88025_ifattach(struct ifnet *ifp, const u_int8_t *lla, int bpf)
9944165Sjulian{
100112296Smdodd    struct ifaddr *ifa;
101111774Smdodd    struct sockaddr_dl *sdl;
10244165Sjulian
103112296Smdodd    ifa = NULL;
104112296Smdodd
10544165Sjulian    ifp->if_type = IFT_ISO88025;
10658313Slile    ifp->if_addrlen = ISO88025_ADDR_LEN;
10758313Slile    ifp->if_hdrlen = ISO88025_HDR_LEN;
108112297Smdodd
109112297Smdodd    if_attach(ifp);	/* Must be called before additional assignments */
110112297Smdodd
111112297Smdodd    ifp->if_output = iso88025_output;
112112297Smdodd    ifp->if_input = iso88025_input;
113112297Smdodd    ifp->if_resolvemulti = iso88025_resolvemulti;
114112297Smdodd    ifp->if_broadcastaddr = iso88025_broadcastaddr;
115112297Smdodd
11644165Sjulian    if (ifp->if_baudrate == 0)
11758313Slile        ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */
11844165Sjulian    if (ifp->if_mtu == 0)
11944165Sjulian        ifp->if_mtu = ISO88025_DEFAULT_MTU;
12044165Sjulian
121152315Sru    ifa = ifp->if_addr;
122152315Sru    KASSERT(ifa != NULL, ("%s: no lladdr!\n", __func__));
123112297Smdodd
124112272Smdodd    sdl = (struct sockaddr_dl *)ifa->ifa_addr;
125112272Smdodd    sdl->sdl_type = IFT_ISO88025;
126112272Smdodd    sdl->sdl_alen = ifp->if_addrlen;
127152296Sru    bcopy(lla, LLADDR(sdl), ifp->if_addrlen);
128112297Smdodd
129112297Smdodd    if (bpf)
130112297Smdodd        bpfattach(ifp, DLT_IEEE802, ISO88025_HDR_LEN);
131112297Smdodd
132112297Smdodd    return;
13344165Sjulian}
13444165Sjulian
13574408Smdodd/*
13674408Smdodd * Perform common duties while detaching a Token Ring interface
13774408Smdodd */
13874408Smdoddvoid
13974408Smdoddiso88025_ifdetach(ifp, bpf)
14074408Smdodd        struct ifnet *ifp;
14174408Smdodd        int bpf;
14274408Smdodd{
143112274Smdodd
14474408Smdodd	if (bpf)
14574408Smdodd                bpfdetach(ifp);
146112274Smdodd
14774408Smdodd	if_detach(ifp);
148112274Smdodd
149112274Smdodd	return;
15074408Smdodd}
15174408Smdodd
15244165Sjulianint
15344165Sjulianiso88025_ioctl(struct ifnet *ifp, int command, caddr_t data)
15444165Sjulian{
155112274Smdodd        struct ifaddr *ifa;
156112274Smdodd        struct ifreq *ifr;
157112274Smdodd        int error;
15844165Sjulian
159112274Smdodd	ifa = (struct ifaddr *) data;
160112274Smdodd	ifr = (struct ifreq *) data;
161112274Smdodd	error = 0;
162112274Smdodd
16344165Sjulian        switch (command) {
16444165Sjulian        case SIOCSIFADDR:
16544165Sjulian                ifp->if_flags |= IFF_UP;
16644165Sjulian
16744165Sjulian                switch (ifa->ifa_addr->sa_family) {
16844165Sjulian#ifdef INET
16944165Sjulian                case AF_INET:
17044165Sjulian                        ifp->if_init(ifp->if_softc);    /* before arpwhohas */
17184931Sfjoe                        arp_ifinit(ifp, ifa);
17244165Sjulian                        break;
17374408Smdodd#endif	/* INET */
17474408Smdodd#ifdef IPX
17574408Smdodd                /*
17674408Smdodd                 * XXX - This code is probably wrong
17774408Smdodd                 */
178120048Smdodd                case AF_IPX: {
179120048Smdodd				struct ipx_addr *ina;
18074408Smdodd
181120048Smdodd				ina = &(IA_SIPX(ifa)->sipx_addr);
18274408Smdodd
183120048Smdodd				if (ipx_nullhost(*ina))
184120048Smdodd					ina->x_host = *(union ipx_host *)
185152315Sru							IF_LLADDR(ifp);
186120048Smdodd				else
187120048Smdodd					bcopy((caddr_t) ina->x_host.c_host,
188152315Sru					      (caddr_t) IF_LLADDR(ifp),
189120048Smdodd					      ISO88025_ADDR_LEN);
190120048Smdodd
191120048Smdodd				/*
192120048Smdodd				 * Set new address
193120048Smdodd				 */
194120048Smdodd				ifp->if_init(ifp->if_softc);
195120048Smdodd			}
196120048Smdodd			break;
19774408Smdodd#endif	/* IPX */
19844165Sjulian                default:
19944165Sjulian                        ifp->if_init(ifp->if_softc);
20044165Sjulian                        break;
20144165Sjulian                }
20244165Sjulian                break;
20344165Sjulian
204120047Smdodd        case SIOCGIFADDR: {
20544165Sjulian                        struct sockaddr *sa;
20644165Sjulian
20744165Sjulian                        sa = (struct sockaddr *) & ifr->ifr_data;
208152315Sru                        bcopy(IF_LLADDR(ifp),
20944165Sjulian                              (caddr_t) sa->sa_data, ISO88025_ADDR_LEN);
21044165Sjulian                }
21144165Sjulian                break;
21244165Sjulian
21344165Sjulian        case SIOCSIFMTU:
21444165Sjulian                /*
21544165Sjulian                 * Set the interface MTU.
21644165Sjulian                 */
21758313Slile                if (ifr->ifr_mtu > ISO88025_MAX_MTU) {
21844165Sjulian                        error = EINVAL;
21944165Sjulian                } else {
22044165Sjulian                        ifp->if_mtu = ifr->ifr_mtu;
22144165Sjulian                }
22244165Sjulian                break;
223112274Smdodd	default:
224112274Smdodd		error = EINVAL;			/* XXX netbsd has ENOTTY??? */
225112274Smdodd		break;
22644165Sjulian        }
227112274Smdodd
22844165Sjulian        return (error);
22944165Sjulian}
23044165Sjulian
23144165Sjulian/*
23244165Sjulian * ISO88025 encapsulation
23344165Sjulian */
23444165Sjulianint
23574408Smdoddiso88025_output(ifp, m, dst, rt0)
23674408Smdodd	struct ifnet *ifp;
23774408Smdodd	struct mbuf *m;
23874408Smdodd	struct sockaddr *dst;
23974408Smdodd	struct rtentry *rt0;
24044165Sjulian{
24174408Smdodd	u_int16_t snap_type = 0;
24287914Sjlemon	int loop_copy = 0, error = 0, rif_len = 0;
24387914Sjlemon	u_char edst[ISO88025_ADDR_LEN];
24474408Smdodd	struct iso88025_header *th;
24544627Sjulian	struct iso88025_header gen_th;
24674408Smdodd	struct sockaddr_dl *sdl = NULL;
247186119Sqingli	struct llentry *lle;
24844165Sjulian
249112285Smdodd#ifdef MAC
250172930Srwatson	error = mac_ifnet_check_transmit(ifp, m);
251112285Smdodd	if (error)
252112285Smdodd		senderr(error);
253112285Smdodd#endif
254112285Smdodd
255112308Smdodd	if (ifp->if_flags & IFF_MONITOR)
256112308Smdodd		senderr(ENETDOWN);
257148887Srwatson	if (!((ifp->if_flags & IFF_UP) &&
258148887Srwatson	    (ifp->if_drv_flags & IFF_DRV_RUNNING)))
25944165Sjulian		senderr(ENETDOWN);
26074408Smdodd	getmicrotime(&ifp->if_lastchange);
26174408Smdodd
262128636Sluigi	/* Calculate routing info length based on arp table entry */
263128636Sluigi	/* XXX any better way to do this ? */
26444627Sjulian
265186119Sqingli	if (rt0 && (sdl = (struct sockaddr_dl *)rt0->rt_gateway))
266102291Sarchie		if (SDL_ISO88025(sdl)->trld_rcf != 0)
26796184Skbyanc			rif_len = TR_RCF_RIFLEN(SDL_ISO88025(sdl)->trld_rcf);
26844627Sjulian
26944627Sjulian	/* Generate a generic 802.5 header for the packet */
27058313Slile	gen_th.ac = TR_AC;
27158313Slile	gen_th.fc = TR_LLC_FRAME;
272152315Sru	(void)memcpy((caddr_t)gen_th.iso88025_shost, IF_LLADDR(ifp),
273112278Smdodd		     ISO88025_ADDR_LEN);
27444627Sjulian	if (rif_len) {
27558313Slile		gen_th.iso88025_shost[0] |= TR_RII;
27644627Sjulian		if (rif_len > 2) {
27796184Skbyanc			gen_th.rcf = SDL_ISO88025(sdl)->trld_rcf;
27874408Smdodd			(void)memcpy((caddr_t)gen_th.rd,
27996184Skbyanc				(caddr_t)SDL_ISO88025(sdl)->trld_route,
28096184Skbyanc				rif_len - 2);
28144627Sjulian		}
28244627Sjulian	}
28344627Sjulian
28444165Sjulian	switch (dst->sa_family) {
28544165Sjulian#ifdef INET
28644165Sjulian	case AF_INET:
287186119Sqingli		error = arpresolve(ifp, rt0, m, dst, edst, &lle);
288128636Sluigi		if (error)
289128636Sluigi			return (error == EWOULDBLOCK ? 0 : error);
29074408Smdodd		snap_type = ETHERTYPE_IP;
29174408Smdodd		break;
292126951Smdodd	case AF_ARP:
293126951Smdodd	{
294126951Smdodd		struct arphdr *ah;
295126951Smdodd		ah = mtod(m, struct arphdr *);
296126951Smdodd		ah->ar_hrd = htons(ARPHRD_IEEE802);
297126951Smdodd
298126951Smdodd		loop_copy = -1; /* if this is for us, don't do it */
299126951Smdodd
300126951Smdodd		switch(ntohs(ah->ar_op)) {
301126951Smdodd		case ARPOP_REVREQUEST:
302126951Smdodd		case ARPOP_REVREPLY:
303126951Smdodd			snap_type = ETHERTYPE_REVARP;
304126951Smdodd			break;
305126951Smdodd		case ARPOP_REQUEST:
306126951Smdodd		case ARPOP_REPLY:
307126951Smdodd		default:
308126951Smdodd			snap_type = ETHERTYPE_ARP;
309126951Smdodd			break;
310126951Smdodd		}
311126951Smdodd
312126951Smdodd		if (m->m_flags & M_BCAST)
313126951Smdodd			bcopy(ifp->if_broadcastaddr, edst, ISO88025_ADDR_LEN);
314126951Smdodd		else
315126951Smdodd			bcopy(ar_tha(ah), edst, ISO88025_ADDR_LEN);
316126951Smdodd
317126951Smdodd	}
318126951Smdodd	break;
31974408Smdodd#endif	/* INET */
32074408Smdodd#ifdef INET6
32174408Smdodd	case AF_INET6:
322186217Sqingli		error = nd6_storelladdr(ifp, m, dst, (u_char *)edst, &lle);
323128636Sluigi		if (error)
324128636Sluigi			return (error);
32574408Smdodd		snap_type = ETHERTYPE_IPV6;
32674408Smdodd		break;
32774408Smdodd#endif	/* INET6 */
32874408Smdodd#ifdef IPX
32974408Smdodd	case AF_IPX:
33074408Smdodd	{
33174408Smdodd		u_int8_t	*cp;
33274408Smdodd
33374408Smdodd		bcopy((caddr_t)&(satoipx_addr(dst).x_host), (caddr_t)edst,
334112278Smdodd		      ISO88025_ADDR_LEN);
33574408Smdodd
336177599Sru		M_PREPEND(m, 3, M_WAIT);
33774408Smdodd		m = m_pullup(m, 3);
33874408Smdodd		if (m == 0)
33974408Smdodd			senderr(ENOBUFS);
34074408Smdodd		cp = mtod(m, u_int8_t *);
34174408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
34274408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
34374408Smdodd		*cp++ = LLC_UI;
34474408Smdodd	}
34574408Smdodd	break;
34674408Smdodd#endif	/* IPX */
34744165Sjulian	case AF_UNSPEC:
34874408Smdodd	{
34974408Smdodd		struct iso88025_sockaddr_data *sd;
35044627Sjulian		/*
35144627Sjulian		 * For AF_UNSPEC sockaddr.sa_data must contain all of the
35244627Sjulian		 * mac information needed to send the packet.  This allows
35344627Sjulian		 * full mac, llc, and source routing function to be controlled.
35444627Sjulian		 * llc and source routing information must already be in the
35544627Sjulian		 * mbuf provided, ac/fc are set in sa_data.  sockaddr.sa_data
356108533Sschweikh		 * should be an iso88025_sockaddr_data structure see iso88025.h
35744627Sjulian		 */
35844165Sjulian                loop_copy = -1;
35944627Sjulian		sd = (struct iso88025_sockaddr_data *)dst->sa_data;
36044627Sjulian		gen_th.ac = sd->ac;
36144627Sjulian		gen_th.fc = sd->fc;
36274408Smdodd		(void)memcpy((caddr_t)edst, (caddr_t)sd->ether_dhost,
363112278Smdodd			     ISO88025_ADDR_LEN);
36474408Smdodd		(void)memcpy((caddr_t)gen_th.iso88025_shost,
365112280Smdodd			     (caddr_t)sd->ether_shost, ISO88025_ADDR_LEN);
36644627Sjulian		rif_len = 0;
36744165Sjulian		break;
36874408Smdodd	}
36944165Sjulian	default:
370105598Sbrooks		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
37144165Sjulian		senderr(EAFNOSUPPORT);
37274408Smdodd		break;
37344165Sjulian	}
37444165Sjulian
375112274Smdodd	/*
376112274Smdodd	 * Add LLC header.
377112274Smdodd	 */
37874408Smdodd	if (snap_type != 0) {
37974408Smdodd        	struct llc *l;
380111790Smdodd		M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
38174408Smdodd		if (m == 0)
38274408Smdodd			senderr(ENOBUFS);
38374408Smdodd		l = mtod(m, struct llc *);
384112281Smdodd		l->llc_control = LLC_UI;
38574408Smdodd		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
386112268Smdodd		l->llc_snap.org_code[0] =
387112268Smdodd			l->llc_snap.org_code[1] =
388112268Smdodd			l->llc_snap.org_code[2] = 0;
389112274Smdodd		l->llc_snap.ether_type = htons(snap_type);
39074408Smdodd	}
39174408Smdodd
39244165Sjulian	/*
39344165Sjulian	 * Add local net header.  If no space in first mbuf,
39444165Sjulian	 * allocate another.
39544165Sjulian	 */
396111119Simp	M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT);
39744165Sjulian	if (m == 0)
39844165Sjulian		senderr(ENOBUFS);
399112274Smdodd	th = mtod(m, struct iso88025_header *);
400112291Smdodd	bcopy((caddr_t)edst, (caddr_t)&gen_th.iso88025_dhost, ISO88025_ADDR_LEN);
40144627Sjulian
40244627Sjulian	/* Copy as much of the generic header as is needed into the mbuf */
40344627Sjulian	memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len);
40444627Sjulian
40544165Sjulian        /*
40644165Sjulian         * If a simplex interface, and the packet is being sent to our
40744165Sjulian         * Ethernet address or a broadcast address, loopback a copy.
40844165Sjulian         * XXX To make a simplex device behave exactly like a duplex
40944165Sjulian         * device, we should copy in the case of sending to our own
41044165Sjulian         * ethernet address (thus letting the original actually appear
41144165Sjulian         * on the wire). However, we don't do that here for security
41244165Sjulian         * reasons and compatibility with the original behavior.
41344165Sjulian         */
41474408Smdodd        if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
41544165Sjulian                if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
41674408Smdodd                        struct mbuf *n;
41774408Smdodd			n = m_copy(m, 0, (int)M_COPYALL);
418112279Smdodd                        (void) if_simloop(ifp, n, dst->sa_family,
41974408Smdodd					  ISO88025_HDR_LEN);
420112279Smdodd                } else if (bcmp(th->iso88025_dhost, th->iso88025_shost,
42174408Smdodd				 ETHER_ADDR_LEN) == 0) {
422112279Smdodd			(void) if_simloop(ifp, m, dst->sa_family,
423112279Smdodd					  ISO88025_HDR_LEN);
424112279Smdodd                       	return(0);      /* XXX */
425112279Smdodd		}
42644165Sjulian        }
42744165Sjulian
428130549Smlaier	IFQ_HANDOFF_ADJ(ifp, m, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN, error);
429130549Smlaier	if (error) {
43069152Sjlemon		printf("iso88025_output: packet dropped QFULL.\n");
431130549Smlaier		ifp->if_oerrors++;
43244165Sjulian	}
43344165Sjulian	return (error);
43444165Sjulian
43544165Sjulianbad:
436112296Smdodd	ifp->if_oerrors++;
43744165Sjulian	if (m)
43844165Sjulian		m_freem(m);
43944165Sjulian	return (error);
44044165Sjulian}
44144165Sjulian
44244165Sjulian/*
44344165Sjulian * ISO 88025 de-encapsulation
44444165Sjulian */
44544165Sjulianvoid
446112299Smdoddiso88025_input(ifp, m)
44774408Smdodd	struct ifnet *ifp;
44874408Smdodd	struct mbuf *m;
44944165Sjulian{
450112299Smdodd	struct iso88025_header *th;
451112299Smdodd	struct llc *l;
452111888Sjlemon	int isr;
453112299Smdodd	int mac_hdr_len;
45444165Sjulian
455112308Smdodd	/*
456112308Smdodd	 * Do consistency checks to verify assumptions
457112308Smdodd	 * made by code past this point.
458112308Smdodd	 */
459112308Smdodd	if ((m->m_flags & M_PKTHDR) == 0) {
460112308Smdodd		if_printf(ifp, "discard frame w/o packet header\n");
461112308Smdodd		ifp->if_ierrors++;
462112308Smdodd		m_freem(m);
463112308Smdodd		return;
464112308Smdodd	}
465112308Smdodd	if (m->m_pkthdr.rcvif == NULL) {
466112308Smdodd		if_printf(ifp, "discard frame w/o interface pointer\n");
467112308Smdodd		ifp->if_ierrors++;
468112308Smdodd 		m_freem(m);
469112308Smdodd		return;
470112308Smdodd	}
471112308Smdodd
472112299Smdodd	m = m_pullup(m, ISO88025_HDR_LEN);
473112299Smdodd	if (m == NULL) {
474112299Smdodd		ifp->if_ierrors++;
475112299Smdodd		goto dropanyway;
476112299Smdodd	}
477112299Smdodd	th = mtod(m, struct iso88025_header *);
478112299Smdodd	m->m_pkthdr.header = (void *)th;
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
678111888Sjlemon	netisr_dispatch(isr, m);
679112274Smdodd	return;
680112289Smdodd
681112289Smdodddropanyway:
682112289Smdodd	ifp->if_iqdrops++;
683112289Smdodd	if (m)
684112289Smdodd		m_freem(m);
685112289Smdodd	return;
68644165Sjulian}
687112269Smdodd
688112273Smdoddstatic int
689112273Smdoddiso88025_resolvemulti (ifp, llsa, sa)
690112273Smdodd	struct ifnet *ifp;
691112273Smdodd	struct sockaddr **llsa;
692112273Smdodd	struct sockaddr *sa;
693112273Smdodd{
694112273Smdodd	struct sockaddr_dl *sdl;
695184710Sbz#ifdef INET
696112273Smdodd	struct sockaddr_in *sin;
697184710Sbz#endif
698112273Smdodd#ifdef INET6
699112273Smdodd	struct sockaddr_in6 *sin6;
700112273Smdodd#endif
701112273Smdodd	u_char *e_addr;
702112273Smdodd
703112273Smdodd	switch(sa->sa_family) {
704112273Smdodd	case AF_LINK:
705112273Smdodd		/*
706112273Smdodd		 * No mapping needed. Just check that it's a valid MC address.
707112273Smdodd		 */
708112273Smdodd		sdl = (struct sockaddr_dl *)sa;
709112273Smdodd		e_addr = LLADDR(sdl);
710112273Smdodd		if ((e_addr[0] & 1) != 1) {
711112273Smdodd			return (EADDRNOTAVAIL);
712112273Smdodd		}
713112273Smdodd		*llsa = 0;
714112273Smdodd		return (0);
715112273Smdodd
716112273Smdodd#ifdef INET
717112273Smdodd	case AF_INET:
718112273Smdodd		sin = (struct sockaddr_in *)sa;
719112273Smdodd		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
720112273Smdodd			return (EADDRNOTAVAIL);
721112273Smdodd		}
722184205Sdes		sdl = malloc(sizeof *sdl, M_IFMADDR,
723148641Srwatson		       M_NOWAIT|M_ZERO);
724148641Srwatson		if (sdl == NULL)
725148641Srwatson			return (ENOMEM);
726112273Smdodd		sdl->sdl_len = sizeof *sdl;
727112273Smdodd		sdl->sdl_family = AF_LINK;
728112273Smdodd		sdl->sdl_index = ifp->if_index;
729112273Smdodd		sdl->sdl_type = IFT_ISO88025;
730112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
731112273Smdodd		e_addr = LLADDR(sdl);
732112273Smdodd		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
733112273Smdodd		*llsa = (struct sockaddr *)sdl;
734112273Smdodd		return (0);
735112273Smdodd#endif
736112273Smdodd#ifdef INET6
737112273Smdodd	case AF_INET6:
738112273Smdodd		sin6 = (struct sockaddr_in6 *)sa;
739112273Smdodd		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
740112273Smdodd			/*
741112273Smdodd			 * An IP6 address of 0 means listen to all
742112273Smdodd			 * of the Ethernet multicast address used for IP6.
743112273Smdodd			 * (This is used for multicast routers.)
744112273Smdodd			 */
745112273Smdodd			ifp->if_flags |= IFF_ALLMULTI;
746112273Smdodd			*llsa = 0;
747112273Smdodd			return (0);
748112273Smdodd		}
749112273Smdodd		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
750112273Smdodd			return (EADDRNOTAVAIL);
751112273Smdodd		}
752184205Sdes		sdl = malloc(sizeof *sdl, M_IFMADDR,
753148641Srwatson		       M_NOWAIT|M_ZERO);
754148641Srwatson		if (sdl == NULL)
755148641Srwatson			return (ENOMEM);
756112273Smdodd		sdl->sdl_len = sizeof *sdl;
757112273Smdodd		sdl->sdl_family = AF_LINK;
758112273Smdodd		sdl->sdl_index = ifp->if_index;
759112273Smdodd		sdl->sdl_type = IFT_ISO88025;
760112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
761112273Smdodd		e_addr = LLADDR(sdl);
762112273Smdodd		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
763112273Smdodd		*llsa = (struct sockaddr *)sdl;
764112273Smdodd		return (0);
765112273Smdodd#endif
766112273Smdodd
767112273Smdodd	default:
768112273Smdodd		/*
769112273Smdodd		 * Well, the text isn't quite right, but it's the name
770112273Smdodd		 * that counts...
771112273Smdodd		 */
772112273Smdodd		return (EAFNOSUPPORT);
773112273Smdodd	}
774112273Smdodd
775112273Smdodd	return (0);
776112273Smdodd}
777112273Smdodd
778147256SbrooksMALLOC_DEFINE(M_ISO88025, "arpcom", "802.5 interface internals");
779147256Sbrooks
780147256Sbrooksstatic void*
781147256Sbrooksiso88025_alloc(u_char type, struct ifnet *ifp)
782147256Sbrooks{
783147256Sbrooks	struct arpcom	*ac;
784147256Sbrooks
785147256Sbrooks        ac = malloc(sizeof(struct arpcom), M_ISO88025, M_WAITOK | M_ZERO);
786147256Sbrooks	ac->ac_ifp = ifp;
787147256Sbrooks
788147256Sbrooks	return (ac);
789147256Sbrooks}
790147256Sbrooks
791147256Sbrooksstatic void
792147256Sbrooksiso88025_free(void *com, u_char type)
793147256Sbrooks{
794147256Sbrooks
795147256Sbrooks        free(com, M_ISO88025);
796147256Sbrooks}
797147256Sbrooks
798147256Sbrooksstatic int
799147256Sbrooksiso88025_modevent(module_t mod, int type, void *data)
800147256Sbrooks{
801147256Sbrooks
802147256Sbrooks        switch (type) {
803147256Sbrooks        case MOD_LOAD:
804147256Sbrooks                if_register_com_alloc(IFT_ISO88025, iso88025_alloc,
805147256Sbrooks                    iso88025_free);
806147256Sbrooks                break;
807147256Sbrooks        case MOD_UNLOAD:
808147256Sbrooks                if_deregister_com_alloc(IFT_ISO88025);
809147256Sbrooks                break;
810147256Sbrooks        default:
811147256Sbrooks                return EOPNOTSUPP;
812147256Sbrooks        }
813147256Sbrooks
814147256Sbrooks        return (0);
815147256Sbrooks}
816147256Sbrooks
817112269Smdoddstatic moduledata_t iso88025_mod = {
818112269Smdodd	"iso88025",
819147256Sbrooks	iso88025_modevent,
820112269Smdodd	0
821112269Smdodd};
822112269Smdodd
823112269SmdoddDECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
824112269SmdoddMODULE_VERSION(iso88025, 1);
825