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