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