if_iso88025subr.c revision 130549
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 130549 2004-06-15 23:57:42Z mlaier $ 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 82126907Srwatsonstatic const 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 senderr(e) do { error = (e); goto bad; } while (0) 8974408Smdodd 90112297Smdodd/* 91112297Smdodd * Perform common duties while attaching to interface list 92112297Smdodd */ 9344165Sjulianvoid 94112297Smdoddiso88025_ifattach(struct ifnet *ifp, int bpf) 9544165Sjulian{ 96112296Smdodd struct ifaddr *ifa; 97111774Smdodd struct sockaddr_dl *sdl; 9844165Sjulian 99112296Smdodd ifa = NULL; 100112296Smdodd 10144165Sjulian ifp->if_type = IFT_ISO88025; 10258313Slile ifp->if_addrlen = ISO88025_ADDR_LEN; 10358313Slile ifp->if_hdrlen = ISO88025_HDR_LEN; 104112297Smdodd 105112297Smdodd if_attach(ifp); /* Must be called before additional assignments */ 106112297Smdodd 107112297Smdodd ifp->if_output = iso88025_output; 108112297Smdodd ifp->if_input = iso88025_input; 109112297Smdodd ifp->if_resolvemulti = iso88025_resolvemulti; 110112297Smdodd ifp->if_broadcastaddr = iso88025_broadcastaddr; 111112297Smdodd 11244165Sjulian if (ifp->if_baudrate == 0) 11358313Slile ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */ 11444165Sjulian if (ifp->if_mtu == 0) 11544165Sjulian ifp->if_mtu = ISO88025_DEFAULT_MTU; 11644165Sjulian 117112272Smdodd ifa = ifaddr_byindex(ifp->if_index); 118112272Smdodd if (ifa == 0) { 119112298Smdodd if_printf(ifp, "%s() no lladdr!\n", __func__); 120112298Smdodd return; 121112272Smdodd } 122112297Smdodd 123112272Smdodd sdl = (struct sockaddr_dl *)ifa->ifa_addr; 124112272Smdodd sdl->sdl_type = IFT_ISO88025; 125112272Smdodd sdl->sdl_alen = ifp->if_addrlen; 126112272Smdodd bcopy(IFP2AC(ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); 127112297Smdodd 128112297Smdodd if (bpf) 129112297Smdodd bpfattach(ifp, DLT_IEEE802, ISO88025_HDR_LEN); 130112297Smdodd 131112297Smdodd return; 13244165Sjulian} 13344165Sjulian 13474408Smdodd/* 13574408Smdodd * Perform common duties while detaching a Token Ring interface 13674408Smdodd */ 13774408Smdoddvoid 13874408Smdoddiso88025_ifdetach(ifp, bpf) 13974408Smdodd struct ifnet *ifp; 14074408Smdodd int bpf; 14174408Smdodd{ 142112274Smdodd 14374408Smdodd if (bpf) 14474408Smdodd bpfdetach(ifp); 145112274Smdodd 14674408Smdodd if_detach(ifp); 147112274Smdodd 148112274Smdodd return; 14974408Smdodd} 15074408Smdodd 15144165Sjulianint 15244165Sjulianiso88025_ioctl(struct ifnet *ifp, int command, caddr_t data) 15344165Sjulian{ 154112274Smdodd struct ifaddr *ifa; 155112274Smdodd struct ifreq *ifr; 156112274Smdodd int error; 15744165Sjulian 158112274Smdodd ifa = (struct ifaddr *) data; 159112274Smdodd ifr = (struct ifreq *) data; 160112274Smdodd error = 0; 161112274Smdodd 16244165Sjulian switch (command) { 16344165Sjulian case SIOCSIFADDR: 16444165Sjulian ifp->if_flags |= IFF_UP; 16544165Sjulian 16644165Sjulian switch (ifa->ifa_addr->sa_family) { 16744165Sjulian#ifdef INET 16844165Sjulian case AF_INET: 16944165Sjulian ifp->if_init(ifp->if_softc); /* before arpwhohas */ 17084931Sfjoe arp_ifinit(ifp, ifa); 17144165Sjulian break; 17274408Smdodd#endif /* INET */ 17374408Smdodd#ifdef IPX 17474408Smdodd /* 17574408Smdodd * XXX - This code is probably wrong 17674408Smdodd */ 177120048Smdodd case AF_IPX: { 178120048Smdodd struct ipx_addr *ina; 179120048Smdodd struct arpcom *ac; 18074408Smdodd 181120048Smdodd ina = &(IA_SIPX(ifa)->sipx_addr); 182120048Smdodd ac = IFP2AC(ifp); 18374408Smdodd 184120048Smdodd if (ipx_nullhost(*ina)) 185120048Smdodd ina->x_host = *(union ipx_host *) 186120048Smdodd ac->ac_enaddr; 187120048Smdodd else 188120048Smdodd bcopy((caddr_t) ina->x_host.c_host, 189120048Smdodd (caddr_t) ac->ac_enaddr, 190120048Smdodd ISO88025_ADDR_LEN); 191120048Smdodd 192120048Smdodd /* 193120048Smdodd * Set new address 194120048Smdodd */ 195120048Smdodd ifp->if_init(ifp->if_softc); 196120048Smdodd } 197120048Smdodd break; 19874408Smdodd#endif /* IPX */ 19944165Sjulian default: 20044165Sjulian ifp->if_init(ifp->if_softc); 20144165Sjulian break; 20244165Sjulian } 20344165Sjulian break; 20444165Sjulian 205120047Smdodd case SIOCGIFADDR: { 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; 24944165Sjulian 250112285Smdodd#ifdef MAC 251112285Smdodd error = mac_check_ifnet_transmit(ifp, m); 252112285Smdodd if (error) 253112285Smdodd senderr(error); 254112285Smdodd#endif 255112285Smdodd 256112308Smdodd if (ifp->if_flags & IFF_MONITOR) 257112308Smdodd senderr(ENETDOWN); 25844165Sjulian if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_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 ? */ 264111767Smdodd error = rt_check(&rt, &rt0, dst); 265112274Smdodd if (error) 266111767Smdodd goto bad; 26744627Sjulian 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; 275128615Sluigi (void)memcpy((caddr_t)gen_th.iso88025_shost, IFP2AC(ifp)->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: 290128636Sluigi error = arpresolve(ifp, rt0, m, dst, edst); 291128636Sluigi if (error) 292128636Sluigi return (error == EWOULDBLOCK ? 0 : error); 29374408Smdodd snap_type = ETHERTYPE_IP; 29474408Smdodd break; 295126951Smdodd case AF_ARP: 296126951Smdodd { 297126951Smdodd struct arphdr *ah; 298126951Smdodd ah = mtod(m, struct arphdr *); 299126951Smdodd ah->ar_hrd = htons(ARPHRD_IEEE802); 300126951Smdodd 301126951Smdodd loop_copy = -1; /* if this is for us, don't do it */ 302126951Smdodd 303126951Smdodd switch(ntohs(ah->ar_op)) { 304126951Smdodd case ARPOP_REVREQUEST: 305126951Smdodd case ARPOP_REVREPLY: 306126951Smdodd snap_type = ETHERTYPE_REVARP; 307126951Smdodd break; 308126951Smdodd case ARPOP_REQUEST: 309126951Smdodd case ARPOP_REPLY: 310126951Smdodd default: 311126951Smdodd snap_type = ETHERTYPE_ARP; 312126951Smdodd break; 313126951Smdodd } 314126951Smdodd 315126951Smdodd if (m->m_flags & M_BCAST) 316126951Smdodd bcopy(ifp->if_broadcastaddr, edst, ISO88025_ADDR_LEN); 317126951Smdodd else 318126951Smdodd bcopy(ar_tha(ah), edst, ISO88025_ADDR_LEN); 319126951Smdodd 320126951Smdodd } 321126951Smdodd break; 32274408Smdodd#endif /* INET */ 32374408Smdodd#ifdef INET6 32474408Smdodd case AF_INET6: 325128636Sluigi error = nd6_storelladdr(ifp, rt0, m, dst, (u_char *)edst); 326128636Sluigi if (error) 327128636Sluigi return (error); 32874408Smdodd snap_type = ETHERTYPE_IPV6; 32974408Smdodd break; 33074408Smdodd#endif /* INET6 */ 33174408Smdodd#ifdef IPX 33274408Smdodd case AF_IPX: 33374408Smdodd { 33474408Smdodd u_int8_t *cp; 33574408Smdodd 33674408Smdodd bcopy((caddr_t)&(satoipx_addr(dst).x_host), (caddr_t)edst, 337112278Smdodd ISO88025_ADDR_LEN); 33874408Smdodd 339111119Simp M_PREPEND(m, 3, M_TRYWAIT); 34044627Sjulian if (m == 0) 34144627Sjulian senderr(ENOBUFS); 34274408Smdodd m = m_pullup(m, 3); 34374408Smdodd if (m == 0) 34474408Smdodd senderr(ENOBUFS); 34574408Smdodd cp = mtod(m, u_int8_t *); 34674408Smdodd *cp++ = ETHERTYPE_IPX_8022; 34774408Smdodd *cp++ = ETHERTYPE_IPX_8022; 34874408Smdodd *cp++ = LLC_UI; 34974408Smdodd } 35074408Smdodd break; 35174408Smdodd#endif /* IPX */ 35244165Sjulian case AF_UNSPEC: 35374408Smdodd { 35474408Smdodd struct iso88025_sockaddr_data *sd; 35544627Sjulian /* 35644627Sjulian * For AF_UNSPEC sockaddr.sa_data must contain all of the 35744627Sjulian * mac information needed to send the packet. This allows 35844627Sjulian * full mac, llc, and source routing function to be controlled. 35944627Sjulian * llc and source routing information must already be in the 36044627Sjulian * mbuf provided, ac/fc are set in sa_data. sockaddr.sa_data 361108533Sschweikh * should be an iso88025_sockaddr_data structure see iso88025.h 36244627Sjulian */ 36344165Sjulian loop_copy = -1; 36444627Sjulian sd = (struct iso88025_sockaddr_data *)dst->sa_data; 36544627Sjulian gen_th.ac = sd->ac; 36644627Sjulian gen_th.fc = sd->fc; 36774408Smdodd (void)memcpy((caddr_t)edst, (caddr_t)sd->ether_dhost, 368112278Smdodd ISO88025_ADDR_LEN); 36974408Smdodd (void)memcpy((caddr_t)gen_th.iso88025_shost, 370112280Smdodd (caddr_t)sd->ether_shost, ISO88025_ADDR_LEN); 37144627Sjulian rif_len = 0; 37244165Sjulian break; 37374408Smdodd } 37444165Sjulian default: 375105598Sbrooks if_printf(ifp, "can't handle af%d\n", dst->sa_family); 37644165Sjulian senderr(EAFNOSUPPORT); 37774408Smdodd break; 37844165Sjulian } 37944165Sjulian 380112274Smdodd /* 381112274Smdodd * Add LLC header. 382112274Smdodd */ 38374408Smdodd if (snap_type != 0) { 38474408Smdodd struct llc *l; 385111790Smdodd M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT); 38674408Smdodd if (m == 0) 38774408Smdodd senderr(ENOBUFS); 38874408Smdodd l = mtod(m, struct llc *); 389112281Smdodd l->llc_control = LLC_UI; 39074408Smdodd l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP; 391112268Smdodd l->llc_snap.org_code[0] = 392112268Smdodd l->llc_snap.org_code[1] = 393112268Smdodd l->llc_snap.org_code[2] = 0; 394112274Smdodd l->llc_snap.ether_type = htons(snap_type); 39574408Smdodd } 39674408Smdodd 39744165Sjulian /* 39844165Sjulian * Add local net header. If no space in first mbuf, 39944165Sjulian * allocate another. 40044165Sjulian */ 401111119Simp M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT); 40244165Sjulian if (m == 0) 40344165Sjulian senderr(ENOBUFS); 404112274Smdodd th = mtod(m, struct iso88025_header *); 405112291Smdodd bcopy((caddr_t)edst, (caddr_t)&gen_th.iso88025_dhost, ISO88025_ADDR_LEN); 40644627Sjulian 40744627Sjulian /* Copy as much of the generic header as is needed into the mbuf */ 40844627Sjulian memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len); 40944627Sjulian 41044165Sjulian /* 41144165Sjulian * If a simplex interface, and the packet is being sent to our 41244165Sjulian * Ethernet address or a broadcast address, loopback a copy. 41344165Sjulian * XXX To make a simplex device behave exactly like a duplex 41444165Sjulian * device, we should copy in the case of sending to our own 41544165Sjulian * ethernet address (thus letting the original actually appear 41644165Sjulian * on the wire). However, we don't do that here for security 41744165Sjulian * reasons and compatibility with the original behavior. 41844165Sjulian */ 41974408Smdodd if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) { 42044165Sjulian if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { 42174408Smdodd struct mbuf *n; 42274408Smdodd n = m_copy(m, 0, (int)M_COPYALL); 423112279Smdodd (void) if_simloop(ifp, n, dst->sa_family, 42474408Smdodd ISO88025_HDR_LEN); 425112279Smdodd } else if (bcmp(th->iso88025_dhost, th->iso88025_shost, 42674408Smdodd ETHER_ADDR_LEN) == 0) { 427112279Smdodd (void) if_simloop(ifp, m, dst->sa_family, 428112279Smdodd ISO88025_HDR_LEN); 429112279Smdodd return(0); /* XXX */ 430112279Smdodd } 43144165Sjulian } 43244165Sjulian 433130549Smlaier IFQ_HANDOFF_ADJ(ifp, m, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN, error); 434130549Smlaier if (error) { 43569152Sjlemon printf("iso88025_output: packet dropped QFULL.\n"); 436130549Smlaier ifp->if_oerrors++; 43744165Sjulian } 43844165Sjulian return (error); 43944165Sjulian 44044165Sjulianbad: 441112296Smdodd ifp->if_oerrors++; 44244165Sjulian if (m) 44344165Sjulian m_freem(m); 44444165Sjulian return (error); 44544165Sjulian} 44644165Sjulian 44744165Sjulian/* 44844165Sjulian * ISO 88025 de-encapsulation 44944165Sjulian */ 45044165Sjulianvoid 451112299Smdoddiso88025_input(ifp, m) 45274408Smdodd struct ifnet *ifp; 45374408Smdodd struct mbuf *m; 45444165Sjulian{ 455112299Smdodd struct iso88025_header *th; 456112299Smdodd struct llc *l; 457111888Sjlemon int isr; 458112299Smdodd int mac_hdr_len; 45944165Sjulian 460112308Smdodd /* 461112308Smdodd * Do consistency checks to verify assumptions 462112308Smdodd * made by code past this point. 463112308Smdodd */ 464112308Smdodd if ((m->m_flags & M_PKTHDR) == 0) { 465112308Smdodd if_printf(ifp, "discard frame w/o packet header\n"); 466112308Smdodd ifp->if_ierrors++; 467112308Smdodd m_freem(m); 468112308Smdodd return; 469112308Smdodd } 470112308Smdodd if (m->m_pkthdr.rcvif == NULL) { 471112308Smdodd if_printf(ifp, "discard frame w/o interface pointer\n"); 472112308Smdodd ifp->if_ierrors++; 473112308Smdodd m_freem(m); 474112308Smdodd return; 475112308Smdodd } 476112308Smdodd 477112299Smdodd m = m_pullup(m, ISO88025_HDR_LEN); 478112299Smdodd if (m == NULL) { 479112299Smdodd ifp->if_ierrors++; 480112299Smdodd goto dropanyway; 481112299Smdodd } 482112299Smdodd th = mtod(m, struct iso88025_header *); 483112299Smdodd m->m_pkthdr.header = (void *)th; 484112299Smdodd 485112286Smdodd /* 486112286Smdodd * Discard packet if interface is not up. 487112286Smdodd */ 488112286Smdodd if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 489112286Smdodd goto dropanyway; 49044165Sjulian 491112308Smdodd /* 492112308Smdodd * Give bpf a chance at the packet. 493112308Smdodd */ 494112308Smdodd BPF_MTAP(ifp, m); 495112308Smdodd 496112308Smdodd /* 497112308Smdodd * Interface marked for monitoring; discard packet. 498112308Smdodd */ 499112308Smdodd if (ifp->if_flags & IFF_MONITOR) { 500112308Smdodd m_freem(m); 501112308Smdodd return; 502112308Smdodd } 503112308Smdodd 504112285Smdodd#ifdef MAC 505112285Smdodd mac_create_mbuf_from_ifnet(ifp, m); 506112285Smdodd#endif 507112285Smdodd 508112286Smdodd /* 509112286Smdodd * Update interface statistics. 510112286Smdodd */ 511112299Smdodd ifp->if_ibytes += m->m_pkthdr.len; 51274408Smdodd getmicrotime(&ifp->if_lastchange); 51358313Slile 514112280Smdodd /* 515112286Smdodd * Discard non local unicast packets when interface 516112286Smdodd * is in promiscuous mode. 517112286Smdodd */ 518112286Smdodd if ((ifp->if_flags & IFF_PROMISC) && 519112286Smdodd ((th->iso88025_dhost[0] & 1) == 0) && 520112286Smdodd (bcmp(IFP2AC(ifp)->ac_enaddr, (caddr_t) th->iso88025_dhost, 521112286Smdodd ISO88025_ADDR_LEN) != 0)) 522112286Smdodd goto dropanyway; 523112286Smdodd 524112286Smdodd /* 525112280Smdodd * Set mbuf flags for bcast/mcast. 526112280Smdodd */ 52744165Sjulian if (th->iso88025_dhost[0] & 1) { 528126907Srwatson if (bcmp(iso88025_broadcastaddr, th->iso88025_dhost, 529126907Srwatson ISO88025_ADDR_LEN) == 0) 53044165Sjulian m->m_flags |= M_BCAST; 53144165Sjulian else 53244165Sjulian m->m_flags |= M_MCAST; 53374408Smdodd ifp->if_imcasts++; 534112274Smdodd } 53544165Sjulian 536112299Smdodd mac_hdr_len = ISO88025_HDR_LEN; 537112299Smdodd /* Check for source routing info */ 538112299Smdodd if (th->iso88025_shost[0] & TR_RII) 539112299Smdodd mac_hdr_len += TR_RCF_RIFLEN(th->rcf); 540112299Smdodd 541112299Smdodd /* Strip off ISO88025 header. */ 542112299Smdodd m_adj(m, mac_hdr_len); 543112299Smdodd 544112299Smdodd m = m_pullup(m, LLC_SNAPFRAMELEN); 545112299Smdodd if (m == 0) { 546112299Smdodd ifp->if_ierrors++; 547112299Smdodd goto dropanyway; 548112299Smdodd } 54974408Smdodd l = mtod(m, struct llc *); 55044165Sjulian 55174408Smdodd switch (l->llc_dsap) { 55274408Smdodd#ifdef IPX 55374408Smdodd case ETHERTYPE_IPX_8022: /* Thanks a bunch Novell */ 55474408Smdodd if ((l->llc_control != LLC_UI) || 555112289Smdodd (l->llc_ssap != ETHERTYPE_IPX_8022)) { 556112289Smdodd ifp->if_noproto++; 55774408Smdodd goto dropanyway; 558112289Smdodd } 55974408Smdodd 56074408Smdodd th->iso88025_shost[0] &= ~(TR_RII); 56174408Smdodd m_adj(m, 3); 562111888Sjlemon isr = NETISR_IPX; 56374408Smdodd break; 56474408Smdodd#endif /* IPX */ 56574408Smdodd case LLC_SNAP_LSAP: { 56674408Smdodd u_int16_t type; 56774408Smdodd if ((l->llc_control != LLC_UI) || 568112289Smdodd (l->llc_ssap != LLC_SNAP_LSAP)) { 569112289Smdodd ifp->if_noproto++; 57074408Smdodd goto dropanyway; 571112289Smdodd } 57274408Smdodd 573112268Smdodd if (l->llc_snap.org_code[0] != 0 || 574112268Smdodd l->llc_snap.org_code[1] != 0 || 575112294Smdodd l->llc_snap.org_code[2] != 0) { 576112294Smdodd ifp->if_noproto++; 57774408Smdodd goto dropanyway; 578112294Smdodd } 57974408Smdodd 580112268Smdodd type = ntohs(l->llc_snap.ether_type); 581111790Smdodd m_adj(m, LLC_SNAPFRAMELEN); 58274408Smdodd switch (type) { 58344165Sjulian#ifdef INET 58474408Smdodd case ETHERTYPE_IP: 58574408Smdodd th->iso88025_shost[0] &= ~(TR_RII); 586122702Sandre if (ip_fastforward(m)) 58774408Smdodd return; 588111888Sjlemon isr = NETISR_IP; 58974408Smdodd break; 59074408Smdodd 59174408Smdodd case ETHERTYPE_ARP: 59278295Sjlemon if (ifp->if_flags & IFF_NOARP) 59378295Sjlemon goto dropanyway; 594111888Sjlemon isr = NETISR_ARP; 59574408Smdodd break; 59674408Smdodd#endif /* INET */ 59774408Smdodd#ifdef IPX_SNAP /* XXX: Not supported! */ 59874408Smdodd case ETHERTYPE_IPX: 59974408Smdodd th->iso88025_shost[0] &= ~(TR_RII); 600111888Sjlemon isr = NETISR_IPX; 60174408Smdodd break; 60274408Smdodd#endif /* IPX_SNAP */ 60374408Smdodd#ifdef INET6 60474408Smdodd case ETHERTYPE_IPV6: 60574408Smdodd th->iso88025_shost[0] &= ~(TR_RII); 606111888Sjlemon isr = NETISR_IPV6; 60774408Smdodd break; 60874408Smdodd#endif /* INET6 */ 60974408Smdodd default: 61074408Smdodd printf("iso88025_input: unexpected llc_snap ether_type 0x%02x\n", type); 611112289Smdodd ifp->if_noproto++; 612112289Smdodd goto dropanyway; 61374408Smdodd } 61444165Sjulian break; 61574408Smdodd } 616112296Smdodd#ifdef ISO 61774408Smdodd case LLC_ISO_LSAP: 61874408Smdodd switch (l->llc_control) { 61974408Smdodd case LLC_UI: 620112289Smdodd ifp->if_noproto++; 62174408Smdodd goto dropanyway; 62274408Smdodd break; 62374408Smdodd case LLC_XID: 62474408Smdodd case LLC_XID_P: 62574408Smdodd if(m->m_len < ISO88025_ADDR_LEN) 62674408Smdodd goto dropanyway; 62774408Smdodd l->llc_window = 0; 62874408Smdodd l->llc_fid = 9; 62974408Smdodd l->llc_class = 1; 63074408Smdodd l->llc_dsap = l->llc_ssap = 0; 63174408Smdodd /* Fall through to */ 63274408Smdodd case LLC_TEST: 63374408Smdodd case LLC_TEST_P: 63474408Smdodd { 63574408Smdodd struct sockaddr sa; 636112296Smdodd struct arpcom *ac; 63774408Smdodd struct iso88025_sockaddr_data *th2; 63874408Smdodd int i; 639112296Smdodd u_char c; 64044165Sjulian 641112308Smdodd ac = IFP2AC(ifp); 642112296Smdodd c = l->llc_dsap; 643112296Smdodd 64474408Smdodd if (th->iso88025_shost[0] & TR_RII) { /* XXX */ 64574408Smdodd printf("iso88025_input: dropping source routed LLC_TEST\n"); 646112289Smdodd goto dropanyway; 64774408Smdodd } 64874408Smdodd l->llc_dsap = l->llc_ssap; 64974408Smdodd l->llc_ssap = c; 65074408Smdodd if (m->m_flags & (M_BCAST | M_MCAST)) 65174408Smdodd bcopy((caddr_t)ac->ac_enaddr, 652112280Smdodd (caddr_t)th->iso88025_dhost, 65374408Smdodd ISO88025_ADDR_LEN); 65474408Smdodd sa.sa_family = AF_UNSPEC; 65574408Smdodd sa.sa_len = sizeof(sa); 65674408Smdodd th2 = (struct iso88025_sockaddr_data *)sa.sa_data; 65774408Smdodd for (i = 0; i < ISO88025_ADDR_LEN; i++) { 65874408Smdodd th2->ether_shost[i] = c = th->iso88025_dhost[i]; 65974408Smdodd th2->ether_dhost[i] = th->iso88025_dhost[i] = 66074408Smdodd th->iso88025_shost[i]; 66174408Smdodd th->iso88025_shost[i] = c; 66274408Smdodd } 66374408Smdodd th2->ac = TR_AC; 66474408Smdodd th2->fc = TR_LLC_FRAME; 66574408Smdodd ifp->if_output(ifp, m, &sa, NULL); 66674408Smdodd return; 66774408Smdodd } 66874408Smdodd default: 66974408Smdodd printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control); 670112289Smdodd ifp->if_noproto++; 671112289Smdodd goto dropanyway; 672112294Smdodd break; 67374408Smdodd } 67474408Smdodd break; 675112296Smdodd#endif /* ISO */ 67644165Sjulian default: 67774408Smdodd printf("iso88025_input: unknown dsap 0x%x\n", l->llc_dsap); 67874408Smdodd ifp->if_noproto++; 679112289Smdodd goto dropanyway; 680112294Smdodd break; 68144165Sjulian } 682112274Smdodd 683111888Sjlemon netisr_dispatch(isr, m); 684112274Smdodd return; 685112289Smdodd 686112289Smdodddropanyway: 687112289Smdodd ifp->if_iqdrops++; 688112289Smdodd if (m) 689112289Smdodd m_freem(m); 690112289Smdodd return; 69144165Sjulian} 692112269Smdodd 693112273Smdoddstatic int 694112273Smdoddiso88025_resolvemulti (ifp, llsa, sa) 695112273Smdodd struct ifnet *ifp; 696112273Smdodd struct sockaddr **llsa; 697112273Smdodd struct sockaddr *sa; 698112273Smdodd{ 699112273Smdodd struct sockaddr_dl *sdl; 700112273Smdodd struct sockaddr_in *sin; 701112273Smdodd#ifdef INET6 702112273Smdodd struct sockaddr_in6 *sin6; 703112273Smdodd#endif 704112273Smdodd u_char *e_addr; 705112273Smdodd 706112273Smdodd switch(sa->sa_family) { 707112273Smdodd case AF_LINK: 708112273Smdodd /* 709112273Smdodd * No mapping needed. Just check that it's a valid MC address. 710112273Smdodd */ 711112273Smdodd sdl = (struct sockaddr_dl *)sa; 712112273Smdodd e_addr = LLADDR(sdl); 713112273Smdodd if ((e_addr[0] & 1) != 1) { 714112273Smdodd return (EADDRNOTAVAIL); 715112273Smdodd } 716112273Smdodd *llsa = 0; 717112273Smdodd return (0); 718112273Smdodd 719112273Smdodd#ifdef INET 720112273Smdodd case AF_INET: 721112273Smdodd sin = (struct sockaddr_in *)sa; 722112273Smdodd if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 723112273Smdodd return (EADDRNOTAVAIL); 724112273Smdodd } 725112273Smdodd MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, 726112273Smdodd M_WAITOK|M_ZERO); 727112273Smdodd sdl->sdl_len = sizeof *sdl; 728112273Smdodd sdl->sdl_family = AF_LINK; 729112273Smdodd sdl->sdl_index = ifp->if_index; 730112273Smdodd sdl->sdl_type = IFT_ISO88025; 731112273Smdodd sdl->sdl_alen = ISO88025_ADDR_LEN; 732112273Smdodd e_addr = LLADDR(sdl); 733112273Smdodd ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr); 734112273Smdodd *llsa = (struct sockaddr *)sdl; 735112273Smdodd return (0); 736112273Smdodd#endif 737112273Smdodd#ifdef INET6 738112273Smdodd case AF_INET6: 739112273Smdodd sin6 = (struct sockaddr_in6 *)sa; 740112273Smdodd if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 741112273Smdodd /* 742112273Smdodd * An IP6 address of 0 means listen to all 743112273Smdodd * of the Ethernet multicast address used for IP6. 744112273Smdodd * (This is used for multicast routers.) 745112273Smdodd */ 746112273Smdodd ifp->if_flags |= IFF_ALLMULTI; 747112273Smdodd *llsa = 0; 748112273Smdodd return (0); 749112273Smdodd } 750112273Smdodd if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { 751112273Smdodd return (EADDRNOTAVAIL); 752112273Smdodd } 753112273Smdodd MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, 754112273Smdodd M_WAITOK|M_ZERO); 755112273Smdodd sdl->sdl_len = sizeof *sdl; 756112273Smdodd sdl->sdl_family = AF_LINK; 757112273Smdodd sdl->sdl_index = ifp->if_index; 758112273Smdodd sdl->sdl_type = IFT_ISO88025; 759112273Smdodd sdl->sdl_alen = ISO88025_ADDR_LEN; 760112273Smdodd e_addr = LLADDR(sdl); 761112273Smdodd ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr); 762112273Smdodd *llsa = (struct sockaddr *)sdl; 763112273Smdodd return (0); 764112273Smdodd#endif 765112273Smdodd 766112273Smdodd default: 767112273Smdodd /* 768112273Smdodd * Well, the text isn't quite right, but it's the name 769112273Smdodd * that counts... 770112273Smdodd */ 771112273Smdodd return (EAFNOSUPPORT); 772112273Smdodd } 773112273Smdodd 774112273Smdodd return (0); 775112273Smdodd} 776112273Smdodd 777112269Smdoddstatic moduledata_t iso88025_mod = { 778112269Smdodd "iso88025", 779112269Smdodd NULL, 780112269Smdodd 0 781112269Smdodd}; 782112269Smdodd 783112269SmdoddDECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 784112269SmdoddMODULE_VERSION(iso88025, 1); 785