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