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