if_iso88025subr.c revision 112308
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 112308 2003-03-16 00:17:44Z 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 **,
86112294Smdodd				  struct sockaddr *);
87112273Smdodd
88112276Smdodd#define	IFP2AC(IFP)	((struct arpcom *)IFP)
89112276Smdodd#define	senderr(e)	do { error = (e); goto bad; } while (0)
9074408Smdodd
91112297Smdodd/*
92112297Smdodd * Perform common duties while attaching to interface list
93112297Smdodd */
9444165Sjulianvoid
95112297Smdoddiso88025_ifattach(struct ifnet *ifp, 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
118112272Smdodd    ifa = ifaddr_byindex(ifp->if_index);
119112272Smdodd    if (ifa == 0) {
120112298Smdodd        if_printf(ifp, "%s() no lladdr!\n", __func__);
121112298Smdodd        return;
122112272Smdodd    }
123112297Smdodd
124112272Smdodd    sdl = (struct sockaddr_dl *)ifa->ifa_addr;
125112272Smdodd    sdl->sdl_type = IFT_ISO88025;
126112272Smdodd    sdl->sdl_alen = ifp->if_addrlen;
127112272Smdodd    bcopy(IFP2AC(ifp)->ac_enaddr, 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                 */
17874408Smdodd                case AF_IPX:
17974408Smdodd                        {
180111774Smdodd                        struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr);
18174408Smdodd                        struct arpcom *ac = IFP2AC(ifp);
18274408Smdodd
18374408Smdodd                        if (ipx_nullhost(*ina))
184112274Smdodd                                ina->x_host = *(union ipx_host *)ac->ac_enaddr;
18574408Smdodd                        else {
18674408Smdodd                                bcopy((caddr_t) ina->x_host.c_host,
18774408Smdodd                                      (caddr_t) ac->ac_enaddr,
188112278Smdodd                                      ISO88025_ADDR_LEN);
18974408Smdodd                        }
19074408Smdodd
19174408Smdodd                        /*
19274408Smdodd                         * Set new address
19374408Smdodd                         */
19474408Smdodd                        ifp->if_init(ifp->if_softc);
19574408Smdodd                        break;
19674408Smdodd                        }
19774408Smdodd#endif	/* IPX */
19844165Sjulian                default:
19944165Sjulian                        ifp->if_init(ifp->if_softc);
20044165Sjulian                        break;
20144165Sjulian                }
20244165Sjulian                break;
20344165Sjulian
20444165Sjulian        case SIOCGIFADDR:
20544165Sjulian                {
20644165Sjulian                        struct sockaddr *sa;
20744165Sjulian
20844165Sjulian                        sa = (struct sockaddr *) & ifr->ifr_data;
209111775Smdodd                        bcopy(IFP2AC(ifp)->ac_enaddr,
21044165Sjulian                              (caddr_t) sa->sa_data, ISO88025_ADDR_LEN);
21144165Sjulian                }
21244165Sjulian                break;
21344165Sjulian
21444165Sjulian        case SIOCSIFMTU:
21544165Sjulian                /*
21644165Sjulian                 * Set the interface MTU.
21744165Sjulian                 */
21858313Slile                if (ifr->ifr_mtu > ISO88025_MAX_MTU) {
21944165Sjulian                        error = EINVAL;
22044165Sjulian                } else {
22144165Sjulian                        ifp->if_mtu = ifr->ifr_mtu;
22244165Sjulian                }
22344165Sjulian                break;
224112274Smdodd	default:
225112274Smdodd		error = EINVAL;			/* XXX netbsd has ENOTTY??? */
226112274Smdodd		break;
22744165Sjulian        }
228112274Smdodd
22944165Sjulian        return (error);
23044165Sjulian}
23144165Sjulian
23244165Sjulian/*
23344165Sjulian * ISO88025 encapsulation
23444165Sjulian */
23544165Sjulianint
23674408Smdoddiso88025_output(ifp, m, dst, rt0)
23774408Smdodd	struct ifnet *ifp;
23874408Smdodd	struct mbuf *m;
23974408Smdodd	struct sockaddr *dst;
24074408Smdodd	struct rtentry *rt0;
24144165Sjulian{
24274408Smdodd	u_int16_t snap_type = 0;
24387914Sjlemon	int loop_copy = 0, error = 0, rif_len = 0;
24487914Sjlemon	u_char edst[ISO88025_ADDR_LEN];
24574408Smdodd	struct iso88025_header *th;
24644627Sjulian	struct iso88025_header gen_th;
24774408Smdodd	struct sockaddr_dl *sdl = NULL;
24874408Smdodd	struct rtentry *rt;
249112308Smdodd	struct arpcom *ac = IFP2AC(ifp);
25044165Sjulian
251112285Smdodd#ifdef MAC
252112285Smdodd	error = mac_check_ifnet_transmit(ifp, m);
253112285Smdodd	if (error)
254112285Smdodd		senderr(error);
255112285Smdodd#endif
256112285Smdodd
257112308Smdodd	if (ifp->if_flags & IFF_MONITOR)
258112308Smdodd		senderr(ENETDOWN);
25944165Sjulian	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
26044165Sjulian		senderr(ENETDOWN);
26174408Smdodd	getmicrotime(&ifp->if_lastchange);
26274408Smdodd
263111767Smdodd	error = rt_check(&rt, &rt0, dst);
264112274Smdodd	if (error)
265111767Smdodd		goto bad;
26644627Sjulian
26744627Sjulian	/* Calculate routing info length based on arp table entry */
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;
27574408Smdodd	(void)memcpy((caddr_t)gen_th.iso88025_shost, (caddr_t)ac->ac_enaddr,
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:
29084931Sfjoe		if (!arpresolve(ifp, rt, m, dst, edst, rt0))
29144165Sjulian			return (0);	/* if not yet resolved */
29274408Smdodd		snap_type = ETHERTYPE_IP;
29374408Smdodd		break;
29474408Smdodd#endif	/* INET */
29574408Smdodd#ifdef NOT_YET
29674408Smdodd#ifdef INET6
29774408Smdodd	case AF_INET6:
29874408Smdodd		if (!nd6_storelladdr(&ac->ac_if, rt, m, dst, (u_char *)edst)) {
29974408Smdodd			/* Something bad happened */
30074408Smdodd			return(0);
30174408Smdodd		}
30274408Smdodd		snap_type = ETHERTYPE_IPV6;
30374408Smdodd		break;
30474408Smdodd#endif	/* INET6 */
30574408Smdodd#endif	/* NOT_YET */
30674408Smdodd#ifdef IPX
30774408Smdodd	case AF_IPX:
30874408Smdodd	{
30974408Smdodd		u_int8_t	*cp;
31074408Smdodd
31174408Smdodd		bcopy((caddr_t)&(satoipx_addr(dst).x_host), (caddr_t)edst,
312112278Smdodd		      ISO88025_ADDR_LEN);
31374408Smdodd
314111119Simp		M_PREPEND(m, 3, M_TRYWAIT);
31544627Sjulian		if (m == 0)
31644627Sjulian			senderr(ENOBUFS);
31774408Smdodd		m = m_pullup(m, 3);
31874408Smdodd		if (m == 0)
31974408Smdodd			senderr(ENOBUFS);
32074408Smdodd		cp = mtod(m, u_int8_t *);
32174408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
32274408Smdodd		*cp++ = ETHERTYPE_IPX_8022;
32374408Smdodd		*cp++ = LLC_UI;
32474408Smdodd	}
32574408Smdodd	break;
32674408Smdodd#endif	/* IPX */
32744165Sjulian	case AF_UNSPEC:
32874408Smdodd	{
32974408Smdodd		struct iso88025_sockaddr_data *sd;
33044627Sjulian		/*
33144627Sjulian		 * For AF_UNSPEC sockaddr.sa_data must contain all of the
33244627Sjulian		 * mac information needed to send the packet.  This allows
33344627Sjulian		 * full mac, llc, and source routing function to be controlled.
33444627Sjulian		 * llc and source routing information must already be in the
33544627Sjulian		 * mbuf provided, ac/fc are set in sa_data.  sockaddr.sa_data
336108533Sschweikh		 * should be an iso88025_sockaddr_data structure see iso88025.h
33744627Sjulian		 */
33844165Sjulian                loop_copy = -1;
33944627Sjulian		sd = (struct iso88025_sockaddr_data *)dst->sa_data;
34044627Sjulian		gen_th.ac = sd->ac;
34144627Sjulian		gen_th.fc = sd->fc;
34274408Smdodd		(void)memcpy((caddr_t)edst, (caddr_t)sd->ether_dhost,
343112278Smdodd			     ISO88025_ADDR_LEN);
34474408Smdodd		(void)memcpy((caddr_t)gen_th.iso88025_shost,
345112280Smdodd			     (caddr_t)sd->ether_shost, ISO88025_ADDR_LEN);
34644627Sjulian		rif_len = 0;
34744165Sjulian		break;
34874408Smdodd	}
34944165Sjulian	default:
350105598Sbrooks		if_printf(ifp, "can't handle af%d\n", dst->sa_family);
35144165Sjulian		senderr(EAFNOSUPPORT);
35274408Smdodd		break;
35344165Sjulian	}
35444165Sjulian
355112274Smdodd	/*
356112274Smdodd	 * Add LLC header.
357112274Smdodd	 */
35874408Smdodd	if (snap_type != 0) {
35974408Smdodd        	struct llc *l;
360111790Smdodd		M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
36174408Smdodd		if (m == 0)
36274408Smdodd			senderr(ENOBUFS);
36374408Smdodd		l = mtod(m, struct llc *);
364112281Smdodd		l->llc_control = LLC_UI;
36574408Smdodd		l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP;
366112268Smdodd		l->llc_snap.org_code[0] =
367112268Smdodd			l->llc_snap.org_code[1] =
368112268Smdodd			l->llc_snap.org_code[2] = 0;
369112274Smdodd		l->llc_snap.ether_type = htons(snap_type);
37074408Smdodd	}
37174408Smdodd
37244165Sjulian	/*
37344165Sjulian	 * Add local net header.  If no space in first mbuf,
37444165Sjulian	 * allocate another.
37544165Sjulian	 */
376111119Simp	M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT);
37744165Sjulian	if (m == 0)
37844165Sjulian		senderr(ENOBUFS);
379112274Smdodd	th = mtod(m, struct iso88025_header *);
380112291Smdodd	bcopy((caddr_t)edst, (caddr_t)&gen_th.iso88025_dhost, ISO88025_ADDR_LEN);
38144627Sjulian
38244627Sjulian	/* Copy as much of the generic header as is needed into the mbuf */
38344627Sjulian	memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len);
38444627Sjulian
38544165Sjulian        /*
38644165Sjulian         * If a simplex interface, and the packet is being sent to our
38744165Sjulian         * Ethernet address or a broadcast address, loopback a copy.
38844165Sjulian         * XXX To make a simplex device behave exactly like a duplex
38944165Sjulian         * device, we should copy in the case of sending to our own
39044165Sjulian         * ethernet address (thus letting the original actually appear
39144165Sjulian         * on the wire). However, we don't do that here for security
39244165Sjulian         * reasons and compatibility with the original behavior.
39344165Sjulian         */
39474408Smdodd        if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) {
39544165Sjulian                if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
39674408Smdodd                        struct mbuf *n;
39774408Smdodd			n = m_copy(m, 0, (int)M_COPYALL);
398112279Smdodd                        (void) if_simloop(ifp, n, dst->sa_family,
39974408Smdodd					  ISO88025_HDR_LEN);
400112279Smdodd                } else if (bcmp(th->iso88025_dhost, th->iso88025_shost,
40174408Smdodd				 ETHER_ADDR_LEN) == 0) {
402112279Smdodd			(void) if_simloop(ifp, m, dst->sa_family,
403112279Smdodd					  ISO88025_HDR_LEN);
404112279Smdodd                       	return(0);      /* XXX */
405112279Smdodd		}
40644165Sjulian        }
40744165Sjulian
408111790Smdodd	if (! IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN) ) {
40969152Sjlemon		printf("iso88025_output: packet dropped QFULL.\n");
41044165Sjulian		senderr(ENOBUFS);
41144165Sjulian	}
41244165Sjulian	return (error);
41344165Sjulian
41444165Sjulianbad:
415112296Smdodd	ifp->if_oerrors++;
41644165Sjulian	if (m)
41744165Sjulian		m_freem(m);
41844165Sjulian	return (error);
41944165Sjulian}
42044165Sjulian
42144165Sjulian/*
42244165Sjulian * ISO 88025 de-encapsulation
42344165Sjulian */
42444165Sjulianvoid
425112299Smdoddiso88025_input(ifp, m)
42674408Smdodd	struct ifnet *ifp;
42774408Smdodd	struct mbuf *m;
42844165Sjulian{
429112299Smdodd	struct iso88025_header *th;
430112299Smdodd	struct llc *l;
431111888Sjlemon	int isr;
432112299Smdodd	int mac_hdr_len;
43344165Sjulian
434112308Smdodd	/*
435112308Smdodd	 * Do consistency checks to verify assumptions
436112308Smdodd	 * made by code past this point.
437112308Smdodd	 */
438112308Smdodd	if ((m->m_flags & M_PKTHDR) == 0) {
439112308Smdodd		if_printf(ifp, "discard frame w/o packet header\n");
440112308Smdodd		ifp->if_ierrors++;
441112308Smdodd		m_freem(m);
442112308Smdodd		return;
443112308Smdodd	}
444112308Smdodd	if (m->m_pkthdr.rcvif == NULL) {
445112308Smdodd		if_printf(ifp, "discard frame w/o interface pointer\n");
446112308Smdodd		ifp->if_ierrors++;
447112308Smdodd 		m_freem(m);
448112308Smdodd		return;
449112308Smdodd	}
450112308Smdodd
451112299Smdodd	m = m_pullup(m, ISO88025_HDR_LEN);
452112299Smdodd	if (m == NULL) {
453112299Smdodd		ifp->if_ierrors++;
454112299Smdodd		goto dropanyway;
455112299Smdodd	}
456112299Smdodd	th = mtod(m, struct iso88025_header *);
457112299Smdodd	m->m_pkthdr.header = (void *)th;
458112299Smdodd
459112286Smdodd	/*
460112286Smdodd	 * Discard packet if interface is not up.
461112286Smdodd	 */
462112286Smdodd	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
463112286Smdodd		goto dropanyway;
46444165Sjulian
465112308Smdodd	/*
466112308Smdodd	 * Give bpf a chance at the packet.
467112308Smdodd	 */
468112308Smdodd	BPF_MTAP(ifp, m);
469112308Smdodd
470112308Smdodd	/*
471112308Smdodd	 * Interface marked for monitoring; discard packet.
472112308Smdodd	 */
473112308Smdodd	if (ifp->if_flags & IFF_MONITOR) {
474112308Smdodd		m_freem(m);
475112308Smdodd		return;
476112308Smdodd	}
477112308Smdodd
478112285Smdodd#ifdef MAC
479112285Smdodd	mac_create_mbuf_from_ifnet(ifp, m);
480112285Smdodd#endif
481112285Smdodd
482112286Smdodd	/*
483112286Smdodd	 * Update interface statistics.
484112286Smdodd	 */
485112299Smdodd	ifp->if_ibytes += m->m_pkthdr.len;
48674408Smdodd	getmicrotime(&ifp->if_lastchange);
48758313Slile
488112280Smdodd	/*
489112286Smdodd	 * Discard non local unicast packets when interface
490112286Smdodd	 * is in promiscuous mode.
491112286Smdodd	 */
492112286Smdodd	if ((ifp->if_flags & IFF_PROMISC) &&
493112286Smdodd	    ((th->iso88025_dhost[0] & 1) == 0) &&
494112286Smdodd	     (bcmp(IFP2AC(ifp)->ac_enaddr, (caddr_t) th->iso88025_dhost,
495112286Smdodd	     ISO88025_ADDR_LEN) != 0))
496112286Smdodd		goto dropanyway;
497112286Smdodd
498112286Smdodd	/*
499112280Smdodd	 * Set mbuf flags for bcast/mcast.
500112280Smdodd	 */
50144165Sjulian	if (th->iso88025_dhost[0] & 1) {
502112277Smdodd		if (bcmp((caddr_t)iso88025_broadcastaddr,
503112277Smdodd			 (caddr_t)th->iso88025_dhost, ISO88025_ADDR_LEN) == 0)
50444165Sjulian			m->m_flags |= M_BCAST;
50544165Sjulian		else
50644165Sjulian			m->m_flags |= M_MCAST;
50774408Smdodd		ifp->if_imcasts++;
508112274Smdodd	}
50944165Sjulian
510112299Smdodd	mac_hdr_len = ISO88025_HDR_LEN;
511112299Smdodd	/* Check for source routing info */
512112299Smdodd	if (th->iso88025_shost[0] & TR_RII)
513112299Smdodd		mac_hdr_len += TR_RCF_RIFLEN(th->rcf);
514112299Smdodd
515112299Smdodd	/* Strip off ISO88025 header. */
516112299Smdodd	m_adj(m, mac_hdr_len);
517112299Smdodd
518112299Smdodd	m = m_pullup(m, LLC_SNAPFRAMELEN);
519112299Smdodd	if (m == 0) {
520112299Smdodd		ifp->if_ierrors++;
521112299Smdodd		goto dropanyway;
522112299Smdodd	}
52374408Smdodd	l = mtod(m, struct llc *);
52444165Sjulian
52574408Smdodd	switch (l->llc_dsap) {
52674408Smdodd#ifdef IPX
52774408Smdodd	case ETHERTYPE_IPX_8022:	/* Thanks a bunch Novell */
52874408Smdodd		if ((l->llc_control != LLC_UI) ||
529112289Smdodd		    (l->llc_ssap != ETHERTYPE_IPX_8022)) {
530112289Smdodd			ifp->if_noproto++;
53174408Smdodd			goto dropanyway;
532112289Smdodd		}
53374408Smdodd
53474408Smdodd		th->iso88025_shost[0] &= ~(TR_RII);
53574408Smdodd		m_adj(m, 3);
536111888Sjlemon		isr = NETISR_IPX;
53774408Smdodd		break;
53874408Smdodd#endif	/* IPX */
53974408Smdodd	case LLC_SNAP_LSAP: {
54074408Smdodd		u_int16_t type;
54174408Smdodd		if ((l->llc_control != LLC_UI) ||
542112289Smdodd		    (l->llc_ssap != LLC_SNAP_LSAP)) {
543112289Smdodd			ifp->if_noproto++;
54474408Smdodd			goto dropanyway;
545112289Smdodd		}
54674408Smdodd
547112268Smdodd		if (l->llc_snap.org_code[0] != 0 ||
548112268Smdodd		    l->llc_snap.org_code[1] != 0 ||
549112294Smdodd		    l->llc_snap.org_code[2] != 0) {
550112294Smdodd			ifp->if_noproto++;
55174408Smdodd			goto dropanyway;
552112294Smdodd		}
55374408Smdodd
554112268Smdodd		type = ntohs(l->llc_snap.ether_type);
555111790Smdodd		m_adj(m, LLC_SNAPFRAMELEN);
55674408Smdodd		switch (type) {
55744165Sjulian#ifdef INET
55874408Smdodd		case ETHERTYPE_IP:
55974408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
56074408Smdodd			if (ipflow_fastforward(m))
56174408Smdodd				return;
562111888Sjlemon			isr = NETISR_IP;
56374408Smdodd			break;
56474408Smdodd
56574408Smdodd		case ETHERTYPE_ARP:
56678295Sjlemon			if (ifp->if_flags & IFF_NOARP)
56778295Sjlemon				goto dropanyway;
568111888Sjlemon			isr = NETISR_ARP;
56974408Smdodd			break;
57074408Smdodd#endif	/* INET */
57174408Smdodd#ifdef IPX_SNAP	/* XXX: Not supported! */
57274408Smdodd		case ETHERTYPE_IPX:
57374408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
574111888Sjlemon			isr = NETISR_IPX;
57574408Smdodd			break;
57674408Smdodd#endif	/* IPX_SNAP */
57774408Smdodd#ifdef NOT_YET
57874408Smdodd#ifdef INET6
57974408Smdodd		case ETHERTYPE_IPV6:
58074408Smdodd			th->iso88025_shost[0] &= ~(TR_RII);
581111888Sjlemon			isr = NETISR_IPV6;
58274408Smdodd			break;
58374408Smdodd#endif	/* INET6 */
58474408Smdodd#endif	/* NOT_YET */
58574408Smdodd		default:
58674408Smdodd			printf("iso88025_input: unexpected llc_snap ether_type  0x%02x\n", type);
587112289Smdodd			ifp->if_noproto++;
588112289Smdodd			goto dropanyway;
58974408Smdodd		}
59044165Sjulian		break;
59174408Smdodd	}
592112296Smdodd#ifdef ISO
59374408Smdodd	case LLC_ISO_LSAP:
59474408Smdodd		switch (l->llc_control) {
59574408Smdodd		case LLC_UI:
596112289Smdodd			ifp->if_noproto++;
59774408Smdodd			goto dropanyway;
59874408Smdodd			break;
59974408Smdodd                case LLC_XID:
60074408Smdodd                case LLC_XID_P:
60174408Smdodd			if(m->m_len < ISO88025_ADDR_LEN)
60274408Smdodd				goto dropanyway;
60374408Smdodd			l->llc_window = 0;
60474408Smdodd			l->llc_fid = 9;
60574408Smdodd			l->llc_class = 1;
60674408Smdodd			l->llc_dsap = l->llc_ssap = 0;
60774408Smdodd			/* Fall through to */
60874408Smdodd		case LLC_TEST:
60974408Smdodd		case LLC_TEST_P:
61074408Smdodd		{
61174408Smdodd			struct sockaddr sa;
612112296Smdodd			struct arpcom *ac;
61374408Smdodd			struct iso88025_sockaddr_data *th2;
61474408Smdodd			int i;
615112296Smdodd			u_char c;
61644165Sjulian
617112308Smdodd			ac = IFP2AC(ifp);
618112296Smdodd			c = l->llc_dsap;
619112296Smdodd
62074408Smdodd			if (th->iso88025_shost[0] & TR_RII) { /* XXX */
62174408Smdodd				printf("iso88025_input: dropping source routed LLC_TEST\n");
622112289Smdodd				goto dropanyway;
62374408Smdodd			}
62474408Smdodd			l->llc_dsap = l->llc_ssap;
62574408Smdodd			l->llc_ssap = c;
62674408Smdodd			if (m->m_flags & (M_BCAST | M_MCAST))
62774408Smdodd				bcopy((caddr_t)ac->ac_enaddr,
628112280Smdodd				      (caddr_t)th->iso88025_dhost,
62974408Smdodd					ISO88025_ADDR_LEN);
63074408Smdodd			sa.sa_family = AF_UNSPEC;
63174408Smdodd			sa.sa_len = sizeof(sa);
63274408Smdodd			th2 = (struct iso88025_sockaddr_data *)sa.sa_data;
63374408Smdodd			for (i = 0; i < ISO88025_ADDR_LEN; i++) {
63474408Smdodd				th2->ether_shost[i] = c = th->iso88025_dhost[i];
63574408Smdodd				th2->ether_dhost[i] = th->iso88025_dhost[i] =
63674408Smdodd					th->iso88025_shost[i];
63774408Smdodd				th->iso88025_shost[i] = c;
63874408Smdodd			}
63974408Smdodd			th2->ac = TR_AC;
64074408Smdodd			th2->fc = TR_LLC_FRAME;
64174408Smdodd			ifp->if_output(ifp, m, &sa, NULL);
64274408Smdodd			return;
64374408Smdodd		}
64474408Smdodd		default:
64574408Smdodd			printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control);
646112289Smdodd			ifp->if_noproto++;
647112289Smdodd			goto dropanyway;
648112294Smdodd			break;
64974408Smdodd		}
65074408Smdodd		break;
651112296Smdodd#endif	/* ISO */
65244165Sjulian	default:
65374408Smdodd		printf("iso88025_input: unknown dsap 0x%x\n", l->llc_dsap);
65474408Smdodd		ifp->if_noproto++;
655112289Smdodd		goto dropanyway;
656112294Smdodd		break;
65744165Sjulian	}
658112274Smdodd
659111888Sjlemon	netisr_dispatch(isr, m);
660112274Smdodd	return;
661112289Smdodd
662112289Smdodddropanyway:
663112289Smdodd	ifp->if_iqdrops++;
664112289Smdodd	if (m)
665112289Smdodd		m_freem(m);
666112289Smdodd	return;
66744165Sjulian}
668112269Smdodd
669112273Smdoddstatic int
670112273Smdoddiso88025_resolvemulti (ifp, llsa, sa)
671112273Smdodd	struct ifnet *ifp;
672112273Smdodd	struct sockaddr **llsa;
673112273Smdodd	struct sockaddr *sa;
674112273Smdodd{
675112273Smdodd	struct sockaddr_dl *sdl;
676112273Smdodd	struct sockaddr_in *sin;
677112273Smdodd#ifdef INET6
678112273Smdodd	struct sockaddr_in6 *sin6;
679112273Smdodd#endif
680112273Smdodd	u_char *e_addr;
681112273Smdodd
682112273Smdodd	switch(sa->sa_family) {
683112273Smdodd	case AF_LINK:
684112273Smdodd		/*
685112273Smdodd		 * No mapping needed. Just check that it's a valid MC address.
686112273Smdodd		 */
687112273Smdodd		sdl = (struct sockaddr_dl *)sa;
688112273Smdodd		e_addr = LLADDR(sdl);
689112273Smdodd		if ((e_addr[0] & 1) != 1) {
690112273Smdodd			return (EADDRNOTAVAIL);
691112273Smdodd		}
692112273Smdodd		*llsa = 0;
693112273Smdodd		return (0);
694112273Smdodd
695112273Smdodd#ifdef INET
696112273Smdodd	case AF_INET:
697112273Smdodd		sin = (struct sockaddr_in *)sa;
698112273Smdodd		if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
699112273Smdodd			return (EADDRNOTAVAIL);
700112273Smdodd		}
701112273Smdodd		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
702112273Smdodd		       M_WAITOK|M_ZERO);
703112273Smdodd		sdl->sdl_len = sizeof *sdl;
704112273Smdodd		sdl->sdl_family = AF_LINK;
705112273Smdodd		sdl->sdl_index = ifp->if_index;
706112273Smdodd		sdl->sdl_type = IFT_ISO88025;
707112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
708112273Smdodd		e_addr = LLADDR(sdl);
709112273Smdodd		ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr);
710112273Smdodd		*llsa = (struct sockaddr *)sdl;
711112273Smdodd		return (0);
712112273Smdodd#endif
713112273Smdodd#ifdef INET6
714112273Smdodd	case AF_INET6:
715112273Smdodd		sin6 = (struct sockaddr_in6 *)sa;
716112273Smdodd		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
717112273Smdodd			/*
718112273Smdodd			 * An IP6 address of 0 means listen to all
719112273Smdodd			 * of the Ethernet multicast address used for IP6.
720112273Smdodd			 * (This is used for multicast routers.)
721112273Smdodd			 */
722112273Smdodd			ifp->if_flags |= IFF_ALLMULTI;
723112273Smdodd			*llsa = 0;
724112273Smdodd			return (0);
725112273Smdodd		}
726112273Smdodd		if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
727112273Smdodd			return (EADDRNOTAVAIL);
728112273Smdodd		}
729112273Smdodd		MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR,
730112273Smdodd		       M_WAITOK|M_ZERO);
731112273Smdodd		sdl->sdl_len = sizeof *sdl;
732112273Smdodd		sdl->sdl_family = AF_LINK;
733112273Smdodd		sdl->sdl_index = ifp->if_index;
734112273Smdodd		sdl->sdl_type = IFT_ISO88025;
735112273Smdodd		sdl->sdl_alen = ISO88025_ADDR_LEN;
736112273Smdodd		e_addr = LLADDR(sdl);
737112273Smdodd		ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr);
738112273Smdodd		*llsa = (struct sockaddr *)sdl;
739112273Smdodd		return (0);
740112273Smdodd#endif
741112273Smdodd
742112273Smdodd	default:
743112273Smdodd		/*
744112273Smdodd		 * Well, the text isn't quite right, but it's the name
745112273Smdodd		 * that counts...
746112273Smdodd		 */
747112273Smdodd		return (EAFNOSUPPORT);
748112273Smdodd	}
749112273Smdodd
750112273Smdodd	return (0);
751112273Smdodd}
752112273Smdodd
753112269Smdoddstatic moduledata_t iso88025_mod = {
754112269Smdodd	"iso88025",
755112269Smdodd	NULL,
756112269Smdodd	0
757112269Smdodd};
758112269Smdodd
759112269SmdoddDECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);
760112269SmdoddMODULE_VERSION(iso88025, 1);
761