1 /* if_imp.c 4.2 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 if (m->m_len < sizeof(struct imp_leader) && 131 m_pullup(m, sizeof(struct imp_leader)) == 0) 132 goto drop; 133 ip = mtod(m, struct imp_leader *); 134 135 /* check leader type. */ 136 if (ip->il_format != IMP_NFF) 137 goto drop; 138 139 /* 140 * Certain messages require a host structure. 141 * Do this in one shot here. 142 */ 143 switch (ip->il_mtype) { 144 145 case IMPTYPE_RFNM: 146 case IMPTYPE_INCOMPLETE: 147 case IMPTYPE_HOSTDEAD: 148 case IMPTYPE_HOSTUNREACH: 149 case IMPTYPE_BADDATA: 150 addr.s_host = ntohs(ip->il_host); 151 hp = h_lookup(addr); 152 break; 153 } 154 155 switch (ip->il_mtype) { 156 157 /* 158 * Data for a protocol. Dispatch to the appropriate 159 * protocol routine (running at software interrupt). 160 * If this isn't a raw interface, advance pointer 161 * into mbuf past leader. 162 */ 163 case IMPTYPE_DATA: 164 ip->il_length = ntohs(ip->il_length) >> 3; 165 break; 166 167 /* 168 * IMP leader error. Reset the IMP and discard the packet. 169 */ 170 case IMPTYPE_BADLEADER: 171 imperr(sc, "leader error"); 172 h_reset(sc->imp_if.if_net); /* XXX */ 173 impnoops(sc); 174 goto drop; 175 176 /* 177 * IMP going down. Print message, and if not immediate, 178 * set off a timer to insure things will be reset at the 179 * appropriate time. 180 */ 181 case IMPTYPE_DOWN: 182 if ((ip->il_link & IMP_DMASK) == 0) { 183 sc->imp_state = IMPS_GOINGDOWN; 184 sc->imp_timer = IMPTV_DOWN; 185 } 186 imperr(sc, "going down %s", impmsg[ip->il_link & IMP_DMASK]); 187 goto drop; 188 189 /* 190 * A NOP usually seen during the initialization sequence. 191 * Compare the local address with that in the message. 192 * Reset the local address notion if it doesn't match. 193 */ 194 case IMPTYPE_NOOP: 195 if (sc->imp_state == IMPS_INIT && --sc->imp_dropcnt == 0) { 196 sc->imp_state = IMPS_UP; 197 /* restart output in case something was q'd */ 198 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 199 } 200 if (ip->il_host != sc->imp_if.if_addr.s_host || 201 ip->il_impno != sc->imp_if.if_addr.s_imp) { 202 sc->imp_if.if_addr.s_host = ip->il_host; 203 sc->imp_if.if_addr.s_imp = ip->il_imp; 204 imperr(sc, "imp%d: address set to %d/%d\n", 205 ip->il_host, ip->il_impno); 206 } 207 goto drop; 208 209 /* 210 * RFNM or INCOMPLETE message, record in 211 * host table and prime output routine. 212 * 213 * SHOULD RETRANSMIT ON INCOMPLETE. 214 */ 215 case IMPTYPE_RFNM: 216 case IMPTYPE_INCOMPLETE: 217 if (hp && hp->h_rfnm) { 218 register struct mbuf *n; 219 220 hp->h_rfnm--; 221 /* poke holding queue */ 222 if (n = hp->h_q) { 223 if (n->m_act == n) 224 hp->h_q = 0; 225 else { 226 n = n->m_act; 227 hp->h_q->m_act = n->m_act; 228 } 229 (void) impsnd(n, sc); 230 } 231 } 232 break; 233 234 /* 235 * Host or IMP can't be reached. Flush any packets 236 * awaiting transmission and release the host structure. 237 * 238 * HOW DO WE NOTIFY THE PROTOCOL? 239 * HOW DO WE AGE THE HOST STRUCTURE TO SAVE STATUS? 240 */ 241 case IMPTYPE_HOSTDEAD: 242 case IMPTYPE_HOSTUNREACH: 243 if (hp) 244 h_free(hp); /* won't work right */ 245 break; 246 247 /* 248 * Error in data. Clear RFNM status for this host and send 249 * noops to the IMP to clear the interface. 250 */ 251 case IMPTYPE_BADDATA: 252 imperr(sc, "data error"); 253 if (hp) 254 hp->h_rfnm = 0; 255 impnoops(sc); 256 break; 257 258 /* 259 * IMP reset complete. 260 */ 261 case IMPTYPE_RESET: 262 if (sc->imp_state == IMPS_DOWN) 263 sc->imp_state = IMPS_UP; 264 else 265 imperr(sc, "unexpected reset"); 266 goto drop; 267 268 default: 269 sc->imp_if.if_collisions++; /* XXX */ 270 goto drop; 271 } 272 273 /* 274 * Queue on protocol's input queue. 275 */ 276 switch (ip->il_link) { 277 278 #ifdef INET 279 case IMPLINK_IP: 280 m->m_len -= sizeof(struct imp_leader); 281 m->m_off += sizeof(struct imp_leader); 282 setipintr(); 283 inq = &ipintrq; 284 break; 285 #endif 286 287 default: 288 impproto.sp_protocol = ip->il_link; 289 impdst.sin_addr = sc->imp_if.if_addr; 290 impsrc.sin_addr.s_net = ip->il_network; 291 impsrc.sin_addr.s_host = ip->il_host; 292 impsrc.sin_addr.s_imp = ip->il_imp; 293 raw_input(m, impproto, impdst, impsrc); 294 return; 295 } 296 IF_ENQUEUE(inq, m); 297 return; 298 299 drop: 300 m_freem(m); 301 } 302 303 /*VARARGS*/ 304 imperr(sc, fmt, a1, a2) 305 struct imp_softc *sc; 306 char *fmt; 307 { 308 printf("imp%d: ", sc->imp_if.if_unit); 309 printf(fmt, a1, a2); 310 printf("\n"); 311 } 312 313 /* 314 * ARPAnet 1822 output routine. 315 * Called from higher level protocol routines to set up messages for 316 * transmission to the imp. Sets up the header and calls impsnd to 317 * enqueue the message for this IMP's hardware driver. 318 */ 319 impoutput(ifp, m0, pf) 320 register struct ifnet *ifp; 321 struct mbuf *m0; 322 { 323 register struct imp_leader *imp; 324 register struct mbuf *m = m0; 325 int x, dhost, dimp, dlink, len; 326 327 /* 328 * Don't even try if the IMP is unavailable. 329 */ 330 if (imp_softc[ifp->if_unit].imp_state == IMPS_DOWN) { 331 m_freem(m0); 332 return (0); 333 } 334 335 switch (pf) { 336 337 #ifdef INET 338 case PF_INET: { 339 register struct ip *ip = mtod(m0, struct ip *); 340 341 dhost = ip->ip_dst.s_host; 342 dimp = ip->ip_dst.s_imp; 343 dlink = IMPLINK_IP; 344 len = ntohs(ip->ip_len); 345 break; 346 } 347 #endif 348 case PF_IMPLINK: 349 goto leaderexists; 350 351 default: 352 printf("imp%d: can't encapsulate pf%d\n", ifp->if_unit, pf); 353 m_freem(m0); 354 return (0); 355 } 356 357 /* 358 * Add IMP leader. If there's not enough space in the 359 * first mbuf, allocate another. If that should fail, we 360 * drop this sucker. 361 */ 362 if (m->m_off > MMAXOFF || 363 MMINOFF + sizeof(struct imp_leader) > m->m_off) { 364 m = m_get(M_DONTWAIT); 365 if (m == 0) { 366 m_freem(m0); 367 return (0); 368 } 369 m->m_next = m0; 370 m->m_off = MMINOFF; 371 m->m_len = sizeof(struct imp_leader); 372 } else { 373 m->m_off -= sizeof(struct imp_leader); 374 m->m_len += sizeof(struct imp_leader); 375 } 376 imp = mtod(m, struct imp_leader *); 377 imp->il_format = IMP_NFF; 378 imp->il_host = dhost; 379 imp->il_impno = dimp; 380 imp->il_length = (len + sizeof(struct imp_leader)) << 3; 381 imp->il_link = dlink; 382 383 leaderexists: 384 /* 385 * Hand message to impsnd to perform RFNM counting 386 * and eventual transmission. 387 */ 388 return (impsnd(ifp, m)); 389 } 390 391 /* 392 * Put a message on an interface's output queue. 393 * Perform RFNM counting: no more than 8 message may be 394 * in flight to any one host. 395 */ 396 impsnd(ifp, m) 397 struct ifnet *ifp; 398 struct mbuf *m; 399 { 400 register struct imp_leader *ip; 401 register struct host *hp; 402 struct impcb *icp; 403 int x; 404 405 ip = mtod(m, struct imp_leader *); 406 407 /* 408 * Do RFNM counting for data messages 409 * (no more than 8 outstanding to any host) 410 */ 411 if (ip->il_mtype == IMPTYPE_DATA) { 412 struct in_addr addr; 413 414 addr.s_net = ifp->if_net; 415 addr.s_host = ip->il_host; 416 addr.s_imp = ip->il_imp; 417 hp = h_enter(addr); 418 419 /* 420 * If IMP would block, queue until rfnm 421 */ 422 if (hp) { 423 register struct mbuf *n; 424 int cnt; 425 426 if (hp->h_rfnm < 8) { 427 hp->h_rfnm++; 428 goto enque; 429 } 430 /* 431 * Keeping the count in the host structure 432 * causes the packing scheme to lose too much. 433 */ 434 cnt = 0, n = hp->h_q; 435 for (; n != (struct mbuf *)hp; n = n->m_act) 436 cnt++; 437 if (cnt >= 8) 438 goto drop; 439 if ((n = hp->h_q) == 0) 440 hp->h_q = m->m_act = m; 441 else { 442 m->m_act = n->m_act; 443 hp->h_q = n->m_act = m; 444 } 445 goto start; 446 } 447 drop: 448 m_freem(m); 449 return (0); 450 } 451 enque: 452 x = splimp(); 453 IF_ENQUEUE(&ifp->if_snd, m); 454 splx(x); 455 456 start: 457 icp = &imp_softc[ifp->if_unit].imp_cb; 458 if (icp->ic_oactive == 0) 459 (*icp->ic_start)(ifp->if_unit); 460 return (1); 461 } 462 463 /* 464 * Put three 1822 NOOPs at the head of the output queue. 465 * Part of host-IMP initialization procedure. 466 * (Should return success/failure, but noone knows 467 * what to do with this, so why bother?) 468 */ 469 impnoops(sc) 470 register struct imp_softc *sc; 471 { 472 register i; 473 register struct mbuf *m; 474 register struct imp_leader *ip; 475 int x; 476 477 sc->imp_state = IMPS_INIT; 478 sc->imp_dropcnt = IMP_DROPCNT; 479 for (i = 0; i < IMP_DROPCNT; i++ ) { 480 if ((m = m_getclr(M_DONTWAIT)) == 0) 481 return; 482 m->m_off = MMINOFF; 483 m->m_len = sizeof(struct imp_leader); 484 ip = mtod(m, struct imp_leader *); 485 ip->il_format = IMP_NFF; 486 ip->il_link = i; 487 ip->il_mtype = IMPTYPE_NOOP; 488 x = splimp(); 489 IF_PREPEND(&sc->imp_if.if_snd, m); 490 splx(x); 491 } 492 if (sc->imp_cb.ic_oactive == 0) 493 (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 494 } 495 #endif 496