xref: /csrg-svn/sys/deprecated/netimp/if_imp.c (revision 33453)
1 /*
2  * Copyright (c) 1982,1986,1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that this notice is preserved and that due credit is given
7  * to the University of California at Berkeley. The name of the University
8  * may not be used to endorse or promote products derived from this
9  * software without specific prior written permission. This software
10  * is provided ``as is'' without express or implied warranty.
11  *
12  *	@(#)if_imp.c	7.4 (Berkeley) 02/08/88
13  */
14 
15 #include "imp.h"
16 #if NIMP > 0
17 /*
18  * ARPANET IMP (PSN) interface driver.
19  *
20  * The IMP-host protocol (AHIP) is handled here, leaving
21  * hardware specifics to the lower level interface driver.
22  */
23 #include "param.h"
24 #include "systm.h"
25 #include "mbuf.h"
26 #include "buf.h"
27 #include "protosw.h"
28 #include "socket.h"
29 #include "time.h"
30 #include "kernel.h"
31 #include "errno.h"
32 #include "ioctl.h"
33 #include "syslog.h"
34 
35 #include "../machine/mtpr.h"
36 
37 #include "../vaxuba/ubavar.h"
38 
39 #include "../net/if.h"
40 
41 #include "../net/netisr.h"
42 #include "../netinet/in.h"
43 #include "../netinet/in_systm.h"
44 #include "../netinet/in_var.h"
45 #include "../netinet/ip.h"
46 #include "../netinet/ip_var.h"
47 #define IMPMESSAGES
48 /* define IMPLEADERS here to get leader printing code */
49 #include "if_imp.h"
50 #include "if_imphost.h"
51 
52 struct	imp_softc imp_softc[NIMP];
53 #ifndef lint
54 int	nimp = NIMP;			/* for netstat */
55 #endif
56 struct	ifqueue impintrq;
57 int	impqmaxlen = IFQ_MAXLEN;
58 int	imphqlen = 12 + IMP_MAXHOSTMSG;	/* max packets to queue per host */
59 
60 int	imppri = LOG_ERR;
61 #ifdef IMPLEADERS
62 int	impprintfs = 0;
63 #endif
64 #ifdef IMPINIT
65 int	imptraceinit = 0;
66 #endif
67 
68 
69 #define HOSTDEADTIMER	(30 * PR_SLOWHZ)	/* How long to wait when down */
70 
71 int	impdown(), impinit(), impioctl(), impoutput(), imptimo();
72 
73 /*
74  * IMP attach routine.  Called from hardware device attach routine
75  * at configuration time with a pointer to the device structure.
76  * Sets up local state and returns pointer to base of ifnet+impcb
77  * structures.  This is then used by the device's attach routine
78  * set up its back pointers.
79  */
80 struct imp_softc *
81 impattach(ui, reset)
82 	struct uba_device *ui;
83 	int (*reset)();
84 {
85 	struct imp_softc *sc;
86 	register struct ifnet *ifp;
87 	static int impunit;
88 
89 #ifdef lint
90 	impintr();
91 #endif
92 	if (impunit >= NIMP) {
93 		printf("imp%d: not configured\n", impunit++);
94 		return (0);
95 	}
96 	sc = &imp_softc[impunit];
97 	ifp = &sc->imp_if;
98 	sc->imp_cb.ic_hwunit = ui->ui_unit;
99 	sc->imp_cb.ic_hwname = ui->ui_driver->ud_dname;
100 	ifp->if_unit = impunit;
101 	ifp->if_name = "imp";
102 	ifp->if_mtu = IMPMTU - sizeof(struct imp_leader);
103 	ifp->if_reset = reset;
104 	ifp->if_init = impinit;
105 	ifp->if_ioctl = impioctl;
106 	ifp->if_output = impoutput;
107 	ifp->if_watchdog = imptimo;
108 	if_attach(ifp);
109 	impunit++;
110 	return (sc);
111 }
112 
113 /*
114  * IMP initialization routine: call hardware module to
115  * setup resources, init state and get ready for
116  * NOOPs the IMP should send us, and that we want to drop.
117  */
118 impinit(unit)
119 	int unit;
120 {
121 	int s;
122 	register struct imp_softc *sc = &imp_softc[unit];
123 
124 	if (sc->imp_if.if_addrlist == 0)
125 		return;
126 	s = splimp();
127 #ifdef IMPINIT
128 	if (imptraceinit)
129 		log(imppri, "impinit\n");
130 #endif
131 	sc->imp_state = IMPS_WINIT;
132 	if ((*sc->imp_cb.ic_init)(sc->imp_cb.ic_hwunit) == 0)
133 		sc->imp_if.if_flags &= ~IFF_UP;
134 	impintrq.ifq_maxlen = impqmaxlen;
135 	splx(s);
136 }
137 
138 /*
139  * ARPAnet 1822/AHIP input routine.
140  * Called from hardware input interrupt routine to handle 1822
141  * IMP-host messages.  Data messages are passed to higher-level
142  * protocol processors on the basis of link number.
143  * Other type messages (control) are handled here.
144  */
145 impinput(unit, m)
146 	int unit;
147 	register struct mbuf *m;
148 {
149 	register struct control_leader *cp;
150 #define	ip	((struct imp_leader *)cp)
151 	register struct imp_softc *sc = &imp_softc[unit];
152 	struct ifnet *ifp;
153 	register struct host *hp;
154 	register struct ifqueue *inq;
155 	struct mbuf *next;
156 	struct sockaddr_in *sin;
157 	int s;
158 
159 	/*
160 	 * Pull the interface pointer out of the mbuf
161 	 * and save for later; adjust mbuf to look at rest of data.
162 	 */
163 	ifp = *(mtod(m, struct ifnet **));
164 	IF_ADJ(m);
165 	/* verify leader length. */
166 	if (m->m_len < sizeof(struct control_leader) &&
167 	    (m = m_pullup(m, sizeof(struct control_leader))) == 0)
168 		return;
169 	cp = mtod(m, struct control_leader *);
170 	if (cp->dl_mtype == IMPTYPE_DATA &&
171 	    m->m_len < sizeof(struct imp_leader)) {
172 		if ((m = m_pullup(m, sizeof(struct imp_leader))) == 0)
173 			return;
174 		cp = mtod(m, struct control_leader *);
175 	}
176 #ifdef IMPLEADERS
177 	if (impprintfs)
178 		printleader("impinput", ip);
179 #endif
180 	inq = &impintrq;
181 
182 	/* check leader type */
183 	if (cp->dl_format != IMP_NFF) {
184 		/* ignore old-style noops on reset */
185 		if (cp->dl_mtype != IMPTYPE_NOOP &&
186 		    cp->dl_mtype != IMPTYPE_RESET) {
187 			sc->imp_garbage++;
188 			sc->imp_if.if_collisions++;	/* XXX */
189 		}
190 	} else switch (cp->dl_mtype) {
191 
192 	case IMPTYPE_DATA:
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.
198 		 */
199 		switch (cp->dl_link) {
200 
201 		case IMPLINK_IP:
202 			m->m_len -= sizeof(struct imp_leader);
203 			m->m_off += sizeof(struct imp_leader);
204 			schednetisr(NETISR_IP);
205 			inq = &ipintrq;
206 			break;
207 
208 		default:
209 			break;
210 		}
211 		break;
212 
213 	/*
214 	 * IMP leader error.  Reset the IMP and discard the packet.
215 	 */
216 	case IMPTYPE_BADLEADER:
217 		/*
218 		 * According to 1822 document, this message
219 		 * will be generated in response to the
220 		 * first noop sent to the IMP after
221 		 * the host resets the IMP interface.
222 		 */
223 #ifdef IMPINIT
224 		if (imptraceinit)
225 			log(imppri, "badleader\n");
226 #endif
227 		if (sc->imp_state != IMPS_INIT) {
228 			impmsg(sc, "leader error");
229 			sc->imp_msgready = 0;
230 			hostreset(unit);
231 			impnoops(sc);
232 			sc->imp_garbage++;
233 		}
234 		break;
235 
236 	/*
237 	 * IMP going down.  Print message, and if not immediate,
238 	 * set off a timer to insure things will be reset at the
239 	 * appropriate time.
240 	 */
241 	case IMPTYPE_DOWN:
242 	    {	int type, when;
243 
244 		type = cp->dl_link & IMP_DMASK;
245 		when = (cp->dl_link & IMPDOWN_WHENMASK) >> IMPDOWN_WHENSHIFT;
246 #ifdef IMPINIT
247 		if (imptraceinit)
248 			log(imppri, "input DOWN %s %d\n",
249 			    impmessage[type], when * IMPDOWN_WHENUNIT);
250 #endif
251 		if (type != IMPDOWN_GOING && when)
252 			impmsg(sc, "going down %s in %d minutes",
253 			    (u_int)impmessage[type], when * IMPDOWN_WHENUNIT);
254 		else
255 			impmsg(sc, "going down %s", (u_int)impmessage[type]);
256 		if (sc->imp_state != IMPS_UP)
257 			break;
258 		if (type == IMPDOWN_GOING) {
259 			sc->imp_state = IMPS_GOINGDOWN;
260 			timeout(impdown, (caddr_t)sc, IMPTV_DOWN * hz);
261 		} else if (when == 0)
262 			sc->imp_state = IMPS_WINIT;
263 		sc->imp_dropcnt = 0;
264 		break;
265 	    }
266 
267 	/*
268 	 * A NOP, usually seen during the initialization sequence.
269 	 * Compare the local address with that in the message.
270 	 * Reset the local address notion if it doesn't match.
271 	 */
272 	case IMPTYPE_NOOP:
273 #ifdef IMPINIT
274 		if (imptraceinit)
275 			log(imppri, "noop\n");
276 #endif
277 		if (sc->imp_state == IMPS_WINIT) {
278 			sc->imp_dropcnt = 0;
279 			impnoops(sc);
280 			sc->imp_state = IMPS_INIT;
281 		}
282 		sc->imp_dropcnt++;
283 		if (sc->imp_state == IMPS_INIT && cp->dl_imp != 0) {
284 			struct in_addr leader_addr;
285 
286 			sin = (struct sockaddr_in *)&sc->imp_if.if_addrlist->ifa_addr;
287 			imp_leader_to_addr(&leader_addr, cp, &sc->imp_if);
288 			if (sin->sin_addr.s_addr != leader_addr.s_addr) {
289 				impmsg(sc, "address reset to x%x (%d/%d)",
290 					ntohl(leader_addr.s_addr),
291 					(u_int)cp->dl_host,
292 					ntohs(cp->dl_imp));
293 				sin->sin_addr.s_addr = leader_addr.s_addr;
294 			}
295 		}
296 		break;
297 
298 	/*
299 	 * RFNM or INCOMPLETE message, decrement rfnm count
300 	 * and prepare to send next message.
301 	 * We could pass incomplete's up to the next level,
302 	 * but this currently isn't needed.
303 	 * Pass "bad" incompletes and rfnms to the log.
304 	 */
305 	case IMPTYPE_INCOMPLETE:
306 		sc->imp_incomplete++;
307 		/* FALL THROUGH */
308 	case IMPTYPE_RFNM:
309 		if (hp = hostlookup(cp->dl_imp, cp->dl_host, unit)) {
310 			if (hp->h_rfnm == 0) {
311 				sc->imp_badrfnm++;
312 				break;
313 			}
314 			if (--hp->h_rfnm > 0) {
315 				hp->h_timer = RFNMTIMER;
316 				if (hp->h_qcnt) {
317 					/*
318 					 * If the rfnm allows another queued
319 					 * message to be sent, bump msgready
320 					 * and start IMP if idle.
321 					 */
322 					if (hp->h_qcnt >
323 					   IMP_MAXHOSTMSG - 1 - hp->h_rfnm)
324 						sc->imp_msgready++;
325 					if (sc->imp_cb.ic_oactive == 0)
326 						impstarthost(sc, hp);
327 				}
328 			} else if (hp->h_qcnt == 0)
329 				hostfree(hp);
330 			/* else messages on queue waiting their turn */
331 			goto drop;
332 		} else
333 			sc->imp_badrfnm++;
334 		break;
335 
336 	/*
337 	 * Host or IMP can't be reached.  Flush any packets
338 	 * awaiting transmission and release the host structure.
339 	 * Enqueue for notifying protocols at software interrupt time.
340 	 */
341 	case IMPTYPE_HOSTDEAD:
342 	case IMPTYPE_HOSTUNREACH:
343 		if (hp = hostlookup(cp->dl_imp, cp->dl_host, unit)) {
344 			hp->h_flags |= (1 << (int)cp->dl_mtype);
345 			sc->imp_msgready -=
346 			   MIN(hp->h_qcnt, IMP_MAXHOSTMSG - hp->h_rfnm);
347 			hp->h_rfnm = 0;
348 			hostflush(hp);
349 			hp->h_timer = HOSTDEADTIMER;
350 		}
351 		break;
352 
353 	/*
354 	 * Error in data.  Clear RFNM status for this host and send
355 	 * noops to the IMP to clear the interface.
356 	 */
357 	case IMPTYPE_BADDATA:
358 		impmsg(sc, "data error");
359 		if ((hp = hostlookup(cp->dl_imp, cp->dl_host, unit)) &&
360 		    hp->h_rfnm) {
361 			sc->imp_msgready -=
362 			   MIN(hp->h_qcnt, IMP_MAXHOSTMSG - hp->h_rfnm);
363 			hostrelease(hp);
364 		}
365 		impnoops(sc);
366 		break;
367 
368 	/*
369 	 * Interface reset.
370 	 */
371 	case IMPTYPE_RESET:
372 #ifdef IMPINIT
373 		if (imptraceinit)
374 			log(imppri, "reset complete\n");
375 #endif
376 		if (sc->imp_state != IMPS_INIT) {
377 			impmsg(sc, "interface reset");
378 			impnoops(sc);
379 		}
380 		/* clear RFNM counts */
381 		sc->imp_msgready = 0;
382 		hostreset(unit);
383 		if (sc->imp_state != IMPS_DOWN) {
384 			sc->imp_state = IMPS_UP;
385 			sc->imp_if.if_flags |= IFF_UP;
386 #ifdef IMPINIT
387 			if (imptraceinit)
388 				log(imppri, "IMP UP\n");
389 #endif
390 		}
391 		break;
392 
393 	default:
394 		sc->imp_garbage++;
395 		sc->imp_if.if_collisions++;		/* XXX */
396 		break;
397 	}
398 
399 	if (inq == &impintrq)
400 		schednetisr(NETISR_IMP);
401 	/*
402 	 * Re-insert interface pointer in the mbuf chain
403 	 * for the next protocol up.
404 	 */
405 	if (M_HASCL(m) && (mtod(m, int) & CLOFSET) < sizeof(struct ifnet *)) {
406 		struct mbuf *n;
407 
408 		MGET(n, M_DONTWAIT, MT_HEADER);
409 		if (n == 0)
410 			goto drop;
411 		n->m_next = m;
412 		m = n;
413 		m->m_len = 0;
414 		m->m_off = MMINOFF + sizeof(struct ifnet  *);
415 	}
416 	m->m_off -= sizeof(struct ifnet *);
417 	m->m_len += sizeof(struct ifnet *);
418 	*(mtod(m, struct ifnet **)) = ifp;
419 
420 	s = splimp();
421 	if (!IF_QFULL(inq)) {
422 		IF_ENQUEUE(inq, m);
423 		splx(s);
424 		return;
425 	}
426 	splx(s);
427 drop:
428 	IF_DROP(inq);
429 	m_freem(m);
430 #undef ip
431 }
432 
433 /*
434  * Bring the IMP down after notification.
435  */
436 impdown(sc)
437 	struct imp_softc *sc;
438 {
439 	int s = splimp();
440 
441 	if (sc->imp_state == IMPS_GOINGDOWN) {
442 		sc->imp_state = IMPS_WINIT;
443 		impmsg(sc, "marked down");
444 		hostreset(sc->imp_if.if_unit);
445 		if_down(&sc->imp_if);
446 	}
447 #ifdef IMPINIT
448 	else if (imptraceinit)
449 		log(imppri, "impdown, state now %d (ignored)\n", sc->imp_state);
450 #endif
451 	splx(s);
452 }
453 
454 /*VARARGS2*/
455 impmsg(sc, fmt, a1, a2, a3)
456 	struct imp_softc *sc;
457 	char *fmt;
458 	u_int a1;
459 {
460 
461 	log(imppri, "imp%d: %r\n", sc->imp_if.if_unit, fmt, &a1);
462 }
463 
464 struct sockproto impproto = { PF_IMPLINK };
465 struct sockaddr_in impdst = { AF_IMPLINK };
466 struct sockaddr_in impsrc = { AF_IMPLINK };
467 
468 /*
469  * Pick up the IMP "error" messages enqueued earlier,
470  * passing these up to the higher level protocol
471  * and the raw interface.
472  */
473 impintr()
474 {
475 	register struct mbuf *m;
476 	register struct control_leader *cp;
477 	struct ifnet *ifp;
478 	int s;
479 
480 	for (;;) {
481 		s = splimp();
482 		IF_DEQUEUEIF(&impintrq, m, ifp);
483 		splx(s);
484 		if (m == 0)
485 			return;
486 
487 		cp = mtod(m, struct control_leader *);
488 		imp_leader_to_addr(&impsrc.sin_addr, cp, ifp);
489 		impproto.sp_protocol = cp->dl_link;
490 		impdst.sin_addr = IA_SIN(ifp->if_addrlist)->sin_addr;
491 
492 		if (cp->dl_mtype == IMPTYPE_HOSTDEAD ||
493 		    cp->dl_mtype == IMPTYPE_HOSTUNREACH)
494 			switch (cp->dl_link) {
495 
496 			case IMPLINK_IP:
497 				pfctlinput((int)cp->dl_mtype,
498 				    (struct sockaddr *)&impsrc);
499 				break;
500 			default:
501 				raw_ctlinput((int)cp->dl_mtype,
502 				    (struct sockaddr *)&impsrc);
503 				break;
504 			}
505 
506 		raw_input(m, &impproto, (struct sockaddr *)&impsrc,
507 		  (struct sockaddr *)&impdst);
508 	}
509 }
510 
511 /*
512  * ARPAnet 1822 output routine.
513  * Called from higher level protocol routines to set up messages for
514  * transmission to the imp.  Sets up the header and calls impsnd to
515  * enqueue the message for this IMP's hardware driver.
516  */
517 impoutput(ifp, m0, dst)
518 	register struct ifnet *ifp;
519 	struct mbuf *m0;
520 	struct sockaddr *dst;
521 {
522 	register struct imp_leader *imp;
523 	register struct mbuf *m = m0;
524 	int error = 0;
525 
526 	/*
527 	 * Don't even try if the IMP is unavailable.
528 	 */
529 	if (!IMPS_RUNNING(imp_softc[ifp->if_unit].imp_state)) {
530 		error = ENETDOWN;
531 		goto drop;
532 	}
533 
534 	/*
535 	 * If AF_IMPLINK, leader exists; just send.
536 	 * Otherwise, construct leader according to address family.
537 	 */
538 	if (dst->sa_family != AF_IMPLINK) {
539 		/*
540 		 * Add IMP leader.  If there's not enough space in the
541 		 * first mbuf, allocate another.  If that should fail, we
542 		 * drop this sucker.
543 		 */
544 		if (m->m_off > MMAXOFF ||
545 		    MMINOFF + sizeof(struct imp_leader) > m->m_off) {
546 			m = m_get(M_DONTWAIT, MT_HEADER);
547 			if (m == 0) {
548 				error = ENOBUFS;
549 				goto drop;
550 			}
551 			m->m_next = m0;
552 			m->m_len = sizeof(struct imp_leader);
553 		} else {
554 			m->m_off -= sizeof(struct imp_leader);
555 			m->m_len += sizeof(struct imp_leader);
556 		}
557 		imp = mtod(m, struct imp_leader *);
558 		imp->il_format = IMP_NFF;
559 		imp->il_mtype = IMPTYPE_DATA;
560 		imp->il_flags = imp->il_htype = imp->il_subtype = 0;
561 
562 		switch (dst->sa_family) {
563 
564 		case AF_INET: {
565 			struct ip *ip = mtod(m, struct ip *);
566 
567 			imp->il_link = IMPLINK_IP;
568 			imp_addr_to_leader((struct control_leader *)imp,
569 				((struct sockaddr_in *)dst)->sin_addr.s_addr);
570 			imp->il_length = htons(ntohs((u_short)ip->ip_len) << 3);
571 			break;
572 		}
573 
574 		default:
575 			printf("imp%d: can't handle af%d\n", ifp->if_unit,
576 				dst->sa_family);
577 			error = EAFNOSUPPORT;
578 			goto drop;
579 		}
580 	}
581 	return (impsnd(ifp, m));
582 drop:
583 	m_freem(m0);
584 	return (error);
585 }
586 
587 /*
588  * Put a message on an interface's output queue.
589  * Perform RFNM counting: no more than 8 message may be
590  * in flight to any one host.
591  */
592 impsnd(ifp, m)
593 	struct ifnet *ifp;
594 	struct mbuf *m;
595 {
596 	register struct control_leader *imp;
597 	register struct host *hp;
598 	register struct imp_softc *sc = &imp_softc[ifp->if_unit];
599 	int s, error = 0;
600 
601 	imp = mtod(m, struct control_leader *);
602 
603 	/*
604 	 * Do RFNM counting for data messages
605 	 * (no more than 8 outstanding to any host).
606 	 * Queue data messages per host if 8 are already outstanding
607 	 * or if the hardware interface is already doing output.
608 	 * Increment imp_msgready if the message could be sent now,
609 	 * but must be queued because the imp output is busy.
610 	 */
611 	s = splimp();
612 	if (imp->dl_mtype == IMPTYPE_DATA) {
613 		hp = hostenter(imp->dl_imp, imp->dl_host, ifp->if_unit);
614 		if (hp && (hp->h_flags & (HF_DEAD|HF_UNREACH))) {
615 			error = hp->h_flags&HF_DEAD ? EHOSTDOWN : EHOSTUNREACH;
616 			goto bad;
617 		}
618 
619 		/*
620 		 * If IMP would block, queue until RFNM;
621 		 * if IMP is busy, queue until our turn.
622 		 */
623 		if (hp) {
624 			if (hp->h_rfnm < IMP_MAXHOSTMSG) {
625 				if (sc->imp_cb.ic_oactive == 0) {
626 					/*
627 					 * Send without queuing;
628 					 * adjust rfnm count and timer.
629 					 */
630 					if (hp->h_rfnm++ == 0)
631 					    hp->h_timer = RFNMTIMER;
632 					goto send;
633 				} else {
634 					sc->imp_msgready++;
635 					goto q;
636 				}
637 			} else if (hp->h_rfnm + hp->h_qcnt < imphqlen) {
638 		q:
639 				HOST_ENQUE(hp, m);
640 			} else
641 				error = ENOBUFS;
642 		} else
643 			error = ENOBUFS;
644 	} else if (sc->imp_cb.ic_oactive == 0)
645 		goto send;
646 	else
647 		IF_ENQUEUE(&ifp->if_snd, m);
648 	splx(s);
649 	return (0);
650 
651 send:
652 	sc->imp_if.if_timer = IMP_OTIMER;
653 	(*sc->imp_cb.ic_output)(sc->imp_cb.ic_hwunit, m);
654 	splx(s);
655 	return (0);
656 bad:
657 	m_freem(m);
658 	splx(s);
659 	return (error);
660 }
661 
662 /*
663  * Start another output operation on IMP; called from hardware
664  * transmit-complete interrupt routine at splimp or from imp routines
665  * when output is not in progress and more output is ready.  If any packets
666  * on shared output queue, send them, otherwise send the next data
667  * packet for a host.  Host data packets are sent round-robin based
668  * on destination.
669  */
670 impstart(sc)
671 	register struct imp_softc *sc;
672 {
673 	register struct mbuf *m;
674 	struct mbuf *m0;
675 	register struct host *hp;
676 	int index;
677 
678 	IF_DEQUEUE(&sc->imp_if.if_snd, m);
679 	if (m) {
680 		sc->imp_if.if_timer = IMP_OTIMER;
681 		(*sc->imp_cb.ic_output)(sc->imp_cb.ic_hwunit, m);
682 		return;
683 	}
684 	if (sc->imp_msgready) {
685 		if ((m = sc->imp_hostq) == 0 && (m = sc->imp_hosts) == 0)
686 			panic ("imp msgready");
687 		if ((index = sc->imp_hostent) >= HPMBUF)
688 			index = 0;
689 		m0 = m;
690 		for (;;) {
691 			for (hp = &mtod(m, struct hmbuf *)->hm_hosts[index];
692 			    index < HPMBUF; hp++, index++) {
693 				if (hp->h_q && hp->h_rfnm < IMP_MAXHOSTMSG) {
694 					impstarthost(sc, hp);
695 					sc->imp_hostq = m;
696 					sc->imp_hostent = index;
697 					return;
698 				}
699 			}
700 			if ((m = m->m_next) == 0)
701 				m = sc->imp_hosts;
702 			if (m == m0) {
703 				if (sc->imp_hostent != 0)
704 					sc->imp_hostent = 0;
705 				else {
706 					log(LOG_ERR,
707 					    "imp can't find %d msgready\n",
708 					    sc->imp_msgready);
709 					sc->imp_msgready = 0;
710 					break;
711 				}
712 			}
713 			index = 0;
714 		}
715 	}
716 	sc->imp_if.if_timer = 0;
717 }
718 
719 /*
720  * Restart output for a host that has timed out
721  * while waiting for a RFNM and has more packets to send.
722  * Must be called at splimp.
723  */
724 imprestarthost(unit, hp)
725 	int unit;
726 	struct host *hp;
727 {
728 	register struct imp_softc *sc = &imp_softc[unit];
729 
730 	sc->imp_lostrfnm++;
731 	if (--hp->h_rfnm > 0)
732 		hp->h_timer = RFNMTIMER;
733 	if (hp->h_qcnt) {
734 		/*
735 		 * If the rfnm allows another queued
736 		 * message to be sent, bump msgready
737 		 * and start IMP if idle.
738 		 */
739 		if (hp->h_qcnt >
740 		   IMP_MAXHOSTMSG - 1 - hp->h_rfnm)
741 			sc->imp_msgready++;
742 		if (sc->imp_cb.ic_oactive == 0)
743 			impstarthost(sc, hp);
744 	}
745 }
746 
747 /*
748  * Send the next message queued for a host
749  * when ready to send another message to the IMP.
750  * Called only when output is not in progress.
751  * Bump RFNM counter and start RFNM timer
752  * when we send the message to the IMP.
753  * Must be called at splimp.
754  */
755 impstarthost(sc, hp)
756 	register struct imp_softc *sc;
757 	register struct host *hp;
758 {
759 	struct mbuf *m;
760 
761 	if (hp->h_rfnm++ == 0)
762 		hp->h_timer = RFNMTIMER;
763 	HOST_DEQUE(hp, m);
764 	sc->imp_if.if_timer = IMP_OTIMER;
765 	(*sc->imp_cb.ic_output)(sc->imp_cb.ic_hwunit, m);
766 	sc->imp_msgready--;
767 }
768 
769 /*
770  * "Watchdog" timeout.  When the output timer expires,
771  * we assume we have been blocked by the imp.
772  * No need to restart, just collect statistics.
773  */
774 imptimo(unit)
775 	int unit;
776 {
777 
778 	imp_softc[unit].imp_block++;
779 }
780 
781 /*
782  * Put three 1822 NOOPs at the head of the output queue.
783  * Part of host-IMP initialization procedure.
784  * (Should return success/failure, but noone knows
785  * what to do with this, so why bother?)
786  * This routine is always called at splimp, so we don't
787  * protect the call to IF_PREPEND.
788  */
789 impnoops(sc)
790 	register struct imp_softc *sc;
791 {
792 	register i;
793 	register struct mbuf *m;
794 	register struct control_leader *cp;
795 
796 #ifdef IMPINIT
797 	if (imptraceinit)
798 		log(imppri, "impnoops\n");
799 #endif
800 	for (i = 0; i < IMP_NOOPCNT; i++) {
801 		if ((m = m_getclr(M_DONTWAIT, MT_HEADER)) == 0)
802 			return;
803 		m->m_len = sizeof(struct control_leader);
804 		cp = mtod(m, struct control_leader *);
805 		cp->dl_format = IMP_NFF;
806 		cp->dl_link = i;
807 		cp->dl_mtype = IMPTYPE_NOOP;
808 		IF_PREPEND(&sc->imp_if.if_snd, m);
809 	}
810 	if (sc->imp_cb.ic_oactive == 0)
811 		impstart(sc);
812 }
813 
814 /*
815  * Process an ioctl request.
816  */
817 impioctl(ifp, cmd, data)
818 	register struct ifnet *ifp;
819 	int cmd;
820 	caddr_t data;
821 {
822 	struct ifaddr *ifa = (struct ifaddr *) data;
823 	int s = splimp(), error = 0;
824 #define sc	((struct imp_softc *)ifp)
825 
826 	switch (cmd) {
827 
828 	case SIOCSIFADDR:
829 		if (ifa->ifa_addr.sa_family != AF_INET) {
830 			error = EINVAL;
831 			break;
832 		}
833 		if ((ifp->if_flags & IFF_UP) == 0)
834 			impinit(ifp->if_unit);
835 		break;
836 
837 	case SIOCSIFFLAGS:
838 		if ((ifp->if_flags & IFF_UP) == 0 &&
839 		    sc->imp_state != IMPS_DOWN) {
840 			if (sc->imp_cb.ic_down &&
841 			    (*sc->imp_cb.ic_down)(sc->imp_cb.ic_hwunit))
842 				sc->imp_state = IMPS_DOWN;
843 		} else if (ifp->if_flags & IFF_UP && sc->imp_state == IMPS_DOWN)
844 			impinit(ifp->if_unit);
845 		break;
846 
847 	default:
848 		error = EINVAL;
849 		break;
850 	}
851 	splx(s);
852 	return (error);
853 }
854 
855 #ifdef IMPLEADERS
856 printleader(routine, ip)
857 	char *routine;
858 	register struct imp_leader *ip;
859 {
860 	printf("%s: ", routine);
861 	printbyte((char *)ip, 12);
862 	printf("<fmt=%x,net=%x,flags=%x,mtype=", ip->il_format, ip->il_network,
863 		ip->il_flags);
864 	if (ip->il_mtype <= IMPTYPE_READY)
865 		printf("%s,", impleaders[ip->il_mtype]);
866 	else
867 		printf("%x,", ip->il_mtype);
868 	printf("htype=%x,host=%x,imp=%x,link=", ip->il_htype, ip->il_host,
869 		ntohs(ip->il_imp));
870 	if (ip->il_link == IMPLINK_IP)
871 		printf("ip,");
872 	else
873 		printf("%x,", ip->il_link);
874 	printf("subtype=%x,len=%x>\n",ip->il_subtype,ntohs(ip->il_length)>>3);
875 }
876 
877 printbyte(cp, n)
878 	register char *cp;
879 	int n;
880 {
881 	register i, j, c;
882 
883 	for (i=0; i<n; i++) {
884 		c = *cp++;
885 		for (j=0; j<2; j++)
886 			putchar("0123456789abcdef"[(c>>((1-j)*4))&0xf], 0);
887 		putchar(' ', 0);
888 	}
889 	putchar('\n', 0);
890 }
891 #endif
892 
893 /*
894  * Routine to convert from IMP Leader to InterNet Address.
895  *
896  * This procedure is necessary because IMPs may be assigned Class A, B, or C
897  * network numbers, but only have 8 bits in the leader to reflect the
898  * IMP "network number".  The strategy is to take the network number from
899  * the ifnet structure, and blend in the host-on-imp and imp-on-net numbers
900  * from the leader.
901  *
902  * There is no support for "Logical Hosts".
903  *
904  * Class A:	Net.Host.0.Imp
905  * Class B:	Net.net.Host.Imp
906  * Class C:	Net.net.net.(Host4|Imp4)
907  */
908 imp_leader_to_addr(ap, cp, ifp)
909 	struct in_addr *ap;
910 	register struct control_leader *cp;
911 	struct ifnet *ifp;
912 {
913 	register u_long final;
914 	register struct sockaddr_in *sin;
915 	int imp = ntohs(cp->dl_imp);
916 
917 	sin = (struct sockaddr_in *)(&ifp->if_addrlist->ifa_addr);
918 	final = ntohl(sin->sin_addr.s_addr);
919 
920 	if (IN_CLASSA(final)) {
921 		final &= IN_CLASSA_NET;
922 		final |= (imp & 0xFF) | ((cp->dl_host & 0xFF)<<16);
923 	} else if (IN_CLASSB(final)) {
924 		final &= IN_CLASSB_NET;
925 		final |= (imp & 0xFF) | ((cp->dl_host & 0xFF)<<8);
926 	} else {
927 		final &= IN_CLASSC_NET;
928 		final |= (imp & 0x0F) | ((cp->dl_host & 0x0F)<<4);
929 	}
930 	ap->s_addr = htonl(final);
931 }
932 
933 /*
934  * Function to take InterNet address and fill in IMP leader fields.
935  */
936 imp_addr_to_leader(imp, a)
937 	register struct control_leader *imp;
938 	u_long a;
939 {
940 	register u_long addr = ntohl(a);
941 
942 	imp->dl_network = 0;	/* !! */
943 
944 	if (IN_CLASSA(addr)) {
945 		imp->dl_host = ((addr>>16) & 0xFF);
946 		imp->dl_imp = addr & 0xFF;
947 	} else if (IN_CLASSB(addr)) {
948 		imp->dl_host = ((addr>>8) & 0xFF);
949 		imp->dl_imp = addr & 0xFF;
950 	} else {
951 		imp->dl_host = ((addr>>4) & 0xF);
952 		imp->dl_imp = addr & 0xF;
953 	}
954 	imp->dl_imp = htons(imp->dl_imp);
955 }
956 #endif
957