FreeBSD/Linux Kernel Cross Reference
sys/net/ppp_tty.c
1 /* $NetBSD: ppp_tty.c,v 1.35 2005/02/26 22:45:09 perry Exp $ */
2 /* Id: ppp_tty.c,v 1.3 1996/07/01 01:04:11 paulus Exp */
3
4 /*
5 * ppp_tty.c - Point-to-Point Protocol (PPP) driver for asynchronous
6 * tty devices.
7 *
8 * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 *
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 *
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in
19 * the documentation and/or other materials provided with the
20 * distribution.
21 *
22 * 3. The name "Carnegie Mellon University" must not be used to
23 * endorse or promote products derived from this software without
24 * prior written permission. For permission or any legal
25 * details, please contact
26 * Office of Technology Transfer
27 * Carnegie Mellon University
28 * 5000 Forbes Avenue
29 * Pittsburgh, PA 15213-3890
30 * (412) 268-4387, fax: (412) 268-7395
31 * tech-transfer@andrew.cmu.edu
32 *
33 * 4. Redistributions of any form whatsoever must retain the following
34 * acknowledgment:
35 * "This product includes software developed by Computing Services
36 * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
37 *
38 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
39 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
40 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
41 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
42 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
43 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
44 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
45 *
46 * Based on:
47 * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89
48 *
49 * Copyright (c) 1987 Regents of the University of California.
50 * All rights reserved.
51 *
52 * Redistribution and use in source and binary forms are permitted
53 * provided that the above copyright notice and this paragraph are
54 * duplicated in all such forms and that any documentation,
55 * advertising materials, and other materials related to such
56 * distribution and use acknowledge that the software was developed
57 * by the University of California, Berkeley. The name of the
58 * University may not be used to endorse or promote products derived
59 * from this software without specific prior written permission.
60 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
61 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
62 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
63 *
64 * Serial Line interface
65 *
66 * Rick Adams
67 * Center for Seismic Studies
68 * 1300 N 17th Street, Suite 1450
69 * Arlington, Virginia 22209
70 * (703)276-7900
71 * rick@seismo.ARPA
72 * seismo!rick
73 *
74 * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
75 * Converted to 4.3BSD Beta by Chris Torek.
76 * Other changes made at Berkeley, based in part on code by Kirk Smith.
77 *
78 * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com)
79 * Added VJ tcp header compression; more unified ioctls
80 *
81 * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au).
82 * Cleaned up a lot of the mbuf-related code to fix bugs that
83 * caused system crashes and packet corruption. Changed pppstart
84 * so that it doesn't just give up with a "collision" if the whole
85 * packet doesn't fit in the output ring buffer.
86 *
87 * Added priority queueing for interactive IP packets, following
88 * the model of if_sl.c, plus hooks for bpf.
89 * Paul Mackerras (paulus@cs.anu.edu.au).
90 */
91
92 /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
93 /* from NetBSD: if_ppp.c,v 1.15.2.2 1994/07/28 05:17:58 cgd Exp */
94
95 #include <sys/cdefs.h>
96 __KERNEL_RCSID(0, "$NetBSD: ppp_tty.c,v 1.35 2005/02/26 22:45:09 perry Exp $");
97
98 #include "ppp.h"
99
100 #include "opt_ppp.h"
101 #define VJC
102 #define PPP_COMPRESS
103
104 #include <sys/param.h>
105 #include <sys/proc.h>
106 #include <sys/mbuf.h>
107 #include <sys/dkstat.h>
108 #include <sys/socket.h>
109 #include <sys/ioctl.h>
110 #include <sys/file.h>
111 #include <sys/tty.h>
112 #include <sys/kernel.h>
113 #include <sys/conf.h>
114 #include <sys/vnode.h>
115 #include <sys/systm.h>
116
117 #include <net/if.h>
118 #include <net/if_types.h>
119
120 #ifdef VJC
121 #include <netinet/in.h>
122 #include <netinet/in_systm.h>
123 #include <netinet/ip.h>
124 #include <net/slcompress.h>
125 #endif
126
127 #include "bpfilter.h"
128 #if NBPFILTER > 0 || defined(PPP_FILTER)
129 #include <net/bpf.h>
130 #endif
131 #include <net/ppp_defs.h>
132 #include <net/if_ppp.h>
133 #include <net/if_pppvar.h>
134
135 int pppopen __P((dev_t dev, struct tty *tp));
136 int pppclose __P((struct tty *tp, int flag));
137 int pppread __P((struct tty *tp, struct uio *uio, int flag));
138 int pppwrite __P((struct tty *tp, struct uio *uio, int flag));
139 int ppptioctl __P((struct tty *tp, u_long cmd, caddr_t data, int flag,
140 struct proc *));
141 int pppinput __P((int c, struct tty *tp));
142 int pppstart __P((struct tty *tp));
143
144 static void ppprcvframe __P((struct ppp_softc *sc, struct mbuf *m));
145 static u_int16_t pppfcs __P((u_int16_t fcs, u_char *cp, int len));
146 static void pppsyncstart __P((struct ppp_softc *sc));
147 static void pppasyncstart __P((struct ppp_softc *));
148 static void pppasyncctlp __P((struct ppp_softc *));
149 static void pppasyncrelinq __P((struct ppp_softc *));
150 static void ppp_timeout __P((void *));
151 static void pppgetm __P((struct ppp_softc *sc));
152 static void pppdumpb __P((u_char *b, int l));
153 static void ppplogchar __P((struct ppp_softc *, int));
154 static void pppdumpframe __P((struct ppp_softc *sc, struct mbuf* m,
155 int xmit));
156
157 /*
158 * Some useful mbuf macros not in mbuf.h.
159 */
160 #define M_IS_CLUSTER(m) ((m)->m_flags & M_EXT)
161
162 #define M_DATASTART(m) \
163 (M_IS_CLUSTER(m) ? (m)->m_ext.ext_buf : \
164 (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat)
165
166 #define M_DATASIZE(m) \
167 (M_IS_CLUSTER(m) ? (m)->m_ext.ext_size : \
168 (m)->m_flags & M_PKTHDR ? MHLEN: MLEN)
169
170 /*
171 * Does c need to be escaped?
172 */
173 #define ESCAPE_P(c) (sc->sc_asyncmap[(c) >> 5] & (1 << ((c) & 0x1F)))
174
175 /*
176 * Procedures for using an async tty interface for PPP.
177 */
178
179 /* This is a NetBSD-1.0 or later kernel. */
180 #define CCOUNT(q) ((q)->c_cc)
181
182 #define PPP_LOWAT 100 /* Process more output when < LOWAT on queue */
183 #define PPP_HIWAT 400 /* Don't start a new packet if HIWAT on que */
184
185 /*
186 * Line specific open routine for async tty devices.
187 * Attach the given tty to the first available ppp unit.
188 * Called from device open routine or ttioctl.
189 */
190 /* ARGSUSED */
191 int
192 pppopen(dev, tp)
193 dev_t dev;
194 struct tty *tp;
195 {
196 struct proc *p = curproc; /* XXX */
197 struct ppp_softc *sc;
198 int error, s;
199
200 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
201 return (error);
202
203 s = spltty();
204
205 if (tp->t_linesw->l_no == PPPDISC) {
206 sc = (struct ppp_softc *) tp->t_sc;
207 if (sc != NULL && sc->sc_devp == (void *) tp) {
208 splx(s);
209 return (0);
210 }
211 }
212
213 if ((sc = pppalloc(p->p_pid)) == NULL) {
214 splx(s);
215 return ENXIO;
216 }
217
218 if (sc->sc_relinq)
219 (*sc->sc_relinq)(sc); /* get previous owner to relinquish the unit */
220
221 #if NBPFILTER > 0
222 /* Switch DLT to PPP-over-serial. */
223 bpf_change_type(&sc->sc_if, DLT_PPP_SERIAL, PPP_HDRLEN);
224 #endif
225
226 sc->sc_ilen = 0;
227 sc->sc_m = NULL;
228 memset(sc->sc_asyncmap, 0, sizeof(sc->sc_asyncmap));
229 sc->sc_asyncmap[0] = 0xffffffff;
230 sc->sc_asyncmap[3] = 0x60000000;
231 sc->sc_rasyncmap = 0;
232 sc->sc_devp = (void *) tp;
233 sc->sc_start = pppasyncstart;
234 sc->sc_ctlp = pppasyncctlp;
235 sc->sc_relinq = pppasyncrelinq;
236 sc->sc_outm = NULL;
237 pppgetm(sc);
238 sc->sc_if.if_flags |= IFF_RUNNING;
239 sc->sc_if.if_baudrate = tp->t_ospeed;
240
241 tp->t_sc = (caddr_t) sc;
242 ttyflush(tp, FREAD | FWRITE);
243
244 splx(s);
245 return (0);
246 }
247
248 /*
249 * Line specific close routine, called from device close routine
250 * and from ttioctl.
251 * Detach the tty from the ppp unit.
252 * Mimics part of ttyclose().
253 */
254 int
255 pppclose(tp, flag)
256 struct tty *tp;
257 int flag;
258 {
259 struct ppp_softc *sc;
260 int s;
261
262 s = spltty();
263 ttyflush(tp, FREAD|FWRITE);
264 tp->t_linesw = linesw[0]; /* default line discipline */
265 sc = (struct ppp_softc *) tp->t_sc;
266 if (sc != NULL) {
267 tp->t_sc = NULL;
268 if (tp == (struct tty *) sc->sc_devp) {
269 pppasyncrelinq(sc);
270 pppdealloc(sc);
271 }
272 }
273 splx(s);
274 return 0;
275 }
276
277 /*
278 * Relinquish the interface unit to another device.
279 */
280 static void
281 pppasyncrelinq(sc)
282 struct ppp_softc *sc;
283 {
284 int s;
285
286 #if NBPFILTER > 0
287 /* Change DLT to back none. */
288 bpf_change_type(&sc->sc_if, DLT_NULL, 0);
289 #endif
290
291 s = spltty();
292 if (sc->sc_outm) {
293 m_freem(sc->sc_outm);
294 sc->sc_outm = NULL;
295 }
296 if (sc->sc_m) {
297 m_freem(sc->sc_m);
298 sc->sc_m = NULL;
299 }
300 if (sc->sc_flags & SC_TIMEOUT) {
301 callout_stop(&sc->sc_timo_ch);
302 sc->sc_flags &= ~SC_TIMEOUT;
303 }
304 splx(s);
305 }
306
307 /*
308 * Line specific (tty) read routine.
309 */
310 int
311 pppread(tp, uio, flag)
312 struct tty *tp;
313 struct uio *uio;
314 int flag;
315 {
316 struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
317 struct mbuf *m, *m0;
318 int s;
319 int error = 0;
320
321 if (sc == NULL)
322 return 0;
323 /*
324 * Loop waiting for input, checking that nothing disasterous
325 * happens in the meantime.
326 */
327 s = spltty();
328 for (;;) {
329 if (tp != (struct tty *) sc->sc_devp ||
330 tp->t_linesw->l_no != PPPDISC) {
331 splx(s);
332 return 0;
333 }
334 if (sc->sc_inq.ifq_head != NULL)
335 break;
336 if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0
337 && (tp->t_state & TS_ISOPEN)) {
338 splx(s);
339 return 0; /* end of file */
340 }
341 if (tp->t_state & TS_ASYNC || flag & IO_NDELAY) {
342 splx(s);
343 return (EWOULDBLOCK);
344 }
345 error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI|PCATCH, ttyin, 0);
346 if (error) {
347 splx(s);
348 return error;
349 }
350 }
351
352 /* Pull place-holder byte out of canonical queue */
353 getc(&tp->t_canq);
354
355 /* Get the packet from the input queue */
356 IF_DEQUEUE(&sc->sc_inq, m0);
357 splx(s);
358
359 for (m = m0; m && uio->uio_resid; m = m->m_next)
360 if ((error = uiomove(mtod(m, u_char *), m->m_len, uio)) != 0)
361 break;
362 m_freem(m0);
363 return (error);
364 }
365
366 /*
367 * Line specific (tty) write routine.
368 */
369 int
370 pppwrite(tp, uio, flag)
371 struct tty *tp;
372 struct uio *uio;
373 int flag;
374 {
375 struct ppp_softc *sc = (struct ppp_softc *)tp->t_sc;
376 struct mbuf *m, *m0, **mp;
377 struct sockaddr dst;
378 int len, error;
379
380 if ((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0)
381 return 0; /* wrote 0 bytes */
382 if (tp->t_linesw->l_no != PPPDISC)
383 return (EINVAL);
384 if (sc == NULL || tp != (struct tty *) sc->sc_devp)
385 return EIO;
386 if (uio->uio_resid > sc->sc_if.if_mtu + PPP_HDRLEN ||
387 uio->uio_resid < PPP_HDRLEN)
388 return (EMSGSIZE);
389 for (mp = &m0; uio->uio_resid; mp = &m->m_next) {
390 m = m_get(M_WAIT, MT_DATA);
391 if ((*mp = m) == NULL) {
392 m_freem(m0);
393 return (ENOBUFS);
394 }
395 m->m_len = 0;
396 if (uio->uio_resid >= MCLBYTES / 2)
397 MCLGET(m, M_DONTWAIT);
398 len = M_TRAILINGSPACE(m);
399 if (len > uio->uio_resid)
400 len = uio->uio_resid;
401 if ((error = uiomove(mtod(m, u_char *), len, uio)) != 0) {
402 m_freem(m0);
403 return (error);
404 }
405 m->m_len = len;
406 }
407 dst.sa_family = AF_UNSPEC;
408 bcopy(mtod(m0, u_char *), dst.sa_data, PPP_HDRLEN);
409 m0->m_data += PPP_HDRLEN;
410 m0->m_len -= PPP_HDRLEN;
411 return ((*sc->sc_if.if_output)(&sc->sc_if, m0, &dst, (struct rtentry *)0));
412 }
413
414 /*
415 * Line specific (tty) ioctl routine.
416 * This discipline requires that tty device drivers call
417 * the line specific l_ioctl routine from their ioctl routines.
418 */
419 /* ARGSUSED */
420 int
421 ppptioctl(tp, cmd, data, flag, p)
422 struct tty *tp;
423 u_long cmd;
424 caddr_t data;
425 int flag;
426 struct proc *p;
427 {
428 struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
429 int error, s;
430
431 if (sc == NULL || tp != (struct tty *) sc->sc_devp)
432 return (EPASSTHROUGH);
433
434 error = 0;
435 switch (cmd) {
436 case TIOCRCVFRAME:
437 ppprcvframe(sc,*((struct mbuf **)data));
438 break;
439
440 case PPPIOCSASYNCMAP:
441 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
442 break;
443 sc->sc_asyncmap[0] = *(u_int *)data;
444 break;
445
446 case PPPIOCGASYNCMAP:
447 *(u_int *)data = sc->sc_asyncmap[0];
448 break;
449
450 case PPPIOCSRASYNCMAP:
451 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
452 break;
453 sc->sc_rasyncmap = *(u_int *)data;
454 break;
455
456 case PPPIOCGRASYNCMAP:
457 *(u_int *)data = sc->sc_rasyncmap;
458 break;
459
460 case PPPIOCSXASYNCMAP:
461 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
462 break;
463 s = spltty();
464 bcopy(data, sc->sc_asyncmap, sizeof(sc->sc_asyncmap));
465 sc->sc_asyncmap[1] = 0; /* mustn't escape 0x20 - 0x3f */
466 sc->sc_asyncmap[2] &= ~0x40000000; /* mustn't escape 0x5e */
467 sc->sc_asyncmap[3] |= 0x60000000; /* must escape 0x7d, 0x7e */
468 splx(s);
469 break;
470
471 case PPPIOCGXASYNCMAP:
472 bcopy(sc->sc_asyncmap, data, sizeof(sc->sc_asyncmap));
473 break;
474
475 default:
476 error = pppioctl(sc, cmd, data, flag, p);
477 if (error == 0 && cmd == PPPIOCSMRU)
478 pppgetm(sc);
479 }
480
481 return error;
482 }
483
484 /* receive a complete ppp frame from device in synchronous
485 * hdlc mode. caller gives up ownership of mbuf
486 */
487 static void
488 ppprcvframe(sc, m)
489 struct ppp_softc *sc;
490 struct mbuf *m;
491 {
492 int len, s;
493 struct mbuf *n;
494 u_char hdr[4];
495 int hlen,count;
496
497 for (n=m,len=0;n != NULL;n = n->m_next)
498 len += n->m_len;
499 if (len==0) {
500 m_freem(m);
501 return;
502 }
503
504 /* extract PPP header from mbuf chain (1 to 4 bytes) */
505 for (n=m,hlen=0;n!=NULL && hlen<sizeof(hdr);n=n->m_next) {
506 count = (sizeof(hdr)-hlen) < n->m_len ?
507 sizeof(hdr)-hlen : n->m_len;
508 bcopy(mtod(n,u_char*),&hdr[hlen],count);
509 hlen+=count;
510 }
511
512 s = spltty();
513
514 /* if AFCF compressed then prepend AFCF */
515 if (hdr[0] != PPP_ALLSTATIONS) {
516 if (sc->sc_flags & SC_REJ_COMP_AC) {
517 if (sc->sc_flags & SC_DEBUG)
518 printf(
519 "%s: garbage received: 0x%x (need 0xFF)\n",
520 sc->sc_if.if_xname, hdr[0]);
521 goto bail;
522 }
523 M_PREPEND(m,2,M_DONTWAIT);
524 if (m==NULL) {
525 splx(s);
526 return;
527 }
528 hdr[3] = hdr[1];
529 hdr[2] = hdr[0];
530 hdr[0] = PPP_ALLSTATIONS;
531 hdr[1] = PPP_UI;
532 len += 2;
533 }
534
535 /* if protocol field compressed, add MSB of protocol field = 0 */
536 if (hdr[2] & 1) {
537 /* a compressed protocol */
538 M_PREPEND(m,1,M_DONTWAIT);
539 if (m==NULL) {
540 splx(s);
541 return;
542 }
543 hdr[3] = hdr[2];
544 hdr[2] = 0;
545 len++;
546 }
547
548 /* valid LSB of protocol field has bit0 set */
549 if (!(hdr[3] & 1)) {
550 if (sc->sc_flags & SC_DEBUG)
551 printf("%s: bad protocol %x\n", sc->sc_if.if_xname,
552 (hdr[2] << 8) + hdr[3]);
553 goto bail;
554 }
555
556 /* packet beyond configured mru? */
557 if (len > sc->sc_mru + PPP_HDRLEN) {
558 if (sc->sc_flags & SC_DEBUG)
559 printf("%s: packet too big\n", sc->sc_if.if_xname);
560 goto bail;
561 }
562
563 /* add expanded 4 byte header to mbuf chain */
564 for (n=m,hlen=0;n!=NULL && hlen<sizeof(hdr);n=n->m_next) {
565 count = (sizeof(hdr)-hlen) < n->m_len ?
566 sizeof(hdr)-hlen : n->m_len;
567 bcopy(&hdr[hlen],mtod(n,u_char*),count);
568 hlen+=count;
569 }
570
571 /* if_ppp.c requires the PPP header and IP header */
572 /* to be contiguous */
573 count = len < MHLEN ? len : MHLEN;
574 if (m->m_len < count) {
575 m = m_pullup(m,count);
576 if (m==NULL)
577 goto bail;
578 }
579
580 sc->sc_stats.ppp_ibytes += len;
581
582 if (sc->sc_flags & SC_LOG_RAWIN)
583 pppdumpframe(sc,m,0);
584
585 ppppktin(sc, m, 0);
586 splx(s);
587 return;
588 bail:
589 m_freem(m);
590 splx(s);
591 }
592
593 /*
594 * FCS lookup table as calculated by genfcstab.
595 */
596 static const u_int16_t fcstab[256] = {
597 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
598 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
599 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
600 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
601 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
602 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
603 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
604 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
605 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
606 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
607 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
608 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
609 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
610 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
611 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
612 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
613 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
614 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
615 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
616 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
617 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
618 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
619 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
620 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
621 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
622 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
623 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
624 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
625 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
626 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
627 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
628 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
629 };
630
631 /*
632 * Calculate a new FCS given the current FCS and the new data.
633 */
634 static u_int16_t
635 pppfcs(fcs, cp, len)
636 u_int16_t fcs;
637 u_char *cp;
638 int len;
639 {
640 while (len--)
641 fcs = PPP_FCS(fcs, *cp++);
642 return (fcs);
643 }
644
645 /* This gets called at splsoftnet from pppasyncstart at various times
646 * when there is data ready to be sent.
647 */
648 static void
649 pppsyncstart(sc)
650 struct ppp_softc *sc;
651 {
652 struct tty *tp = (struct tty *) sc->sc_devp;
653 struct mbuf *m, *n;
654 const struct cdevsw *cdev;
655 int len;
656
657 for(m = sc->sc_outm;;) {
658 if (m == NULL) {
659 m = ppp_dequeue(sc); /* get new packet */
660 if (m == NULL)
661 break; /* no more packets */
662 if (sc->sc_flags & SC_DEBUG)
663 pppdumpframe(sc,m,1);
664 }
665 for(n=m,len=0;n!=NULL;n=n->m_next)
666 len += n->m_len;
667
668 /* call device driver IOCTL to transmit a frame */
669 cdev = cdevsw_lookup(tp->t_dev);
670 if (cdev == NULL ||
671 (*cdev->d_ioctl)(tp->t_dev, TIOCXMTFRAME, (caddr_t)&m,
672 0, 0)) {
673 /* busy or error, set as current packet */
674 sc->sc_outm = m;
675 break;
676 }
677 sc->sc_outm = m = NULL;
678 sc->sc_stats.ppp_obytes += len;
679 }
680 }
681
682 /*
683 * This gets called at splsoftnet from if_ppp.c at various times
684 * when there is data ready to be sent.
685 */
686 static void
687 pppasyncstart(sc)
688 struct ppp_softc *sc;
689 {
690 struct tty *tp = (struct tty *) sc->sc_devp;
691 struct mbuf *m;
692 int len;
693 u_char *start, *stop, *cp;
694 int n, ndone, done, idle;
695 struct mbuf *m2;
696 int s;
697
698 if (sc->sc_flags & SC_SYNC){
699 pppsyncstart(sc);
700 return;
701 }
702
703 idle = 0;
704 while (CCOUNT(&tp->t_outq) < PPP_HIWAT) {
705 /*
706 * See if we have an existing packet partly sent.
707 * If not, get a new packet and start sending it.
708 */
709 m = sc->sc_outm;
710 if (m == NULL) {
711 /*
712 * Get another packet to be sent.
713 */
714 m = ppp_dequeue(sc);
715 if (m == NULL) {
716 idle = 1;
717 break;
718 }
719
720 /*
721 * The extra PPP_FLAG will start up a new packet, and thus
722 * will flush any accumulated garbage. We do this whenever
723 * the line may have been idle for some time.
724 */
725 if (CCOUNT(&tp->t_outq) == 0) {
726 ++sc->sc_stats.ppp_obytes;
727 (void) putc(PPP_FLAG, &tp->t_outq);
728 }
729
730 /* Calculate the FCS for the first mbuf's worth. */
731 sc->sc_outfcs = pppfcs(PPP_INITFCS, mtod(m, u_char *), m->m_len);
732 }
733
734 for (;;) {
735 start = mtod(m, u_char *);
736 len = m->m_len;
737 stop = start + len;
738 while (len > 0) {
739 /*
740 * Find out how many bytes in the string we can
741 * handle without doing something special.
742 */
743 for (cp = start; cp < stop; cp++)
744 if (ESCAPE_P(*cp))
745 break;
746 n = cp - start;
747 if (n) {
748 /* NetBSD (0.9 or later), 4.3-Reno or similar. */
749 ndone = n - b_to_q(start, n, &tp->t_outq);
750 len -= ndone;
751 start += ndone;
752 sc->sc_stats.ppp_obytes += ndone;
753
754 if (ndone < n)
755 break; /* packet doesn't fit */
756 }
757 /*
758 * If there are characters left in the mbuf,
759 * the first one must be special.
760 * Put it out in a different form.
761 */
762 if (len) {
763 s = spltty();
764 if (putc(PPP_ESCAPE, &tp->t_outq)) {
765 splx(s);
766 break;
767 }
768 if (putc(*start ^ PPP_TRANS, &tp->t_outq)) {
769 (void) unputc(&tp->t_outq);
770 splx(s);
771 break;
772 }
773 splx(s);
774 sc->sc_stats.ppp_obytes += 2;
775 start++;
776 len--;
777 }
778 }
779
780 /*
781 * If we didn't empty this mbuf, remember where we're up to.
782 * If we emptied the last mbuf, try to add the FCS and closing
783 * flag, and if we can't, leave sc_outm pointing to m, but with
784 * m->m_len == 0, to remind us to output the FCS and flag later.
785 */
786 done = len == 0;
787 if (done && m->m_next == NULL) {
788 u_char *p, *q;
789 int c;
790 u_char endseq[8];
791
792 /*
793 * We may have to escape the bytes in the FCS.
794 */
795 p = endseq;
796 c = ~sc->sc_outfcs & 0xFF;
797 if (ESCAPE_P(c)) {
798 *p++ = PPP_ESCAPE;
799 *p++ = c ^ PPP_TRANS;
800 } else
801 *p++ = c;
802 c = (~sc->sc_outfcs >> 8) & 0xFF;
803 if (ESCAPE_P(c)) {
804 *p++ = PPP_ESCAPE;
805 *p++ = c ^ PPP_TRANS;
806 } else
807 *p++ = c;
808 *p++ = PPP_FLAG;
809
810 /*
811 * Try to output the FCS and flag. If the bytes
812 * don't all fit, back out.
813 */
814 s = spltty();
815 for (q = endseq; q < p; ++q)
816 if (putc(*q, &tp->t_outq)) {
817 done = 0;
818 for (; q > endseq; --q)
819 unputc(&tp->t_outq);
820 break;
821 }
822 splx(s);
823 if (done)
824 sc->sc_stats.ppp_obytes += q - endseq;
825 }
826
827 if (!done) {
828 /* remember where we got to */
829 m->m_data = start;
830 m->m_len = len;
831 break;
832 }
833
834 /* Finished with this mbuf; free it and move on. */
835 MFREE(m, m2);
836 m = m2;
837 if (m == NULL) {
838 /* Finished a packet */
839 break;
840 }
841 sc->sc_outfcs = pppfcs(sc->sc_outfcs, mtod(m, u_char *), m->m_len);
842 }
843
844 /*
845 * If m == NULL, we have finished a packet.
846 * If m != NULL, we've either done as much work this time
847 * as we need to, or else we've filled up the output queue.
848 */
849 sc->sc_outm = m;
850 if (m)
851 break;
852 }
853
854 /* Call pppstart to start output again if necessary. */
855 s = spltty();
856 pppstart(tp);
857
858 /*
859 * This timeout is needed for operation on a pseudo-tty,
860 * because the pty code doesn't call pppstart after it has
861 * drained the t_outq.
862 */
863 if (!idle && (sc->sc_flags & SC_TIMEOUT) == 0) {
864 callout_reset(&sc->sc_timo_ch, 1, ppp_timeout, sc);
865 sc->sc_flags |= SC_TIMEOUT;
866 }
867
868 splx(s);
869 }
870
871 /*
872 * This gets called when a received packet is placed on
873 * the inq, at splsoftnet.
874 */
875 static void
876 pppasyncctlp(sc)
877 struct ppp_softc *sc;
878 {
879 struct tty *tp;
880 int s;
881
882 /* Put a placeholder byte in canq for ttselect()/ttnread(). */
883 s = spltty();
884 tp = (struct tty *) sc->sc_devp;
885 putc(0, &tp->t_canq);
886 ttwakeup(tp);
887 splx(s);
888 }
889
890 /*
891 * Start output on async tty interface. If the transmit queue
892 * has drained sufficiently, arrange for pppasyncstart to be
893 * called later at splsoftnet.
894 * Called at spltty or higher.
895 */
896 int
897 pppstart(tp)
898 struct tty *tp;
899 {
900 struct ppp_softc *sc = (struct ppp_softc *) tp->t_sc;
901
902 /*
903 * If there is stuff in the output queue, send it now.
904 * We are being called in lieu of ttstart and must do what it would.
905 */
906 if (tp->t_oproc != NULL)
907 (*tp->t_oproc)(tp);
908
909 /*
910 * If the transmit queue has drained and the tty has not hung up
911 * or been disconnected from the ppp unit, then tell if_ppp.c that
912 * we need more output.
913 */
914 if ((CCOUNT(&tp->t_outq) >= PPP_LOWAT)
915 && ((sc == NULL) || (sc->sc_flags & SC_TIMEOUT)))
916 return 0;
917 #ifdef ALTQ
918 /*
919 * if ALTQ is enabled, don't invoke NETISR_PPP.
920 * pppintr() could loop without doing anything useful
921 * under rate-limiting.
922 */
923 if (ALTQ_IS_ENABLED(&sc->sc_if.if_snd))
924 return 0;
925 #endif
926 if (!((tp->t_state & TS_CARR_ON) == 0 && (tp->t_cflag & CLOCAL) == 0)
927 && sc != NULL && tp == (struct tty *) sc->sc_devp) {
928 ppp_restart(sc);
929 }
930
931 return 0;
932 }
933
934 /*
935 * Timeout routine - try to start some more output.
936 */
937 static void
938 ppp_timeout(x)
939 void *x;
940 {
941 struct ppp_softc *sc = (struct ppp_softc *) x;
942 struct tty *tp = (struct tty *) sc->sc_devp;
943 int s;
944
945 s = spltty();
946 sc->sc_flags &= ~SC_TIMEOUT;
947 pppstart(tp);
948 splx(s);
949 }
950
951 /*
952 * Allocate enough mbuf to handle current MRU.
953 */
954 static void
955 pppgetm(sc)
956 struct ppp_softc *sc;
957 {
958 struct mbuf *m, **mp;
959 int len;
960
961 mp = &sc->sc_m;
962 for (len = sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN; len > 0; ){
963 if ((m = *mp) == NULL) {
964 MGETHDR(m, M_DONTWAIT, MT_DATA);
965 if (m == NULL)
966 break;
967 *mp = m;
968 MCLGET(m, M_DONTWAIT);
969 }
970 len -= M_DATASIZE(m);
971 mp = &m->m_next;
972 }
973 }
974
975 /*
976 * tty interface receiver interrupt.
977 */
978 static const unsigned paritytab[8] = {
979 0x96696996, 0x69969669, 0x69969669, 0x96696996,
980 0x69969669, 0x96696996, 0x96696996, 0x69969669
981 };
982
983 int
984 pppinput(c, tp)
985 int c;
986 struct tty *tp;
987 {
988 struct ppp_softc *sc;
989 struct mbuf *m;
990 const struct cdevsw *cdev;
991 int ilen, s;
992
993 sc = (struct ppp_softc *) tp->t_sc;
994 if (sc == NULL || tp != (struct tty *) sc->sc_devp)
995 return 0;
996
997 ++tk_nin;
998 ++sc->sc_stats.ppp_ibytes;
999
1000 if (c & TTY_FE) {
1001 /* framing error or overrun on this char - abort packet */
1002 if (sc->sc_flags & SC_DEBUG)
1003 printf("%s: bad char %x\n", sc->sc_if.if_xname, c);
1004 goto flush;
1005 }
1006
1007 c &= 0xff;
1008
1009 /*
1010 * Handle software flow control of output.
1011 */
1012 if (tp->t_iflag & IXON) {
1013 if (c == tp->t_cc[VSTOP] && tp->t_cc[VSTOP] != _POSIX_VDISABLE) {
1014 if ((tp->t_state & TS_TTSTOP) == 0) {
1015 tp->t_state |= TS_TTSTOP;
1016 cdev = cdevsw_lookup(tp->t_dev);
1017 if (cdev != NULL)
1018 (*cdev->d_stop)(tp, 0);
1019 }
1020 return 0;
1021 }
1022 if (c == tp->t_cc[VSTART] && tp->t_cc[VSTART] != _POSIX_VDISABLE) {
1023 tp->t_state &= ~TS_TTSTOP;
1024 if (tp->t_oproc != NULL)
1025 (*tp->t_oproc)(tp);
1026 return 0;
1027 }
1028 }
1029
1030 s = spltty();
1031 if (c & 0x80)
1032 sc->sc_flags |= SC_RCV_B7_1;
1033 else
1034 sc->sc_flags |= SC_RCV_B7_0;
1035 if (paritytab[c >> 5] & (1 << (c & 0x1F)))
1036 sc->sc_flags |= SC_RCV_ODDP;
1037 else
1038 sc->sc_flags |= SC_RCV_EVNP;
1039 splx(s);
1040
1041 ppplogchar(sc, c);
1042
1043 if (c == PPP_FLAG) {
1044 ilen = sc->sc_ilen;
1045 sc->sc_ilen = 0;
1046
1047 if ((sc->sc_flags & SC_LOG_RAWIN) && sc->sc_rawin.count > 0)
1048 ppplogchar(sc, -1);
1049
1050 /*
1051 * If SC_ESCAPED is set, then we've seen the packet
1052 * abort sequence "}~".
1053 */
1054 if (sc->sc_flags & (SC_FLUSH | SC_ESCAPED)
1055 || (ilen > 0 && sc->sc_fcs != PPP_GOODFCS)) {
1056 s = spltty();
1057 sc->sc_flags |= SC_PKTLOST; /* note the dropped packet */
1058 if ((sc->sc_flags & (SC_FLUSH | SC_ESCAPED)) == 0){
1059 if (sc->sc_flags & SC_DEBUG)
1060 printf("%s: bad fcs %x\n", sc->sc_if.if_xname,
1061 sc->sc_fcs);
1062 sc->sc_if.if_ierrors++;
1063 sc->sc_stats.ppp_ierrors++;
1064 } else
1065 sc->sc_flags &= ~(SC_FLUSH | SC_ESCAPED);
1066 splx(s);
1067 return 0;
1068 }
1069
1070 if (ilen < PPP_HDRLEN + PPP_FCSLEN) {
1071 if (ilen) {
1072 if (sc->sc_flags & SC_DEBUG)
1073 printf("%s: too short (%d)\n", sc->sc_if.if_xname, ilen);
1074 s = spltty();
1075 sc->sc_if.if_ierrors++;
1076 sc->sc_stats.ppp_ierrors++;
1077 sc->sc_flags |= SC_PKTLOST;
1078 splx(s);
1079 }
1080 return 0;
1081 }
1082
1083 /*
1084 * Remove FCS trailer. Somewhat painful...
1085 */
1086 ilen -= 2;
1087 if (--sc->sc_mc->m_len == 0) {
1088 for (m = sc->sc_m; m->m_next != sc->sc_mc; m = m->m_next)
1089 ;
1090 sc->sc_mc = m;
1091 }
1092 sc->sc_mc->m_len--;
1093
1094 /* excise this mbuf chain */
1095 m = sc->sc_m;
1096 sc->sc_m = sc->sc_mc->m_next;
1097 sc->sc_mc->m_next = NULL;
1098
1099 ppppktin(sc, m, sc->sc_flags & SC_PKTLOST);
1100 if (sc->sc_flags & SC_PKTLOST) {
1101 s = spltty();
1102 sc->sc_flags &= ~SC_PKTLOST;
1103 splx(s);
1104 }
1105
1106 pppgetm(sc);
1107 return 0;
1108 }
1109
1110 if (sc->sc_flags & SC_FLUSH) {
1111 if (sc->sc_flags & SC_LOG_FLUSH)
1112 ppplogchar(sc, c);
1113 return 0;
1114 }
1115
1116 if (c < 0x20 && (sc->sc_rasyncmap & (1 << c)))
1117 return 0;
1118
1119 s = spltty();
1120 if (sc->sc_flags & SC_ESCAPED) {
1121 sc->sc_flags &= ~SC_ESCAPED;
1122 c ^= PPP_TRANS;
1123 } else if (c == PPP_ESCAPE) {
1124 sc->sc_flags |= SC_ESCAPED;
1125 splx(s);
1126 return 0;
1127 }
1128 splx(s);
1129
1130 /*
1131 * Initialize buffer on first octet received.
1132 * First octet could be address or protocol (when compressing
1133 * address/control).
1134 * Second octet is control.
1135 * Third octet is first or second (when compressing protocol)
1136 * octet of protocol.
1137 * Fourth octet is second octet of protocol.
1138 */
1139 if (sc->sc_ilen == 0) {
1140 /* reset the first input mbuf */
1141 if (sc->sc_m == NULL) {
1142 pppgetm(sc);
1143 if (sc->sc_m == NULL) {
1144 if (sc->sc_flags & SC_DEBUG)
1145 printf("%s: no input mbufs!\n", sc->sc_if.if_xname);
1146 goto flush;
1147 }
1148 }
1149 m = sc->sc_m;
1150 m->m_len = 0;
1151 m->m_data = M_DATASTART(sc->sc_m);
1152 sc->sc_mc = m;
1153 sc->sc_mp = mtod(m, char *);
1154 sc->sc_fcs = PPP_INITFCS;
1155 if (c != PPP_ALLSTATIONS) {
1156 if (sc->sc_flags & SC_REJ_COMP_AC) {
1157 if (sc->sc_flags & SC_DEBUG)
1158 printf("%s: garbage received: 0x%x (need 0xFF)\n",
1159 sc->sc_if.if_xname, c);
1160 goto flush;
1161 }
1162 *sc->sc_mp++ = PPP_ALLSTATIONS;
1163 *sc->sc_mp++ = PPP_UI;
1164 sc->sc_ilen += 2;
1165 m->m_len += 2;
1166 }
1167 }
1168 if (sc->sc_ilen == 1 && c != PPP_UI) {
1169 if (sc->sc_flags & SC_DEBUG)
1170 printf("%s: missing UI (0x3), got 0x%x\n",
1171 sc->sc_if.if_xname, c);
1172 goto flush;
1173 }
1174 if (sc->sc_ilen == 2 && (c & 1) == 1) {
1175 /* a compressed protocol */
1176 *sc->sc_mp++ = 0;
1177 sc->sc_ilen++;
1178 sc->sc_mc->m_len++;
1179 }
1180 if (sc->sc_ilen == 3 && (c & 1) == 0) {
1181 if (sc->sc_flags & SC_DEBUG)
1182 printf("%s: bad protocol %x\n", sc->sc_if.if_xname,
1183 (sc->sc_mp[-1] << 8) + c);
1184 goto flush;
1185 }
1186
1187 /* packet beyond configured mru? */
1188 if (++sc->sc_ilen > sc->sc_mru + PPP_HDRLEN + PPP_FCSLEN) {
1189 if (sc->sc_flags & SC_DEBUG)
1190 printf("%s: packet too big\n", sc->sc_if.if_xname);
1191 goto flush;
1192 }
1193
1194 /* is this mbuf full? */
1195 m = sc->sc_mc;
1196 if (M_TRAILINGSPACE(m) <= 0) {
1197 if (m->m_next == NULL) {
1198 pppgetm(sc);
1199 if (m->m_next == NULL) {
1200 if (sc->sc_flags & SC_DEBUG)
1201 printf("%s: too few input mbufs!\n", sc->sc_if.if_xname);
1202 goto flush;
1203 }
1204 }
1205 sc->sc_mc = m = m->m_next;
1206 m->m_len = 0;
1207 m->m_data = M_DATASTART(m);
1208 sc->sc_mp = mtod(m, char *);
1209 }
1210
1211 ++m->m_len;
1212 *sc->sc_mp++ = c;
1213 sc->sc_fcs = PPP_FCS(sc->sc_fcs, c);
1214 return 0;
1215
1216 flush:
1217 if (!(sc->sc_flags & SC_FLUSH)) {
1218 s = spltty();
1219 sc->sc_if.if_ierrors++;
1220 sc->sc_stats.ppp_ierrors++;
1221 sc->sc_flags |= SC_FLUSH;
1222 splx(s);
1223 if (sc->sc_flags & SC_LOG_FLUSH)
1224 ppplogchar(sc, c);
1225 }
1226 return 0;
1227 }
1228
1229 #define MAX_DUMP_BYTES 128
1230
1231 static void
1232 ppplogchar(sc, c)
1233 struct ppp_softc *sc;
1234 int c;
1235 {
1236 if (c >= 0) {
1237 sc->sc_rawin.buf[sc->sc_rawin_start++] = c;
1238 if (sc->sc_rawin.count < sizeof(sc->sc_rawin.buf))
1239 sc->sc_rawin.count++;
1240 }
1241 if (sc->sc_rawin_start >= sizeof(sc->sc_rawin.buf)
1242 || (c < 0 && sc->sc_rawin_start > 0)) {
1243 if (sc->sc_flags & (SC_LOG_FLUSH|SC_LOG_RAWIN)) {
1244 printf("%s input: ", sc->sc_if.if_xname);
1245 pppdumpb(sc->sc_rawin.buf, sc->sc_rawin_start);
1246 }
1247 if (c < 0)
1248 sc->sc_rawin.count = 0;
1249 sc->sc_rawin_start = 0;
1250 }
1251 }
1252
1253 static void
1254 pppdumpb(b, l)
1255 u_char *b;
1256 int l;
1257 {
1258 char buf[3*MAX_DUMP_BYTES+4];
1259 char *bp = buf;
1260 static char digits[] = "0123456789abcdef";
1261
1262 while (l--) {
1263 if (bp >= buf + sizeof(buf) - 3) {
1264 *bp++ = '>';
1265 break;
1266 }
1267 *bp++ = digits[*b >> 4]; /* convert byte to ascii hex */
1268 *bp++ = digits[*b++ & 0xf];
1269 *bp++ = ' ';
1270 }
1271
1272 *bp = 0;
1273 printf("%s\n", buf);
1274 }
1275
1276 static void
1277 pppdumpframe(sc, m, xmit)
1278 struct ppp_softc *sc;
1279 struct mbuf* m;
1280 int xmit;
1281 {
1282 int i,lcount,copycount,count;
1283 char lbuf[16];
1284 char *data;
1285
1286 if (m == NULL)
1287 return;
1288
1289 for(count=m->m_len,data=mtod(m,char*);m != NULL;) {
1290 /* build a line of output */
1291 for(lcount=0;lcount < sizeof(lbuf);lcount += copycount) {
1292 if (!count) {
1293 m = m->m_next;
1294 if (m == NULL)
1295 break;
1296 count = m->m_len;
1297 data = mtod(m,char*);
1298 }
1299 copycount = (count > sizeof(lbuf)-lcount) ?
1300 sizeof(lbuf)-lcount : count;
1301 bcopy(data,&lbuf[lcount],copycount);
1302 data += copycount;
1303 count -= copycount;
1304 }
1305
1306 /* output line (hex 1st, then ascii) */
1307 printf("%s %s:", sc->sc_if.if_xname,
1308 xmit ? "output" : "input ");
1309 for(i=0;i<lcount;i++)
1310 printf("%02x ",(u_char)lbuf[i]);
1311 for(;i<sizeof(lbuf);i++)
1312 printf(" ");
1313 for(i=0;i<lcount;i++)
1314 printf("%c",(lbuf[i] >= 040 &&
1315 lbuf[i] <= 0176) ? lbuf[i] : '.');
1316 printf("\n");
1317 }
1318 }
Cache object: b5e52007209e84c021c34542b2384b9b
|