1 /* if_imp.c 4.15 82/03/15 */ 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 * TODO: 12 * rethink coupling between this module and device driver 13 * pass more error indications up to protocol modules 14 */ 15 #include "../h/param.h" 16 #include "../h/systm.h" 17 #include "../h/mbuf.h" 18 #include "../h/pte.h" 19 #include "../h/buf.h" 20 #include "../h/protosw.h" 21 #include "../h/socket.h" 22 #include "../h/ubareg.h" 23 #include "../h/ubavar.h" 24 #include "../h/cpu.h" 25 #include "../h/mtpr.h" 26 #include "../h/vmmac.h" 27 #include "../net/in.h" 28 #include "../net/in_systm.h" 29 #include "../net/if.h" 30 #include "../net/if_imp.h" 31 #include "../net/if_imphost.h" 32 #include "../net/ip.h" 33 #include "../net/ip_var.h" 34 35 /* 36 * IMP software status per interface. 37 * (partially shared with the hardware specific module) 38 * 39 * Each interface is referenced by a network interface structure, 40 * imp_if, which the routing code uses to locate the interface. 41 * This structure contains the output queue for the interface, its 42 * address, ... IMP specific structures used in connecting the 43 * IMP software modules to the hardware specific interface routines 44 * are stored here. The common structures are made visible to the 45 * interface driver by passing a pointer to the hardware routine 46 * at "attach" time. 47 * 48 * NOTE: imp_if and imp_cb are assumed adjacent in hardware code. 49 */ 50 struct imp_softc { 51 struct ifnet imp_if; /* network visible interface */ 52 struct impcb imp_cb; /* hooks to hardware module */ 53 u_char imp_state; /* current state of IMP */ 54 char imp_dropcnt; /* used during initialization */ 55 } imp_softc[NIMP]; 56 57 /* 58 * Messages from IMP regarding why 59 * it's going down. 60 */ 61 static char *impmessage[] = { 62 "in 30 seconds", 63 "for hardware PM", 64 "to reload software", 65 "for emergency reset" 66 }; 67 68 int impdown(), impinit(), impoutput(); 69 70 /* 71 * IMP attach routine. Called from hardware device attach routine 72 * at configuration time with a pointer to the UNIBUS device structure. 73 * Sets up local state and returns pointer to base of ifnet+impcb 74 * structures. This is then used by the device's attach routine 75 * set up its back pointers. 76 */ 77 impattach(ui) 78 struct uba_device *ui; 79 { 80 struct imp_softc *sc = &imp_softc[ui->ui_unit]; 81 register struct ifnet *ifp = &sc->imp_if; 82 83 COUNT(IMPATTACH); 84 /* UNIT COULD BE AMBIGUOUS */ 85 ifp->if_unit = ui->ui_unit; 86 ifp->if_name = "imp"; 87 ifp->if_mtu = IMP_MTU; 88 ifp->if_net = ui->ui_flags; 89 /* the host and imp fields will be filled in by the imp */ 90 ifp->if_addr = if_makeaddr(ifp->if_net, 0); 91 ifp->if_init = impinit; 92 ifp->if_output = impoutput; 93 /* reset is handled at the hardware level */ 94 if_attach(ifp); 95 /* kludge to hand pointers back to hardware attach routine */ 96 return ((int)&sc->imp_if); 97 } 98 99 #ifdef notdef 100 /* 101 * Timer routine to keep priming the IMP until it sends 102 * us the noops we need. Since we depend on the host and 103 * imp values returned in the noop messages, we must wait 104 * for them before we allow any outgoing traffic. 105 */ 106 imptimer(sc) 107 register struct imp_softc *sc; 108 { 109 int s = splimp(); 110 111 if (sc->imp_state != IMPS_INIT) { 112 splx(s); 113 return; 114 } 115 sc->imp_dropcnt = IMP_DROPCNT; 116 impnoops(sc); 117 timeout(imptimer, (caddr_t)sc, 30 * hz); 118 splx(s); 119 } 120 #endif 121 122 /* 123 * IMP initialization routine: call hardware module to 124 * setup UNIBUS resources, init state and get ready for 125 * NOOPs the IMP should send us, and that we want to drop. 126 */ 127 impinit(unit) 128 int unit; 129 { 130 register struct imp_softc *sc = &imp_softc[unit]; 131 132 if ((*sc->imp_cb.ic_init)(unit) == 0) { 133 sc->imp_state = IMPS_DOWN; 134 return; 135 } 136 sc->imp_state = IMPS_INIT; 137 #ifdef notdef 138 imptimer(sc); 139 #else 140 sc->imp_dropcnt = IMP_DROPCNT; 141 impnoops(sc); 142 #endif 143 } 144 145 struct sockproto impproto = { PF_IMPLINK }; 146 struct sockaddr_in impdst = { AF_IMPLINK }; 147 struct sockaddr_in impsrc = { AF_IMPLINK }; 148 149 /* 150 * ARPAnet 1822 input routine. 151 * Called from hardware input interrupt routine to handle 1822 152 * IMP-host messages. Type 0 messages (non-control) are 153 * passed to higher level protocol processors on the basis 154 * of link number. Other type messages (control) are handled here. 155 */ 156 impinput(unit, m) 157 int unit; 158 register struct mbuf *m; 159 { 160 register struct imp_leader *ip; 161 register struct imp_softc *sc = &imp_softc[unit]; 162 register struct host *hp; 163 register struct ifqueue *inq; 164 struct control_leader *cp; 165 struct in_addr addr; 166 struct mbuf *next; 167 168 COUNT(IMPINPUT); 169 /* 170 * Verify leader length. Be careful with control 171 * message which don't get a length included. 172 * We should generate a "bad leader" message 173 * to the IMP about messages too short. 174 */ 175 if (m->m_len < sizeof(struct control_leader) && 176 (m = m_pullup(m, sizeof(struct control_leader))) == 0) 177 return; 178 cp = mtod(m, struct control_leader *); 179 if (cp->dl_mtype == IMPTYPE_DATA) 180 if (m->m_len < sizeof(struct imp_leader) && 181 (m = m_pullup(m, sizeof(struct imp_leader))) == 0) 182 return; 183 ip = mtod(m, struct imp_leader *); 184 185 /* 186 * Check leader type -- should notify IMP 187 * in case of failure... 188 */ 189 if (ip->il_format != IMP_NFF) { 190 sc->imp_if.if_collisions++; /* XXX */ 191 goto drop; 192 } 193 194 /* 195 * Certain messages require a host structure. 196 * Do this in one shot here. 197 */ 198 switch (ip->il_mtype) { 199 200 case IMPTYPE_RFNM: 201 case IMPTYPE_INCOMPLETE: 202 case IMPTYPE_HOSTDEAD: 203 case IMPTYPE_HOSTUNREACH: 204 case IMPTYPE_BADDATA: 205 #ifdef notdef 206 addr.s_net = ip->il_network; 207 #else 208 addr.s_net = 0; 209 #endif 210 addr.s_imp = ip->il_imp; 211 addr.s_host = ip->il_host; 212 hp = hostlookup(addr); 213 break; 214 } 215 216 switch (ip->il_mtype) { 217 218 /* 219 * Data for a protocol. Dispatch to the appropriate 220 * protocol routine (running at software interrupt). 221 * If this isn't a raw interface, advance pointer 222 * into mbuf past leader (done below). 223 */ 224 case IMPTYPE_DATA: 225 ip->il_length = 226 (ntohs(ip->il_length) >> 3) - sizeof(struct imp_leader); 227 break; 228 229 /* 230 * IMP leader error. Reset the IMP and discard the packet. 231 */ 232 case IMPTYPE_BADLEADER: 233 /* 234 * According to 1822 document, this message 235 * will be generated in response to the 236 * first noop sent to the IMP after 237 * the host resets the IMP interface. 238 */ 239 if (sc->imp_state != IMPS_INIT) { 240 impmsg(sc, "leader error"); 241 hostreset(sc->imp_if.if_net); /* XXX */ 242 impnoops(sc); 243 } 244 goto rawlinkin; 245 246 /* 247 * IMP going down. Print message, and if not immediate, 248 * set off a timer to insure things will be reset at the 249 * appropriate time. 250 */ 251 case IMPTYPE_DOWN: 252 if ((ip->il_link & IMP_DMASK) == 0) { 253 sc->imp_state = IMPS_GOINGDOWN; 254 timeout(impdown, (caddr_t)sc, 30 * hz); 255 } 256 impmsg(sc, "going down %s", 257 (u_int)impmessage[ip->il_link&IMP_DMASK]); 258 goto rawlinkin; 259 260 /* 261 * A NOP usually seen during the initialization sequence. 262 * Compare the local address with that in the message. 263 * Reset the local address notion if it doesn't match. 264 */ 265 case IMPTYPE_NOOP: { 266 register struct in_addr *sin; 267 268 if (sc->imp_state == IMPS_DOWN) { 269 sc->imp_state = IMPS_INIT; 270 sc->imp_dropcnt = IMP_DROPCNT; 271 } 272 if (sc->imp_state != IMPS_INIT || --sc->imp_dropcnt > 0) 273 goto rawlinkin; 274 sc->imp_state = IMPS_UP; 275 sin = &sc->imp_if.if_addr; 276 sc->imp_if.if_host[0] = sin->s_host = ip->il_host; 277 sin->s_imp = ip->il_imp; 278 impmsg(sc, "reset (host %d/imp %d)", (u_int)ip->il_host, 279 ntohs(ip->il_imp)); 280 /* restart output in case something was q'd */ 281 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 282 goto rawlinkin; 283 } 284 285 /* 286 * RFNM or INCOMPLETE message, record in 287 * host table and prime output routine. 288 * 289 * SHOULD NOTIFY PROTOCOL ABOUT INCOMPLETES. 290 */ 291 case IMPTYPE_RFNM: 292 case IMPTYPE_INCOMPLETE: 293 if (hp && hp->h_rfnm) 294 if (next = hostdeque(hp)) 295 (void) impsnd(&sc->imp_if, next); 296 goto rawlinkin; 297 298 /* 299 * Host or IMP can't be reached. Flush any packets 300 * awaiting transmission and release the host structure. 301 * 302 * TODO: NOTIFY THE PROTOCOL 303 */ 304 case IMPTYPE_HOSTDEAD: 305 impmsg(sc, "host dead"); /* XXX */ 306 goto common; /* XXX */ 307 308 /* SHOULD SIGNAL ROUTING DAEMON */ 309 case IMPTYPE_HOSTUNREACH: 310 impmsg(sc, "host unreachable"); /* XXX */ 311 common: 312 if (hp) 313 hostfree(hp); /* won't work right */ 314 goto rawlinkin; 315 316 /* 317 * Error in data. Clear RFNM status for this host and send 318 * noops to the IMP to clear the interface. 319 */ 320 case IMPTYPE_BADDATA: 321 impmsg(sc, "data error"); 322 if (hp) 323 hp->h_rfnm = 0; 324 impnoops(sc); 325 goto rawlinkin; 326 327 /* 328 * Interface reset. 329 */ 330 case IMPTYPE_RESET: 331 impmsg(sc, "interface reset"); 332 impnoops(sc); 333 goto rawlinkin; 334 335 default: 336 sc->imp_if.if_collisions++; /* XXX */ 337 goto rawlinkin; 338 } 339 340 /* 341 * Queue on protocol's input queue. 342 */ 343 switch (ip->il_link) { 344 345 #ifdef INET 346 case IMPLINK_IP: 347 m->m_len -= sizeof(struct imp_leader); 348 m->m_off += sizeof(struct imp_leader); 349 setipintr(); 350 inq = &ipintrq; 351 break; 352 #endif 353 354 default: 355 rawlinkin: 356 impproto.sp_protocol = ip->il_link; 357 impdst.sin_addr = sc->imp_if.if_addr; 358 impsrc.sin_addr.s_net = ip->il_network; 359 impsrc.sin_addr.s_host = ip->il_host; 360 impsrc.sin_addr.s_imp = ip->il_imp; 361 raw_input(m, &impproto, (struct sockaddr *)&impdst, 362 (struct sockaddr *)&impsrc); 363 return; 364 } 365 if (IF_QFULL(inq)) { 366 IF_DROP(inq); 367 goto drop; 368 } 369 IF_ENQUEUE(inq, m); 370 return; 371 372 drop: 373 m_freem(m); 374 } 375 376 /* 377 * Bring the IMP down after notification. 378 */ 379 impdown(sc) 380 struct imp_softc *sc; 381 { 382 383 sc->imp_state = IMPS_DOWN; 384 impmsg(sc, "marked down"); 385 /* notify protocols with messages waiting? */ 386 } 387 388 /*VARARGS*/ 389 impmsg(sc, fmt, a1, a2) 390 struct imp_softc *sc; 391 char *fmt; 392 u_int a1; 393 { 394 395 printf("imp%d: ", sc->imp_if.if_unit); 396 printf(fmt, a1, a2); 397 printf("\n"); 398 } 399 400 /* 401 * ARPAnet 1822 output routine. 402 * Called from higher level protocol routines to set up messages for 403 * transmission to the imp. Sets up the header and calls impsnd to 404 * enqueue the message for this IMP's hardware driver. 405 */ 406 impoutput(ifp, m0, pf) 407 register struct ifnet *ifp; 408 struct mbuf *m0; 409 { 410 register struct imp_leader *imp; 411 register struct mbuf *m = m0; 412 int x, dhost, dimp, dlink, len; 413 414 COUNT(IMPOUTPUT); 415 /* 416 * Don't even try if the IMP is unavailable. 417 */ 418 x = imp_softc[ifp->if_unit].imp_state; 419 if (x == IMPS_DOWN || x == IMPS_GOINGDOWN) 420 goto drop; 421 422 switch (pf) { 423 424 #ifdef INET 425 case PF_INET: { 426 register struct ip *ip = mtod(m0, struct ip *); 427 428 dhost = ip->ip_dst.s_host; 429 dimp = ip->ip_dst.s_imp; 430 dlink = IMPLINK_IP; 431 len = ntohs((u_short)ip->ip_len); 432 break; 433 } 434 #endif 435 case PF_IMPLINK: 436 goto leaderexists; 437 438 default: 439 printf("imp%d: can't encapsulate pf%d\n", ifp->if_unit, pf); 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); 451 if (m == 0) 452 goto drop; 453 m->m_next = m0; 454 m->m_off = MMINOFF; 455 m->m_len = sizeof(struct imp_leader); 456 } else { 457 m->m_off -= sizeof(struct imp_leader); 458 m->m_len += sizeof(struct imp_leader); 459 } 460 imp = mtod(m, struct imp_leader *); 461 imp->il_format = IMP_NFF; 462 imp->il_mtype = IMPTYPE_DATA; 463 imp->il_network = 0; 464 imp->il_host = dhost; 465 imp->il_imp = dimp; 466 imp->il_length = 467 htons((u_short)(len + sizeof(struct imp_leader)) << 3); 468 imp->il_link = dlink; 469 imp->il_flags = imp->il_htype = imp->il_subtype = 0; 470 471 leaderexists: 472 /* 473 * Hand message to impsnd to perform RFNM counting 474 * and eventual transmission. 475 */ 476 return (impsnd(ifp, m)); 477 drop: 478 m_freem(m0); 479 return (0); 480 } 481 482 /* 483 * Put a message on an interface's output queue. 484 * Perform RFNM counting: no more than 8 message may be 485 * in flight to any one host. 486 */ 487 impsnd(ifp, m) 488 struct ifnet *ifp; 489 struct mbuf *m; 490 { 491 register struct imp_leader *ip; 492 register struct host *hp; 493 struct impcb *icp; 494 int x; 495 496 COUNT(IMPSND); 497 ip = mtod(m, struct imp_leader *); 498 499 /* 500 * Do RFNM counting for data messages 501 * (no more than 8 outstanding to any host) 502 */ 503 x = splimp(); 504 if (ip->il_mtype == IMPTYPE_DATA) { 505 struct in_addr addr; 506 507 #ifdef notdef 508 addr.s_net = ip->il_network; 509 #else 510 addr.s_net = 0; 511 #endif 512 addr.s_host = ip->il_host; 513 addr.s_imp = ip->il_imp; 514 if ((hp = hostlookup(addr)) == 0) 515 hp = hostenter(addr); 516 517 /* 518 * If IMP would block, queue until RFNM 519 */ 520 if (hp) { 521 if (hp->h_rfnm < 8) { 522 hp->h_rfnm++; 523 goto enque; 524 } 525 if (hp->h_qcnt < 8) { /* high water mark */ 526 HOST_ENQUE(hp, m); 527 goto start; 528 } 529 } 530 m_freem(m); 531 splx(x); 532 return (0); 533 } 534 enque: 535 if (IF_QFULL(&ifp->if_snd)) { 536 IF_DROP(&ifp->if_snd); 537 m_freem(m); 538 splx(x); 539 return (0); 540 } 541 IF_ENQUEUE(&ifp->if_snd, m); 542 start: 543 splx(x); 544 icp = &imp_softc[ifp->if_unit].imp_cb; 545 if (icp->ic_oactive == 0) 546 (*icp->ic_start)(ifp->if_unit); 547 return (1); 548 } 549 550 /* 551 * Put three 1822 NOOPs at the head of the output queue. 552 * Part of host-IMP initialization procedure. 553 * (Should return success/failure, but noone knows 554 * what to do with this, so why bother?) 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 int x; 563 564 COUNT(IMPNOOPS); 565 sc->imp_state = IMPS_INIT; 566 sc->imp_dropcnt = IMP_DROPCNT; 567 for (i = 0; i < IMP_DROPCNT + 1; i++ ) { 568 if ((m = m_getclr(M_DONTWAIT)) == 0) 569 return; 570 m->m_off = MMINOFF; 571 m->m_len = sizeof(struct control_leader); 572 cp = mtod(m, struct control_leader *); 573 cp->dl_format = IMP_NFF; 574 cp->dl_link = i; 575 cp->dl_mtype = IMPTYPE_NOOP; 576 x = splimp(); 577 IF_PREPEND(&sc->imp_if.if_snd, m); 578 splx(x); 579 } 580 if (sc->imp_cb.ic_oactive == 0) 581 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 582 } 583 584 #ifdef IMPLEADERS 585 printleader(routine, ip) 586 char *routine; 587 register struct imp_leader *ip; 588 { 589 printf("%s: ", routine); 590 printbyte((char *)ip, 12); 591 printf("<fmt=%x,net=%x,flags=%x,mtype=", ip->il_format, ip->il_network, 592 ip->il_flags); 593 if (ip->il_mtype <= IMPTYPE_READY) 594 printf("%s,", impleaders[ip->il_mtype]); 595 else 596 printf("%x,", ip->il_mtype); 597 printf("htype=%x,host=%x,imp=%x,link=", ip->il_htype, ip->il_host, 598 ip->il_impno); 599 if (ip->il_link == IMPLINK_IP) 600 printf("ip,"); 601 else 602 printf("%x,", ip->il_link); 603 printf("subtype=%x,len=%x>\n",ip->il_subtype,ntohs(ip->il_length)>>3); 604 } 605 606 printbyte(cp, n) 607 register char *cp; 608 int n; 609 { 610 register i, j, c; 611 612 for (i=0; i<n; i++) { 613 c = *cp++; 614 for (j=0; j<2; j++) 615 putchar("0123456789abcdef"[(c>>((1-j)*4))&0xf]); 616 putchar(' '); 617 } 618 putchar('\n'); 619 } 620 #endif 621 #endif 622