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