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