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