if_iso88025subr.c revision 112280
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 112280 2003-03-15 20:33:30Z mdodd $ 34 * 35 */ 36 37/* 38 * 39 * General ISO 802.5 (Token Ring) support routines 40 * 41 */ 42 43#include "opt_inet.h" 44#include "opt_inet6.h" 45#include "opt_ipx.h" 46 47#include <sys/param.h> 48#include <sys/systm.h> 49#include <sys/kernel.h> 50#include <sys/malloc.h> 51#include <sys/mbuf.h> 52#include <sys/module.h> 53#include <sys/socket.h> 54#include <sys/sockio.h> 55 56#include <net/if.h> 57#include <net/if_dl.h> 58#include <net/if_llc.h> 59#include <net/if_types.h> 60 61#include <net/netisr.h> 62#include <net/route.h> 63#include <net/bpf.h> 64#include <net/iso88025.h> 65 66#if defined(INET) || defined(INET6) 67#include <netinet/in.h> 68#include <netinet/in_var.h> 69#include <netinet/if_ether.h> 70#endif 71#ifdef INET6 72#include <netinet6/nd6.h> 73#endif 74 75#ifdef IPX 76#include <netipx/ipx.h> 77#include <netipx/ipx_if.h> 78#endif 79 80static u_char iso88025_broadcastaddr[ISO88025_ADDR_LEN] = 81 { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 82 83static int iso88025_resolvemulti (struct ifnet *, struct sockaddr **, 84 struct sockaddr *)); 85 86#define IFP2AC(IFP) ((struct arpcom *)IFP) 87#define senderr(e) do { error = (e); goto bad; } while (0) 88 89void 90iso88025_ifattach(struct ifnet *ifp) 91{ 92 struct ifaddr *ifa = NULL; 93 struct sockaddr_dl *sdl; 94 95 ifp->if_type = IFT_ISO88025; 96 ifp->if_addrlen = ISO88025_ADDR_LEN; 97 ifp->if_hdrlen = ISO88025_HDR_LEN; 98 if (ifp->if_baudrate == 0) 99 ifp->if_baudrate = TR_16MBPS; /* 16Mbit should be a safe default */ 100 if (ifp->if_mtu == 0) 101 ifp->if_mtu = ISO88025_DEFAULT_MTU; 102 ifp->if_broadcastaddr = iso88025_broadcastaddr; 103 104 ifa = ifaddr_byindex(ifp->if_index); 105 if (ifa == 0) { 106 printf("iso88025_ifattach: no lladdr!\n"); 107 return; 108 } 109 sdl = (struct sockaddr_dl *)ifa->ifa_addr; 110 sdl->sdl_type = IFT_ISO88025; 111 sdl->sdl_alen = ifp->if_addrlen; 112 bcopy(IFP2AC(ifp)->ac_enaddr, LLADDR(sdl), ifp->if_addrlen); 113} 114 115/* 116 * Perform common duties while detaching a Token Ring interface 117 */ 118void 119iso88025_ifdetach(ifp, bpf) 120 struct ifnet *ifp; 121 int bpf; 122{ 123 124 if (bpf) 125 bpfdetach(ifp); 126 127 if_detach(ifp); 128 129 return; 130} 131 132int 133iso88025_ioctl(struct ifnet *ifp, int command, caddr_t data) 134{ 135 struct ifaddr *ifa; 136 struct ifreq *ifr; 137 int error; 138 139 ifa = (struct ifaddr *) data; 140 ifr = (struct ifreq *) data; 141 error = 0; 142 143 switch (command) { 144 case SIOCSIFADDR: 145 ifp->if_flags |= IFF_UP; 146 147 switch (ifa->ifa_addr->sa_family) { 148#ifdef INET 149 case AF_INET: 150 ifp->if_init(ifp->if_softc); /* before arpwhohas */ 151 arp_ifinit(ifp, ifa); 152 break; 153#endif /* INET */ 154#ifdef IPX 155 /* 156 * XXX - This code is probably wrong 157 */ 158 case AF_IPX: 159 { 160 struct ipx_addr *ina = &(IA_SIPX(ifa)->sipx_addr); 161 struct arpcom *ac = IFP2AC(ifp); 162 163 if (ipx_nullhost(*ina)) 164 ina->x_host = *(union ipx_host *)ac->ac_enaddr; 165 else { 166 bcopy((caddr_t) ina->x_host.c_host, 167 (caddr_t) ac->ac_enaddr, 168 ISO88025_ADDR_LEN); 169 } 170 171 /* 172 * Set new address 173 */ 174 ifp->if_init(ifp->if_softc); 175 break; 176 } 177#endif /* IPX */ 178 default: 179 ifp->if_init(ifp->if_softc); 180 break; 181 } 182 break; 183 184 case SIOCGIFADDR: 185 { 186 struct sockaddr *sa; 187 188 sa = (struct sockaddr *) & ifr->ifr_data; 189 bcopy(IFP2AC(ifp)->ac_enaddr, 190 (caddr_t) sa->sa_data, ISO88025_ADDR_LEN); 191 } 192 break; 193 194 case SIOCSIFMTU: 195 /* 196 * Set the interface MTU. 197 */ 198 if (ifr->ifr_mtu > ISO88025_MAX_MTU) { 199 error = EINVAL; 200 } else { 201 ifp->if_mtu = ifr->ifr_mtu; 202 } 203 break; 204 default: 205 error = EINVAL; /* XXX netbsd has ENOTTY??? */ 206 break; 207 } 208 209 return (error); 210} 211 212/* 213 * ISO88025 encapsulation 214 */ 215int 216iso88025_output(ifp, m, dst, rt0) 217 struct ifnet *ifp; 218 struct mbuf *m; 219 struct sockaddr *dst; 220 struct rtentry *rt0; 221{ 222 u_int16_t snap_type = 0; 223 int loop_copy = 0, error = 0, rif_len = 0; 224 u_char edst[ISO88025_ADDR_LEN]; 225 struct iso88025_header *th; 226 struct iso88025_header gen_th; 227 struct sockaddr_dl *sdl = NULL; 228 struct rtentry *rt; 229 struct arpcom *ac = (struct arpcom *)ifp; 230 231 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 232 senderr(ENETDOWN); 233 getmicrotime(&ifp->if_lastchange); 234 235 error = rt_check(&rt, &rt0, dst); 236 if (error) 237 goto bad; 238 239 /* Calculate routing info length based on arp table entry */ 240 if (rt && (sdl = (struct sockaddr_dl *)rt->rt_gateway)) 241 if (SDL_ISO88025(sdl)->trld_rcf != 0) 242 rif_len = TR_RCF_RIFLEN(SDL_ISO88025(sdl)->trld_rcf); 243 244 /* Generate a generic 802.5 header for the packet */ 245 gen_th.ac = TR_AC; 246 gen_th.fc = TR_LLC_FRAME; 247 (void)memcpy((caddr_t)gen_th.iso88025_shost, (caddr_t)ac->ac_enaddr, 248 ISO88025_ADDR_LEN); 249 if (rif_len) { 250 gen_th.iso88025_shost[0] |= TR_RII; 251 if (rif_len > 2) { 252 gen_th.rcf = SDL_ISO88025(sdl)->trld_rcf; 253 (void)memcpy((caddr_t)gen_th.rd, 254 (caddr_t)SDL_ISO88025(sdl)->trld_route, 255 rif_len - 2); 256 } 257 } 258 259 switch (dst->sa_family) { 260#ifdef INET 261 case AF_INET: 262 if (!arpresolve(ifp, rt, m, dst, edst, rt0)) 263 return (0); /* if not yet resolved */ 264 snap_type = ETHERTYPE_IP; 265 break; 266#endif /* INET */ 267#ifdef NOT_YET 268#ifdef INET6 269 case AF_INET6: 270 if (!nd6_storelladdr(&ac->ac_if, rt, m, dst, (u_char *)edst)) { 271 /* Something bad happened */ 272 return(0); 273 } 274 snap_type = ETHERTYPE_IPV6; 275 break; 276#endif /* INET6 */ 277#endif /* NOT_YET */ 278#ifdef IPX 279 case AF_IPX: 280 { 281 u_int8_t *cp; 282 283 snap_type = 0; 284 bcopy((caddr_t)&(satoipx_addr(dst).x_host), (caddr_t)edst, 285 ISO88025_ADDR_LEN); 286 287 M_PREPEND(m, 3, M_TRYWAIT); 288 if (m == 0) 289 senderr(ENOBUFS); 290 m = m_pullup(m, 3); 291 if (m == 0) 292 senderr(ENOBUFS); 293 cp = mtod(m, u_int8_t *); 294 *cp++ = ETHERTYPE_IPX_8022; 295 *cp++ = ETHERTYPE_IPX_8022; 296 *cp++ = LLC_UI; 297 } 298 break; 299#endif /* IPX */ 300 case AF_UNSPEC: 301 { 302 struct iso88025_sockaddr_data *sd; 303 /* 304 * For AF_UNSPEC sockaddr.sa_data must contain all of the 305 * mac information needed to send the packet. This allows 306 * full mac, llc, and source routing function to be controlled. 307 * llc and source routing information must already be in the 308 * mbuf provided, ac/fc are set in sa_data. sockaddr.sa_data 309 * should be an iso88025_sockaddr_data structure see iso88025.h 310 */ 311 loop_copy = -1; 312 sd = (struct iso88025_sockaddr_data *)dst->sa_data; 313 gen_th.ac = sd->ac; 314 gen_th.fc = sd->fc; 315 (void)memcpy((caddr_t)edst, (caddr_t)sd->ether_dhost, 316 ISO88025_ADDR_LEN); 317 (void)memcpy((caddr_t)gen_th.iso88025_shost, 318 (caddr_t)sd->ether_shost, ISO88025_ADDR_LEN); 319 rif_len = 0; 320 break; 321 } 322 default: 323 if_printf(ifp, "can't handle af%d\n", dst->sa_family); 324 senderr(EAFNOSUPPORT); 325 break; 326 } 327 328 /* 329 * Add LLC header. 330 */ 331 if (snap_type != 0) { 332 struct llc *l; 333 M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT); 334 if (m == 0) 335 senderr(ENOBUFS); 336 l = mtod(m, struct llc *); 337 l->llc_snap.control = LLC_UI; 338 l->llc_dsap = l->llc_ssap = LLC_SNAP_LSAP; 339 l->llc_snap.org_code[0] = 340 l->llc_snap.org_code[1] = 341 l->llc_snap.org_code[2] = 0; 342 l->llc_snap.ether_type = htons(snap_type); 343 } 344 345 (void)memcpy((caddr_t)&gen_th.iso88025_dhost, (caddr_t)edst, 346 ISO88025_ADDR_LEN); 347 348 /* 349 * Add local net header. If no space in first mbuf, 350 * allocate another. 351 */ 352 M_PREPEND(m, ISO88025_HDR_LEN + rif_len, M_DONTWAIT); 353 if (m == 0) 354 senderr(ENOBUFS); 355 th = mtod(m, struct iso88025_header *); 356 357 /* Copy as much of the generic header as is needed into the mbuf */ 358 memcpy(th, &gen_th, ISO88025_HDR_LEN + rif_len); 359 360 /* 361 * If a simplex interface, and the packet is being sent to our 362 * Ethernet address or a broadcast address, loopback a copy. 363 * XXX To make a simplex device behave exactly like a duplex 364 * device, we should copy in the case of sending to our own 365 * ethernet address (thus letting the original actually appear 366 * on the wire). However, we don't do that here for security 367 * reasons and compatibility with the original behavior. 368 */ 369 if ((ifp->if_flags & IFF_SIMPLEX) && (loop_copy != -1)) { 370 if ((m->m_flags & M_BCAST) || (loop_copy > 0)) { 371 struct mbuf *n; 372 n = m_copy(m, 0, (int)M_COPYALL); 373 (void) if_simloop(ifp, n, dst->sa_family, 374 ISO88025_HDR_LEN); 375 } else if (bcmp(th->iso88025_dhost, th->iso88025_shost, 376 ETHER_ADDR_LEN) == 0) { 377 (void) if_simloop(ifp, m, dst->sa_family, 378 ISO88025_HDR_LEN); 379 return(0); /* XXX */ 380 } 381 } 382 383 if (! IF_HANDOFF_ADJ(&ifp->if_snd, m, ifp, ISO88025_HDR_LEN + LLC_SNAPFRAMELEN) ) { 384 printf("iso88025_output: packet dropped QFULL.\n"); 385 senderr(ENOBUFS); 386 } 387 return (error); 388 389bad: 390 if (m) 391 m_freem(m); 392 return (error); 393} 394 395/* 396 * ISO 88025 de-encapsulation 397 */ 398void 399iso88025_input(ifp, th, m) 400 struct ifnet *ifp; 401 struct iso88025_header *th; 402 struct mbuf *m; 403{ 404 int isr; 405 struct llc *l; 406 407 if ((ifp->if_flags & IFF_UP) == 0) { 408 m_freem(m); 409 return; 410 } 411 412 getmicrotime(&ifp->if_lastchange); 413 ifp->if_ibytes += m->m_pkthdr.len + sizeof(*th); 414 415 /* 416 * Set mbuf flags for bcast/mcast. 417 */ 418 if (th->iso88025_dhost[0] & 1) { 419 if (bcmp((caddr_t)iso88025_broadcastaddr, 420 (caddr_t)th->iso88025_dhost, ISO88025_ADDR_LEN) == 0) 421 m->m_flags |= M_BCAST; 422 else 423 m->m_flags |= M_MCAST; 424 ifp->if_imcasts++; 425 } 426 427 l = mtod(m, struct llc *); 428 429 switch (l->llc_dsap) { 430#ifdef IPX 431 case ETHERTYPE_IPX_8022: /* Thanks a bunch Novell */ 432 if ((l->llc_control != LLC_UI) || 433 (l->llc_ssap != ETHERTYPE_IPX_8022)) 434 goto dropanyway; 435 436 th->iso88025_shost[0] &= ~(TR_RII); 437 m_adj(m, 3); 438 isr = NETISR_IPX; 439 break; 440#endif /* IPX */ 441 case LLC_SNAP_LSAP: { 442 u_int16_t type; 443 if ((l->llc_control != LLC_UI) || 444 (l->llc_ssap != LLC_SNAP_LSAP)) 445 goto dropanyway; 446 447 if (l->llc_snap.org_code[0] != 0 || 448 l->llc_snap.org_code[1] != 0 || 449 l->llc_snap.org_code[2] != 0) 450 goto dropanyway; 451 452 type = ntohs(l->llc_snap.ether_type); 453 m_adj(m, LLC_SNAPFRAMELEN); 454 switch (type) { 455#ifdef INET 456 case ETHERTYPE_IP: 457 th->iso88025_shost[0] &= ~(TR_RII); 458 if (ipflow_fastforward(m)) 459 return; 460 isr = NETISR_IP; 461 break; 462 463 case ETHERTYPE_ARP: 464 if (ifp->if_flags & IFF_NOARP) 465 goto dropanyway; 466 isr = NETISR_ARP; 467 break; 468#endif /* INET */ 469#ifdef IPX_SNAP /* XXX: Not supported! */ 470 case ETHERTYPE_IPX: 471 th->iso88025_shost[0] &= ~(TR_RII); 472 isr = NETISR_IPX; 473 break; 474#endif /* IPX_SNAP */ 475#ifdef NOT_YET 476#ifdef INET6 477 case ETHERTYPE_IPV6: 478 th->iso88025_shost[0] &= ~(TR_RII); 479 isr = NETISR_IPV6; 480 break; 481#endif /* INET6 */ 482#endif /* NOT_YET */ 483 default: 484 printf("iso88025_input: unexpected llc_snap ether_type 0x%02x\n", type); 485 m_freem(m); 486 return; 487 } 488 break; 489 } 490 case LLC_ISO_LSAP: 491 switch (l->llc_control) { 492 case LLC_UI: 493 goto dropanyway; 494 break; 495 case LLC_XID: 496 case LLC_XID_P: 497 if(m->m_len < ISO88025_ADDR_LEN) 498 goto dropanyway; 499 l->llc_window = 0; 500 l->llc_fid = 9; 501 l->llc_class = 1; 502 l->llc_dsap = l->llc_ssap = 0; 503 /* Fall through to */ 504 case LLC_TEST: 505 case LLC_TEST_P: 506 { 507 struct sockaddr sa; 508 struct arpcom *ac = (struct arpcom *)ifp; 509 struct iso88025_sockaddr_data *th2; 510 int i; 511 u_char c = l->llc_dsap; 512 513 if (th->iso88025_shost[0] & TR_RII) { /* XXX */ 514 printf("iso88025_input: dropping source routed LLC_TEST\n"); 515 m_free(m); 516 return; 517 } 518 l->llc_dsap = l->llc_ssap; 519 l->llc_ssap = c; 520 if (m->m_flags & (M_BCAST | M_MCAST)) 521 bcopy((caddr_t)ac->ac_enaddr, 522 (caddr_t)th->iso88025_dhost, 523 ISO88025_ADDR_LEN); 524 sa.sa_family = AF_UNSPEC; 525 sa.sa_len = sizeof(sa); 526 th2 = (struct iso88025_sockaddr_data *)sa.sa_data; 527 for (i = 0; i < ISO88025_ADDR_LEN; i++) { 528 th2->ether_shost[i] = c = th->iso88025_dhost[i]; 529 th2->ether_dhost[i] = th->iso88025_dhost[i] = 530 th->iso88025_shost[i]; 531 th->iso88025_shost[i] = c; 532 } 533 th2->ac = TR_AC; 534 th2->fc = TR_LLC_FRAME; 535 ifp->if_output(ifp, m, &sa, NULL); 536 return; 537 } 538 default: 539 printf("iso88025_input: unexpected llc control 0x%02x\n", l->llc_control); 540 m_freem(m); 541 return; 542 } 543 break; 544 default: 545 printf("iso88025_input: unknown dsap 0x%x\n", l->llc_dsap); 546 ifp->if_noproto++; 547 dropanyway: 548 m_freem(m); 549 return; 550 } 551 552 netisr_dispatch(isr, m); 553 return; 554} 555 556static int 557iso88025_resolvemulti (ifp, llsa, sa) 558 struct ifnet *ifp; 559 struct sockaddr **llsa; 560 struct sockaddr *sa; 561{ 562 struct sockaddr_dl *sdl; 563 struct sockaddr_in *sin; 564#ifdef INET6 565 struct sockaddr_in6 *sin6; 566#endif 567 u_char *e_addr; 568 569 switch(sa->sa_family) { 570 case AF_LINK: 571 /* 572 * No mapping needed. Just check that it's a valid MC address. 573 */ 574 sdl = (struct sockaddr_dl *)sa; 575 e_addr = LLADDR(sdl); 576 if ((e_addr[0] & 1) != 1) { 577 return (EADDRNOTAVAIL); 578 } 579 *llsa = 0; 580 return (0); 581 582#ifdef INET 583 case AF_INET: 584 sin = (struct sockaddr_in *)sa; 585 if (!IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) { 586 return (EADDRNOTAVAIL); 587 } 588 MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, 589 M_WAITOK|M_ZERO); 590 sdl->sdl_len = sizeof *sdl; 591 sdl->sdl_family = AF_LINK; 592 sdl->sdl_index = ifp->if_index; 593 sdl->sdl_type = IFT_ISO88025; 594 sdl->sdl_alen = ISO88025_ADDR_LEN; 595 e_addr = LLADDR(sdl); 596 ETHER_MAP_IP_MULTICAST(&sin->sin_addr, e_addr); 597 *llsa = (struct sockaddr *)sdl; 598 return (0); 599#endif 600#ifdef INET6 601 case AF_INET6: 602 sin6 = (struct sockaddr_in6 *)sa; 603 if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 604 /* 605 * An IP6 address of 0 means listen to all 606 * of the Ethernet multicast address used for IP6. 607 * (This is used for multicast routers.) 608 */ 609 ifp->if_flags |= IFF_ALLMULTI; 610 *llsa = 0; 611 return (0); 612 } 613 if (!IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) { 614 return (EADDRNOTAVAIL); 615 } 616 MALLOC(sdl, struct sockaddr_dl *, sizeof *sdl, M_IFMADDR, 617 M_WAITOK|M_ZERO); 618 sdl->sdl_len = sizeof *sdl; 619 sdl->sdl_family = AF_LINK; 620 sdl->sdl_index = ifp->if_index; 621 sdl->sdl_type = IFT_ISO88025; 622 sdl->sdl_alen = ISO88025_ADDR_LEN; 623 e_addr = LLADDR(sdl); 624 ETHER_MAP_IPV6_MULTICAST(&sin6->sin6_addr, e_addr); 625 *llsa = (struct sockaddr *)sdl; 626 return (0); 627#endif 628 629 default: 630 /* 631 * Well, the text isn't quite right, but it's the name 632 * that counts... 633 */ 634 return (EAFNOSUPPORT); 635 } 636 637 return (0); 638} 639 640static moduledata_t iso88025_mod = { 641 "iso88025", 642 NULL, 643 0 644}; 645 646DECLARE_MODULE(iso88025, iso88025_mod, SI_SUB_PSEUDO, SI_ORDER_ANY); 647MODULE_VERSION(iso88025, 1); 648