if_iso88025subr.c revision 112291
144165Sjulian/*
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 112291 2003-03-15 22:09:29Z mdodd $
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>
51112285Smdodd#include <sys/mac.h>
52112271Smdodd#include <sys/malloc.h>
5344165Sjulian#include <sys/mbuf.h>
54112271Smdodd#include <sys/module.h>
5544165Sjulian#include <sys/socket.h>
5644165Sjulian#include <sys/sockio.h>
5744165Sjulian
5844165Sjulian#include <net/if.h>
59112271Smdodd#include <net/if_dl.h>
6044165Sjulian#include <net/if_llc.h>
6144165Sjulian#include <net/if_types.h>
6244165Sjulian
63112271Smdodd#include <net/netisr.h>
64112271Smdodd#include <net/route.h>
65112271Smdodd#include <net/bpf.h>
6644165Sjulian#include <net/iso88025.h>
6744165Sjulian
6874408Smdodd#if defined(INET) || defined(INET6)
6944165Sjulian#include <netinet/in.h>
7044165Sjulian#include <netinet/in_var.h>
7144165Sjulian#include <netinet/if_ether.h>
7244165Sjulian#endif
7374408Smdodd#ifdef INET6
7474408Smdodd#include <netinet6/nd6.h>
7574408Smdodd#endif
7644165Sjulian
7774408Smdodd#ifdef IPX
7874408Smdodd#include <netipx/ipx.h>
7974408Smdodd#include <netipx/ipx_if.h>
8074408Smdodd#endif
8174408Smdodd
82112277Smdoddstatic u_char iso88025_broadcastaddr[ISO88025_ADDR_LEN] =
83112277Smdodd			{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
84112277Smdodd
85112273Smdoddstatic int iso88025_resolvemulti (struct ifnet *, struct sockaddr **,
86112273Smdodd				  struct sockaddr *));
87112273Smdodd
88112276Smdodd#define	IFP2AC(IFP)	((struct arpcom *)IFP)
89112276Smdodd#define	senderr(e)	do { error = (e); goto bad; } while (0)
9074408Smdodd
9144165Sjulianvoid
9258313Slileiso88025_ifattach(struct ifnet *ifp)
9344165Sjulian{
94111774Smdodd    struct ifaddr *ifa = NULL;
95111774Smdodd    struct sockaddr_dl *sdl;
9644165Sjulian
9744165Sjulian    ifp->if_type = IFT_ISO88025;
9858313Slile    ifp->if_addrlen = ISO88025_ADDR_LEN;
9958313Slile    ifp->if_hdrlen = ISO88025_HDR_LEN;
10044165Sjulian    if (ifp->if_baudrate == 0)
10158313Slile        ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */
10244165Sjulian    if (ifp->if_mtu == 0)
10344165Sjulian        ifp->if_mtu = ISO88025_DEFAULT_MTU;
104112277Smdodd    ifp->if_broadcastaddr = iso88025_broadcastaddr;
10544165Sjulian
106112272Smdodd    ifa = ifaddr_byindex(ifp->if_index);
107112272Smdodd    if (ifa == 0) {
108112272Smdodd            printf("iso88025_ifattach: no lladdr!\n");
109112272Smdodd            return;
110112272Smdodd    }
111112272Smdodd    sdl = (struct sockaddr_dl *)ifa->ifa_addr;
112112272Smdodd    sdl->sdl_type = IFT_ISO88025;
113112272Smdodd    sdl->sdl_alen = ifp->if_addrlen;
114112272Smdodd    bcopy(IFP2AC(ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);
11544165Sjulian}
11644165Sjulian
11774408Smdodd/*
11874408Smdodd * Perform common duties while detaching a Token Ring interface
11974408Smdodd */
12074408Smdoddvoid
12174408Smdoddiso88025_ifdetach(ifp, bpf)
12274408Smdodd        struct ifnet *ifp;
12374408Smdodd        int bpf;
12474408Smdodd{
125112274Smdodd
12674408Smdodd	if (bpf)
12774408Smdodd                bpfdetach(ifp);
128112274Smdodd
12974408Smdodd	if_detach(ifp);
130112274Smdodd
131112274Smdodd	return;
13274408Smdodd}
13374408Smdodd
13444165Sjulianint
13544165Sjulianiso88025_ioctl(struct ifnet *ifp, int command, caddr_t data)
13644165Sjulian{
137112274Smdodd        struct ifaddr *ifa;
138112274Smdodd        struct ifreq *ifr;
139112274Smdodd        int error;
14044165Sjulian
141112274Smdodd	ifa = (struct ifaddr *) data;
142112274Smdodd	ifr = (struct ifreq *) data;
143112274Smdodd	error = 0;
144112274Smdodd
14544165Sjulian        switch (command) {
14644165Sjulian        case SIOCSIFADDR:
14744165Sjulian                ifp->if_flags |= IFF_UP;
14844165Sjulian
14944165Sjulian                switch (ifa->ifa_addr->sa_family) {
15044165Sjulian#ifdef INET
15144165Sjulian                case AF_INET:
15244165Sjulian                        ifp->if_init(ifp->if_softc);    /* before arpwhohas */
15384931Sfjoe                        arp_ifinit(ifp, ifa);
15444165Sjulian                        break;
15574408Smdodd#endif	/* INET */
15674408Smdodd#ifdef IPX
15774408Smdodd                /*
15874408Smdodd                 * XXX - This code is probably wrong
15974408Smdodd                 */
16074408Smdodd                case AF_IPX:
16174408Smdodd                        {
162111774Smdodd                        struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
16374408Smdodd                        struct arpcom *ac = IFP2AC(ifp);
16474408Smdodd
16574408Smdodd                        if (ipx_nullhost(*ina))
166112274Smdodd                                ina->x_host = *(union ipx_host *)ac->ac_enaddr;
16774408Smdodd                        else {
16874408Smdodd                                bcopy((caddr_t) ina->x_host.c_host,
16974408Smdodd                                      (caddr_t) ac->ac_enaddr,
170112278Smdodd                                      ISO88025_ADDR_LEN);
17174408Smdodd                        }
17274408Smdodd
17374408Smdodd                        /*
17474408Smdodd                         * Set new address
17574408Smdodd                         */
17674408Smdodd                        ifp->if_init(ifp->if_softc);
17774408Smdodd                        break;
17874408Smdodd                        }
17974408Smdodd#endif	/* IPX */
18044165Sjulian                default:
18144165Sjulian                        ifp->if_init(ifp->if_softc);
18244165Sjulian                        break;
18344165Sjulian                }
18444165Sjulian                break;
18544165Sjulian
18644165Sjulian        case SIOCGIFADDR:
18744165Sjulian                {
18844165Sjulian                        struct sockaddr *sa;
18944165Sjulian
19044165Sjulian                        sa = (struct sockaddr *) & ifr->ifr_data;
191111775Smdodd                        bcopy(IFP2AC(ifp)->ac_enaddr,
19244165Sjulian                              (caddr_t) sa->sa_data, ISO88025_ADDR_LEN);
19344165Sjulian                }
19444165Sjulian                break;
19544165Sjulian
19644165Sjulian        case SIOCSIFMTU:
19744165Sjulian                /*
19844165Sjulian                 * Set the interface MTU.
19944165Sjulian                 */
20058313Slile                if (ifr->ifr_mtu > ISO88025_MAX_MTU) {
20144165Sjulian                        error = EINVAL;
20244165Sjulian                } else {
20344165Sjulian                        ifp->if_mtu = ifr->ifr_mtu;
20444165Sjulian                }
20544165Sjulian                break;
206112274Smdodd	default:
207112274Smdodd		error = EINVAL;			/* XXX netbsd has ENOTTY??? */
208112274Smdodd		break;
20944165Sjulian        }
210112274Smdodd
21144165Sjulian        return (error);
21244165Sjulian}
21344165Sjulian
21444165Sjulian/*
21544165Sjulian * ISO88025 encapsulation
21644165Sjulian */
21744165Sjulianint
21874408Smdoddiso88025_output(ifp, m, dst, rt0)
21974408Smdodd	struct ifnet *ifp;
22074408Smdodd	struct mbuf *m;
22174408Smdodd	struct sockaddr *dst;
22274408Smdodd	struct rtentry *rt0;
22344165Sjulian{
22474408Smdodd	u_int16_t snap_type = 0;
22587914Sjlemon	int loop_copy = 0, error = 0, rif_len = 0;
22687914Sjlemon	u_char edst[ISO88025_ADDR_LEN];
22774408Smdodd	struct iso88025_header *th;
22844627Sjulian	struct iso88025_header gen_th;
22974408Smdodd	struct sockaddr_dl *sdl = NULL;
23074408Smdodd	struct rtentry *rt;
23144165Sjulian	struct arpcom *ac = (struct arpcom *)ifp;
23244165Sjulian
233112285Smdodd#ifdef MAC
234112285Smdodd	error = mac_check_ifnet_transmit(ifp, m);
235112285Smdodd	if (error)
236112285Smdodd		senderr(error);
237112285Smdodd#endif
238112285Smdodd
23944165Sjulian	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
24044165Sjulian		senderr(ENETDOWN);
24174408Smdodd	getmicrotime(&ifp->if_lastchange);
24274408Smdodd
243111767Smdodd	error = rt_check(&rt, &rt0, dst);
244112274Smdodd	if (error)
245111767Smdodd		goto bad;
24644627Sjulian
24744627Sjulian	/* Calculate routing info length based on arp table entry */
24844627Sjulian	if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway))
249102291Sarchie		if (SDL_ISO88025(sdl)->trld_rcf != 0)
25096184Skbyanc			rif_len = TR_RCF_RIFLEN(SDL_ISO88025(sdl)->trld_rcf);
25144627Sjulian
25244627Sjulian	/* Generate a generic 802.5 header for the packet */
25358313Slile	gen_th.ac = TR_AC;
25458313Slile	gen_th.fc = TR_LLC_FRAME;
25574408Smdodd	(void)memcpy((caddr_t)gen_th.iso88025_shost, (caddr_t)ac->ac_enaddr,
256112278Smdodd		     ISO88025_ADDR_LEN);
25744627Sjulian	if (rif_len) {
25858313Slile		gen_th.iso88025_shost[0] |= TR_RII;
25944627Sjulian		if (rif_len > 2) {
26096184Skbyanc			gen_th.rcf = SDL_ISO88025(sdl)->trld_rcf;
26174408Smdodd			(void)memcpy((caddr_t)gen_th.rd,
26296184Skbyanc				(caddr_t)SDL_ISO88025(sdl)->trld_route,
26396184Skbyanc				rif_len - 2);
26444627Sjulian		}
26544627Sjulian	}
26644627Sjulian
26744165Sjulian	switch (dst->sa_family) {
26844165Sjulian#ifdef INET
26944165Sjulian	case AF_INET:
27084931Sfjoe		if (!arpresolve(ifp, rt, m, dst, edst, rt0))
27144165Sjulian			return (0);	/* if not yet resolved */
27274408Smdodd		snap_type = ETHERTYPE_IP;
27374408Smdodd		break;
27474408Smdodd#endif	/* INET */
27574408Smdodd#ifdef NOT_YET
27674408Smdodd#ifdef INET6
27774408Smdodd	case AF_INET6:
27874408Smdodd		if (!nd6_storelladdr(&ac->ac_if, rt, m, dst, (u_char *)edst)) {
27974408Smdodd			/* Something bad happened */
28074408Smdodd			return(0);
28174408Smdodd		}
28274408Smdodd		snap_type = ETHERTYPE_IPV6;
28374408Smdodd		break;
28474408Smdodd#endif	/* INET6 */
28574408Smdodd#endif	/* NOT_YET */
28674408Smdodd#ifdef IPX
28774408Smdodd	case AF_IPX:
28874408Smdodd	{
28974408Smdodd		u_int8_t	*cp;
29074408Smdodd
29174408Smdodd		snap_type = 0;
29274408Smdodd		bcopy((caddr_t)&(satoipx_addr(dst).x_host), (caddr_t)edst,
293112278Smdodd		      ISO88025_ADDR_LEN);
29474408Smdodd
295111119Simp		M_PREPEND(m, 3, M_TRYWAIT);
29644627Sjulian		if (m == 0)
29744627Sjulian			senderr(ENOBUFS);
29874408Smdodd		m = m_pullup(m, 3);
29974408Smdodd		if (m == 0)
30074408Smdodd			senderr(ENOBUFS);
30174408Smdodd		cp = mtod(m, u_int8_t *);
30274408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
30374408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
30474408Smdodd		*cp++ = LLC_UI;
30574408Smdodd	}
30674408Smdodd	break;
30774408Smdodd#endif	/* IPX */
30844165Sjulian	case AF_UNSPEC:
30974408Smdodd	{
31074408Smdodd		struct iso88025_sockaddr_data *sd;
31144627Sjulian		/*
31244627Sjulian		 * For AF_UNSPEC sockaddr.sa_data must contain all of the
31344627Sjulian		 * mac information needed to send the packet.  This allows
31444627Sjulian		 * full mac, llc, and source routing function to be controlled.
31544627Sjulian		 * llc and source routing information must already be in the
31644627Sjulian		 * mbuf provided, ac/fc are set in sa_data.  sockaddr.sa_data
317108533Sschweikh		 * should be an iso88025_sockaddr_data structure see iso88025.h
31844627Sjulian		 */
31944165Sjulian                loop_copy = -1;
32044627Sjulian		sd = (struct iso88025_sockaddr_data *)dst->sa_data;
32144627Sjulian		gen_th.ac = sd->ac;
32244627Sjulian		gen_th.fc = sd->fc;
32374408Smdodd		(void)memcpy((caddr_t)edst, (caddr_t)sd->ether_dhost,
324112278Smdodd			     ISO88025_ADDR_LEN);
32574408Smdodd		(void)memcpy((caddr_t)gen_th.iso88025_shost,
326112280Smdodd			     (caddr_t)sd->ether_shost, ISO88025_ADDR_LEN);
32744627Sjulian		rif_len = 0;
32844165Sjulian		break;
32974408Smdodd	}
33044165Sjulian	default:
331105598Sbrooks		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
33244165Sjulian		senderr(EAFNOSUPPORT);
33374408Smdodd		break;
33444165Sjulian	}
33544165Sjulian
336112274Smdodd	/*
337112274Smdodd	 * Add LLC header.
338112274Smdodd	 */
33974408Smdodd	if (snap_type != 0) {
34074408Smdodd        	struct llc *l;
341111790Smdodd		M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
34274408Smdodd		if (m == 0)
34374408Smdodd			senderr(ENOBUFS);
34474408Smdodd		l = mtod(m, struct llc *);
345112281Smdodd		l->llc_control = LLC_UI;
34674408Smdodd		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
347112268Smdodd		l->llc_snap.org_code[0] =
348112268Smdodd			l->llc_snap.org_code[1] =
349112268Smdodd			l->llc_snap.org_code[2] = 0;
350112274Smdodd		l->llc_snap.ether_type = htons(snap_type);
35174408Smdodd	}
35274408Smdodd
35344165Sjulian	/*
35444165Sjulian	 * Add local net header.  If no space in first mbuf,
35544165Sjulian	 * allocate another.
35644165Sjulian	 */
357111119Simp	M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT);
35844165Sjulian	if (m == 0)
35944165Sjulian		senderr(ENOBUFS);
360112274Smdodd	th = mtod(m, struct iso88025_header *);
361112291Smdodd	bcopy((caddr_t)edst, (caddr_t)&gen_th.iso88025_dhost, ISO88025_ADDR_LEN);
36244627Sjulian
36344627Sjulian	/* Copy as much of the generic header as is needed into the mbuf */
36444627Sjulian	memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len);
36544627Sjulian
36644165Sjulian        /*
36744165Sjulian         * If a simplex interface, and the packet is being sent to our
36844165Sjulian         * Ethernet address or a broadcast address, loopback a copy.
36944165Sjulian         * XXX To make a simplex device behave exactly like a duplex
37044165Sjulian         * device, we should copy in the case of sending to our own
37144165Sjulian         * ethernet address (thus letting the original actually appear
37244165Sjulian         * on the wire). However, we don't do that here for security
37344165Sjulian         * reasons and compatibility with the original behavior.
37444165Sjulian         */
37574408Smdodd        if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
37644165Sjulian                if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
37774408Smdodd                        struct mbuf *n;
37874408Smdodd			n = m_copy(m, 0, (int)M_COPYALL);
379112279Smdodd                        (void) if_simloop(ifp, n, dst->sa_family,
38074408Smdodd					  ISO88025_HDR_LEN);
381112279Smdodd                } else if (bcmp(th->iso88025_dhost, th->iso88025_shost,
38274408Smdodd				 ETHER_ADDR_LEN) == 0) {
383112279Smdodd			(void) if_simloop(ifp, m, dst->sa_family,
384112279Smdodd					  ISO88025_HDR_LEN);
385112279Smdodd                       	return(0);      /* XXX */
386112279Smdodd		}
38744165Sjulian        }
38844165Sjulian
389111790Smdodd	if (! IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN) ) {
39069152Sjlemon		printf("iso88025_output: packet dropped QFULL.\n");
39144165Sjulian		senderr(ENOBUFS);
39244165Sjulian	}
39344165Sjulian	return (error);
39444165Sjulian
39544165Sjulianbad:
39644165Sjulian	if (m)
39744165Sjulian		m_freem(m);
39844165Sjulian	return (error);
39944165Sjulian}
40044165Sjulian
40144165Sjulian/*
40244165Sjulian * ISO 88025 de-encapsulation
40344165Sjulian */
40444165Sjulianvoid
40574408Smdoddiso88025_input(ifp, th, m)
40674408Smdodd	struct ifnet *ifp;
40774408Smdodd	struct iso88025_header *th;
40874408Smdodd	struct mbuf *m;
40944165Sjulian{
410111888Sjlemon	int isr;
411111774Smdodd	struct llc *l;
41244165Sjulian
413112286Smdodd	/*
414112286Smdodd	 * Discard packet if interface is not up.
415112286Smdodd	 */
416112286Smdodd	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
417112286Smdodd		goto dropanyway;
41844165Sjulian
419112285Smdodd#ifdef MAC
420112285Smdodd	mac_create_mbuf_from_ifnet(ifp, m);
421112285Smdodd#endif
422112285Smdodd
423112286Smdodd	/*
424112286Smdodd	 * Update interface statistics.
425112286Smdodd	 */
426112286Smdodd	ifp->if_ibytes += m->m_pkthdr.len + sizeof(*th);
42774408Smdodd	getmicrotime(&ifp->if_lastchange);
42858313Slile
429112280Smdodd	/*
430112286Smdodd	 * Discard non local unicast packets when interface
431112286Smdodd	 * is in promiscuous mode.
432112286Smdodd	 */
433112286Smdodd	if ((ifp->if_flags & IFF_PROMISC) &&
434112286Smdodd	    ((th->iso88025_dhost[0] & 1) == 0) &&
435112286Smdodd	     (bcmp(IFP2AC(ifp)->ac_enaddr, (caddr_t) th->iso88025_dhost,
436112286Smdodd	     ISO88025_ADDR_LEN) != 0))
437112286Smdodd		goto dropanyway;
438112286Smdodd	}
439112286Smdodd
440112286Smdodd	/*
441112280Smdodd	 * Set mbuf flags for bcast/mcast.
442112280Smdodd	 */
44344165Sjulian	if (th->iso88025_dhost[0] & 1) {
444112277Smdodd		if (bcmp((caddr_t)iso88025_broadcastaddr,
445112277Smdodd			 (caddr_t)th->iso88025_dhost, ISO88025_ADDR_LEN) == 0)
44644165Sjulian			m->m_flags |= M_BCAST;
44744165Sjulian		else
44844165Sjulian			m->m_flags |= M_MCAST;
44974408Smdodd		ifp->if_imcasts++;
450112274Smdodd	}
45144165Sjulian
45274408Smdodd	l = mtod(m, struct llc *);
45344165Sjulian
45474408Smdodd	switch (l->llc_dsap) {
45574408Smdodd#ifdef IPX
45674408Smdodd	case ETHERTYPE_IPX_8022:	/* Thanks a bunch Novell */
45774408Smdodd		if ((l->llc_control != LLC_UI) ||
458112289Smdodd		    (l->llc_ssap != ETHERTYPE_IPX_8022)) {
459112289Smdodd			ifp->if_noproto++;
46074408Smdodd			goto dropanyway;
461112289Smdodd		}
46274408Smdodd
46374408Smdodd		th->iso88025_shost[0] &= ~(TR_RII);
46474408Smdodd		m_adj(m, 3);
465111888Sjlemon		isr = NETISR_IPX;
46674408Smdodd		break;
46774408Smdodd#endif	/* IPX */
46874408Smdodd	case LLC_SNAP_LSAP: {
46974408Smdodd		u_int16_t type;
47074408Smdodd		if ((l->llc_control != LLC_UI) ||
471112289Smdodd		    (l->llc_ssap != LLC_SNAP_LSAP)) {
472112289Smdodd			ifp->if_noproto++;
47374408Smdodd			goto dropanyway;
474112289Smdodd		}
47574408Smdodd
476112268Smdodd		if (l->llc_snap.org_code[0] != 0 ||
477112268Smdodd		    l->llc_snap.org_code[1] != 0 ||
478112268Smdodd		    l->llc_snap.org_code[2] != 0)
47974408Smdodd			goto dropanyway;
48074408Smdodd
481112268Smdodd		type = ntohs(l->llc_snap.ether_type);
482111790Smdodd		m_adj(m, LLC_SNAPFRAMELEN);
48374408Smdodd		switch (type) {
48444165Sjulian#ifdef INET
48574408Smdodd		case ETHERTYPE_IP:
48674408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
48774408Smdodd			if (ipflow_fastforward(m))
48874408Smdodd				return;
489111888Sjlemon			isr = NETISR_IP;
49074408Smdodd			break;
49174408Smdodd
49274408Smdodd		case ETHERTYPE_ARP:
49378295Sjlemon			if (ifp->if_flags & IFF_NOARP)
49478295Sjlemon				goto dropanyway;
495111888Sjlemon			isr = NETISR_ARP;
49674408Smdodd			break;
49774408Smdodd#endif	/* INET */
49874408Smdodd#ifdef IPX_SNAP	/* XXX: Not supported! */
49974408Smdodd		case ETHERTYPE_IPX:
50074408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
501111888Sjlemon			isr = NETISR_IPX;
50274408Smdodd			break;
50374408Smdodd#endif	/* IPX_SNAP */
50474408Smdodd#ifdef NOT_YET
50574408Smdodd#ifdef INET6
50674408Smdodd		case ETHERTYPE_IPV6:
50774408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
508111888Sjlemon			isr = NETISR_IPV6;
50974408Smdodd			break;
51074408Smdodd#endif	/* INET6 */
51174408Smdodd#endif	/* NOT_YET */
51274408Smdodd		default:
51374408Smdodd			printf("iso88025_input: unexpected llc_snap ether_type  0x%02x\n", type);
514112289Smdodd			ifp->if_noproto++;
515112289Smdodd			goto dropanyway;
51674408Smdodd		}
51744165Sjulian		break;
51874408Smdodd	}
51974408Smdodd	case LLC_ISO_LSAP:
52074408Smdodd		switch (l->llc_control) {
52174408Smdodd		case LLC_UI:
522112289Smdodd			ifp->if_noproto++;
52374408Smdodd			goto dropanyway;
52474408Smdodd			break;
52574408Smdodd                case LLC_XID:
52674408Smdodd                case LLC_XID_P:
52774408Smdodd			if(m->m_len < ISO88025_ADDR_LEN)
52874408Smdodd				goto dropanyway;
52974408Smdodd			l->llc_window = 0;
53074408Smdodd			l->llc_fid = 9;
53174408Smdodd			l->llc_class = 1;
53274408Smdodd			l->llc_dsap = l->llc_ssap = 0;
53374408Smdodd			/* Fall through to */
53474408Smdodd		case LLC_TEST:
53574408Smdodd		case LLC_TEST_P:
53674408Smdodd		{
53774408Smdodd			struct sockaddr sa;
53874408Smdodd			struct arpcom *ac = (struct arpcom *)ifp;
53974408Smdodd			struct iso88025_sockaddr_data *th2;
54074408Smdodd			int i;
54174408Smdodd			u_char c = l->llc_dsap;
54244165Sjulian
54374408Smdodd			if (th->iso88025_shost[0] & TR_RII) { /* XXX */
54474408Smdodd				printf("iso88025_input: dropping source routed LLC_TEST\n");
545112289Smdodd				goto dropanyway;
54674408Smdodd			}
54774408Smdodd			l->llc_dsap = l->llc_ssap;
54874408Smdodd			l->llc_ssap = c;
54974408Smdodd			if (m->m_flags & (M_BCAST | M_MCAST))
55074408Smdodd				bcopy((caddr_t)ac->ac_enaddr,
551112280Smdodd				      (caddr_t)th->iso88025_dhost,
55274408Smdodd					ISO88025_ADDR_LEN);
55374408Smdodd			sa.sa_family = AF_UNSPEC;
55474408Smdodd			sa.sa_len = sizeof(sa);
55574408Smdodd			th2 = (struct iso88025_sockaddr_data *)sa.sa_data;
55674408Smdodd			for (i = 0; i < ISO88025_ADDR_LEN; i++) {
55774408Smdodd				th2->ether_shost[i] = c = th->iso88025_dhost[i];
55874408Smdodd				th2->ether_dhost[i] = th->iso88025_dhost[i] =
55974408Smdodd					th->iso88025_shost[i];
56074408Smdodd				th->iso88025_shost[i] = c;
56174408Smdodd			}
56274408Smdodd			th2->ac = TR_AC;
56374408Smdodd			th2->fc = TR_LLC_FRAME;
56474408Smdodd			ifp->if_output(ifp, m, &sa, NULL);
56574408Smdodd			return;
56674408Smdodd		}
56774408Smdodd		default:
56874408Smdodd			printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control);
569112289Smdodd			ifp->if_noproto++;
570112289Smdodd			goto dropanyway;
57174408Smdodd		}
57274408Smdodd		break;
57344165Sjulian	default:
57474408Smdodd		printf("iso88025_input: unknown dsap 0x%x\n", l->llc_dsap);
57574408Smdodd		ifp->if_noproto++;
576112289Smdodd		goto dropanyway;
57744165Sjulian	}
578112274Smdodd
579111888Sjlemon	netisr_dispatch(isr, m);
580112274Smdodd	return;
581112289Smdodd
582112289Smdodddropanyway:
583112289Smdodd	ifp->if_iqdrops++;
584112289Smdodd	if (m)
585112289Smdodd		m_freem(m);
586112289Smdodd	return;
58744165Sjulian}
588112269Smdodd
589112273Smdoddstatic int
590112273Smdoddiso88025_resolvemulti (ifp, llsa, sa)
591112273Smdodd	struct ifnet *ifp;
592112273Smdodd	struct sockaddr **llsa;
593112273Smdodd	struct sockaddr *sa;
594112273Smdodd{
595112273Smdodd	struct sockaddr_dl *sdl;
596112273Smdodd	struct sockaddr_in *sin;
597112273Smdodd#ifdef INET6
598112273Smdodd	struct sockaddr_in6 *sin6;
599112273Smdodd#endif
600112273Smdodd	u_char *e_addr;
601112273Smdodd
602112273Smdodd	switch(sa->sa_family) {
603112273Smdodd	case AF_LINK:
604112273Smdodd		/*
605112273Smdodd		 * No mapping needed. Just check that it's a valid MC address.
606112273Smdodd		 */
607112273Smdodd		sdl = (struct sockaddr_dl *)sa;
608112273Smdodd		e_addr = LLADDR(sdl);
609112273Smdodd		if ((e_addr[0] & 1) != 1) {
610112273Smdodd			return (EADDRNOTAVAIL);
611112273Smdodd		}
612112273Smdodd		*llsa = 0;
613112273Smdodd		return (0);
614112273Smdodd
615112273Smdodd#ifdef INET
616112273Smdodd	case AF_INET:
617112273Smdodd		sin = (struct sockaddr_in *)sa;
618112273Smdodd		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
619112273Smdodd			return (EADDRNOTAVAIL);
620112273Smdodd		}
621112273Smdodd		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
622112273Smdodd		       M_WAITOK|M_ZERO);
623112273Smdodd		sdl->sdl_len = sizeof *sdl;
624112273Smdodd		sdl->sdl_family = AF_LINK;
625112273Smdodd		sdl->sdl_index = ifp->if_index;
626112273Smdodd		sdl->sdl_type = IFT_ISO88025;
627112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
628112273Smdodd		e_addr = LLADDR(sdl);
629112273Smdodd		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
630112273Smdodd		*llsa = (struct sockaddr *)sdl;
631112273Smdodd		return (0);
632112273Smdodd#endif
633112273Smdodd#ifdef INET6
634112273Smdodd	case AF_INET6:
635112273Smdodd		sin6 = (struct sockaddr_in6 *)sa;
636112273Smdodd		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
637112273Smdodd			/*
638112273Smdodd			 * An IP6 address of 0 means listen to all
639112273Smdodd			 * of the Ethernet multicast address used for IP6.
640112273Smdodd			 * (This is used for multicast routers.)
641112273Smdodd			 */
642112273Smdodd			ifp->if_flags |= IFF_ALLMULTI;
643112273Smdodd			*llsa = 0;
644112273Smdodd			return (0);
645112273Smdodd		}
646112273Smdodd		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
647112273Smdodd			return (EADDRNOTAVAIL);
648112273Smdodd		}
649112273Smdodd		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
650112273Smdodd		       M_WAITOK|M_ZERO);
651112273Smdodd		sdl->sdl_len = sizeof *sdl;
652112273Smdodd		sdl->sdl_family = AF_LINK;
653112273Smdodd		sdl->sdl_index = ifp->if_index;
654112273Smdodd		sdl->sdl_type = IFT_ISO88025;
655112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
656112273Smdodd		e_addr = LLADDR(sdl);
657112273Smdodd		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
658112273Smdodd		*llsa = (struct sockaddr *)sdl;
659112273Smdodd		return (0);
660112273Smdodd#endif
661112273Smdodd
662112273Smdodd	default:
663112273Smdodd		/*
664112273Smdodd		 * Well, the text isn't quite right, but it's the name
665112273Smdodd		 * that counts...
666112273Smdodd		 */
667112273Smdodd		return (EAFNOSUPPORT);
668112273Smdodd	}
669112273Smdodd
670112273Smdodd	return (0);
671112273Smdodd}
672112273Smdodd
673112269Smdoddstatic moduledata_t iso88025_mod = {
674112269Smdodd	"iso88025",
675112269Smdodd	NULL,
676112269Smdodd	0
677112269Smdodd};
678112269Smdodd
679112269SmdoddDECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
680112269SmdoddMODULE_VERSION(iso88025, 1);
681