if_iso88025subr.c revision 50477
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 50477 1999-08-28 01:08:13Z peter $
3444165Sjulian *
3544165Sjulian */
3644165Sjulian
3744165Sjulian/*
3844165Sjulian *
3944165Sjulian * General ISO 802.5 (Token Ring) support routines
4044165Sjulian *
4144165Sjulian */
4244165Sjulian
4344165Sjulian#include "opt_inet.h"
4444165Sjulian
4544165Sjulian#include <sys/param.h>
4644165Sjulian#include <sys/systm.h>
4744165Sjulian#include <sys/kernel.h>
4844165Sjulian#include <sys/malloc.h>
4944165Sjulian#include <sys/mbuf.h>
5044165Sjulian#include <sys/socket.h>
5144165Sjulian#include <sys/sockio.h>
5244165Sjulian#include <sys/sysctl.h>
5344165Sjulian
5444165Sjulian#include <net/if.h>
5544165Sjulian#include <net/netisr.h>
5644165Sjulian#include <net/route.h>
5744165Sjulian#include <net/if_llc.h>
5844165Sjulian#include <net/if_dl.h>
5944165Sjulian#include <net/if_types.h>
6044165Sjulian
6144165Sjulian#include <net/if_arp.h>
6244165Sjulian
6344165Sjulian#include <net/iso88025.h>
6444165Sjulian
6544165Sjulian#ifdef INET
6644165Sjulian#include <netinet/in.h>
6744165Sjulian#include <netinet/in_var.h>
6844165Sjulian#include <netinet/if_ether.h>
6944165Sjulian#endif
7044165Sjulian
7148645Sdes#if NBPF > 0
7244165Sjulian#include <net/bpf.h>
7344165Sjulian#include <net/bpfdesc.h>
7444165Sjulian#endif
7544165Sjulian
7644165Sjulian#include <machine/clock.h>
7744165Sjulian#include <machine/md_var.h>
7844165Sjulian
7944165Sjulian#include <i386/isa/isa_device.h>
8044165Sjulian
8144165Sjulian#include <vm/vm.h>
8244165Sjulian#include <vm/vm_param.h>
8344165Sjulian#include <vm/pmap.h>
8444165Sjulian
8544165Sjulian#include <sys/kernel.h>
8644165Sjulian#include <net/iso88025.h>
8744165Sjulian
8844165Sjulianvoid
8944165Sjulianiso88025_ifattach(ifp)
9044165Sjulian    register struct ifnet *ifp;
9144165Sjulian{
9244165Sjulian    register struct ifaddr *ifa = NULL;
9344165Sjulian    register struct sockaddr_dl *sdl;
9444165Sjulian
9544165Sjulian    ifp->if_type = IFT_ISO88025;
9644165Sjulian    ifp->if_addrlen = 6;
9744165Sjulian    ifp->if_hdrlen=18;
9844165Sjulian    if (ifp->if_baudrate == 0)
9944165Sjulian        ifp->if_baudrate = 16000000; /* 1, 4, or 16Mbit default? */
10044165Sjulian    if (ifp->if_mtu == 0)
10144165Sjulian        ifp->if_mtu = ISO88025_DEFAULT_MTU;
10244165Sjulian
10344165Sjulian        ifa = ifnet_addrs[ifp->if_index - 1];
10444165Sjulian        if (ifa == 0) {
10544165Sjulian                printf("iso88025_ifattach: no lladdr!\n");
10644165Sjulian                return;
10744165Sjulian        }
10844165Sjulian        sdl = (struct sockaddr_dl *)ifa->ifa_addr;
10944165Sjulian        sdl->sdl_type = IFT_ISO88025;
11044165Sjulian        sdl->sdl_alen = ifp->if_addrlen;
11144165Sjulian        bcopy(((struct arpcom *)ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen);
11244165Sjulian}
11344165Sjulian
11444165Sjulianint
11544165Sjulianiso88025_ioctl(struct ifnet *ifp, int command, caddr_t data)
11644165Sjulian{
11744165Sjulian        struct ifaddr *ifa = (struct ifaddr *) data;
11844165Sjulian        struct ifreq *ifr = (struct ifreq *) data;
11944165Sjulian        int error = 0;
12044165Sjulian
12144165Sjulian        switch (command) {
12244165Sjulian        case SIOCSIFADDR:
12344165Sjulian                ifp->if_flags |= IFF_UP;
12444165Sjulian
12544165Sjulian                switch (ifa->ifa_addr->sa_family) {
12644165Sjulian#ifdef INET
12744165Sjulian                case AF_INET:
12844165Sjulian                        ifp->if_init(ifp->if_softc);    /* before arpwhohas */
12944165Sjulian                        arp_ifinit((struct arpcom *)ifp, ifa);
13044165Sjulian                        break;
13144165Sjulian#endif
13244165Sjulian                default:
13344165Sjulian                        ifp->if_init(ifp->if_softc);
13444165Sjulian                        break;
13544165Sjulian                }
13644165Sjulian                break;
13744165Sjulian
13844165Sjulian        case SIOCGIFADDR:
13944165Sjulian                {
14044165Sjulian                        struct sockaddr *sa;
14144165Sjulian
14244165Sjulian                        sa = (struct sockaddr *) & ifr->ifr_data;
14344165Sjulian                        bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr,
14444165Sjulian                              (caddr_t) sa->sa_data, ISO88025_ADDR_LEN);
14544165Sjulian                }
14644165Sjulian                break;
14744165Sjulian
14844165Sjulian        case SIOCSIFMTU:
14944165Sjulian                /*
15044165Sjulian                 * Set the interface MTU.
15144165Sjulian                 */
15244165Sjulian                if (ifr->ifr_mtu > ISO88025MTU) {
15344165Sjulian                        error = EINVAL;
15444165Sjulian                } else {
15544165Sjulian                        ifp->if_mtu = ifr->ifr_mtu;
15644165Sjulian                }
15744165Sjulian                break;
15844165Sjulian        }
15944165Sjulian        return (error);
16044165Sjulian}
16144165Sjulian
16244165Sjulian/*
16344165Sjulian * ISO88025 encapsulation
16444165Sjulian */
16544165Sjulianint
16644165Sjulianiso88025_output(ifp, m0, dst, rt0)
16744165Sjulian	register struct ifnet *ifp;
16844165Sjulian	struct mbuf *m0;
16944165Sjulian	struct sockaddr *dst;
17044165Sjulian	struct rtentry *rt0;
17144165Sjulian{
17244165Sjulian	register struct iso88025_header *th;
17344627Sjulian	struct iso88025_header gen_th;
17444627Sjulian	register struct iso88025_sockaddr_data *sd = (struct iso88025_sockaddr_data *)dst->sa_data;
17544165Sjulian        register struct llc *l;
17644627Sjulian	register struct sockaddr_dl *sdl = NULL;
17744627Sjulian        int s, error = 0, rif_len = 0;
17844165Sjulian 	u_char edst[6];
17944165Sjulian	register struct mbuf *m = m0;
18044165Sjulian	register struct rtentry *rt;
18144627Sjulian	int len = m->m_pkthdr.len, loop_copy = 0;
18244165Sjulian	struct arpcom *ac = (struct arpcom *)ifp;
18344165Sjulian
18444165Sjulian	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING))
18544165Sjulian		senderr(ENETDOWN);
18644165Sjulian	rt = rt0;
18744165Sjulian	if (rt) {
18844165Sjulian		if ((rt->rt_flags & RTF_UP) == 0) {
18944165Sjulian			rt0 = rt = rtalloc1(dst, 1, 0UL);
19044165Sjulian			if (rt0)
19144165Sjulian				rt->rt_refcnt--;
19244165Sjulian			else
19344165Sjulian				senderr(EHOSTUNREACH);
19444165Sjulian		}
19544165Sjulian		if (rt->rt_flags & RTF_GATEWAY) {
19644165Sjulian			if (rt->rt_gwroute == 0)
19744165Sjulian				goto lookup;
19844165Sjulian			if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
19944165Sjulian				rtfree(rt); rt = rt0;
20044165Sjulian			lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1,
20144165Sjulian							  0UL);
20244165Sjulian				if ((rt = rt->rt_gwroute) == 0)
20344165Sjulian					senderr(EHOSTUNREACH);
20444165Sjulian			}
20544165Sjulian		}
20644165Sjulian		if (rt->rt_flags & RTF_REJECT)
20744165Sjulian			if (rt->rt_rmx.rmx_expire == 0 ||
20844165Sjulian			    time_second < rt->rt_rmx.rmx_expire)
20944165Sjulian				senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
21044165Sjulian	}
21144627Sjulian
21244627Sjulian	/* Calculate routing info length based on arp table entry */
21344627Sjulian	if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway))
21444627Sjulian		if (sdl->sdl_rcf != NULL)
21544627Sjulian			rif_len = (ntohs(sdl->sdl_rcf) & 0x1f00) >> 8;
21644627Sjulian
21744627Sjulian	/* Generate a generic 802.5 header for the packet */
21844627Sjulian	gen_th.ac = 0x10;
21944627Sjulian	gen_th.fc = 0x40;
22044627Sjulian	memcpy(gen_th.iso88025_shost, ac->ac_enaddr, sizeof(ac->ac_enaddr));
22144627Sjulian	if (rif_len) {
22244627Sjulian		gen_th.iso88025_shost[0] |= 0x80;
22344627Sjulian		if (rif_len > 2) {
22444627Sjulian			gen_th.rcf = sdl->sdl_rcf;
22544627Sjulian			memcpy(gen_th.rseg, sdl->sdl_route, rif_len - 2);
22644627Sjulian		}
22744627Sjulian	}
22844627Sjulian
22944627Sjulian
23044165Sjulian	switch (dst->sa_family) {
23144165Sjulian#ifdef INET
23244165Sjulian	case AF_INET:
23344165Sjulian		if (!arpresolve(ac, rt, m, dst, edst, rt0))
23444165Sjulian			return (0);	/* if not yet resolved */
23544627Sjulian		/* Add LLC and SNAP headers */
23644627Sjulian		M_PREPEND(m, 8, M_DONTWAIT)
23744627Sjulian		if (m == 0)
23844627Sjulian			senderr(ENOBUFS);
23944627Sjulian		l = mtod(m, struct llc *);
24044627Sjulian	        l->llc_un.type_snap.ether_type = htons(ETHERTYPE_IP);
24144627Sjulian	        l->llc_dsap = 0xaa;
24244627Sjulian		l->llc_ssap = 0xaa;
24344627Sjulian		l->llc_un.type_snap.control = 0x3;
24444627Sjulian		l->llc_un.type_snap.org_code[0] = 0x0;
24544627Sjulian		l->llc_un.type_snap.org_code[1] = 0x0;
24644627Sjulian		l->llc_un.type_snap.org_code[2] = 0x0;
24744627Sjulian		memcpy(gen_th.iso88025_dhost, edst, sizeof(edst));
24844165Sjulian		break;
24944165Sjulian#endif
25044165Sjulian
25144165Sjulian	case AF_UNSPEC:
25244627Sjulian		/*
25344627Sjulian		 * For AF_UNSPEC sockaddr.sa_data must contain all of the
25444627Sjulian		 * mac information needed to send the packet.  This allows
25544627Sjulian		 * full mac, llc, and source routing function to be controlled.
25644627Sjulian		 * llc and source routing information must already be in the
25744627Sjulian		 * mbuf provided, ac/fc are set in sa_data.  sockaddr.sa_data
25844627Sjulian		 * should be a iso88025_sockaddr_data structure see iso88025.h
25944627Sjulian		 */
26044165Sjulian                loop_copy = -1;
26144627Sjulian		sd = (struct iso88025_sockaddr_data *)dst->sa_data;
26244627Sjulian		gen_th.ac = sd->ac;
26344627Sjulian		gen_th.fc = sd->fc;
26444627Sjulian		memcpy(gen_th.iso88025_dhost, sd->ether_dhost, sizeof(sd->ether_dhost));
26544627Sjulian		memcpy(gen_th.iso88025_shost, sd->ether_shost, sizeof(sd->ether_shost));
26644627Sjulian		rif_len = 0;
26744165Sjulian		break;
26844165Sjulian
26944165Sjulian	default:
27044165Sjulian		printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit,
27144165Sjulian			dst->sa_family);
27244165Sjulian		senderr(EAFNOSUPPORT);
27344165Sjulian	}
27444165Sjulian
27544165Sjulian	/*
27644165Sjulian	 * Add local net header.  If no space in first mbuf,
27744165Sjulian	 * allocate another.
27844165Sjulian	 */
27944627Sjulian
28044627Sjulian	M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT);
28144165Sjulian	if (m == 0)
28244165Sjulian		senderr(ENOBUFS);
28344627Sjulian
28444627Sjulian	/* Copy as much of the generic header as is needed into the mbuf */
28544165Sjulian	th = mtod(m, struct iso88025_header *);
28644627Sjulian	memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len);
28744627Sjulian
28844165Sjulian        /*
28944165Sjulian         * If a simplex interface, and the packet is being sent to our
29044165Sjulian         * Ethernet address or a broadcast address, loopback a copy.
29144165Sjulian         * XXX To make a simplex device behave exactly like a duplex
29244165Sjulian         * device, we should copy in the case of sending to our own
29344165Sjulian         * ethernet address (thus letting the original actually appear
29444165Sjulian         * on the wire). However, we don't do that here for security
29544165Sjulian         * reasons and compatibility with the original behavior.
29644165Sjulian         */
29744165Sjulian        if ((ifp->if_flags & IFF_SIMPLEX) &&
29844165Sjulian           (loop_copy != -1)) {
29944165Sjulian                if ((m->m_flags & M_BCAST) || (loop_copy > 0)) {
30044165Sjulian                        struct mbuf *n = m_copy(m, 0, (int)M_COPYALL);
30144165Sjulian                        /*printf("iso88025_output: if_simloop broadcast.\n");*/
30244165Sjulian                        (void) if_simloop(ifp, n, dst, ISO88025_HDR_LEN);
30344165Sjulian                } else if (bcmp(th->iso88025_dhost,
30444165Sjulian                    th->iso88025_shost, ETHER_ADDR_LEN) == 0) {
30544165Sjulian                        /*printf("iso88025_output: if_simloop to ourselves.\n");*/
30644165Sjulian                        (void) if_simloop(ifp, m, dst, ISO88025_HDR_LEN);
30744165Sjulian                        return(0);      /* XXX */
30844165Sjulian                }
30944165Sjulian        }
31044165Sjulian
31144165Sjulian        s = splimp();
31244165Sjulian	/*
31344165Sjulian	 * Queue message on interface, and start output if interface
31444165Sjulian	 * not yet active.
31544165Sjulian	 */
31644165Sjulian	if (IF_QFULL(&ifp->if_snd)) {
31744165Sjulian            printf("iso88025_output: packet dropped QFULL.\n");
31844165Sjulian		IF_DROP(&ifp->if_snd);
31944165Sjulian		splx(s);
32044165Sjulian		senderr(ENOBUFS);
32144165Sjulian	}
32244165Sjulian	IF_ENQUEUE(&ifp->if_snd, m);
32344165Sjulian        /*printf("iso88025_output: packet queued.\n");*/
32444165Sjulian        if ((ifp->if_flags & IFF_OACTIVE) == 0)
32544165Sjulian		(*ifp->if_start)(ifp);
32644165Sjulian	splx(s);
32744165Sjulian	ifp->if_obytes += len + ISO88025_HDR_LEN + 8;
32844165Sjulian	if (m->m_flags & M_MCAST)
32944165Sjulian		ifp->if_omcasts++;
33044165Sjulian	return (error);
33144165Sjulian
33244165Sjulianbad:
33344165Sjulian	if (m)
33444165Sjulian		m_freem(m);
33544627Sjulian        /*printf("iso88025_output: something went wrong, bailing to bad.\n");*/
33644165Sjulian	return (error);
33744165Sjulian}
33844165Sjulian
33944165Sjulian/*
34044165Sjulian * ISO 88025 de-encapsulation
34144165Sjulian */
34244165Sjulianvoid
34344165Sjulianiso88025_input(ifp, th, m)
34444165Sjulian	struct ifnet *ifp;
34544165Sjulian	register struct iso88025_header *th;
34644165Sjulian	struct mbuf *m;
34744165Sjulian{
34844165Sjulian	register struct ifqueue *inq;
34944165Sjulian	u_short ether_type;
35044165Sjulian	int s;
35144165Sjulian	register struct llc *l = mtod(m, struct llc *);
35244165Sjulian
35344165Sjulian        /*printf("iso88025_input: entered.\n");*/
35444165Sjulian
35544165Sjulian        /*m->m_pkthdr.len = m->m_len = m->m_len - 8;*/ /* Length of LLC header in our case */
35644165Sjulian        m->m_pkthdr.len -= 8;
35744165Sjulian        m->m_len -= 8;
35844165Sjulian        m->m_data += 8; /* Length of LLC header in our case */
35944165Sjulian
36044165Sjulian	if ((ifp->if_flags & IFF_UP) == 0) {
36144165Sjulian		m_freem(m);
36244165Sjulian		return;
36344165Sjulian	}
36444165Sjulian	ifp->if_ibytes += m->m_pkthdr.len + sizeof (*th);
36544165Sjulian	if (th->iso88025_dhost[0] & 1) {
36644165Sjulian		if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)th->iso88025_dhost,
36744165Sjulian			 sizeof(etherbroadcastaddr)) == 0)
36844165Sjulian			m->m_flags |= M_BCAST;
36944165Sjulian		else
37044165Sjulian			m->m_flags |= M_MCAST;
37144165Sjulian	}
37244165Sjulian	if (m->m_flags & (M_BCAST|M_MCAST))
37344165Sjulian		ifp->if_imcasts++;
37444165Sjulian
37544165Sjulian	ether_type = ntohs(l->llc_un.type_snap.ether_type);
37644165Sjulian
37744165Sjulian        /*printf("iso88025_input: source %6D dest %6D ethertype %x\n", th->iso88025_shost, ":", th->iso88025_dhost, ":", ether_type);*/
37844165Sjulian
37944165Sjulian	switch (ether_type) {
38044165Sjulian#ifdef INET
38144165Sjulian	case ETHERTYPE_IP:
38244165Sjulian            /*printf("iso88025_input: IP Packet\n");*/
38344627Sjulian		th->iso88025_shost[0] &= ~(0x80); /* Turn off source route bit XXX */
38444165Sjulian		if (ipflow_fastforward(m))
38544165Sjulian			return;
38644165Sjulian		schednetisr(NETISR_IP);
38744165Sjulian		inq = &ipintrq;
38844165Sjulian		break;
38944165Sjulian
39044165Sjulian	case ETHERTYPE_ARP:
39144165Sjulian            /*printf("iso88025_input: ARP Packet\n");*/
39244165Sjulian		schednetisr(NETISR_ARP);
39344165Sjulian		inq = &arpintrq;
39444165Sjulian                break;
39544165Sjulian#endif
39644165Sjulian	default:
39744165Sjulian	    m_freem(m);
39844165Sjulian	    return;
39944165Sjulian	}
40044165Sjulian
40144165Sjulian	s = splimp();
40244165Sjulian	if (IF_QFULL(inq)) {
40344165Sjulian		IF_DROP(inq);
40444165Sjulian		m_freem(m);
40544165Sjulian                printf("iso88025_input: Packet dropped (Queue full).\n");
40644165Sjulian	} else
40744165Sjulian		IF_ENQUEUE(inq, m);
40844165Sjulian                /*printf("iso88025_input: Packet queued.\n");*/
40944165Sjulian	splx(s);
41044165Sjulian}
411