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