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