1 /* if_imp.c 4.17 82/03/19 */ 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 - sizeof (struct imp_leader); 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 /* verify leader length. */ 170 if (m->m_len < sizeof(struct control_leader) && 171 (m = m_pullup(m, sizeof(struct control_leader))) == 0) 172 return; 173 cp = mtod(m, struct control_leader *); 174 if (cp->dl_mtype == IMPTYPE_DATA) 175 if (m->m_len < sizeof(struct imp_leader) && 176 (m = m_pullup(m, sizeof(struct imp_leader))) == 0) 177 return; 178 ip = mtod(m, struct imp_leader *); 179 180 /* check leader type */ 181 if (ip->il_format != IMP_NFF) { 182 sc->imp_if.if_collisions++; /* XXX */ 183 goto drop; 184 } 185 186 /* 187 * Certain messages require a host structure. 188 * Do this in one shot here. 189 */ 190 switch (ip->il_mtype) { 191 192 case IMPTYPE_RFNM: 193 case IMPTYPE_INCOMPLETE: 194 case IMPTYPE_HOSTDEAD: 195 case IMPTYPE_HOSTUNREACH: 196 case IMPTYPE_BADDATA: 197 #ifdef notdef 198 addr.s_net = ip->il_network; 199 #else 200 addr.s_net = 0; 201 #endif 202 addr.s_imp = ip->il_imp; 203 addr.s_host = ip->il_host; 204 hp = hostlookup(addr); 205 break; 206 } 207 208 switch (ip->il_mtype) { 209 210 case IMPTYPE_DATA: 211 break; 212 213 /* 214 * IMP leader error. Reset the IMP and discard the packet. 215 */ 216 case IMPTYPE_BADLEADER: 217 /* 218 * According to 1822 document, this message 219 * will be generated in response to the 220 * first noop sent to the IMP after 221 * the host resets the IMP interface. 222 */ 223 if (sc->imp_state != IMPS_INIT) { 224 impmsg(sc, "leader error"); 225 hostreset(sc->imp_if.if_net); /* XXX */ 226 impnoops(sc); 227 } 228 goto rawlinkin; 229 230 /* 231 * IMP going down. Print message, and if not immediate, 232 * set off a timer to insure things will be reset at the 233 * appropriate time. 234 */ 235 case IMPTYPE_DOWN: 236 if ((ip->il_link & IMP_DMASK) == 0) { 237 sc->imp_state = IMPS_GOINGDOWN; 238 timeout(impdown, (caddr_t)sc, 30 * hz); 239 } 240 impmsg(sc, "going down %s", 241 (u_int)impmessage[ip->il_link&IMP_DMASK]); 242 goto rawlinkin; 243 244 /* 245 * A NOP usually seen during the initialization sequence. 246 * Compare the local address with that in the message. 247 * Reset the local address notion if it doesn't match. 248 */ 249 case IMPTYPE_NOOP: { 250 register struct in_addr *sin; 251 252 if (sc->imp_state == IMPS_DOWN) { 253 sc->imp_state = IMPS_INIT; 254 sc->imp_dropcnt = IMP_DROPCNT; 255 } 256 if (sc->imp_state != IMPS_INIT || --sc->imp_dropcnt > 0) 257 goto rawlinkin; 258 sc->imp_state = IMPS_UP; 259 sin = &sc->imp_if.if_addr; 260 sc->imp_if.if_host[0] = sin->s_host = ip->il_host; 261 sin->s_imp = ip->il_imp; 262 impmsg(sc, "reset (host %d/imp %d)", (u_int)ip->il_host, 263 ntohs(ip->il_imp)); 264 /* restart output in case something was q'd */ 265 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 266 goto rawlinkin; 267 } 268 269 /* 270 * RFNM or INCOMPLETE message, record in 271 * host table and prime output routine. 272 * 273 * SHOULD NOTIFY PROTOCOL ABOUT INCOMPLETES. 274 */ 275 case IMPTYPE_RFNM: 276 case IMPTYPE_INCOMPLETE: 277 if (hp && hp->h_rfnm) 278 if (next = hostdeque(hp)) 279 (void) impsnd(&sc->imp_if, next); 280 goto rawlinkin; 281 282 /* 283 * Host or IMP can't be reached. Flush any packets 284 * awaiting transmission and release the host structure. 285 * 286 * TODO: NOTIFY THE PROTOCOL 287 */ 288 case IMPTYPE_HOSTDEAD: 289 impmsg(sc, "host dead"); /* XXX */ 290 goto common; /* XXX */ 291 292 /* SHOULD SIGNAL ROUTING DAEMON */ 293 case IMPTYPE_HOSTUNREACH: 294 impmsg(sc, "host unreachable"); /* XXX */ 295 common: 296 if (hp) 297 hostfree(hp); /* won't work right */ 298 goto rawlinkin; 299 300 /* 301 * Error in data. Clear RFNM status for this host and send 302 * noops to the IMP to clear the interface. 303 */ 304 case IMPTYPE_BADDATA: 305 impmsg(sc, "data error"); 306 if (hp) 307 hp->h_rfnm = 0; 308 impnoops(sc); 309 goto rawlinkin; 310 311 /* 312 * Interface reset. 313 */ 314 case IMPTYPE_RESET: 315 impmsg(sc, "interface reset"); 316 impnoops(sc); 317 goto rawlinkin; 318 319 default: 320 sc->imp_if.if_collisions++; /* XXX */ 321 goto rawlinkin; 322 } 323 324 /* 325 * Data for a protocol. Dispatch to the appropriate 326 * protocol routine (running at software interrupt). 327 * If this isn't a raw interface, advance pointer 328 * into mbuf past leader. 329 */ 330 switch (ip->il_link) { 331 332 #ifdef INET 333 case IMPLINK_IP: 334 m->m_len -= sizeof(struct imp_leader); 335 m->m_off += sizeof(struct imp_leader); 336 setipintr(); 337 inq = &ipintrq; 338 break; 339 #endif 340 341 default: 342 rawlinkin: 343 impproto.sp_protocol = ip->il_link; 344 impdst.sin_addr = sc->imp_if.if_addr; 345 impsrc.sin_addr.s_net = ip->il_network; 346 impsrc.sin_addr.s_host = ip->il_host; 347 impsrc.sin_addr.s_imp = ip->il_imp; 348 raw_input(m, &impproto, (struct sockaddr *)&impdst, 349 (struct sockaddr *)&impsrc); 350 return; 351 } 352 if (IF_QFULL(inq)) { 353 IF_DROP(inq); 354 goto drop; 355 } 356 IF_ENQUEUE(inq, m); 357 return; 358 359 drop: 360 m_freem(m); 361 } 362 363 /* 364 * Bring the IMP down after notification. 365 */ 366 impdown(sc) 367 struct imp_softc *sc; 368 { 369 370 sc->imp_state = IMPS_DOWN; 371 impmsg(sc, "marked down"); 372 /* notify protocols with messages waiting? */ 373 } 374 375 /*VARARGS*/ 376 impmsg(sc, fmt, a1, a2) 377 struct imp_softc *sc; 378 char *fmt; 379 u_int a1; 380 { 381 382 printf("imp%d: ", sc->imp_if.if_unit); 383 printf(fmt, a1, a2); 384 printf("\n"); 385 } 386 387 /* 388 * ARPAnet 1822 output routine. 389 * Called from higher level protocol routines to set up messages for 390 * transmission to the imp. Sets up the header and calls impsnd to 391 * enqueue the message for this IMP's hardware driver. 392 */ 393 impoutput(ifp, m0, pf) 394 register struct ifnet *ifp; 395 struct mbuf *m0; 396 { 397 register struct imp_leader *imp; 398 register struct mbuf *m = m0; 399 int x, dhost, dimp, dlink, len, dnet; 400 401 COUNT(IMPOUTPUT); 402 /* 403 * Don't even try if the IMP is unavailable. 404 */ 405 x = imp_softc[ifp->if_unit].imp_state; 406 if (x == IMPS_DOWN || x == IMPS_GOINGDOWN) 407 goto drop; 408 409 switch (pf) { 410 411 #ifdef INET 412 case PF_INET: { 413 register struct ip *ip = mtod(m0, struct ip *); 414 415 dhost = ip->ip_dst.s_host; 416 dimp = ip->ip_dst.s_impno; 417 dlink = IMPLINK_IP; 418 dnet = 0; 419 len = ntohs((u_short)ip->ip_len); 420 break; 421 } 422 #endif 423 case PF_IMPLINK: 424 goto leaderexists; 425 426 default: 427 printf("imp%d: can't encapsulate pf%d\n", ifp->if_unit, pf); 428 goto drop; 429 } 430 431 /* 432 * Add IMP leader. If there's not enough space in the 433 * first mbuf, allocate another. If that should fail, we 434 * drop this sucker. 435 */ 436 if (m->m_off > MMAXOFF || 437 MMINOFF + sizeof(struct imp_leader) > m->m_off) { 438 m = m_get(M_DONTWAIT); 439 if (m == 0) 440 goto drop; 441 m->m_next = m0; 442 m->m_off = MMINOFF; 443 m->m_len = sizeof(struct imp_leader); 444 } else { 445 m->m_off -= sizeof(struct imp_leader); 446 m->m_len += sizeof(struct imp_leader); 447 } 448 imp = mtod(m, struct imp_leader *); 449 imp->il_format = IMP_NFF; 450 imp->il_mtype = IMPTYPE_DATA; 451 imp->il_network = dnet; 452 imp->il_host = dhost; 453 imp->il_imp = htons((u_short)dimp); 454 imp->il_length = 455 htons((u_short)(len + sizeof(struct imp_leader)) << 3); 456 imp->il_link = dlink; 457 imp->il_flags = imp->il_htype = imp->il_subtype = 0; 458 459 leaderexists: 460 /* 461 * Hand message to impsnd to perform RFNM counting 462 * and eventual transmission. 463 */ 464 return (impsnd(ifp, m)); 465 drop: 466 m_freem(m0); 467 return (0); 468 } 469 470 /* 471 * Put a message on an interface's output queue. 472 * Perform RFNM counting: no more than 8 message may be 473 * in flight to any one host. 474 */ 475 impsnd(ifp, m) 476 struct ifnet *ifp; 477 struct mbuf *m; 478 { 479 register struct imp_leader *ip; 480 register struct host *hp; 481 struct impcb *icp; 482 int x; 483 484 COUNT(IMPSND); 485 ip = mtod(m, struct imp_leader *); 486 487 /* 488 * Do RFNM counting for data messages 489 * (no more than 8 outstanding to any host) 490 */ 491 x = splimp(); 492 if (ip->il_mtype == IMPTYPE_DATA) { 493 struct in_addr addr; 494 495 #ifdef notdef 496 addr.s_net = ip->il_network; 497 #else 498 addr.s_net = 0; 499 #endif 500 addr.s_host = ip->il_host; 501 addr.s_imp = ip->il_imp; 502 if ((hp = hostlookup(addr)) == 0) 503 hp = hostenter(addr); 504 505 /* 506 * If IMP would block, queue until RFNM 507 */ 508 if (hp) { 509 if (hp->h_rfnm < 8) { 510 hp->h_rfnm++; 511 goto enque; 512 } 513 if (hp->h_qcnt < 8) { /* high water mark */ 514 HOST_ENQUE(hp, m); 515 goto start; 516 } 517 } 518 m_freem(m); 519 splx(x); 520 return (0); 521 } 522 enque: 523 if (IF_QFULL(&ifp->if_snd)) { 524 IF_DROP(&ifp->if_snd); 525 m_freem(m); 526 splx(x); 527 return (0); 528 } 529 IF_ENQUEUE(&ifp->if_snd, m); 530 start: 531 splx(x); 532 icp = &imp_softc[ifp->if_unit].imp_cb; 533 if (icp->ic_oactive == 0) 534 (*icp->ic_start)(ifp->if_unit); 535 return (1); 536 } 537 538 /* 539 * Put three 1822 NOOPs at the head of the output queue. 540 * Part of host-IMP initialization procedure. 541 * (Should return success/failure, but noone knows 542 * what to do with this, so why bother?) 543 */ 544 impnoops(sc) 545 register struct imp_softc *sc; 546 { 547 register i; 548 register struct mbuf *m; 549 register struct control_leader *cp; 550 int x; 551 552 COUNT(IMPNOOPS); 553 sc->imp_state = IMPS_INIT; 554 sc->imp_dropcnt = IMP_DROPCNT; 555 for (i = 0; i < IMP_DROPCNT + 1; i++ ) { 556 if ((m = m_getclr(M_DONTWAIT)) == 0) 557 return; 558 m->m_off = MMINOFF; 559 m->m_len = sizeof(struct control_leader); 560 cp = mtod(m, struct control_leader *); 561 cp->dl_format = IMP_NFF; 562 cp->dl_link = i; 563 cp->dl_mtype = IMPTYPE_NOOP; 564 x = splimp(); 565 IF_PREPEND(&sc->imp_if.if_snd, m); 566 splx(x); 567 } 568 if (sc->imp_cb.ic_oactive == 0) 569 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 570 } 571 572 #ifdef IMPLEADERS 573 printleader(routine, ip) 574 char *routine; 575 register struct imp_leader *ip; 576 { 577 printf("%s: ", routine); 578 printbyte((char *)ip, 12); 579 printf("<fmt=%x,net=%x,flags=%x,mtype=", ip->il_format, ip->il_network, 580 ip->il_flags); 581 if (ip->il_mtype <= IMPTYPE_READY) 582 printf("%s,", impleaders[ip->il_mtype]); 583 else 584 printf("%x,", ip->il_mtype); 585 printf("htype=%x,host=%x,imp=%x,link=", ip->il_htype, ip->il_host, 586 ntohs(ip->il_imp)); 587 if (ip->il_link == IMPLINK_IP) 588 printf("ip,"); 589 else 590 printf("%x,", ip->il_link); 591 printf("subtype=%x,len=%x>\n",ip->il_subtype,ntohs(ip->il_length)>>3); 592 } 593 594 printbyte(cp, n) 595 register char *cp; 596 int n; 597 { 598 register i, j, c; 599 600 for (i=0; i<n; i++) { 601 c = *cp++; 602 for (j=0; j<2; j++) 603 putchar("0123456789abcdef"[(c>>((1-j)*4))&0xf]); 604 putchar(' '); 605 } 606 putchar('\n'); 607 } 608 #endif 609 #endif 610