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