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