1 /* $NetBSD: if_arcsubr.c,v 1.17 1998/07/05 00:51:26 jonathan Exp $ */ 2 3 /* 4 * Copyright (c) 1994, 1995 Ignatios Souvatzis 5 * Copyright (c) 1982, 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: NetBSD: if_ethersubr.c,v 1.9 1994/06/29 06:36:11 cgd Exp 37 * @(#)if_ethersubr.c 8.1 (Berkeley) 6/10/93 38 * 39 */ 40 #include "opt_inet.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/kernel.h> 45 #include <sys/malloc.h> 46 #include <sys/mbuf.h> 47 #include <sys/protosw.h> 48 #include <sys/socket.h> 49 #include <sys/ioctl.h> 50 #include <sys/errno.h> 51 #include <sys/syslog.h> 52 53 #include <machine/cpu.h> 54 55 #include <net/if.h> 56 #include <net/netisr.h> 57 #include <net/route.h> 58 #include <net/if_dl.h> 59 #include <net/if_types.h> 60 #include <net/if_arc.h> 61 #include <net/if_arp.h> 62 #include <net/if_ether.h> 63 64 #ifdef INET 65 #include <netinet/in.h> 66 #include <netinet/in_var.h> 67 #include <netinet/if_inarp.h> 68 #endif 69 70 #define ARCNET_ALLOW_BROKEN_ARP 71 72 #ifndef ARC_PHDSMTU 73 #define ARC_PHDSMTU 1500 74 #endif 75 76 static struct mbuf *arc_defrag __P((struct ifnet *, struct mbuf *)); 77 78 /* 79 * RC1201 requires us to have this configurable. We have it only per 80 * machine at the moment... there is no generic "set mtu" ioctl, AFAICS. 81 * Anyway, it is possible to binpatch this or set it per kernel config 82 * option. 83 */ 84 #if ARC_PHDSMTU > 60480 85 ERROR: The arc_phdsmtu is ARC_PHDSMTU, but must not exceed 60480. 86 #endif 87 u_int16_t arc_phdsmtu = ARC_PHDSMTU; 88 u_int8_t arcbroadcastaddr = 0; 89 90 #define senderr(e) { error = (e); goto bad;} 91 #define SIN(s) ((struct sockaddr_in *)s) 92 93 /* 94 * ARCnet output routine. 95 * Encapsulate a packet of type family for the local net. 96 * Assumes that ifp is actually pointer to arccom structure. 97 */ 98 int 99 arc_output(ifp, m0, dst, rt0) 100 register struct ifnet *ifp; 101 struct mbuf *m0; 102 struct sockaddr *dst; 103 struct rtentry *rt0; 104 { 105 struct mbuf *m, *m1, *mcopy; 106 struct rtentry *rt; 107 struct arccom *ac; 108 struct arc_header *ah; 109 struct arphdr *arph; 110 int s, error, newencoding; 111 u_int8_t atype, adst, myself; 112 int tfrags, sflag, fsflag, rsflag; 113 114 if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) 115 return(ENETDOWN); /* m, m1 aren't initialized yet */ 116 117 error = newencoding = 0; 118 ac = (struct arccom *)ifp; 119 m = m0; 120 mcopy = m1 = NULL; 121 122 myself = *LLADDR(ifp->if_sadl); 123 124 ifp->if_lastchange = time; 125 if ((rt = rt0)) { 126 if ((rt->rt_flags & RTF_UP) == 0) { 127 if ((rt0 = rt = rtalloc1(dst, 1))) 128 rt->rt_refcnt--; 129 else 130 senderr(EHOSTUNREACH); 131 } 132 if (rt->rt_flags & RTF_GATEWAY) { 133 if (rt->rt_gwroute == 0) 134 goto lookup; 135 if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) { 136 rtfree(rt); rt = rt0; 137 lookup: rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1); 138 if ((rt = rt->rt_gwroute) == 0) 139 senderr(EHOSTUNREACH); 140 } 141 } 142 if (rt->rt_flags & RTF_REJECT) 143 if (rt->rt_rmx.rmx_expire == 0 || 144 time.tv_sec < rt->rt_rmx.rmx_expire) 145 senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH); 146 } 147 148 switch (dst->sa_family) { 149 #ifdef INET 150 case AF_INET: 151 152 /* 153 * For now, use the simple IP addr -> ARCnet addr mapping 154 */ 155 if (m->m_flags & (M_BCAST|M_MCAST)) 156 adst = arcbroadcastaddr; /* ARCnet broadcast address */ 157 else if (ifp->if_flags & IFF_NOARP) 158 adst = ntohl(SIN(dst)->sin_addr.s_addr) & 0xFF; 159 else if (!arpresolve(ifp, rt, m, dst, &adst)) 160 return 0; /* not resolved yet */ 161 162 /* If broadcasting on a simplex interface, loopback a copy */ 163 if ((m->m_flags & (M_BCAST|M_MCAST)) && 164 (ifp->if_flags & IFF_SIMPLEX)) 165 mcopy = m_copy(m, 0, (int)M_COPYALL); 166 if (ifp->if_flags & IFF_LINK0) { 167 atype = ARCTYPE_IP; 168 newencoding = 1; 169 } else { 170 atype = ARCTYPE_IP_OLD; 171 newencoding = 0; 172 } 173 break; 174 175 case AF_ARP: 176 arph = mtod(m, struct arphdr *); 177 if (m->m_flags & M_BCAST) 178 adst = arcbroadcastaddr; 179 else 180 adst = *ar_tha(arph); 181 182 arph->ar_hrd = htons(ARPHRD_ARCNET); 183 184 switch(ntohs(arph->ar_op)) { 185 186 case ARPOP_REVREQUEST: 187 case ARPOP_REVREPLY: 188 if (!(ifp->if_flags & IFF_LINK0)) { 189 printf("%s: can't handle af%d\n", 190 ifp->if_xname, dst->sa_family); 191 senderr(EAFNOSUPPORT); 192 } 193 194 atype = htons(ARCTYPE_REVARP); 195 newencoding = 1; 196 break; 197 198 case ARPOP_REQUEST: 199 case ARPOP_REPLY: 200 default: 201 if (ifp->if_flags & IFF_LINK0) { 202 atype = htons(ARCTYPE_ARP); 203 newencoding = 1; 204 } else { 205 atype = htons(ARCTYPE_ARP_OLD); 206 newencoding = 0; 207 } 208 } 209 #ifdef ARCNET_ALLOW_BROKEN_ARP 210 /* 211 * XXX It's not clear per RFC826 if this is needed, but 212 * "assigned numbers" say this is wrong. 213 * However, e.g., AmiTCP 3.0Beta used it... we make this 214 * switchable for emergency cases. Not perfect, but... 215 */ 216 if (ifp->if_flags & IFF_LINK2) 217 arph->ar_pro = atype - 1; 218 #endif 219 break; 220 #endif 221 222 case AF_UNSPEC: 223 ah = (struct arc_header *)dst->sa_data; 224 adst = ah->arc_dhost; 225 atype = ah->arc_type; 226 break; 227 228 default: 229 printf("%s: can't handle af%d\n", ifp->if_xname, 230 dst->sa_family); 231 senderr(EAFNOSUPPORT); 232 } 233 234 if (mcopy) 235 (void) looutput(ifp, mcopy, dst, rt); 236 237 /* 238 * Add local net header. If no space in first mbuf, 239 * allocate another. 240 * 241 * For ARCnet, this is just symbolic. The header changes 242 * form and position on its way into the hardware and out of 243 * the wire. At this point, it contains source, destination and 244 * packet type. 245 */ 246 if (newencoding) { 247 ++ac->ac_seqid; /* make the seqid unique */ 248 249 tfrags = (m->m_pkthdr.len + 503) / 504; 250 fsflag = 2 * tfrags - 3; 251 sflag = 0; 252 rsflag = fsflag; 253 254 while (sflag < fsflag) { 255 /* we CAN'T have short packets here */ 256 m1 = m_split(m, 504, M_DONTWAIT); 257 if (m1 == 0) 258 senderr(ENOBUFS); 259 260 M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT); 261 if (m == 0) 262 senderr(ENOBUFS); 263 ah = mtod(m, struct arc_header *); 264 ah->arc_type = atype; 265 ah->arc_dhost = adst; 266 ah->arc_shost = myself; 267 ah->arc_flag = rsflag; 268 ah->arc_seqid = ac->ac_seqid; 269 270 s = splimp(); 271 /* 272 * Queue message on interface, and start output if 273 * interface not yet active. 274 */ 275 if (IF_QFULL(&ifp->if_snd)) { 276 IF_DROP(&ifp->if_snd); 277 splx(s); 278 senderr(ENOBUFS); 279 } 280 ifp->if_obytes += m->m_pkthdr.len; 281 IF_ENQUEUE(&ifp->if_snd, m); 282 if ((ifp->if_flags & IFF_OACTIVE) == 0) 283 (*ifp->if_start)(ifp); 284 splx(s); 285 286 m = m1; 287 sflag += 2; 288 rsflag = sflag; 289 } 290 m1 = NULL; 291 292 M_PREPEND(m, ARC_HDRNEWLEN, M_DONTWAIT); 293 if (m == 0) 294 senderr(ENOBUFS); 295 ah = mtod(m, struct arc_header *); 296 ah->arc_type = atype; 297 ah->arc_flag = sflag; 298 ah->arc_seqid = ac->ac_seqid; 299 300 /* here we can have small, especially forbidden packets */ 301 302 if ((m->m_pkthdr.len >= ARC_MIN_FORBID_LEN + 2) && 303 (m->m_pkthdr.len <= ARC_MAX_FORBID_LEN + 2)) { 304 M_PREPEND(m, 4, M_DONTWAIT); 305 if (m == 0) 306 senderr(ENOBUFS); 307 m = m_pullup(m, ARC_HDRNEWLEN); 308 if (m == 0) 309 senderr(ENOBUFS); 310 ah = mtod(m, struct arc_header *); 311 ah->arc_type = atype; 312 ah->arc_flag = 0xFF; 313 ah->arc_seqid = 0xFFFF; 314 } 315 316 ah->arc_dhost = adst; 317 ah->arc_shost = myself; 318 } else { 319 M_PREPEND(m, ARC_HDRLEN, M_DONTWAIT); 320 if (m == 0) 321 senderr(ENOBUFS); 322 ah = mtod(m, struct arc_header *); 323 ah->arc_type = atype; 324 ah->arc_dhost = adst; 325 ah->arc_shost = myself; 326 } 327 328 s = splimp(); 329 /* 330 * Queue message on interface, and start output if interface 331 * not yet active. 332 */ 333 if (IF_QFULL(&ifp->if_snd)) { 334 IF_DROP(&ifp->if_snd); 335 splx(s); 336 senderr(ENOBUFS); 337 } 338 ifp->if_obytes += m->m_pkthdr.len; 339 IF_ENQUEUE(&ifp->if_snd, m); 340 if ((ifp->if_flags & IFF_OACTIVE) == 0) 341 (*ifp->if_start)(ifp); 342 splx(s); 343 344 return (error); 345 346 bad: 347 if (m1) 348 m_freem(m1); 349 if (m) 350 m_freem(m); 351 return (error); 352 } 353 354 /* 355 * Defragmenter. Returns mbuf if last packet found, else 356 * NULL. frees imcoming mbuf as necessary. 357 */ 358 359 __inline struct mbuf * 360 arc_defrag(ifp, m) 361 struct ifnet *ifp; 362 struct mbuf *m; 363 { 364 struct arc_header *ah, *ah1; 365 struct arccom *ac; 366 struct ac_frag *af; 367 struct mbuf *m1; 368 char *s; 369 int newflen; 370 u_char src,dst,typ; 371 372 ac = (struct arccom *)ifp; 373 374 m = m_pullup(m, ARC_HDRNEWLEN); 375 if (m == NULL) { 376 ++ifp->if_ierrors; 377 return NULL; 378 } 379 380 ah = mtod(m, struct arc_header *); 381 typ = ah->arc_type; 382 383 if (!arc_isphds(typ)) 384 return m; 385 386 src = ah->arc_shost; 387 dst = ah->arc_dhost; 388 389 if (ah->arc_flag == 0xff) { 390 m_adj(m, 4); 391 392 m = m_pullup(m, ARC_HDRNEWLEN); 393 if (m == NULL) { 394 ++ifp->if_ierrors; 395 return NULL; 396 } 397 398 ah = mtod(m, struct arc_header *); 399 ah->arc_shost = src; 400 ah->arc_dhost = dst; 401 ah->arc_type = typ; 402 } 403 404 af = &ac->ac_fragtab[src]; 405 m1 = af->af_packet; 406 s = "debug code error"; 407 408 if (ah->arc_flag & 1) { 409 /* 410 * first fragment. We always initialize, which is 411 * about the right thing to do, as we only want to 412 * accept one fragmented packet per src at a time. 413 */ 414 if (m1 != NULL) 415 m_freem(m1); 416 417 af->af_packet = m; 418 m1 = m; 419 af->af_maxflag = ah->arc_flag; 420 af->af_lastseen = 0; 421 af->af_seqid = ah->arc_seqid; 422 423 return NULL; 424 /* notreached */ 425 } else { 426 /* check for unfragmented packet */ 427 if (ah->arc_flag == 0) 428 return m; 429 430 /* do we have a first packet from that src? */ 431 if (m1 == NULL) { 432 s = "no first frag"; 433 goto outofseq; 434 } 435 436 ah1 = mtod(m1, struct arc_header *); 437 438 if (ah->arc_seqid != ah1->arc_seqid) { 439 s = "seqid differs"; 440 goto outofseq; 441 } 442 443 if (ah->arc_type != ah1->arc_type) { 444 s = "type differs"; 445 goto outofseq; 446 } 447 448 if (ah->arc_dhost != ah1->arc_dhost) { 449 s = "dest host differs"; 450 goto outofseq; 451 } 452 453 /* typ, seqid and dst are ok here. */ 454 455 if (ah->arc_flag == af->af_lastseen) { 456 m_freem(m); 457 return NULL; 458 } 459 460 if (ah->arc_flag == af->af_lastseen + 2) { 461 /* ok, this is next fragment */ 462 af->af_lastseen = ah->arc_flag; 463 m_adj(m,ARC_HDRNEWLEN); 464 465 /* 466 * m_cat might free the first mbuf (with pkthdr) 467 * in 2nd chain; therefore: 468 */ 469 470 newflen = m->m_pkthdr.len; 471 472 m_cat(m1,m); 473 474 m1->m_pkthdr.len += newflen; 475 476 /* is it the last one? */ 477 if (af->af_lastseen > af->af_maxflag) { 478 af->af_packet = NULL; 479 return(m1); 480 } else 481 return NULL; 482 } 483 s = "other reason"; 484 /* if all else fails, it is out of sequence, too */ 485 } 486 outofseq: 487 if (m1) { 488 m_freem(m1); 489 af->af_packet = NULL; 490 } 491 492 if (m) 493 m_freem(m); 494 495 log(LOG_INFO,"%s: got out of seq. packet: %s\n", 496 ifp->if_xname, s); 497 498 return NULL; 499 } 500 501 /* 502 * return 1 if Packet Header Definition Standard, else 0. 503 * For now: old IP, old ARP aren't obviously. Lacking correct information, 504 * we guess that besides new IP and new ARP also IPX and APPLETALK are PHDS. 505 * (Apple and Novell corporations were involved, among others, in PHDS work). 506 * Easiest is to assume that everybody else uses that, too. 507 */ 508 int 509 arc_isphds(type) 510 u_int8_t type; 511 { 512 return ((type != ARCTYPE_IP_OLD && 513 type != ARCTYPE_ARP_OLD)); 514 } 515 516 /* 517 * Process a received Arcnet packet; 518 * the packet is in the mbuf chain m with 519 * the ARCnet header. 520 */ 521 void 522 arc_input(ifp, m) 523 struct ifnet *ifp; 524 struct mbuf *m; 525 { 526 register struct arc_header *ah; 527 register struct ifqueue *inq; 528 u_int8_t atype; 529 int s; 530 struct arphdr *arph; 531 532 if ((ifp->if_flags & IFF_UP) == 0) { 533 m_freem(m); 534 return; 535 } 536 537 /* possibly defragment: */ 538 m = arc_defrag(ifp, m); 539 if (m == NULL) 540 return; 541 542 ah = mtod(m, struct arc_header *); 543 544 ifp->if_lastchange = time; 545 ifp->if_ibytes += m->m_pkthdr.len; 546 547 if (arcbroadcastaddr == ah->arc_dhost) { 548 m->m_flags |= M_BCAST|M_MCAST; 549 ifp->if_imcasts++; 550 } 551 552 atype = ah->arc_type; 553 switch (atype) { 554 #ifdef INET 555 case ARCTYPE_IP: 556 m_adj(m, ARC_HDRNEWLEN); 557 schednetisr(NETISR_IP); 558 inq = &ipintrq; 559 break; 560 561 case ARCTYPE_IP_OLD: 562 m_adj(m, ARC_HDRLEN); 563 schednetisr(NETISR_IP); 564 inq = &ipintrq; 565 break; 566 567 case ARCTYPE_ARP: 568 m_adj(m, ARC_HDRNEWLEN); 569 schednetisr(NETISR_ARP); 570 inq = &arpintrq; 571 #ifdef ARCNET_ALLOW_BROKEN_ARP 572 mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP); 573 #endif 574 break; 575 576 case ARCTYPE_ARP_OLD: 577 m_adj(m, ARC_HDRLEN); 578 schednetisr(NETISR_ARP); 579 inq = &arpintrq; 580 arph = mtod(m, struct arphdr *); 581 #ifdef ARCNET_ALLOW_BROKEN_ARP 582 mtod(m, struct arphdr *)->ar_pro = htons(ETHERTYPE_IP); 583 #endif 584 break; 585 #endif 586 default: 587 m_freem(m); 588 return; 589 } 590 591 s = splimp(); 592 if (IF_QFULL(inq)) { 593 IF_DROP(inq); 594 m_freem(m); 595 } else 596 IF_ENQUEUE(inq, m); 597 splx(s); 598 } 599 600 /* 601 * Convert Arcnet address to printable (loggable) representation. 602 */ 603 static char digits[] = "0123456789abcdef"; 604 char * 605 arc_sprintf(ap) 606 register u_int8_t *ap; 607 { 608 static char arcbuf[3]; 609 register char *cp = arcbuf; 610 611 *cp++ = digits[*ap >> 4]; 612 *cp++ = digits[*ap++ & 0xf]; 613 *cp = 0; 614 return (arcbuf); 615 } 616 617 /* 618 * Perform common duties while attaching to interface list 619 */ 620 void 621 arc_ifattach(ifp, lla) 622 register struct ifnet *ifp; 623 u_int8_t lla; 624 { 625 register struct sockaddr_dl *sdl; 626 register struct arccom *ac; 627 628 ifp->if_type = IFT_ARCNET; 629 ifp->if_addrlen = 1; 630 ifp->if_hdrlen = ARC_HDRLEN; 631 if (ifp->if_flags & IFF_BROADCAST) 632 ifp->if_flags |= IFF_MULTICAST|IFF_ALLMULTI; 633 if (ifp->if_flags & IFF_LINK0 && arc_phdsmtu > 60480) 634 log(LOG_ERR, 635 "%s: arc_phdsmtu is %d, but must not exceed 60480", 636 ifp->if_xname, arc_phdsmtu); 637 638 ifp->if_mtu = (ifp->if_flags & IFF_LINK0 ? arc_phdsmtu : ARCMTU); 639 ac = (struct arccom *)ifp; 640 ac->ac_seqid = (time.tv_sec) & 0xFFFF; /* try to make seqid unique */ 641 if (lla == 0) { 642 /* XXX this message isn't entirely clear, to me -- cgd */ 643 log(LOG_ERR,"%s: link address 0 reserved for broadcasts. Please change it and ifconfig %s down up\n", 644 ifp->if_xname, ifp->if_xname); 645 } 646 if ((sdl = ifp->if_sadl) && 647 sdl->sdl_family == AF_LINK) { 648 sdl->sdl_type = IFT_ARCNET; 649 sdl->sdl_alen = ifp->if_addrlen; 650 bcopy((caddr_t)&lla, LLADDR(sdl), ifp->if_addrlen); 651 } 652 ifp->if_broadcastaddr = &arcbroadcastaddr; 653 } 654