1*5645Ssam /* if_imp.c 4.2 82/02/01 */ 25640Sroot 35640Sroot #include "imp.h" 45640Sroot #if NIMP > 0 55640Sroot /* 65640Sroot * ARPAnet IMP interface driver. 75640Sroot * 85640Sroot * The IMP-host protocol is handled here, leaving 95640Sroot * hardware specifics to the lower level interface driver. 105640Sroot */ 115640Sroot #include "../h/param.h" 125640Sroot #include "../h/systm.h" 135640Sroot #include "../h/mbuf.h" 145640Sroot #include "../h/pte.h" 155640Sroot #include "../h/buf.h" 165640Sroot #include "../h/protosw.h" 175640Sroot #include "../h/socket.h" 185640Sroot #include "../h/ubareg.h" 195640Sroot #include "../h/ubavar.h" 205640Sroot #include "../h/cpu.h" 215640Sroot #include "../h/mtpr.h" 225640Sroot #include "../h/vmmac.h" 235640Sroot #include "../net/in.h" 245640Sroot #include "../net/in_systm.h" 255640Sroot #include "../net/if.h" 265640Sroot #include "../net/if_imp.h" 275640Sroot #include "../net/host.h" 285640Sroot #include "../net/ip.h" 295640Sroot #include "../net/ip_var.h" 305640Sroot 315640Sroot /* 325640Sroot * IMP software status per interface. 335640Sroot * (partially shared with the hardware specific module) 345640Sroot * 355640Sroot * Each interface is referenced by a network interface structure, 365640Sroot * imp_if, which the routing code uses to locate the interface. 375640Sroot * This structure contains the output queue for the interface, its 385640Sroot * address, ... IMP specific structures used in connecting the 395640Sroot * IMP software modules to the hardware specific interface routines 405640Sroot * are also stored here. These structures are visible in the interface 415640Sroot * driver through back pointers set up in the hardware's attach routine. 425640Sroot * 435640Sroot * NOTE: imp_if and imp_cb are assumed adjacent in hardware code. 445640Sroot */ 455640Sroot struct imp_softc { 465640Sroot struct ifnet imp_if; /* network visible interface */ 475640Sroot struct impcb imp_cb; /* hooks to hardware module */ 485640Sroot u_char imp_state; /* current state of IMP */ 495640Sroot char imp_dropcnt; /* used during initialization */ 505640Sroot short imp_timer; /* going down timer */ 515640Sroot } imp_softc[NIMP]; 525640Sroot 535640Sroot /* 545640Sroot * Messages from IMP regarding why 555640Sroot * it's going down. 565640Sroot */ 575640Sroot static char *impmsg[] = { 585640Sroot "in 30 seconds", 595640Sroot "for hardware PM", 605640Sroot "to reload software", 615640Sroot "for emergency reset" 625640Sroot }; 635640Sroot 645640Sroot /* 655640Sroot * IMP attach routine. Called from hardware device attach routine 665640Sroot * at configuration time with a pointer to the UNIBUS device structure. 675640Sroot * Sets up local state and returns pointer to base of ifnet+impcb 685640Sroot * structures. This is then used by the device's attach routine 695640Sroot * set up its back pointers. 705640Sroot */ 715640Sroot impattach(ui) 725640Sroot struct uba_device *ui; 735640Sroot { 745640Sroot struct imp_softc *sc = &imp_softc[ui->ui_unit]; 755640Sroot register struct ifnet *ifp = &sc->imp_if; 765640Sroot 775640Sroot COUNT(IMPATTACH); 785640Sroot /* UNIT COULD BE AMBIGUOUS */ 795640Sroot ifp->if_unit = ui->ui_unit; 805640Sroot ifp->if_name = "imp"; 815640Sroot ifp->if_mtu = IMP_MTU; 825640Sroot ifp->if_net = ui->ui_flags; 835640Sroot /* ifp->if_host = ... */ 845640Sroot /* ifp->if_addr = if_makeaddr(ifp->if_net, ifp->if_host); */ 855640Sroot if_attach(ifp); 865640Sroot /* kludge to hand pointers back to hardware attach routine */ 875640Sroot return ((int)&sc->imp_if); 885640Sroot } 895640Sroot 905640Sroot /* 915640Sroot * IMP initialization routine: call hardware module to 925640Sroot * setup UNIBUS resources, init state and get ready for 935640Sroot * NOOPs the IMP should send us, and that we want to drop. 945640Sroot */ 955640Sroot impinit(unit) 965640Sroot int unit; 975640Sroot { 985640Sroot register struct imp_softc *sc = &imp_softc[unit]; 995640Sroot 1005640Sroot (*sc->imp_cb.ic_init)(unit); 1015640Sroot sc->imp_state = IMPS_INIT; 1025640Sroot sc->imp_dropcnt = IMP_DROPCNT; 1035640Sroot } 1045640Sroot 1055640Sroot struct sockproto impproto = { PF_IMPLINK }; 106*5645Ssam struct sockaddr_in impdst = { AF_IMPLINK }; 107*5645Ssam struct sockaddr_in impsrc = { AF_IMPLINK }; 1085640Sroot 1095640Sroot /* 1105640Sroot * ARPAnet 1822 input routine. 1115640Sroot * Called from hardware input interrupt routine to handle 1822 1125640Sroot * IMP-host messages. Type 0 messages (non-control) are 1135640Sroot * passed to higher level protocol processors on the basis 1145640Sroot * of link number. Other type messages (control) are handled here. 1155640Sroot */ 1165640Sroot impinput(unit, m0) 1175640Sroot int unit; 1185640Sroot struct mbuf *m0; 1195640Sroot { 1205640Sroot int s; 1215640Sroot register struct mbuf *m; 1225640Sroot register struct imp_leader *ip; 1235640Sroot register struct imp_softc *sc = &imp_softc[unit]; 1245640Sroot register struct host *hp; 1255640Sroot register struct ifqueue *inq; 1265640Sroot struct in_addr addr; 1275640Sroot 1285640Sroot COUNT(IMP_INPUT); 1295640Sroot m = m0; 1305640Sroot if (m->m_len < sizeof(struct imp_leader) && 1315640Sroot m_pullup(m, sizeof(struct imp_leader)) == 0) 1325640Sroot goto drop; 1335640Sroot ip = mtod(m, struct imp_leader *); 1345640Sroot 1355640Sroot /* check leader type. */ 1365640Sroot if (ip->il_format != IMP_NFF) 1375640Sroot goto drop; 1385640Sroot 1395640Sroot /* 1405640Sroot * Certain messages require a host structure. 1415640Sroot * Do this in one shot here. 1425640Sroot */ 1435640Sroot switch (ip->il_mtype) { 1445640Sroot 1455640Sroot case IMPTYPE_RFNM: 1465640Sroot case IMPTYPE_INCOMPLETE: 1475640Sroot case IMPTYPE_HOSTDEAD: 1485640Sroot case IMPTYPE_HOSTUNREACH: 1495640Sroot case IMPTYPE_BADDATA: 1505640Sroot addr.s_host = ntohs(ip->il_host); 1515640Sroot hp = h_lookup(addr); 1525640Sroot break; 1535640Sroot } 1545640Sroot 1555640Sroot switch (ip->il_mtype) { 1565640Sroot 1575640Sroot /* 1585640Sroot * Data for a protocol. Dispatch to the appropriate 1595640Sroot * protocol routine (running at software interrupt). 1605640Sroot * If this isn't a raw interface, advance pointer 1615640Sroot * into mbuf past leader. 1625640Sroot */ 1635640Sroot case IMPTYPE_DATA: 1645640Sroot ip->il_length = ntohs(ip->il_length) >> 3; 1655640Sroot break; 1665640Sroot 1675640Sroot /* 1685640Sroot * IMP leader error. Reset the IMP and discard the packet. 1695640Sroot */ 1705640Sroot case IMPTYPE_BADLEADER: 1715640Sroot imperr(sc, "leader error"); 1725640Sroot h_reset(sc->imp_if.if_net); /* XXX */ 1735640Sroot impnoops(sc); 1745640Sroot goto drop; 1755640Sroot 1765640Sroot /* 1775640Sroot * IMP going down. Print message, and if not immediate, 1785640Sroot * set off a timer to insure things will be reset at the 1795640Sroot * appropriate time. 1805640Sroot */ 1815640Sroot case IMPTYPE_DOWN: 1825640Sroot if ((ip->il_link & IMP_DMASK) == 0) { 1835640Sroot sc->imp_state = IMPS_GOINGDOWN; 1845640Sroot sc->imp_timer = IMPTV_DOWN; 1855640Sroot } 1865640Sroot imperr(sc, "going down %s", impmsg[ip->il_link & IMP_DMASK]); 1875640Sroot goto drop; 1885640Sroot 1895640Sroot /* 1905640Sroot * A NOP usually seen during the initialization sequence. 1915640Sroot * Compare the local address with that in the message. 1925640Sroot * Reset the local address notion if it doesn't match. 1935640Sroot */ 1945640Sroot case IMPTYPE_NOOP: 1955640Sroot if (sc->imp_state == IMPS_INIT && --sc->imp_dropcnt == 0) { 1965640Sroot sc->imp_state = IMPS_UP; 1975640Sroot /* restart output in case something was q'd */ 1985640Sroot (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 1995640Sroot } 2005640Sroot if (ip->il_host != sc->imp_if.if_addr.s_host || 2015640Sroot ip->il_impno != sc->imp_if.if_addr.s_imp) { 2025640Sroot sc->imp_if.if_addr.s_host = ip->il_host; 2035640Sroot sc->imp_if.if_addr.s_imp = ip->il_imp; 2045640Sroot imperr(sc, "imp%d: address set to %d/%d\n", 2055640Sroot ip->il_host, ip->il_impno); 2065640Sroot } 2075640Sroot goto drop; 2085640Sroot 2095640Sroot /* 2105640Sroot * RFNM or INCOMPLETE message, record in 2115640Sroot * host table and prime output routine. 2125640Sroot * 2135640Sroot * SHOULD RETRANSMIT ON INCOMPLETE. 2145640Sroot */ 2155640Sroot case IMPTYPE_RFNM: 2165640Sroot case IMPTYPE_INCOMPLETE: 2175640Sroot if (hp && hp->h_rfnm) { 2185640Sroot register struct mbuf *n; 2195640Sroot 2205640Sroot hp->h_rfnm--; 2215640Sroot /* poke holding queue */ 2225640Sroot if (n = hp->h_q) { 2235640Sroot if (n->m_act == n) 2245640Sroot hp->h_q = 0; 2255640Sroot else { 2265640Sroot n = n->m_act; 2275640Sroot hp->h_q->m_act = n->m_act; 2285640Sroot } 2295640Sroot (void) impsnd(n, sc); 2305640Sroot } 2315640Sroot } 2325640Sroot break; 2335640Sroot 2345640Sroot /* 2355640Sroot * Host or IMP can't be reached. Flush any packets 2365640Sroot * awaiting transmission and release the host structure. 2375640Sroot * 2385640Sroot * HOW DO WE NOTIFY THE PROTOCOL? 2395640Sroot * HOW DO WE AGE THE HOST STRUCTURE TO SAVE STATUS? 2405640Sroot */ 2415640Sroot case IMPTYPE_HOSTDEAD: 2425640Sroot case IMPTYPE_HOSTUNREACH: 2435640Sroot if (hp) 2445640Sroot h_free(hp); /* won't work right */ 2455640Sroot break; 2465640Sroot 2475640Sroot /* 2485640Sroot * Error in data. Clear RFNM status for this host and send 2495640Sroot * noops to the IMP to clear the interface. 2505640Sroot */ 2515640Sroot case IMPTYPE_BADDATA: 2525640Sroot imperr(sc, "data error"); 2535640Sroot if (hp) 2545640Sroot hp->h_rfnm = 0; 2555640Sroot impnoops(sc); 2565640Sroot break; 2575640Sroot 2585640Sroot /* 2595640Sroot * IMP reset complete. 2605640Sroot */ 2615640Sroot case IMPTYPE_RESET: 2625640Sroot if (sc->imp_state == IMPS_DOWN) 2635640Sroot sc->imp_state = IMPS_UP; 2645640Sroot else 2655640Sroot imperr(sc, "unexpected reset"); 2665640Sroot goto drop; 2675640Sroot 2685640Sroot default: 2695640Sroot sc->imp_if.if_collisions++; /* XXX */ 2705640Sroot goto drop; 2715640Sroot } 2725640Sroot 2735640Sroot /* 2745640Sroot * Queue on protocol's input queue. 2755640Sroot */ 2765640Sroot switch (ip->il_link) { 2775640Sroot 2785640Sroot #ifdef INET 2795640Sroot case IMPLINK_IP: 2805640Sroot m->m_len -= sizeof(struct imp_leader); 2815640Sroot m->m_off += sizeof(struct imp_leader); 2825640Sroot setipintr(); 2835640Sroot inq = &ipintrq; 2845640Sroot break; 2855640Sroot #endif 2865640Sroot 2875640Sroot default: 2885640Sroot impproto.sp_protocol = ip->il_link; 289*5645Ssam impdst.sin_addr = sc->imp_if.if_addr; 290*5645Ssam impsrc.sin_addr.s_net = ip->il_network; 291*5645Ssam impsrc.sin_addr.s_host = ip->il_host; 292*5645Ssam impsrc.sin_addr.s_imp = ip->il_imp; 293*5645Ssam raw_input(m, impproto, impdst, impsrc); 2945640Sroot return; 2955640Sroot } 2965640Sroot IF_ENQUEUE(inq, m); 2975640Sroot return; 2985640Sroot 2995640Sroot drop: 3005640Sroot m_freem(m); 3015640Sroot } 3025640Sroot 3035640Sroot /*VARARGS*/ 3045640Sroot imperr(sc, fmt, a1, a2) 3055640Sroot struct imp_softc *sc; 3065640Sroot char *fmt; 3075640Sroot { 3085640Sroot printf("imp%d: ", sc->imp_if.if_unit); 3095640Sroot printf(fmt, a1, a2); 3105640Sroot printf("\n"); 3115640Sroot } 3125640Sroot 3135640Sroot /* 3145640Sroot * ARPAnet 1822 output routine. 3155640Sroot * Called from higher level protocol routines to set up messages for 3165640Sroot * transmission to the imp. Sets up the header and calls impsnd to 3175640Sroot * enqueue the message for this IMP's hardware driver. 3185640Sroot */ 3195640Sroot impoutput(ifp, m0, pf) 3205640Sroot register struct ifnet *ifp; 3215640Sroot struct mbuf *m0; 3225640Sroot { 3235640Sroot register struct imp_leader *imp; 3245640Sroot register struct mbuf *m = m0; 3255640Sroot int x, dhost, dimp, dlink, len; 3265640Sroot 3275640Sroot /* 3285640Sroot * Don't even try if the IMP is unavailable. 3295640Sroot */ 3305640Sroot if (imp_softc[ifp->if_unit].imp_state == IMPS_DOWN) { 3315640Sroot m_freem(m0); 3325640Sroot return (0); 3335640Sroot } 3345640Sroot 3355640Sroot switch (pf) { 3365640Sroot 3375640Sroot #ifdef INET 3385640Sroot case PF_INET: { 3395640Sroot register struct ip *ip = mtod(m0, struct ip *); 3405640Sroot 3415640Sroot dhost = ip->ip_dst.s_host; 3425640Sroot dimp = ip->ip_dst.s_imp; 3435640Sroot dlink = IMPLINK_IP; 3445640Sroot len = ntohs(ip->ip_len); 3455640Sroot break; 3465640Sroot } 3475640Sroot #endif 3485640Sroot case PF_IMPLINK: 3495640Sroot goto leaderexists; 3505640Sroot 3515640Sroot default: 3525640Sroot printf("imp%d: can't encapsulate pf%d\n", ifp->if_unit, pf); 3535640Sroot m_freem(m0); 3545640Sroot return (0); 3555640Sroot } 3565640Sroot 3575640Sroot /* 3585640Sroot * Add IMP leader. If there's not enough space in the 3595640Sroot * first mbuf, allocate another. If that should fail, we 3605640Sroot * drop this sucker. 3615640Sroot */ 3625640Sroot if (m->m_off > MMAXOFF || 3635640Sroot MMINOFF + sizeof(struct imp_leader) > m->m_off) { 3645640Sroot m = m_get(M_DONTWAIT); 3655640Sroot if (m == 0) { 3665640Sroot m_freem(m0); 3675640Sroot return (0); 3685640Sroot } 3695640Sroot m->m_next = m0; 3705640Sroot m->m_off = MMINOFF; 3715640Sroot m->m_len = sizeof(struct imp_leader); 3725640Sroot } else { 3735640Sroot m->m_off -= sizeof(struct imp_leader); 3745640Sroot m->m_len += sizeof(struct imp_leader); 3755640Sroot } 3765640Sroot imp = mtod(m, struct imp_leader *); 3775640Sroot imp->il_format = IMP_NFF; 3785640Sroot imp->il_host = dhost; 3795640Sroot imp->il_impno = dimp; 3805640Sroot imp->il_length = (len + sizeof(struct imp_leader)) << 3; 3815640Sroot imp->il_link = dlink; 3825640Sroot 3835640Sroot leaderexists: 3845640Sroot /* 3855640Sroot * Hand message to impsnd to perform RFNM counting 3865640Sroot * and eventual transmission. 3875640Sroot */ 3885640Sroot return (impsnd(ifp, m)); 3895640Sroot } 3905640Sroot 3915640Sroot /* 3925640Sroot * Put a message on an interface's output queue. 3935640Sroot * Perform RFNM counting: no more than 8 message may be 3945640Sroot * in flight to any one host. 3955640Sroot */ 3965640Sroot impsnd(ifp, m) 3975640Sroot struct ifnet *ifp; 3985640Sroot struct mbuf *m; 3995640Sroot { 4005640Sroot register struct imp_leader *ip; 4015640Sroot register struct host *hp; 4025640Sroot struct impcb *icp; 4035640Sroot int x; 4045640Sroot 4055640Sroot ip = mtod(m, struct imp_leader *); 4065640Sroot 4075640Sroot /* 4085640Sroot * Do RFNM counting for data messages 4095640Sroot * (no more than 8 outstanding to any host) 4105640Sroot */ 4115640Sroot if (ip->il_mtype == IMPTYPE_DATA) { 4125640Sroot struct in_addr addr; 4135640Sroot 4145640Sroot addr.s_net = ifp->if_net; 4155640Sroot addr.s_host = ip->il_host; 4165640Sroot addr.s_imp = ip->il_imp; 4175640Sroot hp = h_enter(addr); 4185640Sroot 4195640Sroot /* 4205640Sroot * If IMP would block, queue until rfnm 4215640Sroot */ 4225640Sroot if (hp) { 4235640Sroot register struct mbuf *n; 4245640Sroot int cnt; 4255640Sroot 4265640Sroot if (hp->h_rfnm < 8) { 4275640Sroot hp->h_rfnm++; 4285640Sroot goto enque; 4295640Sroot } 4305640Sroot /* 4315640Sroot * Keeping the count in the host structure 4325640Sroot * causes the packing scheme to lose too much. 4335640Sroot */ 4345640Sroot cnt = 0, n = hp->h_q; 4355640Sroot for (; n != (struct mbuf *)hp; n = n->m_act) 4365640Sroot cnt++; 4375640Sroot if (cnt >= 8) 4385640Sroot goto drop; 4395640Sroot if ((n = hp->h_q) == 0) 4405640Sroot hp->h_q = m->m_act = m; 4415640Sroot else { 4425640Sroot m->m_act = n->m_act; 4435640Sroot hp->h_q = n->m_act = m; 4445640Sroot } 4455640Sroot goto start; 4465640Sroot } 4475640Sroot drop: 4485640Sroot m_freem(m); 4495640Sroot return (0); 4505640Sroot } 4515640Sroot enque: 4525640Sroot x = splimp(); 4535640Sroot IF_ENQUEUE(&ifp->if_snd, m); 4545640Sroot splx(x); 4555640Sroot 4565640Sroot start: 4575640Sroot icp = &imp_softc[ifp->if_unit].imp_cb; 4585640Sroot if (icp->ic_oactive == 0) 4595640Sroot (*icp->ic_start)(ifp->if_unit); 4605640Sroot return (1); 4615640Sroot } 4625640Sroot 4635640Sroot /* 4645640Sroot * Put three 1822 NOOPs at the head of the output queue. 4655640Sroot * Part of host-IMP initialization procedure. 4665640Sroot * (Should return success/failure, but noone knows 4675640Sroot * what to do with this, so why bother?) 4685640Sroot */ 4695640Sroot impnoops(sc) 4705640Sroot register struct imp_softc *sc; 4715640Sroot { 4725640Sroot register i; 4735640Sroot register struct mbuf *m; 4745640Sroot register struct imp_leader *ip; 4755640Sroot int x; 4765640Sroot 4775640Sroot sc->imp_state = IMPS_INIT; 4785640Sroot sc->imp_dropcnt = IMP_DROPCNT; 4795640Sroot for (i = 0; i < IMP_DROPCNT; i++ ) { 4805640Sroot if ((m = m_getclr(M_DONTWAIT)) == 0) 4815640Sroot return; 4825640Sroot m->m_off = MMINOFF; 4835640Sroot m->m_len = sizeof(struct imp_leader); 4845640Sroot ip = mtod(m, struct imp_leader *); 4855640Sroot ip->il_format = IMP_NFF; 4865640Sroot ip->il_link = i; 4875640Sroot ip->il_mtype = IMPTYPE_NOOP; 4885640Sroot x = splimp(); 4895640Sroot IF_PREPEND(&sc->imp_if.if_snd, m); 4905640Sroot splx(x); 4915640Sroot } 4925640Sroot if (sc->imp_cb.ic_oactive == 0) 4935640Sroot (*sc->imp_cb.ic_start)(sc->imp_if.if_unit); 4945640Sroot } 4955640Sroot #endif 496