if_iso88025subr.c revision 59760
1/* 2 * Copyright (c) 1998, Larry Lile 3 * All rights reserved. 4 * 5 * For latest sources and information on this driver, please 6 * go to http://anarchy.stdio.com. 7 * 8 * Questions, comments or suggestions should be directed to 9 * Larry Lile <lile@stdio.com>. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice unmodified, this list of conditions, and the following 16 * disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * $FreeBSD: head/sys/net/if_iso88025subr.c 59760 2000-04-29 15:36:14Z phk $ 34 * 35 */ 36 37/* 38 * 39 * General ISO 802.5 (Token Ring) support routines 40 * 41 */ 42 43#include "opt_inet.h" 44 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/mbuf.h> 48#include <sys/socket.h> 49#include <sys/sockio.h> 50#include <sys/sysctl.h> 51 52#include <net/if.h> 53#include <net/netisr.h> 54#include <net/route.h> 55#include <net/if_llc.h> 56#include <net/if_dl.h> 57#include <net/if_types.h> 58 59#include <net/if_arp.h> 60 61#include <net/iso88025.h> 62 63#ifdef INET 64#include <netinet/in.h> 65#include <netinet/in_var.h> 66#include <netinet/if_ether.h> 67#endif 68 69#include <net/bpf.h> 70 71#include <machine/clock.h> 72#include <machine/md_var.h> 73 74#include <i386/isa/isa_device.h> 75 76#include <vm/vm.h> 77#include <vm/vm_param.h> 78#include <vm/pmap.h> 79 80#include <net/iso88025.h> 81 82void 83iso88025_ifattach(struct ifnet *ifp) 84{ 85 register struct ifaddr *ifa = NULL; 86 register struct sockaddr_dl *sdl; 87 88 ifp->if_type = IFT_ISO88025; 89 ifp->if_addrlen = ISO88025_ADDR_LEN; 90 ifp->if_hdrlen = ISO88025_HDR_LEN; 91 if (ifp->if_baudrate == 0) 92 ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */ 93 if (ifp->if_mtu == 0) 94 ifp->if_mtu = ISO88025_DEFAULT_MTU; 95 96 ifa = ifnet_addrs[ifp->if_index - 1]; 97 if (ifa == 0) { 98 printf("iso88025_ifattach: no lladdr!\n"); 99 return; 100 } 101 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 102 sdl->sdl_type = IFT_ISO88025; 103 sdl->sdl_alen = ifp->if_addrlen; 104 bcopy(((struct arpcom *)ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); 105} 106 107int 108iso88025_ioctl(struct ifnet *ifp, int command, caddr_t data) 109{ 110 struct ifaddr *ifa = (struct ifaddr *) data; 111 struct ifreq *ifr = (struct ifreq *) data; 112 int error = 0; 113 114 switch (command) { 115 case SIOCSIFADDR: 116 ifp->if_flags |= IFF_UP; 117 118 switch (ifa->ifa_addr->sa_family) { 119#ifdef INET 120 case AF_INET: 121 ifp->if_init(ifp->if_softc); /* before arpwhohas */ 122 arp_ifinit((struct arpcom *)ifp, ifa); 123 break; 124#endif 125 default: 126 ifp->if_init(ifp->if_softc); 127 break; 128 } 129 break; 130 131 case SIOCGIFADDR: 132 { 133 struct sockaddr *sa; 134 135 sa = (struct sockaddr *) & ifr->ifr_data; 136 bcopy(((struct arpcom *)ifp->if_softc)->ac_enaddr, 137 (caddr_t) sa->sa_data, ISO88025_ADDR_LEN); 138 } 139 break; 140 141 case SIOCSIFMTU: 142 /* 143 * Set the interface MTU. 144 */ 145 if (ifr->ifr_mtu > ISO88025_MAX_MTU) { 146 error = EINVAL; 147 } else { 148 ifp->if_mtu = ifr->ifr_mtu; 149 } 150 break; 151 } 152 return (error); 153} 154 155/* 156 * ISO88025 encapsulation 157 */ 158int 159iso88025_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst, struct rtentry *rt0) 160{ 161 register struct iso88025_header *th; 162 struct iso88025_header gen_th; 163 register struct iso88025_sockaddr_data *sd = (struct iso88025_sockaddr_data *)dst->sa_data; 164 register struct llc *l; 165 register struct sockaddr_dl *sdl = NULL; 166 int s, error = 0, rif_len = 0; 167 u_char edst[6]; 168 register struct rtentry *rt; 169 int len = m->m_pkthdr.len, loop_copy = 0; 170 struct arpcom *ac = (struct arpcom *)ifp; 171 172 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 173 senderr(ENETDOWN); 174 rt = rt0; 175 if (rt) { 176 if ((rt->rt_flags & RTF_UP) == 0) { 177 rt0 = rt = rtalloc1(dst, 1, 0UL); 178 if (rt0) 179 rt->rt_refcnt--; 180 else 181 senderr(EHOSTUNREACH); 182 } 183 if (rt->rt_flags & RTF_GATEWAY) { 184 if (rt->rt_gwroute == 0) 185 goto lookup; 186 if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { 187 rtfree(rt); rt = rt0; 188 lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1, 189 0UL); 190 if ((rt = rt->rt_gwroute) == 0) 191 senderr(EHOSTUNREACH); 192 } 193 } 194 if (rt->rt_flags & RTF_REJECT) 195 if (rt->rt_rmx.rmx_expire == 0 || 196 time_second < rt->rt_rmx.rmx_expire) 197 senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); 198 } 199 200 /* Calculate routing info length based on arp table entry */ 201 if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway)) 202 if (sdl->sdl_rcf != NULL) 203 rif_len = TR_RCF_RIFLEN(sdl->sdl_rcf); 204 205 /* Generate a generic 802.5 header for the packet */ 206 gen_th.ac = TR_AC; 207 gen_th.fc = TR_LLC_FRAME; 208 memcpy(gen_th.iso88025_shost, ac->ac_enaddr, sizeof(ac->ac_enaddr)); 209 if (rif_len) { 210 gen_th.iso88025_shost[0] |= TR_RII; 211 if (rif_len > 2) { 212 gen_th.rcf = sdl->sdl_rcf; 213 memcpy(gen_th.rd, sdl->sdl_route, rif_len - 2); 214 } 215 } 216 217 218 switch (dst->sa_family) { 219#ifdef INET 220 case AF_INET: 221 if (!arpresolve(ac, rt, m, dst, edst, rt0)) 222 return (0); /* if not yet resolved */ 223 /* Add LLC and SNAP headers */ 224 M_PREPEND(m, 8, M_DONTWAIT); 225 if (m == 0) 226 senderr(ENOBUFS); 227 l = mtod(m, struct llc *); 228 l->llc_un.type_snap.ether_type = htons(ETHERTYPE_IP); 229 l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP; 230 l->llc_un.type_snap.control = LLC_UI; 231 l->llc_un.type_snap.org_code[0] = 0x0; 232 l->llc_un.type_snap.org_code[1] = 0x0; 233 l->llc_un.type_snap.org_code[2] = 0x0; 234 memcpy(gen_th.iso88025_dhost, edst, sizeof(edst)); 235 break; 236#endif 237 238 case AF_UNSPEC: 239 /* 240 * For AF_UNSPEC sockaddr.sa_data must contain all of the 241 * mac information needed to send the packet. This allows 242 * full mac, llc, and source routing function to be controlled. 243 * llc and source routing information must already be in the 244 * mbuf provided, ac/fc are set in sa_data. sockaddr.sa_data 245 * should be a iso88025_sockaddr_data structure see iso88025.h 246 */ 247 loop_copy = -1; 248 sd = (struct iso88025_sockaddr_data *)dst->sa_data; 249 gen_th.ac = sd->ac; 250 gen_th.fc = sd->fc; 251 memcpy(gen_th.iso88025_dhost, sd->ether_dhost, sizeof(sd->ether_dhost)); 252 memcpy(gen_th.iso88025_shost, sd->ether_shost, sizeof(sd->ether_shost)); 253 rif_len = 0; 254 break; 255 256 default: 257 printf("%s%d: can't handle af%d\n", ifp->if_name, ifp->if_unit, 258 dst->sa_family); 259 senderr(EAFNOSUPPORT); 260 } 261 262 /* 263 * Add local net header. If no space in first mbuf, 264 * allocate another. 265 */ 266 267 M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT); 268 if (m == 0) 269 senderr(ENOBUFS); 270 271 /* Copy as much of the generic header as is needed into the mbuf */ 272 th = mtod(m, struct iso88025_header *); 273 memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len); 274 275 /* 276 * If a simplex interface, and the packet is being sent to our 277 * Ethernet address or a broadcast address, loopback a copy. 278 * XXX To make a simplex device behave exactly like a duplex 279 * device, we should copy in the case of sending to our own 280 * ethernet address (thus letting the original actually appear 281 * on the wire). However, we don't do that here for security 282 * reasons and compatibility with the original behavior. 283 */ 284 if ((ifp->if_flags & IFF_SIMPLEX) && 285 (loop_copy != -1)) { 286 if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { 287 struct mbuf *n = m_copy(m, 0, (int)M_COPYALL); 288 (void) if_simloop(ifp, n, dst, ISO88025_HDR_LEN); 289 } else if (bcmp(th->iso88025_dhost, 290 th->iso88025_shost, ETHER_ADDR_LEN) == 0) { 291 (void) if_simloop(ifp, m, dst, ISO88025_HDR_LEN); 292 return(0); /* XXX */ 293 } 294 } 295 296 s = splimp(); 297 /* 298 * Queue message on interface, and start output if interface 299 * not yet active. 300 */ 301 if (IF_QFULL(&ifp->if_snd)) { 302 printf("iso88025_output: packet dropped QFULL.\n"); 303 IF_DROP(&ifp->if_snd); 304 splx(s); 305 senderr(ENOBUFS); 306 } 307 IF_ENQUEUE(&ifp->if_snd, m); 308 if ((ifp->if_flags & IFF_OACTIVE) == 0) 309 (*ifp->if_start)(ifp); 310 splx(s); 311 ifp->if_obytes += len + ISO88025_HDR_LEN + 8; 312 if (m->m_flags & M_MCAST) 313 ifp->if_omcasts++; 314 return (error); 315 316bad: 317 if (m) 318 m_freem(m); 319 return (error); 320} 321 322/* 323 * ISO 88025 de-encapsulation 324 */ 325void 326iso88025_input(struct ifnet *ifp, struct iso88025_header *th, struct mbuf *m) 327{ 328 register struct ifqueue *inq; 329 u_short ether_type; 330 int s; 331 register struct llc *l = mtod(m, struct llc *); 332 333 if ((ifp->if_flags & IFF_UP) == 0) { 334 m_freem(m); 335 return; 336 } 337 338 switch (l->llc_control) { 339 case LLC_UI: 340 break; 341 case LLC_TEST: 342 case LLC_TEST_P: 343 { 344 struct sockaddr sa; 345 struct arpcom *ac = (struct arpcom *)ifp; 346 struct iso88025_sockaddr_data *th2; 347 int i; 348 u_char c = l->llc_dsap; 349 350 if (th->iso88025_shost[0] & TR_RII) { /* XXX */ 351 printf("iso88025_input: dropping source routed LLC_TEST\n"); 352 m_free(m); 353 return; 354 } 355 l->llc_dsap = l->llc_ssap; 356 l->llc_ssap = c; 357 if (m->m_flags & (M_BCAST | M_MCAST)) 358 bcopy((caddr_t)ac->ac_enaddr, 359 (caddr_t)th->iso88025_dhost, ISO88025_ADDR_LEN); 360 sa.sa_family = AF_UNSPEC; 361 sa.sa_len = sizeof(sa); 362 th2 = (struct iso88025_sockaddr_data *)sa.sa_data; 363 for (i = 0; i < ISO88025_ADDR_LEN; i++) { 364 th2->ether_shost[i] = c = th->iso88025_dhost[i]; 365 th2->ether_dhost[i] = th->iso88025_dhost[i] = th->iso88025_shost[i]; 366 th->iso88025_shost[i] = c; 367 } 368 th2->ac = TR_AC; 369 th2->fc = TR_LLC_FRAME; 370 ifp->if_output(ifp, m, &sa, NULL); 371 return; 372 } 373 default: 374 printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control); 375 m_freem(m); 376 return; 377 } 378 379 m->m_pkthdr.len -= 8; 380 m->m_len -= 8; 381 m->m_data += 8; /* Length of LLC header in our case */ 382 383 ifp->if_ibytes += m->m_pkthdr.len + sizeof(*th); 384 if (th->iso88025_dhost[0] & 1) { 385 if (bcmp((caddr_t)etherbroadcastaddr, (caddr_t)th->iso88025_dhost, sizeof(etherbroadcastaddr)) == 0) 386 m->m_flags |= M_BCAST; 387 else 388 m->m_flags |= M_MCAST; 389 } 390 if (m->m_flags & (M_BCAST|M_MCAST)) 391 ifp->if_imcasts++; 392 393 ether_type = ntohs(l->llc_un.type_snap.ether_type); 394 395 switch (ether_type) { 396#ifdef INET 397 case ETHERTYPE_IP: 398 th->iso88025_shost[0] &= ~(TR_RII); 399 if (ipflow_fastforward(m)) 400 return; 401 schednetisr(NETISR_IP); 402 inq = &ipintrq; 403 break; 404 405 case ETHERTYPE_ARP: 406 schednetisr(NETISR_ARP); 407 inq = &arpintrq; 408 break; 409#endif 410 default: 411 m_freem(m); 412 return; 413 } 414 415 s = splimp(); 416 if (IF_QFULL(inq)) { 417 IF_DROP(inq); 418 m_freem(m); 419 printf("iso88025_input: Packet dropped (Queue full).\n"); 420 } else 421 IF_ENQUEUE(inq, m); 422 splx(s); 423} 424