1 /* if_imp.c 4.3 82/02/01 */ 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/host.h" 28 #include "../net/ip.h" 29 #include "../net/ip_var.h" 30 31 /* 32 * IMP software status per interface. 33 * (partially shared with the hardware specific module) 34 * 35 * Each interface is referenced by a network interface structure, 36 * imp_if, which the routing code uses to locate the interface. 37 * This structure contains the output queue for the interface, its 38 * address, ... IMP specific structures used in connecting the 39 * IMP software modules to the hardware specific interface routines 40 * are also stored here. These structures are visible in the interface 41 * driver through back pointers set up in the hardware's attach routine. 42 * 43 * NOTE: imp_if and imp_cb are assumed adjacent in hardware code. 44 */ 45 struct imp_softc { 46 struct ifnet imp_if; /* network visible interface */ 47 struct impcb imp_cb; /* hooks to hardware module */ 48 u_char imp_state; /* current state of IMP */ 49 char imp_dropcnt; /* used during initialization */ 50 short imp_timer; /* going down timer */ 51 } imp_softc[NIMP]; 52 53 /* 54 * Messages from IMP regarding why 55 * it's going down. 56 */ 57 static char *impmsg[] = { 58 "in 30 seconds", 59 "for hardware PM", 60 "to reload software", 61 "for emergency reset" 62 }; 63 64 /* 65 * IMP attach routine. Called from hardware device attach routine 66 * at configuration time with a pointer to the UNIBUS device structure. 67 * Sets up local state and returns pointer to base of ifnet+impcb 68 * structures. This is then used by the device's attach routine 69 * set up its back pointers. 70 */ 71 impattach(ui) 72 struct uba_device *ui; 73 { 74 struct imp_softc *sc = &imp_softc[ui->ui_unit]; 75 register struct ifnet *ifp = &sc->imp_if; 76 77 COUNT(IMPATTACH); 78 /* UNIT COULD BE AMBIGUOUS */ 79 ifp->if_unit = ui->ui_unit; 80 ifp->if_name = "imp"; 81 ifp->if_mtu = IMP_MTU; 82 ifp->if_net = ui->ui_flags; 83 /* ifp->if_host = ... */ 84 /* ifp->if_addr = if_makeaddr(ifp->if_net, ifp->if_host); */ 85 if_attach(ifp); 86 /* kludge to hand pointers back to hardware attach routine */ 87 return ((int)&sc->imp_if); 88 } 89 90 /* 91 * IMP initialization routine: call hardware module to 92 * setup UNIBUS resources, init state and get ready for 93 * NOOPs the IMP should send us, and that we want to drop. 94 */ 95 impinit(unit) 96 int unit; 97 { 98 register struct imp_softc *sc = &imp_softc[unit]; 99 100 (*sc->imp_cb.ic_init)(unit); 101 sc->imp_state = IMPS_INIT; 102 sc->imp_dropcnt = IMP_DROPCNT; 103 } 104 105 struct sockproto impproto = { PF_IMPLINK }; 106 struct sockaddr_in impdst = { AF_IMPLINK }; 107 struct sockaddr_in impsrc = { AF_IMPLINK }; 108 109 /* 110 * ARPAnet 1822 input routine. 111 * Called from hardware input interrupt routine to handle 1822 112 * IMP-host messages. Type 0 messages (non-control) are 113 * passed to higher level protocol processors on the basis 114 * of link number. Other type messages (control) are handled here. 115 */ 116 impinput(unit, m0) 117 int unit; 118 struct mbuf *m0; 119 { 120 int s; 121 register struct mbuf *m; 122 register struct imp_leader *ip; 123 register struct imp_softc *sc = &imp_softc[unit]; 124 register struct host *hp; 125 register struct ifqueue *inq; 126 struct in_addr addr; 127 128 COUNT(IMP_INPUT); 129 m = m0; 130 131 /* 132 * We should generate a "bad leader" message 133 * to the IMP about messages too short. 134 */ 135 if (m->m_len < sizeof(struct imp_leader) && 136 m_pullup(m, sizeof(struct imp_leader)) == 0) 137 goto drop; 138 ip = mtod(m, struct imp_leader *); 139 140 /* 141 * Check leader type -- should notify IMP 142 * in case of failure... 143 */ 144 if (ip->il_format != IMP_NFF) 145 goto drop; 146 147 /* 148 * Certain messages require a host structure. 149 * Do this in one shot here. 150 */ 151 switch (ip->il_mtype) { 152 153 case IMPTYPE_RFNM: 154 case IMPTYPE_INCOMPLETE: 155 case IMPTYPE_HOSTDEAD: 156 case IMPTYPE_HOSTUNREACH: 157 case IMPTYPE_BADDATA: 158 addr.s_host = ntohs(ip->il_host); /* XXX */ 159 hp = h_lookup(addr); 160 break; 161 } 162 163 switch (ip->il_mtype) { 164 165 /* 166 * Data for a protocol. Dispatch to the appropriate 167 * protocol routine (running at software interrupt). 168 * If this isn't a raw interface, advance pointer 169 * into mbuf past leader (done below). 170 */ 171 case IMPTYPE_DATA: 172 ip->il_length = ntohs(ip->il_length) >> 3; 173 break; 174 175 /* 176 * IMP leader error. Reset the IMP and discard the packet. 177 */ 178 case IMPTYPE_BADLEADER: 179 /* 180 * According to 1822 document, this message 181 * will be generated in response to the 182 * first noop sent to the IMP after 183 * the host resets the IMP interface. 184 */ 185 if (sc->imp_state != IMPS_RESET) { 186 imperr(sc, "leader error"); 187 h_reset(sc->imp_if.if_net); /* XXX */ 188 impnoops(sc); 189 } 190 goto drop; 191 192 /* 193 * IMP going down. Print message, and if not immediate, 194 * set off a timer to insure things will be reset at the 195 * appropriate time. 196 */ 197 case IMPTYPE_DOWN: 198 if ((ip->il_link & IMP_DMASK) == 0) { 199 sc->imp_state = IMPS_GOINGDOWN; 200 timeout(impdown, sc, 30 * 60 * HZ); 201 } 202 imperr(sc, "going down %s", impmsg[ip->il_link & IMP_DMASK]); 203 goto drop; 204 205 /* 206 * A NOP usually seen during the initialization sequence. 207 * Compare the local address with that in the message. 208 * Reset the local address notion if it doesn't match. 209 */ 210 case IMPTYPE_NOOP: 211 if (sc->imp_state == IMPS_DOWN) { 212 sc->imp_state = IMPS_INIT; 213 sc->imp_dropcnt = IMP_DROPCNT; 214 } 215 if (sc->imp_state == IMPS_INIT && --sc->imp_dropcnt == 0) { 216 sc->imp_state = IMPS_UP; 217 /* restart output in case something was q'd */ 218 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 219 } 220 if (ip->il_host != sc->imp_if.if_addr.s_host || 221 ip->il_impno != sc->imp_if.if_addr.s_imp) { 222 sc->imp_if.if_addr.s_host = ip->il_host; 223 sc->imp_if.if_addr.s_imp = ip->il_imp; 224 imperr(sc, "imp%d: address set to %d/%d\n", 225 ip->il_host, ip->il_impno); 226 } 227 goto drop; 228 229 /* 230 * RFNM or INCOMPLETE message, record in 231 * host table and prime output routine. 232 * 233 * SHOULD NOTIFY PROTOCOL ABOUT INCOMPLETES. 234 */ 235 case IMPTYPE_RFNM: 236 case IMPTYPE_INCOMPLETE: 237 if (hp && hp->h_rfnm) { 238 register struct mbuf *n; 239 240 hp->h_rfnm--; 241 /* poke holding queue */ 242 if (n = hp->h_q) { 243 if (n->m_act == n) 244 hp->h_q = 0; 245 else { 246 n = n->m_act; 247 hp->h_q->m_act = n->m_act; 248 } 249 (void) impsnd(n, sc); 250 } 251 } 252 break; 253 254 /* 255 * Host or IMP can't be reached. Flush any packets 256 * awaiting transmission and release the host structure. 257 * 258 * HOW DO WE NOTIFY THE PROTOCOL? 259 * HOW DO WE AGE THE HOST STRUCTURE TO SAVE STATUS? 260 */ 261 case IMPTYPE_HOSTDEAD: 262 case IMPTYPE_HOSTUNREACH: 263 if (hp) 264 h_free(hp); /* won't work right */ 265 break; 266 267 /* 268 * Error in data. Clear RFNM status for this host and send 269 * noops to the IMP to clear the interface. 270 */ 271 case IMPTYPE_BADDATA: 272 imperr(sc, "data error"); 273 if (hp) 274 hp->h_rfnm = 0; 275 impnoops(sc); 276 break; 277 278 /* 279 * Interface reset. 280 */ 281 case IMPTYPE_RESET: 282 imperr(sc, "interface reset"); 283 sc->imp_state = IMPS_RESET; 284 impnoops(sc); 285 goto drop; 286 287 default: 288 sc->imp_if.if_collisions++; /* XXX */ 289 goto drop; 290 } 291 292 /* 293 * Queue on protocol's input queue. 294 */ 295 switch (ip->il_link) { 296 297 #ifdef INET 298 case IMPLINK_IP: 299 m->m_len -= sizeof(struct imp_leader); 300 m->m_off += sizeof(struct imp_leader); 301 setipintr(); 302 inq = &ipintrq; 303 break; 304 #endif 305 306 default: 307 impproto.sp_protocol = ip->il_link; 308 impdst.sin_addr = sc->imp_if.if_addr; 309 impsrc.sin_addr.s_net = ip->il_network; 310 impsrc.sin_addr.s_host = ip->il_host; 311 impsrc.sin_addr.s_imp = ip->il_imp; 312 raw_input(m, impproto, impdst, impsrc); 313 return; 314 } 315 IF_ENQUEUE(inq, m); 316 return; 317 318 drop: 319 m_freem(m); 320 } 321 322 /* 323 * Bring the IMP down after notification. 324 */ 325 impdown(sc) 326 struct imp_softc *sc; 327 { 328 sc->imp_state = IMPS_DOWN; 329 /* notify protocols with messages waiting? */ 330 } 331 332 /*VARARGS*/ 333 imperr(sc, fmt, a1, a2) 334 struct imp_softc *sc; 335 char *fmt; 336 { 337 printf("imp%d: ", sc->imp_if.if_unit); 338 printf(fmt, a1, a2); 339 printf("\n"); 340 } 341 342 /* 343 * ARPAnet 1822 output routine. 344 * Called from higher level protocol routines to set up messages for 345 * transmission to the imp. Sets up the header and calls impsnd to 346 * enqueue the message for this IMP's hardware driver. 347 */ 348 impoutput(ifp, m0, pf) 349 register struct ifnet *ifp; 350 struct mbuf *m0; 351 { 352 register struct imp_leader *imp; 353 register struct mbuf *m = m0; 354 int x, dhost, dimp, dlink, len; 355 356 /* 357 * Don't even try if the IMP is unavailable. 358 */ 359 x = imp_softc[ifp->if_unit].imp_state; 360 if (x == IMPS_DOWN || x == IMPS_GOINGDOWN) 361 goto drop; 362 363 switch (pf) { 364 365 #ifdef INET 366 case PF_INET: { 367 register struct ip *ip = mtod(m0, struct ip *); 368 369 dhost = ip->ip_dst.s_host; 370 dimp = ip->ip_dst.s_imp; 371 dlink = IMPLINK_IP; 372 len = ntohs(ip->ip_len); 373 break; 374 } 375 #endif 376 case PF_IMPLINK: 377 goto leaderexists; 378 379 default: 380 printf("imp%d: can't encapsulate pf%d\n", ifp->if_unit, pf); 381 goto drop; 382 } 383 384 /* 385 * Add IMP leader. If there's not enough space in the 386 * first mbuf, allocate another. If that should fail, we 387 * drop this sucker. 388 */ 389 if (m->m_off > MMAXOFF || 390 MMINOFF + sizeof(struct imp_leader) > m->m_off) { 391 m = m_get(M_DONTWAIT); 392 if (m == 0) 393 goto drop; 394 m->m_next = m0; 395 m->m_off = MMINOFF; 396 m->m_len = sizeof(struct imp_leader); 397 } else { 398 m->m_off -= sizeof(struct imp_leader); 399 m->m_len += sizeof(struct imp_leader); 400 } 401 imp = mtod(m, struct imp_leader *); 402 imp->il_format = IMP_NFF; 403 imp->il_host = dhost; 404 imp->il_impno = dimp; 405 imp->il_length = (len + sizeof(struct imp_leader)) << 3; 406 imp->il_link = dlink; 407 408 leaderexists: 409 /* 410 * Hand message to impsnd to perform RFNM counting 411 * and eventual transmission. 412 */ 413 return (impsnd(ifp, m)); 414 drop: 415 m_freem(m0); 416 return (0); 417 } 418 419 /* 420 * Put a message on an interface's output queue. 421 * Perform RFNM counting: no more than 8 message may be 422 * in flight to any one host. 423 */ 424 impsnd(ifp, m) 425 struct ifnet *ifp; 426 struct mbuf *m; 427 { 428 register struct imp_leader *ip; 429 register struct host *hp; 430 struct impcb *icp; 431 int x; 432 433 ip = mtod(m, struct imp_leader *); 434 435 /* 436 * Do RFNM counting for data messages 437 * (no more than 8 outstanding to any host) 438 */ 439 if (ip->il_mtype == IMPTYPE_DATA) { 440 struct in_addr addr; 441 442 addr.s_net = ifp->if_net; 443 addr.s_host = ip->il_host; 444 addr.s_imp = ip->il_imp; 445 hp = h_enter(addr); 446 447 /* 448 * If IMP would block, queue until RFNM 449 */ 450 if (hp) { 451 register struct mbuf *n; 452 int cnt; 453 454 if (hp->h_rfnm < 8) { 455 hp->h_rfnm++; 456 goto enque; 457 } 458 /* 459 * Keeping the count in the host structure 460 * causes the packing scheme to lose too much. 461 */ 462 cnt = 0, n = hp->h_q; 463 for (; n != (struct mbuf *)hp; n = n->m_act) 464 cnt++; 465 if (cnt >= 8) 466 goto drop; 467 468 /* 469 * Q is kept as circulare list with h_q 470 * (head) pointing to the last entry. 471 */ 472 if ((n = hp->h_q) == 0) 473 hp->h_q = m->m_act = m; 474 else { 475 m->m_act = n->m_act; 476 hp->h_q = n->m_act = m; 477 } 478 goto start; 479 } 480 drop: 481 m_freem(m); 482 return (0); 483 } 484 enque: 485 x = splimp(); 486 IF_ENQUEUE(&ifp->if_snd, m); 487 splx(x); 488 489 start: 490 icp = &imp_softc[ifp->if_unit].imp_cb; 491 if (icp->ic_oactive == 0) 492 (*icp->ic_start)(ifp->if_unit); 493 return (1); 494 } 495 496 /* 497 * Put three 1822 NOOPs at the head of the output queue. 498 * Part of host-IMP initialization procedure. 499 * (Should return success/failure, but noone knows 500 * what to do with this, so why bother?) 501 */ 502 impnoops(sc) 503 register struct imp_softc *sc; 504 { 505 register i; 506 register struct mbuf *m; 507 register struct imp_leader *ip; 508 int x; 509 510 sc->imp_state = IMPS_INIT; 511 sc->imp_dropcnt = IMP_DROPCNT; 512 for (i = 0; i < IMP_DROPCNT; i++ ) { 513 if ((m = m_getclr(M_DONTWAIT)) == 0) 514 return; 515 m->m_off = MMINOFF; 516 m->m_len = sizeof(struct imp_leader); 517 ip = mtod(m, struct imp_leader *); 518 ip->il_format = IMP_NFF; 519 ip->il_link = i; 520 ip->il_mtype = IMPTYPE_NOOP; 521 x = splimp(); 522 IF_PREPEND(&sc->imp_if.if_snd, m); 523 splx(x); 524 } 525 if (sc->imp_cb.ic_oactive == 0) 526 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 527 } 528 #endif 529