1 /* if_imp.c 6.3 85/02/28 */ 2 3 #include "imp.h" 4 #if NIMP > 0 5 /* 6 * ARPANET IMP interface driver. 7 * 8 * The IMP-host protocol is handled here, leaving 9 * hardware specifics to the lower level interface driver. 10 */ 11 #include "../machine/pte.h" 12 13 #include "param.h" 14 #include "systm.h" 15 #include "mbuf.h" 16 #include "buf.h" 17 #include "protosw.h" 18 #include "socket.h" 19 #include "vmmac.h" 20 #include "time.h" 21 #include "kernel.h" 22 #include "errno.h" 23 #include "ioctl.h" 24 25 #include "../vax/cpu.h" 26 #include "../vax/mtpr.h" 27 #include "../vaxuba/ubareg.h" 28 #include "../vaxuba/ubavar.h" 29 30 #include "../net/if.h" 31 #include "../net/route.h" 32 33 #include "../net/netisr.h" 34 #include "../netinet/in.h" 35 #include "../netinet/in_systm.h" 36 #include "../netinet/ip.h" 37 #include "../netinet/ip_var.h" 38 /* define IMPLEADERS here to get leader printing code */ 39 #include "if_imp.h" 40 #include "if_imphost.h" 41 42 /* 43 * IMP software status per interface. 44 * (partially shared with the hardware specific module) 45 * 46 * Each interface is referenced by a network interface structure, 47 * imp_if, which the routing code uses to locate the interface. 48 * This structure contains the output queue for the interface, its 49 * address, ... IMP specific structures used in connecting the 50 * IMP software modules to the hardware specific interface routines 51 * are stored here. The common structures are made visible to the 52 * interface driver by passing a pointer to the hardware routine 53 * at "attach" time. 54 * 55 * NOTE: imp_if and imp_cb are assumed adjacent in hardware code. 56 */ 57 struct imp_softc { 58 struct ifnet imp_if; /* network visible interface */ 59 struct impcb imp_cb; /* hooks to hardware module */ 60 u_char imp_state; /* current state of IMP */ 61 char imp_dropcnt; /* used during initialization */ 62 } imp_softc[NIMP]; 63 64 /* 65 * Messages from IMP regarding why 66 * it's going down. 67 */ 68 static char *impmessage[] = { 69 "in 30 seconds", 70 "for hardware PM", 71 "to reload software", 72 "for emergency reset" 73 }; 74 75 #define HOSTDEADTIMER 10 /* How long to wait when down */ 76 77 int impdown(), impinit(), impioctl(), impoutput(); 78 79 /* 80 * IMP attach routine. Called from hardware device attach routine 81 * at configuration time with a pointer to the UNIBUS device structure. 82 * Sets up local state and returns pointer to base of ifnet+impcb 83 * structures. This is then used by the device's attach routine 84 * set up its back pointers. 85 */ 86 impattach(ui, reset) 87 struct uba_device *ui; 88 int (*reset)(); 89 { 90 struct imp_softc *sc = &imp_softc[ui->ui_unit]; 91 register struct ifnet *ifp = &sc->imp_if; 92 93 /* UNIT COULD BE AMBIGUOUS */ 94 ifp->if_unit = ui->ui_unit; 95 ifp->if_name = "imp"; 96 ifp->if_mtu = IMPMTU - sizeof(struct imp_leader); 97 ifp->if_reset = reset; 98 ifp->if_init = impinit; 99 ifp->if_ioctl = impioctl; 100 ifp->if_output = impoutput; 101 /* reset is handled at the hardware level */ 102 if_attach(ifp); 103 return ((int)&sc->imp_if); 104 } 105 106 /* 107 * IMP initialization routine: call hardware module to 108 * setup UNIBUS resources, init state and get ready for 109 * NOOPs the IMP should send us, and that we want to drop. 110 */ 111 impinit(unit) 112 int unit; 113 { 114 int s = splimp(); 115 register struct imp_softc *sc = &imp_softc[unit]; 116 struct sockaddr_in *sin; 117 118 sin = (struct sockaddr_in *)&sc->imp_if.if_addr; 119 if (in_netof(sin->sin_addr) == 0) 120 return; 121 if ((*sc->imp_cb.ic_init)(unit) == 0) { 122 sc->imp_state = IMPS_DOWN; 123 sc->imp_if.if_flags &= ~IFF_UP; 124 splx(s); 125 return; 126 } 127 sc->imp_if.if_flags |= IFF_RUNNING; 128 sc->imp_state = IMPS_INIT; 129 impnoops(sc); 130 splx(s); 131 } 132 133 struct sockproto impproto = { PF_IMPLINK }; 134 struct sockaddr_in impdst = { AF_IMPLINK }; 135 struct sockaddr_in impsrc = { AF_IMPLINK }; 136 #ifdef IMPLEADERS 137 int impprintfs = 0; 138 #endif 139 140 /* 141 * ARPAnet 1822 input routine. 142 * Called from hardware input interrupt routine to handle 1822 143 * IMP-host messages. Type 0 messages (non-control) are 144 * passed to higher level protocol processors on the basis 145 * of link number. Other type messages (control) are handled here. 146 */ 147 impinput(unit, m) 148 int unit; 149 register struct mbuf *m; 150 { 151 register struct imp_leader *ip; 152 register struct imp_softc *sc = &imp_softc[unit]; 153 register struct host *hp; 154 register struct ifqueue *inq; 155 struct control_leader *cp; 156 struct in_addr addr; 157 struct mbuf *next; 158 struct sockaddr_in *sin; 159 160 /* verify leader length. */ 161 if (m->m_len < sizeof(struct control_leader) && 162 (m = m_pullup(m, sizeof(struct control_leader))) == 0) 163 return; 164 cp = mtod(m, struct control_leader *); 165 if (cp->dl_mtype == IMPTYPE_DATA) 166 if (m->m_len < sizeof(struct imp_leader) && 167 (m = m_pullup(m, sizeof(struct imp_leader))) == 0) 168 return; 169 ip = mtod(m, struct imp_leader *); 170 #ifdef IMPLEADERS 171 if (impprintfs) 172 printleader("impinput", ip); 173 #endif 174 175 /* check leader type */ 176 if (ip->il_format != IMP_NFF) { 177 sc->imp_if.if_collisions++; /* XXX */ 178 goto drop; 179 } 180 181 if (ip->il_mtype != IMPTYPE_DATA) { 182 /* If not data packet, build IP addr from leader (BRL) */ 183 imp_leader_to_addr( &addr, ip, &sc->imp_if ); 184 } 185 switch (ip->il_mtype) { 186 187 case IMPTYPE_DATA: 188 break; 189 190 /* 191 * IMP leader error. Reset the IMP and discard the packet. 192 */ 193 case IMPTYPE_BADLEADER: 194 /* 195 * According to 1822 document, this message 196 * will be generated in response to the 197 * first noop sent to the IMP after 198 * the host resets the IMP interface. 199 */ 200 if (sc->imp_state != IMPS_INIT) { 201 impmsg(sc, "leader error"); 202 hostreset(sc->imp_if.if_net); 203 impnoops(sc); 204 } 205 goto drop; 206 207 /* 208 * IMP going down. Print message, and if not immediate, 209 * set off a timer to insure things will be reset at the 210 * appropriate time. 211 */ 212 case IMPTYPE_DOWN: 213 if (sc->imp_state < IMPS_INIT) 214 goto drop; 215 if ((ip->il_link & IMP_DMASK) == 0) { 216 sc->imp_state = IMPS_GOINGDOWN; 217 timeout(impdown, (caddr_t)sc, 30 * hz); 218 } 219 impmsg(sc, "going down %s", 220 (u_int)impmessage[ip->il_link&IMP_DMASK]); 221 goto drop; 222 223 /* 224 * A NOP usually seen during the initialization sequence. 225 * Compare the local address with that in the message. 226 * Reset the local address notion if it doesn't match. 227 */ 228 case IMPTYPE_NOOP: 229 if (sc->imp_state == IMPS_DOWN) { 230 sc->imp_state = IMPS_INIT; 231 sc->imp_dropcnt = IMP_DROPCNT; 232 } 233 if (sc->imp_state == IMPS_INIT && --sc->imp_dropcnt > 0) 234 goto drop; 235 sin = (struct sockaddr_in *)&sc->imp_if.if_addr; 236 if( ip->il_imp != 0 ) { /* BRL */ 237 struct in_addr leader_addr; 238 imp_leader_to_addr( &leader_addr, ip, &sc->imp_if ); 239 if( sin->sin_addr.s_addr != leader_addr.s_addr ) { 240 impmsg(sc, "address x%x (%d/%d)", 241 htonl( leader_addr.s_addr ), 242 (u_int)ip->il_host, 243 htons(ip->il_imp) ); 244 sin->sin_addr.s_addr = leader_addr.s_addr; 245 sc->imp_if.if_host[0] = in_lnaof( leader_addr.s_addr ); 246 } 247 } 248 sc->imp_state = IMPS_UP; 249 sc->imp_if.if_flags |= IFF_UP; 250 if_rtinit(&sc->imp_if, RTF_UP); 251 goto drop; 252 253 /* 254 * RFNM or INCOMPLETE message, send next 255 * message on the q. We could pass incomplete's 256 * up to the next level, but this currently isn't 257 * needed. 258 */ 259 case IMPTYPE_RFNM: 260 case IMPTYPE_INCOMPLETE: 261 if (hp = hostlookup(addr)) { 262 if (hp->h_rfnm == 0) 263 hp->h_flags &= ~HF_INUSE; 264 else if (next = hostdeque(hp)) 265 (void) impsnd(&sc->imp_if, next); 266 } 267 goto drop; 268 269 /* 270 * Host or IMP can't be reached. Flush any packets 271 * awaiting transmission and release the host structure. 272 */ 273 case IMPTYPE_HOSTDEAD: 274 case IMPTYPE_HOSTUNREACH: 275 impnotify((int)ip->il_mtype, (struct control_leader *)ip, 276 hostlookup(addr), sc->imp_if); 277 goto rawlinkin; 278 279 /* 280 * Error in data. Clear RFNM status for this host and send 281 * noops to the IMP to clear the interface. 282 */ 283 case IMPTYPE_BADDATA: 284 impmsg(sc, "data error"); 285 if (hp = hostlookup(addr)) 286 hp->h_rfnm = 0; 287 impnoops(sc); 288 goto drop; 289 290 /* 291 * Interface reset. 292 */ 293 case IMPTYPE_RESET: 294 impmsg(sc, "interface reset"); 295 hostreset(sc->imp_if.if_net); /* clear RFNM counts */ 296 impnoops(sc); 297 goto drop; 298 299 default: 300 sc->imp_if.if_collisions++; /* XXX */ 301 goto drop; 302 } 303 304 /* 305 * Data for a protocol. Dispatch to the appropriate 306 * protocol routine (running at software interrupt). 307 * If this isn't a raw interface, advance pointer 308 * into mbuf past leader. 309 */ 310 switch (ip->il_link) { 311 312 #ifdef INET 313 case IMPLINK_IP: 314 m->m_len -= sizeof(struct imp_leader); 315 m->m_off += sizeof(struct imp_leader); 316 schednetisr(NETISR_IP); 317 inq = &ipintrq; 318 break; 319 #endif 320 321 default: 322 rawlinkin: 323 impproto.sp_protocol = ip->il_link; 324 sin = (struct sockaddr_in *)&sc->imp_if.if_addr; 325 impdst.sin_addr = sin->sin_addr; 326 imp_leader_to_addr( &impsrc.sin_addr, ip, &sc->imp_if ); 327 raw_input(m, &impproto, (struct sockaddr *)&impsrc, 328 (struct sockaddr *)&impdst); 329 return; 330 } 331 if (IF_QFULL(inq)) { 332 IF_DROP(inq); 333 goto drop; 334 } 335 IF_ENQUEUE(inq, m); 336 return; 337 338 drop: 339 m_freem(m); 340 } 341 342 /* 343 * Bring the IMP down after notification. 344 */ 345 impdown(sc) 346 struct imp_softc *sc; 347 { 348 int s = splimp(); 349 350 sc->imp_state = IMPS_DOWN; 351 impmsg(sc, "marked down"); 352 hostreset(sc->imp_if.if_net); 353 if_down(&sc->imp_if); 354 splx(s); 355 } 356 357 /*VARARGS*/ 358 impmsg(sc, fmt, a1, a2, a3) 359 struct imp_softc *sc; 360 char *fmt; 361 u_int a1; 362 { 363 364 printf("imp%d: ", sc->imp_if.if_unit); 365 printf(fmt, a1, a2, a3); 366 printf("\n"); 367 } 368 369 /* 370 * Process an IMP "error" message, passing this 371 * up to the higher level protocol. 372 */ 373 impnotify(what, cp, hp, ifp) 374 int what; 375 struct control_leader *cp; 376 struct host *hp; 377 struct ifnet *ifp; /* BRL */ 378 { 379 struct in_addr in; 380 381 imp_leader_to_addr( &in, (struct imp_leader *) cp, ifp ); /* BRL */ 382 383 if (cp->dl_link != IMPLINK_IP) 384 raw_ctlinput(what, (caddr_t)&in); 385 else 386 ip_ctlinput(what, (caddr_t)&in); 387 if (hp) { 388 hp->h_flags |= (1 << what); 389 hostfree(hp); 390 hp->h_timer = HOSTDEADTIMER; 391 } 392 } 393 394 /* 395 * ARPAnet 1822 output routine. 396 * Called from higher level protocol routines to set up messages for 397 * transmission to the imp. Sets up the header and calls impsnd to 398 * enqueue the message for this IMP's hardware driver. 399 */ 400 impoutput(ifp, m0, dst) 401 register struct ifnet *ifp; 402 struct mbuf *m0; 403 struct sockaddr *dst; 404 { 405 register struct imp_leader *imp; 406 register struct mbuf *m = m0; 407 int dhost, dimp, dlink, len, dnet; 408 int error = 0; 409 410 /* 411 * Don't even try if the IMP is unavailable. 412 */ 413 if (imp_softc[ifp->if_unit].imp_state != IMPS_UP) { 414 error = ENETDOWN; 415 goto drop; 416 } 417 418 switch (dst->sa_family) { 419 420 #ifdef INET 421 case AF_INET: { 422 struct ip *ip = mtod(m0, struct ip *); 423 struct sockaddr_in *sin = (struct sockaddr_in *)dst; 424 425 dhost = sin->sin_addr.s_host; 426 dimp = sin->sin_addr.s_impno; 427 dlink = IMPLINK_IP; 428 dnet = 0; 429 len = ntohs((u_short)ip->ip_len); 430 break; 431 } 432 #endif 433 case AF_IMPLINK: 434 goto leaderexists; 435 436 default: 437 printf("imp%d: can't handle af%d\n", ifp->if_unit, 438 dst->sa_family); 439 error = EAFNOSUPPORT; 440 goto drop; 441 } 442 443 /* 444 * Add IMP leader. If there's not enough space in the 445 * first mbuf, allocate another. If that should fail, we 446 * drop this sucker. 447 */ 448 if (m->m_off > MMAXOFF || 449 MMINOFF + sizeof(struct imp_leader) > m->m_off) { 450 m = m_get(M_DONTWAIT, MT_HEADER); 451 if (m == 0) { 452 error = ENOBUFS; 453 goto drop; 454 } 455 m->m_next = m0; 456 m->m_len = sizeof(struct imp_leader); 457 } else { 458 m->m_off -= sizeof(struct imp_leader); 459 m->m_len += sizeof(struct imp_leader); 460 } 461 imp = mtod(m, struct imp_leader *); 462 imp->il_format = IMP_NFF; 463 imp->il_mtype = IMPTYPE_DATA; 464 imp_addr_to_leader(imp, 465 ((struct sockaddr_in *) dst)->sin_addr.s_addr ); /* BRL */ 466 imp->il_length = htons( (u_short) len << 3 ); /* BRL */ 467 imp->il_link = dlink; 468 imp->il_flags = imp->il_htype = imp->il_subtype = 0; 469 470 leaderexists: 471 return (impsnd(ifp, m)); 472 drop: 473 m_freem(m0); 474 return (error); 475 } 476 477 /* 478 * Put a message on an interface's output queue. 479 * Perform RFNM counting: no more than 8 message may be 480 * in flight to any one host. 481 */ 482 impsnd(ifp, m) 483 struct ifnet *ifp; 484 struct mbuf *m; 485 { 486 register struct imp_leader *ip; 487 register struct host *hp; 488 struct impcb *icp; 489 int s, error; 490 491 ip = mtod(m, struct imp_leader *); 492 493 /* 494 * Do RFNM counting for data messages 495 * (no more than 8 outstanding to any host) 496 */ 497 s = splimp(); 498 if (ip->il_mtype == IMPTYPE_DATA) { 499 struct in_addr addr; 500 501 imp_leader_to_addr( &addr, ip, ifp ); /* BRL */ 502 if ((hp = hostlookup(addr)) == 0) 503 hp = hostenter(addr); 504 if (hp && (hp->h_flags & (HF_DEAD|HF_UNREACH))) { 505 error = hp->h_flags&HF_DEAD ? EHOSTDOWN : EHOSTUNREACH; 506 hp->h_timer = HOSTDEADTIMER; 507 hp->h_flags &= ~HF_INUSE; 508 goto bad; 509 } 510 511 /* 512 * If IMP would block, queue until RFNM 513 */ 514 if (hp) { 515 #ifndef NORFNM /* BRL */ 516 if (hp->h_rfnm < 8) 517 #endif 518 { 519 hp->h_rfnm++; 520 goto enque; 521 } 522 if (hp->h_qcnt < 8) { /* high water mark */ 523 HOST_ENQUE(hp, m); 524 goto start; 525 } 526 } 527 error = ENOBUFS; 528 goto bad; 529 } 530 enque: 531 if (IF_QFULL(&ifp->if_snd)) { 532 IF_DROP(&ifp->if_snd); 533 error = ENOBUFS; 534 bad: 535 m_freem(m); 536 splx(s); 537 return (error); 538 } 539 IF_ENQUEUE(&ifp->if_snd, m); 540 start: 541 icp = &imp_softc[ifp->if_unit].imp_cb; 542 if (icp->ic_oactive == 0) 543 (*icp->ic_start)(ifp->if_unit); 544 splx(s); 545 return (0); 546 } 547 548 /* 549 * Put three 1822 NOOPs at the head of the output queue. 550 * Part of host-IMP initialization procedure. 551 * (Should return success/failure, but noone knows 552 * what to do with this, so why bother?) 553 * This routine is always called at splimp, so we don't 554 * protect the call to IF_PREPEND. 555 */ 556 impnoops(sc) 557 register struct imp_softc *sc; 558 { 559 register i; 560 register struct mbuf *m; 561 register struct control_leader *cp; 562 563 sc->imp_dropcnt = IMP_DROPCNT; 564 for (i = 0; i < IMP_DROPCNT + 1; i++ ) { 565 if ((m = m_getclr(M_DONTWAIT, MT_HEADER)) == 0) 566 return; 567 m->m_len = sizeof(struct control_leader); 568 cp = mtod(m, struct control_leader *); 569 cp->dl_format = IMP_NFF; 570 cp->dl_link = i; 571 cp->dl_mtype = IMPTYPE_NOOP; 572 IF_PREPEND(&sc->imp_if.if_snd, m); 573 } 574 if (sc->imp_cb.ic_oactive == 0) 575 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 576 } 577 578 /* 579 * Process an ioctl request. 580 */ 581 impioctl(ifp, cmd, data) 582 register struct ifnet *ifp; 583 int cmd; 584 caddr_t data; 585 { 586 struct ifreq *ifr = (struct ifreq *)data; 587 struct sockaddr_in *sin; 588 int s = splimp(), error = 0; 589 590 switch (cmd) { 591 592 case SIOCSIFADDR: 593 if (ifp->if_flags & IFF_RUNNING) 594 if_rtinit(ifp, -1); /* delete previous route */ 595 sin = (struct sockaddr_in *)&ifr->ifr_addr; 596 ifp->if_net = in_netof(sin->sin_addr); 597 sin = (struct sockaddr_in *)&ifp->if_addr; 598 sin->sin_family = AF_INET; 599 /* host number filled in already, or filled in later */ 600 sin->sin_addr = if_makeaddr(ifp->if_net, ifp->if_host[0]); 601 if (ifp->if_flags & IFF_RUNNING) 602 if_rtinit(ifp, RTF_UP); 603 else 604 impinit(ifp->if_unit); 605 break; 606 607 default: 608 error = EINVAL; 609 } 610 splx(s); 611 return (error); 612 } 613 614 #ifdef IMPLEADERS 615 printleader(routine, ip) 616 char *routine; 617 register struct imp_leader *ip; 618 { 619 printf("%s: ", routine); 620 printbyte((char *)ip, 12); 621 printf("<fmt=%x,net=%x,flags=%x,mtype=", ip->il_format, ip->il_network, 622 ip->il_flags); 623 if (ip->il_mtype <= IMPTYPE_READY) 624 printf("%s,", impleaders[ip->il_mtype]); 625 else 626 printf("%x,", ip->il_mtype); 627 printf("htype=%x,host=%x,imp=%x,link=", ip->il_htype, ip->il_host, 628 ntohs(ip->il_imp)); 629 if (ip->il_link == IMPLINK_IP) 630 printf("ip,"); 631 else 632 printf("%x,", ip->il_link); 633 printf("subtype=%x,len=%x>\n",ip->il_subtype,ntohs(ip->il_length)>>3); 634 } 635 636 printbyte(cp, n) 637 register char *cp; 638 int n; 639 { 640 register i, j, c; 641 642 for (i=0; i<n; i++) { 643 c = *cp++; 644 for (j=0; j<2; j++) 645 putchar("0123456789abcdef"[(c>>((1-j)*4))&0xf]); 646 putchar(' '); 647 } 648 putchar('\n'); 649 } 650 #endif 651 652 /* 653 * Routine to convert from IMP Leader to InterNet Address. 654 * 655 * This procedure is necessary because IMPs may be assigned Class A, B, or C 656 * network numbers, but only have 8 bits in the leader to reflect the 657 * IMP "network number". The strategy is to take the network number from 658 * the ifnet structure, and blend in the host-on-imp and imp-on-net numbers 659 * from the leader. 660 * 661 * There is no support for "Logical Hosts". 662 * 663 * Class A: Net.Host.0.Imp 664 * Class B: Net.net.Host.Imp 665 * Class C: Net.net.net.(Host4|Imp4) 666 */ 667 imp_leader_to_addr( ap, ip, ifp ) 668 register struct in_addr *ap; 669 register struct imp_leader *ip; 670 register struct ifnet *ifp; 671 { 672 register long final; 673 register struct sockaddr_in *sin; 674 int imp = htons(ip->il_imp); 675 676 sin = (struct sockaddr_in *) (&ifp->if_addr); 677 final = htonl( sin->sin_addr.s_addr ); /* host order */ 678 679 if( IN_CLASSA( final ) ) { 680 final &= IN_CLASSA_NET; 681 final |= (imp & 0xFF) | ((ip->il_host & 0xFF)<<16); 682 } else if( IN_CLASSB( final ) ) { 683 final &= IN_CLASSB_NET; 684 final |= (imp & 0xFF) | ((ip->il_host & 0xFF)<<8); 685 } else { 686 final &= IN_CLASSC_NET; 687 final |= (imp & 0x0F) | ((ip->il_host & 0x0F)<<4); 688 } 689 ap->s_addr = htonl( final ); 690 } 691 692 /* 693 * Function to take InterNet address and fill in IMP leader fields. 694 */ 695 imp_addr_to_leader( imp, a ) 696 register struct imp_leader *imp; 697 long a; 698 { 699 register long addr = htonl( a ); /* host order */ 700 701 imp->il_network = 0; /* !! */ 702 703 if( IN_CLASSA( addr ) ) { 704 imp->il_host = ((addr>>16) & 0xFF); 705 imp->il_imp = addr & 0xFF; 706 } else if ( IN_CLASSB( addr ) ) { 707 imp->il_host = ((addr>>8) & 0xFF); 708 imp->il_imp = addr & 0xFF; 709 } else { 710 imp->il_host = ((addr>>4) & 0xF); 711 imp->il_imp = addr & 0xF; 712 } 713 imp->il_imp = htons(imp->il_imp); /* network order! */ 714 } 715 #endif 716