|
|
1.1 root 1: /*
2: * Copyright (c) 1987, 1989 Regents of the University of California.
3: * All rights reserved.
4: *
5: * Redistribution is only permitted until one year after the first shipment
6: * of 4.4BSD by the Regents. Otherwise, redistribution and use in source and
7: * binary forms are permitted provided that: (1) source distributions retain
8: * this entire copyright notice and comment, and (2) distributions including
9: * binaries display the following acknowledgement: This product includes
10: * software developed by the University of California, Berkeley and its
11: * contributors'' in the documentation or other materials provided with the
12: * distribution and in all advertising materials mentioning features or use
13: * of this software. Neither the name of the University nor the names of
14: * its contributors may be used to endorse or promote products derived from
15: * this software without specific prior written permission.
16: * THIS SOFTWARE IS PROVIDED AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
17: * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
18: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19: *
20: * @(#)if_sl.c 7.21 (Berkeley) 6/28/90
21: */
22:
23: /*
24: * Serial Line interface
25: *
26: * Rick Adams
27: * Center for Seismic Studies
28: * 1300 N 17th Street, Suite 1450
29: * Arlington, Virginia 22209
30: * (703)276-7900
31: * [email protected]
32: * seismo!rick
33: *
34: * Pounded on heavily by Chris Torek ([email protected], umcp-cs!chris).
35: * N.B.: this belongs in netinet, not net, the way it stands now.
36: * Should have a link-layer type designation, but wouldn't be
37: * backwards-compatible.
38: *
39: * Converted to 4.3BSD Beta by Chris Torek.
40: * Other changes made at Berkeley, based in part on code by Kirk Smith.
41: * W. Jolitz added slip abort.
42: *
43: * Hacked almost beyond recognition by Van Jacobson ([email protected]).
44: * Added priority queuing for "interactive" traffic; hooks for TCP
45: * header compression; ICMP filtering (at 2400 baud, some cretin
46: * pinging you can use up all your bandwidth). Made low clist behavior
47: * more robust and slightly less likely to hang serial line.
48: * Sped up a bunch of things.
49: *
50: * Note that splimp() is used throughout to block both (tty) input
51: * interrupts and network activity; thus, splimp must be >= spltty.
52: */
53:
54: /* $Header: if_sl.c,v 1.7 89/05/31 02:24:52 van Exp $ */
55: /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
56:
57: #include "sl.h"
58: #if NSL > 0
59:
60: #include "param.h"
61: #include "user.h"
62: #include "mbuf.h"
63: #include "buf.h"
64: #include "dkstat.h"
65: #include "socket.h"
66: #include "ioctl.h"
67: #include "file.h"
68: #include "tty.h"
69: #include "kernel.h"
70: #include "conf.h"
71: #include "errno.h"
72:
73: #include "if.h"
74: #include "if_types.h"
75: #include "netisr.h"
76: #include "route.h"
77: #if INET
78: #include "../netinet/in.h"
79: #include "../netinet/in_systm.h"
80: #include "../netinet/in_var.h"
81: #include "../netinet/ip.h"
82: #endif
83:
84: #include "machine/mtpr.h"
85:
86: #include "slcompress.h"
87: #include "if_slvar.h"
88:
89: /*
90: * SLMAX is a hard limit on input packet size. To simplify the code
91: * and improve performance, we require that packets fit in an mbuf
92: * cluster, and if we get a compressed packet, there's enough extra
93: * room to expand the header into a max length tcp/ip header (128
94: * bytes). So, SLMAX can be at most
95: * MCLBYTES - 128
96: *
97: * SLMTU is a hard limit on output packet size. To insure good
98: * interactive response, SLMTU wants to be the smallest size that
99: * amortizes the header cost. (Remember that even with
100: * type-of-service queuing, we have to wait for any in-progress
101: * packet to finish. I.e., we wait, on the average, 1/2 * mtu /
102: * cps, where cps is the line speed in characters per second.
103: * E.g., 533ms wait for a 1024 byte MTU on a 9600 baud line. The
104: * average compressed header size is 6-8 bytes so any MTU > 90
105: * bytes will give us 90% of the line bandwidth. A 100ms wait is
106: * tolerable (500ms is not), so want an MTU around 296. (Since TCP
107: * will send 256 byte segments (to allow for 40 byte headers), the
108: * typical packet size on the wire will be around 260 bytes). In
109: * 4.3tahoe+ systems, we can set an MTU in a route so we do that &
110: * leave the interface MTU relatively high (so we don't IP fragment
111: * when acting as a gateway to someone using a stupid MTU).
112: *
113: * Similar considerations apply to SLIP_HIWAT: It's the amount of
114: * data that will be queued 'downstream' of us (i.e., in clists
115: * waiting to be picked up by the tty output interrupt). If we
116: * queue a lot of data downstream, it's immune to our t.o.s. queuing.
117: * E.g., if SLIP_HIWAT is 1024, the interactive traffic in mixed
118: * telnet/ftp will see a 1 sec wait, independent of the mtu (the
119: * wait is dependent on the ftp window size but that's typically
120: * 1k - 4k). So, we want SLIP_HIWAT just big enough to amortize
121: * the cost (in idle time on the wire) of the tty driver running
122: * off the end of its clists & having to call back slstart for a
123: * new packet. For a tty interface with any buffering at all, this
124: * cost will be zero. Even with a totally brain dead interface (like
125: * the one on a typical workstation), the cost will be <= 1 character
126: * time. So, setting SLIP_HIWAT to ~100 guarantees that we'll lose
127: * at most 1% while maintaining good interactive response.
128: */
129: #define BUFOFFSET 128
130: #define SLMAX (MCLBYTES - BUFOFFSET)
131: #define SLBUFSIZE (SLMAX + BUFOFFSET)
132: #define SLMTU 296
133: #define SLIP_HIWAT roundup(50,CBSIZE)
134: #define CLISTRESERVE 1024 /* Can't let clists get too low */
135:
136: /*
137: * SLIP ABORT ESCAPE MECHANISM:
138: * (inspired by HAYES modem escape arrangement)
139: * 1sec escape 1sec escape 1sec escape { 1sec escape 1sec escape }
140: * signals a "soft" exit from slip mode by usermode process
141: */
142:
143: #define ABT_ESC '\033' /* can't be t_intr - distant host must know it*/
144: #define ABT_WAIT 1 /* in seconds - idle before an escape & after */
145: #define ABT_RECYCLE (5*2+2) /* in seconds - time window processing abort */
146:
147: #define ABT_SOFT 3 /* count of escapes */
148:
149: /*
150: * The following disgusting hack gets around the problem that IP TOS
151: * can't be set yet. We want to put "interactive" traffic on a high
152: * priority queue. To decide if traffic is interactive, we check that
153: * a) it is TCP and b) one of its ports is telnet, rlogin or ftp control.
154: */
155: static u_short interactive_ports[8] = {
156: 0, 513, 0, 0,
157: 0, 21, 0, 23,
158: };
159: #define INTERACTIVE(p) (interactive_ports[(p) & 7] == (p))
160:
161: struct sl_softc sl_softc[NSL];
162:
163: #define FRAME_END 0xc0 /* Frame End */
164: #define FRAME_ESCAPE 0xdb /* Frame Esc */
165: #define TRANS_FRAME_END 0xdc /* transposed frame end */
166: #define TRANS_FRAME_ESCAPE 0xdd /* transposed frame esc */
167:
168: #define t_sc T_LINEP
169:
170: int sloutput(), slioctl(), ttrstrt();
171: extern struct timeval time;
172:
173: /*
174: * Called from boot code to establish sl interfaces.
175: */
176: slattach()
177: {
178: register struct sl_softc *sc;
179: register int i = 0;
180:
181: for (sc = sl_softc; i < NSL; sc++) {
182: sc->sc_if.if_name = "sl";
183: sc->sc_if.if_unit = i++;
184: sc->sc_if.if_mtu = SLMTU;
185: sc->sc_if.if_flags = IFF_POINTOPOINT;
186: sc->sc_if.if_type = IFT_SLIP;
187: sc->sc_if.if_ioctl = slioctl;
188: sc->sc_if.if_output = sloutput;
189: sc->sc_if.if_snd.ifq_maxlen = 50;
190: sc->sc_fastq.ifq_maxlen = 32;
191: if_attach(&sc->sc_if);
192: }
193: }
194:
195: static int
196: slinit(sc)
197: register struct sl_softc *sc;
198: {
199: register caddr_t p;
200:
201: if (sc->sc_ep == (u_char *) 0) {
202: MCLALLOC(p, M_WAIT);
203: if (p)
204: sc->sc_ep = (u_char *)p + SLBUFSIZE;
205: else {
206: printf("sl%d: can't allocate buffer\n", sc - sl_softc);
207: sc->sc_if.if_flags &= ~IFF_UP;
208: return (0);
209: }
210: }
211: sc->sc_buf = sc->sc_ep - SLMAX;
212: sc->sc_mp = sc->sc_buf;
213: sl_compress_init(&sc->sc_comp);
214: return (1);
215: }
216:
217: /*
218: * Line specific open routine.
219: * Attach the given tty to the first available sl unit.
220: */
221: /* ARGSUSED */
222: slopen(dev, tp)
223: dev_t dev;
224: register struct tty *tp;
225: {
226: register struct sl_softc *sc;
227: register int nsl;
228: int error;
229:
230: if (error = suser(u.u_cred, &u.u_acflag))
231: return (error);
232:
233: if (tp->t_line == SLIPDISC)
234: return (0);
235:
236: for (nsl = NSL, sc = sl_softc; --nsl >= 0; sc++)
237: if (sc->sc_ttyp == NULL) {
238: if (slinit(sc) == 0)
239: return (ENOBUFS);
240: tp->t_sc = (caddr_t)sc;
241: sc->sc_ttyp = tp;
242: sc->sc_if.if_baudrate = tp->t_ospeed;
243: ttyflush(tp, FREAD | FWRITE);
244: return (0);
245: }
246: return (ENXIO);
247: }
248:
249: /*
250: * Line specific close routine.
251: * Detach the tty from the sl unit.
252: * Mimics part of ttyclose().
253: */
254: slclose(tp)
255: struct tty *tp;
256: {
257: register struct sl_softc *sc;
258: int s;
259:
260: ttywflush(tp);
261: s = splimp(); /* actually, max(spltty, splnet) */
262: tp->t_line = 0;
263: sc = (struct sl_softc *)tp->t_sc;
264: if (sc != NULL) {
265: if_down(&sc->sc_if);
266: sc->sc_ttyp = NULL;
267: tp->t_sc = NULL;
268: MCLFREE((caddr_t)(sc->sc_ep - SLBUFSIZE));
269: sc->sc_ep = 0;
270: sc->sc_mp = 0;
271: sc->sc_buf = 0;
272: }
273: splx(s);
274: }
275:
276: /*
277: * Line specific (tty) ioctl routine.
278: * Provide a way to get the sl unit number.
279: */
280: /* ARGSUSED */
281: sltioctl(tp, cmd, data, flag)
282: struct tty *tp;
283: caddr_t data;
284: {
285: struct sl_softc *sc = (struct sl_softc *)tp->t_sc;
286: int s;
287:
288: switch (cmd) {
289: case TIOCGETD: /* XXX */
290: case SLIOGUNIT:
291: *(int *)data = sc->sc_if.if_unit;
292: break;
293:
294: case SLIOCGFLAGS:
295: *(int *)data = sc->sc_flags;
296: break;
297:
298: case SLIOCSFLAGS:
299: #define SC_MASK 0xffff
300: s = splimp();
301: sc->sc_flags =
302: (sc->sc_flags &~ SC_MASK) | ((*(int *)data) & SC_MASK);
303: splx(s);
304: break;
305:
306: default:
307: return (-1);
308: }
309: return (0);
310: }
311:
312: /*
313: * Queue a packet. Start transmission if not active.
314: */
315: sloutput(ifp, m, dst)
316: struct ifnet *ifp;
317: register struct mbuf *m;
318: struct sockaddr *dst;
319: {
320: register struct sl_softc *sc = &sl_softc[ifp->if_unit];
321: register struct ip *ip;
322: register struct ifqueue *ifq;
323: int s;
324:
325: /*
326: * `Cannot happen' (see slioctl). Someday we will extend
327: * the line protocol to support other address families.
328: */
329: if (dst->sa_family != AF_INET) {
330: printf("sl%d: af%d not supported\n", sc->sc_if.if_unit,
331: dst->sa_family);
332: m_freem(m);
333: return (EAFNOSUPPORT);
334: }
335:
336: if (sc->sc_ttyp == NULL) {
337: m_freem(m);
338: return (ENETDOWN); /* sort of */
339: }
340: if ((sc->sc_ttyp->t_state & TS_CARR_ON) == 0) {
341: m_freem(m);
342: return (EHOSTUNREACH);
343: }
344: ifq = &sc->sc_if.if_snd;
345: if ((ip = mtod(m, struct ip *))->ip_p == IPPROTO_TCP) {
346: register int p = ((int *)ip)[ip->ip_hl];
347:
348: if (INTERACTIVE(p & 0xffff) || INTERACTIVE(p >> 16)) {
349: ifq = &sc->sc_fastq;
350: p = 1;
351: } else
352: p = 0;
353:
354: if (sc->sc_flags & SC_COMPRESS) {
355: /*
356: * The last parameter turns off connection id
357: * compression for background traffic: Since
358: * fastq traffic can jump ahead of the background
359: * traffic, we don't know what order packets will
360: * go on the line.
361: */
362: p = sl_compress_tcp(m, ip, &sc->sc_comp, p);
363: *mtod(m, u_char *) |= p;
364: }
365: } else if (sc->sc_flags & SC_NOICMP && ip->ip_p == IPPROTO_ICMP) {
366: m_freem(m);
367: return (0);
368: }
369: s = splimp();
370: if (IF_QFULL(ifq)) {
371: IF_DROP(ifq);
372: m_freem(m);
373: splx(s);
374: sc->sc_if.if_oerrors++;
375: return (ENOBUFS);
376: }
377: IF_ENQUEUE(ifq, m);
378: sc->sc_if.if_lastchange = time;
379: if (sc->sc_ttyp->t_outq.c_cc == 0)
380: slstart(sc->sc_ttyp);
381: splx(s);
382: return (0);
383: }
384:
385: /*
386: * Start output on interface. Get another datagram
387: * to send from the interface queue and map it to
388: * the interface before starting output.
389: */
390: slstart(tp)
391: register struct tty *tp;
392: {
393: register struct sl_softc *sc = (struct sl_softc *)tp->t_sc;
394: register struct mbuf *m;
395: register u_char *cp;
396: int s;
397: struct mbuf *m2;
398: extern int cfreecount;
399:
400: for (;;) {
401: /*
402: * If there is more in the output queue, just send it now.
403: * We are being called in lieu of ttstart and must do what
404: * it would.
405: */
406: if (tp->t_outq.c_cc != 0) {
407: (*tp->t_oproc)(tp);
408: if (tp->t_outq.c_cc > SLIP_HIWAT)
409: return;
410: }
411: /*
412: * This happens briefly when the line shuts down.
413: */
414: if (sc == NULL)
415: return;
416:
417: /*
418: * Get a packet and send it to the interface.
419: */
420: s = splimp();
421: IF_DEQUEUE(&sc->sc_fastq, m);
422: if (m == NULL)
423: IF_DEQUEUE(&sc->sc_if.if_snd, m);
424: splx(s);
425: if (m == NULL)
426: return;
427: sc->sc_if.if_lastchange = time;
428: /*
429: * If system is getting low on clists, just flush our
430: * output queue (if the stuff was important, it'll get
431: * retransmitted).
432: */
433: if (cfreecount < CLISTRESERVE + SLMTU) {
434: m_freem(m);
435: sc->sc_if.if_collisions++;
436: continue;
437: }
438:
439: /*
440: * The extra FRAME_END will start up a new packet, and thus
441: * will flush any accumulated garbage. We do this whenever
442: * the line may have been idle for some time.
443: */
444: if (tp->t_outq.c_cc == 0) {
445: ++sc->sc_bytessent;
446: (void) putc(FRAME_END, &tp->t_outq);
447: }
448:
449: while (m) {
450: register u_char *ep;
451:
452: cp = mtod(m, u_char *); ep = cp + m->m_len;
453: while (cp < ep) {
454: /*
455: * Find out how many bytes in the string we can
456: * handle without doing something special.
457: */
458: register u_char *bp = cp;
459:
460: while (cp < ep) {
461: switch (*cp++) {
462: case FRAME_ESCAPE:
463: case FRAME_END:
464: --cp;
465: goto out;
466: }
467: }
468: out:
469: if (cp > bp) {
470: /*
471: * Put n characters at once
472: * into the tty output queue.
473: */
474: if (b_to_q((char *)bp, cp - bp, &tp->t_outq))
475: break;
476: sc->sc_bytessent += cp - bp;
477: }
478: /*
479: * If there are characters left in the mbuf,
480: * the first one must be special..
481: * Put it out in a different form.
482: */
483: if (cp < ep) {
484: if (putc(FRAME_ESCAPE, &tp->t_outq))
485: break;
486: if (putc(*cp++ == FRAME_ESCAPE ?
487: TRANS_FRAME_ESCAPE : TRANS_FRAME_END,
488: &tp->t_outq)) {
489: (void) unputc(&tp->t_outq);
490: break;
491: }
492: sc->sc_bytessent += 2;
493: }
494: }
495: MFREE(m, m2);
496: m = m2;
497: }
498:
499: if (putc(FRAME_END, &tp->t_outq)) {
500: /*
501: * Not enough room. Remove a char to make room
502: * and end the packet normally.
503: * If you get many collisions (more than one or two
504: * a day) you probably do not have enough clists
505: * and you should increase "nclist" in param.c.
506: */
507: (void) unputc(&tp->t_outq);
508: (void) putc(FRAME_END, &tp->t_outq);
509: sc->sc_if.if_collisions++;
510: } else {
511: ++sc->sc_bytessent;
512: sc->sc_if.if_opackets++;
513: }
514: sc->sc_if.if_obytes = sc->sc_bytessent;
515: }
516: }
517:
518: /*
519: * Copy data buffer to mbuf chain; add ifnet pointer.
520: */
521: static struct mbuf *
522: sl_btom(sc, len)
523: register struct sl_softc *sc;
524: register int len;
525: {
526: register struct mbuf *m;
527:
528: MGETHDR(m, M_DONTWAIT, MT_DATA);
529: if (m == NULL)
530: return (NULL);
531:
532: /*
533: * If we have more than MHLEN bytes, it's cheaper to
534: * queue the cluster we just filled & allocate a new one
535: * for the input buffer. Otherwise, fill the mbuf we
536: * allocated above. Note that code in the input routine
537: * guarantees that packet will fit in a cluster.
538: */
539: if (len >= MHLEN) {
540: MCLGET(m, M_DONTWAIT);
541: if ((m->m_flags & M_EXT) == 0) {
542: /*
543: * we couldn't get a cluster - if memory's this
544: * low, it's time to start dropping packets.
545: */
546: (void) m_free(m);
547: return (NULL);
548: }
549: sc->sc_ep = mtod(m, u_char *) + SLBUFSIZE;
550: m->m_data = (caddr_t)sc->sc_buf;
551: m->m_ext.ext_buf = (caddr_t)((int)sc->sc_buf &~ MCLOFSET);
552: } else
553: bcopy((caddr_t)sc->sc_buf, mtod(m, caddr_t), len);
554:
555: m->m_len = len;
556: m->m_pkthdr.len = len;
557: m->m_pkthdr.rcvif = &sc->sc_if;
558: return (m);
559: }
560:
561: /*
562: * tty interface receiver interrupt.
563: */
564: slinput(c, tp)
565: register int c;
566: register struct tty *tp;
567: {
568: register struct sl_softc *sc;
569: register struct mbuf *m;
570: register int len;
571: int s;
572:
573: tk_nin++;
574: sc = (struct sl_softc *)tp->t_sc;
575: if (sc == NULL)
576: return;
577: if (!(tp->t_state&TS_CARR_ON)) /* XXX */
578: return;
579:
580: ++sc->sc_bytesrcvd;
581: ++sc->sc_if.if_ibytes;
582: c &= 0xff; /* XXX */
583:
584: #ifdef ABT_ESC
585: if (sc->sc_flags & SC_ABORT) {
586: /* if we see an abort after "idle" time, count it */
587: if (c == ABT_ESC && time.tv_sec >= sc->sc_lasttime + ABT_WAIT) {
588: sc->sc_abortcount++;
589: /* record when the first abort escape arrived */
590: if (sc->sc_abortcount == 1)
591: sc->sc_starttime = time.tv_sec;
592: }
593: /*
594: * if we have an abort, see that we have not run out of time,
595: * or that we have an "idle" time after the complete escape
596: * sequence
597: */
598: if (sc->sc_abortcount) {
599: if (time.tv_sec >= sc->sc_starttime + ABT_RECYCLE)
600: sc->sc_abortcount = 0;
601: if (sc->sc_abortcount >= ABT_SOFT &&
602: time.tv_sec >= sc->sc_lasttime + ABT_WAIT) {
603: slclose(tp);
604: return;
605: }
606: }
607: sc->sc_lasttime = time.tv_sec;
608: }
609: #endif
610:
611: switch (c) {
612:
613: case TRANS_FRAME_ESCAPE:
614: if (sc->sc_escape)
615: c = FRAME_ESCAPE;
616: break;
617:
618: case TRANS_FRAME_END:
619: if (sc->sc_escape)
620: c = FRAME_END;
621: break;
622:
623: case FRAME_ESCAPE:
624: sc->sc_escape = 1;
625: return;
626:
627: case FRAME_END:
628: len = sc->sc_mp - sc->sc_buf;
629: if (len < 3)
630: /* less than min length packet - ignore */
631: goto newpack;
632:
633: if ((c = (*sc->sc_buf & 0xf0)) != (IPVERSION << 4)) {
634: if (c & 0x80)
635: c = TYPE_COMPRESSED_TCP;
636: else if (c == TYPE_UNCOMPRESSED_TCP)
637: *sc->sc_buf &= 0x4f; /* XXX */
638: /*
639: * We've got something that's not an IP packet.
640: * If compression is enabled, try to decompress it.
641: * Otherwise, if `auto-enable' compression is on and
642: * it's a reasonable packet, decompress it and then
643: * enable compression. Otherwise, drop it.
644: */
645: if (sc->sc_flags & SC_COMPRESS) {
646: len = sl_uncompress_tcp(&sc->sc_buf, len,
647: (u_int)c, &sc->sc_comp);
648: if (len <= 0)
649: goto error;
650: } else if ((sc->sc_flags & SC_AUTOCOMP) &&
651: c == TYPE_UNCOMPRESSED_TCP && len >= 40) {
652: len = sl_uncompress_tcp(&sc->sc_buf, len,
653: (u_int)c, &sc->sc_comp);
654: if (len <= 0)
655: goto error;
656: sc->sc_flags |= SC_COMPRESS;
657: } else
658: goto error;
659: }
660: m = sl_btom(sc, len);
661: if (m == NULL)
662: goto error;
663:
664: sc->sc_if.if_ipackets++;
665: sc->sc_if.if_lastchange = time;
666: s = splimp();
667: if (IF_QFULL(&ipintrq)) {
668: IF_DROP(&ipintrq);
669: sc->sc_if.if_ierrors++;
670: sc->sc_if.if_iqdrops++;
671: m_freem(m);
672: } else {
673: IF_ENQUEUE(&ipintrq, m);
674: schednetisr(NETISR_IP);
675: }
676: splx(s);
677: goto newpack;
678: }
679: if (sc->sc_mp < sc->sc_ep) {
680: *sc->sc_mp++ = c;
681: sc->sc_escape = 0;
682: return;
683: }
684: error:
685: sc->sc_if.if_ierrors++;
686: newpack:
687: sc->sc_mp = sc->sc_buf = sc->sc_ep - SLMAX;
688: sc->sc_escape = 0;
689: }
690:
691: /*
692: * Process an ioctl request.
693: */
694: slioctl(ifp, cmd, data)
695: register struct ifnet *ifp;
696: int cmd;
697: caddr_t data;
698: {
699: register struct ifaddr *ifa = (struct ifaddr *)data;
700: int s = splimp(), error = 0;
701:
702: switch (cmd) {
703:
704: case SIOCSIFADDR:
705: if (ifa->ifa_addr->sa_family == AF_INET)
706: ifp->if_flags |= IFF_UP;
707: else
708: error = EAFNOSUPPORT;
709: break;
710:
711: case SIOCSIFDSTADDR:
712: if (ifa->ifa_addr->sa_family != AF_INET)
713: error = EAFNOSUPPORT;
714: break;
715:
716: default:
717: error = EINVAL;
718: }
719: splx(s);
720: return (error);
721: }
722: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.