]> git.ozlabs.org Git - ppp.git/blob - freebsd-2.0/if_ppp.c
updated for 2.3 from netbsd stuff
[ppp.git] / freebsd-2.0 / if_ppp.c
1 /*
2  * if_ppp.c - Point-to-Point Protocol (PPP) Asynchronous driver.
3  *
4  * Copyright (c) 1989 Carnegie Mellon University.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms are permitted
8  * provided that the above copyright notice and this paragraph are
9  * duplicated in all such forms and that any documentation,
10  * advertising materials, and other materials related to such
11  * distribution and use acknowledge that the software was developed
12  * by Carnegie Mellon University.  The name of the
13  * University may not be used to endorse or promote products derived
14  * from this software without specific prior written permission.
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
17  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
18  *
19  * Drew D. Perkins
20  * Carnegie Mellon University
21  * 4910 Forbes Ave.
22  * Pittsburgh, PA 15213
23  * (412) 268-8576
24  * ddp@andrew.cmu.edu
25  *
26  * Based on:
27  *      @(#)if_sl.c     7.6.1.2 (Berkeley) 2/15/89
28  *
29  * Copyright (c) 1987 Regents of the University of California.
30  * All rights reserved.
31  *
32  * Redistribution and use in source and binary forms are permitted
33  * provided that the above copyright notice and this paragraph are
34  * duplicated in all such forms and that any documentation,
35  * advertising materials, and other materials related to such
36  * distribution and use acknowledge that the software was developed
37  * by the University of California, Berkeley.  The name of the
38  * University may not be used to endorse or promote products derived
39  * from this software without specific prior written permission.
40  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
41  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
42  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
43  *
44  * Serial Line interface
45  *
46  * Rick Adams
47  * Center for Seismic Studies
48  * 1300 N 17th Street, Suite 1450
49  * Arlington, Virginia 22209
50  * (703)276-7900
51  * rick@seismo.ARPA
52  * seismo!rick
53  *
54  * Pounded on heavily by Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
55  * Converted to 4.3BSD Beta by Chris Torek.
56  * Other changes made at Berkeley, based in part on code by Kirk Smith.
57  *
58  * Converted to 4.3BSD+ 386BSD by Brad Parker (brad@cayman.com)
59  * Added VJ tcp header compression; more unified ioctls
60  *
61  * Extensively modified by Paul Mackerras (paulus@cs.anu.edu.au).
62  * Cleaned up a lot of the mbuf-related code to fix bugs that
63  * caused system crashes and packet corruption.  Changed pppstart
64  * so that it doesn't just give up with a collision if the whole
65  * packet doesn't fit in the output ring buffer.
66  *
67  * Added priority queueing for interactive IP packets, following
68  * the model of if_sl.c, plus hooks for bpf.
69  * Paul Mackerras (paulus@cs.anu.edu.au).
70  */
71
72 /* $Id: if_ppp.c,v 1.7 1996/07/01 01:00:27 paulus Exp $ */
73 /* from if_sl.c,v 1.11 84/10/04 12:54:47 rick Exp */
74 /* from NetBSD: if_ppp.c,v 1.15.2.2 1994/07/28 05:17:58 cgd Exp */
75
76 #include "ppp.h"
77 #if NPPP > 0
78
79 #define VJC
80 #define PPP_COMPRESS
81
82 #include <sys/param.h>
83 #include <sys/systm.h>
84 #include <sys/proc.h>
85 #include <sys/mbuf.h>
86 #include <sys/socket.h>
87 #include <sys/ioctl.h>
88 #include <sys/kernel.h>
89 #include <sys/time.h>
90 #include <sys/malloc.h>
91
92 #include <net/if.h>
93 #include <net/if_types.h>
94 #include <net/netisr.h>
95 #include <net/route.h>
96
97 #if INET
98 #include <netinet/in.h>
99 #include <netinet/in_systm.h>
100 #include <netinet/in_var.h>
101 #include <netinet/ip.h>
102 #endif
103
104 #include "bpfilter.h"
105 #if NBPFILTER > 0
106 #include <net/bpf.h>
107 #endif
108
109 #ifdef VJC
110 #include <net/pppcompress.h>
111 #endif
112
113 #include <net/ppp_defs.h>
114 #include <net/if_ppp.h>
115 #include <net/if_pppvar.h>
116 #include <machine/cpu.h>
117
118 #ifndef NETISR_PPP
119 /* This definition should be moved to net/netisr.h */
120 #define NETISR_PPP      26      /* PPP software interrupt */
121 #endif
122
123 #ifdef PPP_COMPRESS
124 #define PACKETPTR       struct mbuf *
125 #include <net/ppp-comp.h>
126 #endif
127
128 static void     ppp_requeue __P((struct ppp_softc *));
129 static void     ppp_outpkt __P((struct ppp_softc *));
130 static void     ppp_ccp __P((struct ppp_softc *, struct mbuf *m, int rcvd));
131 static void     ppp_ccp_closed __P((struct ppp_softc *));
132 static void     ppp_inproc __P((struct ppp_softc *, struct mbuf *));
133 static void     pppdumpm __P((struct mbuf *m0));
134
135 /*
136  * Some useful mbuf macros not in mbuf.h.
137  */
138 #define M_IS_CLUSTER(m) ((m)->m_flags & M_EXT)
139
140 #define M_DATASTART(m)  \
141         (M_IS_CLUSTER(m) ? (m)->m_ext.ext_buf : \
142             (m)->m_flags & M_PKTHDR ? (m)->m_pktdat : (m)->m_dat)
143
144 #define M_DATASIZE(m)   \
145         (M_IS_CLUSTER(m) ? (m)->m_ext.ext_size : \
146             (m)->m_flags & M_PKTHDR ? MHLEN: MLEN)
147
148 /*
149  * We steal two bits in the mbuf m_flags, to mark high-priority packets
150  * for output, and received packets following lost/corrupted packets.
151  */
152 #define M_HIGHPRI       0x2000  /* output packet for sc_fastq */
153 #define M_ERRMARK       0x4000  /* steal a bit in mbuf m_flags */
154
155
156 #ifdef PPP_COMPRESS
157 /*
158  * List of compressors we know about.
159  * We leave some space so maybe we can modload compressors.
160  */
161
162 extern struct compressor ppp_bsd_compress;
163 extern struct compressor ppp_deflate;
164
165 struct compressor *ppp_compressors[8] = {
166 #if DO_BSD_COMPRESS
167     &ppp_bsd_compress,
168 #endif
169 #if DO_DEFLATE
170     &ppp_deflate,
171 #endif
172     NULL
173 };
174 #endif /* PPP_COMPRESS */
175
176 TEXT_SET(pseudo_set, pppattach);
177
178 /*
179  * Called from boot code to establish ppp interfaces.
180  */
181 void
182 pppattach()
183 {
184     register struct ppp_softc *sc;
185     register int i = 0;
186     extern void (*netisrs[])__P((void));
187
188     for (sc = ppp_softc; i < NPPP; sc++) {
189         sc->sc_if.if_name = "ppp";
190         sc->sc_if.if_unit = i++;
191         sc->sc_if.if_mtu = PPP_MTU;
192         sc->sc_if.if_flags = IFF_POINTOPOINT | IFF_MULTICAST;
193         sc->sc_if.if_type = IFT_PPP;
194         sc->sc_if.if_hdrlen = PPP_HDRLEN;
195         sc->sc_if.if_ioctl = pppsioctl;
196         sc->sc_if.if_output = pppoutput;
197         sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
198         sc->sc_inq.ifq_maxlen = IFQ_MAXLEN;
199         sc->sc_fastq.ifq_maxlen = IFQ_MAXLEN;
200         sc->sc_rawq.ifq_maxlen = IFQ_MAXLEN;
201         if_attach(&sc->sc_if);
202 #if NBPFILTER > 0
203         bpfattach(&sc->sc_bpf, &sc->sc_if, DLT_PPP, PPP_HDRLEN);
204 #endif
205     }
206     netisrs[NETISR_PPP] = pppintr;
207 }
208
209 /*
210  * Allocate a ppp interface unit and initialize it.
211  */
212 struct ppp_softc *
213 pppalloc(pid)
214     pid_t pid;
215 {
216     int nppp, i;
217     struct ppp_softc *sc;
218
219     for (nppp = 0, sc = ppp_softc; nppp < NPPP; nppp++, sc++)
220         if (sc->sc_xfer == pid) {
221             sc->sc_xfer = 0;
222             return sc;
223         }
224     for (nppp = 0, sc = ppp_softc; nppp < NPPP; nppp++, sc++)
225         if (sc->sc_devp == NULL)
226             break;
227     if (nppp >= NPPP)
228         return NULL;
229
230     sc->sc_flags = 0;
231     sc->sc_mru = PPP_MRU;
232     sc->sc_relinq = NULL;
233     bzero((char *)&sc->sc_stats, sizeof(sc->sc_stats));
234 #ifdef VJC
235     MALLOC(sc->sc_comp, struct slcompress *, sizeof(struct slcompress),
236            M_DEVBUF, M_NOWAIT);
237     if (sc->sc_comp)
238         vj_compress_init(sc->sc_comp, -1);
239 #endif
240 #ifdef PPP_COMPRESS
241     sc->sc_xc_state = NULL;
242     sc->sc_rc_state = NULL;
243 #endif /* PPP_COMPRESS */
244     for (i = 0; i < NUM_NP; ++i)
245         sc->sc_npmode[i] = NPMODE_ERROR;
246     sc->sc_npqueue = NULL;
247     sc->sc_npqtail = &sc->sc_npqueue;
248     sc->sc_last_sent = sc->sc_last_recv = time.tv_sec;
249
250     return sc;
251 }
252
253 /*
254  * Deallocate a ppp unit.  Must be called at splnet or higher.
255  */
256 void
257 pppdealloc(sc)
258     struct ppp_softc *sc;
259 {
260     struct mbuf *m;
261
262     if_down(&sc->sc_if);
263     sc->sc_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
264     sc->sc_devp = NULL;
265     sc->sc_xfer = 0;
266     for (;;) {
267         IF_DEQUEUE(&sc->sc_rawq, m);
268         if (m == NULL)
269             break;
270         m_freem(m);
271     }
272     for (;;) {
273         IF_DEQUEUE(&sc->sc_inq, m);
274         if (m == NULL)
275             break;
276         m_freem(m);
277     }
278     for (;;) {
279         IF_DEQUEUE(&sc->sc_fastq, m);
280         if (m == NULL)
281             break;
282         m_freem(m);
283     }
284     while ((m = sc->sc_npqueue) != NULL) {
285         sc->sc_npqueue = m->m_nextpkt;
286         m_freem(m);
287     }
288     if (sc->sc_togo != NULL) {
289         m_freem(sc->sc_togo);
290         sc->sc_togo = NULL;
291     }
292 #ifdef PPP_COMPRESS
293     ppp_ccp_closed(sc);
294     sc->sc_xc_state = NULL;
295     sc->sc_rc_state = NULL;
296 #endif /* PPP_COMPRESS */
297 #ifdef VJC
298     if (sc->sc_comp != 0) {
299         FREE(sc->sc_comp, M_DEVBUF);
300         sc->sc_comp = 0;
301     }
302 #endif
303 }
304
305 /*
306  * Ioctl routine for generic ppp devices.
307  */
308 int
309 pppioctl(sc, cmd, data, flag, p)
310     struct ppp_softc *sc;
311     u_long cmd;
312     caddr_t data;
313     int flag;
314     struct proc *p;
315 {
316     int s, error, flags, mru, nb, npx;
317     struct ppp_option_data *odp;
318     struct compressor **cp;
319     struct npioctl *npi;
320     time_t t;
321 #ifdef  PPP_COMPRESS
322     u_char ccp_option[CCP_MAX_OPTION_LENGTH];
323 #endif
324
325     switch (cmd) {
326     case FIONREAD:
327         *(int *)data = sc->sc_inq.ifq_len;
328         break;
329
330     case PPPIOCGUNIT:
331         *(int *)data = sc->sc_if.if_unit;
332         break;
333
334     case PPPIOCGFLAGS:
335         *(u_int *)data = sc->sc_flags;
336         break;
337
338     case PPPIOCSFLAGS:
339         if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
340             return (error);
341         flags = *(int *)data & SC_MASK;
342         s = splnet();
343 #ifdef PPP_COMPRESS
344         if (sc->sc_flags & SC_CCP_OPEN && !(flags & SC_CCP_OPEN))
345             ppp_ccp_closed(sc);
346 #endif
347         splimp();
348         sc->sc_flags = (sc->sc_flags & ~SC_MASK) | flags;
349         splx(s);
350         break;
351
352     case PPPIOCSMRU:
353         if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
354             return (error);
355         mru = *(int *)data;
356         if (mru >= PPP_MRU && mru <= PPP_MAXMRU)
357             sc->sc_mru = mru;
358         break;
359
360     case PPPIOCGMRU:
361         *(int *)data = sc->sc_mru;
362         break;
363
364 #ifdef VJC
365     case PPPIOCSMAXCID:
366         if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
367             return (error);
368         if (sc->sc_comp) {
369             s = splnet();
370             vj_compress_init(sc->sc_comp, *(int *)data);
371             splx(s);
372         }
373         break;
374 #endif
375
376     case PPPIOCXFERUNIT:
377         if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
378             return (error);
379         sc->sc_xfer = p->p_pid;
380         break;
381
382 #ifdef PPP_COMPRESS
383     case PPPIOCSCOMPRESS:
384         if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
385             return (error);
386         odp = (struct ppp_option_data *) data;
387         nb = odp->length;
388         if (nb > sizeof(ccp_option))
389             nb = sizeof(ccp_option);
390         if ((error = copyin(odp->ptr, ccp_option, nb)) != 0)
391             return (error);
392         if (ccp_option[1] < 2)  /* preliminary check on the length byte */
393             return (EINVAL);
394         for (cp = ppp_compressors; *cp != NULL; ++cp)
395             if ((*cp)->compress_proto == ccp_option[0]) {
396                 /*
397                  * Found a handler for the protocol - try to allocate
398                  * a compressor or decompressor.
399                  */
400                 error = 0;
401                 if (odp->transmit) {
402                     s = splnet();
403                     if (sc->sc_xc_state != NULL)
404                         (*sc->sc_xcomp->comp_free)(sc->sc_xc_state);
405                     sc->sc_xcomp = *cp;
406                     sc->sc_xc_state = (*cp)->comp_alloc(ccp_option, nb);
407                     if (sc->sc_xc_state == NULL) {
408                         if (sc->sc_flags & SC_DEBUG)
409                             printf("ppp%d: comp_alloc failed\n",
410                                sc->sc_if.if_unit);
411                         error = ENOBUFS;
412                     }
413                     splimp();
414                     sc->sc_flags &= ~SC_COMP_RUN;
415                     splx(s);
416                 } else {
417                     s = splnet();
418                     if (sc->sc_rc_state != NULL)
419                         (*sc->sc_rcomp->decomp_free)(sc->sc_rc_state);
420                     sc->sc_rcomp = *cp;
421                     sc->sc_rc_state = (*cp)->decomp_alloc(ccp_option, nb);
422                     if (sc->sc_rc_state == NULL) {
423                         if (sc->sc_flags & SC_DEBUG)
424                             printf("ppp%d: decomp_alloc failed\n",
425                                sc->sc_if.if_unit);
426                         error = ENOBUFS;
427                     }
428                     splimp();
429                     sc->sc_flags &= ~SC_DECOMP_RUN;
430                     splx(s);
431                 }
432                 return (error);
433             }
434         if (sc->sc_flags & SC_DEBUG)
435             printf("ppp%d: no compressor for [%x %x %x], %x\n",
436                    sc->sc_if.if_unit, ccp_option[0], ccp_option[1],
437                    ccp_option[2], nb);
438         return (EINVAL);        /* no handler found */
439 #endif /* PPP_COMPRESS */
440
441     case PPPIOCGNPMODE:
442     case PPPIOCSNPMODE:
443         npi = (struct npioctl *) data;
444         switch (npi->protocol) {
445         case PPP_IP:
446             npx = NP_IP;
447             break;
448         default:
449             return EINVAL;
450         }
451         if (cmd == PPPIOCGNPMODE) {
452             npi->mode = sc->sc_npmode[npx];
453         } else {
454             if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
455                 return (error);
456             if (npi->mode != sc->sc_npmode[npx]) {
457                 s = splnet();
458                 sc->sc_npmode[npx] = npi->mode;
459                 if (npi->mode != NPMODE_QUEUE) {
460                     ppp_requeue(sc);
461                     (*sc->sc_start)(sc);
462                 }
463                 splx(s);
464             }
465         }
466         break;
467
468     case PPPIOCGIDLE:
469         s = splnet();
470         t = time.tv_sec;
471         ((struct ppp_idle *)data)->xmit_idle = t - sc->sc_last_sent;
472         ((struct ppp_idle *)data)->recv_idle = t - sc->sc_last_recv;
473         splx(s);
474         break;
475
476     default:
477         return (-1);
478     }
479     return (0);
480 }
481
482 /*
483  * Process an ioctl request to the ppp network interface.
484  */
485 int
486 pppsioctl(ifp, cmd, data)
487     register struct ifnet *ifp;
488     u_long cmd;
489     caddr_t data;
490 {
491     struct proc *p = curproc;   /* XXX */
492     register struct ppp_softc *sc = &ppp_softc[ifp->if_unit];
493     register struct ifaddr *ifa = (struct ifaddr *)data;
494     register struct ifreq *ifr = (struct ifreq *)data;
495     struct ppp_stats *psp;
496 #ifdef  PPP_COMPRESS
497     struct ppp_comp_stats *pcp;
498 #endif
499     int s = splimp(), error = 0;
500
501     switch (cmd) {
502     case SIOCSIFFLAGS:
503         if ((ifp->if_flags & IFF_RUNNING) == 0)
504             ifp->if_flags &= ~IFF_UP;
505         break;
506
507     case SIOCSIFADDR:
508         if (ifa->ifa_addr->sa_family != AF_INET)
509             error = EAFNOSUPPORT;
510         break;
511
512     case SIOCSIFDSTADDR:
513         if (ifa->ifa_addr->sa_family != AF_INET)
514             error = EAFNOSUPPORT;
515         break;
516
517     case SIOCSIFMTU:
518         if ((error = suser(p->p_ucred, &p->p_acflag)) != 0)
519             break;
520         sc->sc_if.if_mtu = ifr->ifr_mtu;
521         break;
522
523     case SIOCGIFMTU:
524         ifr->ifr_mtu = sc->sc_if.if_mtu;
525         break;
526
527     case SIOCADDMULTI:
528     case SIOCDELMULTI:
529         if (ifr == 0) {
530             error = EAFNOSUPPORT;
531             break;
532         }
533         switch(ifr->ifr_addr.sa_family) {
534 #ifdef INET
535         case AF_INET:
536             break;
537 #endif
538         default:
539             error = EAFNOSUPPORT;
540             break;
541         }
542         break;
543
544     case SIOCGPPPSTATS:
545         psp = &((struct ifpppstatsreq *) data)->stats;
546         bzero(psp, sizeof(*psp));
547         psp->p = sc->sc_stats;
548 #if defined(VJC) && !defined(SL_NO_STATS)
549         if (sc->sc_comp) {
550             psp->vj.vjs_packets = sc->sc_comp->sls_packets;
551             psp->vj.vjs_compressed = sc->sc_comp->sls_compressed;
552             psp->vj.vjs_searches = sc->sc_comp->sls_searches;
553             psp->vj.vjs_misses = sc->sc_comp->sls_misses;
554             psp->vj.vjs_uncompressedin = sc->sc_comp->sls_uncompressedin;
555             psp->vj.vjs_compressedin = sc->sc_comp->sls_compressedin;
556             psp->vj.vjs_errorin = sc->sc_comp->sls_errorin;
557             psp->vj.vjs_tossed = sc->sc_comp->sls_tossed;
558         }
559 #endif /* VJC */
560         break;
561
562 #ifdef PPP_COMPRESS
563     case SIOCGPPPCSTATS:
564         pcp = &((struct ifpppcstatsreq *) data)->stats;
565         bzero(pcp, sizeof(*pcp));
566         if (sc->sc_xc_state != NULL)
567             (*sc->sc_xcomp->comp_stat)(sc->sc_xc_state, &pcp->c);
568         if (sc->sc_rc_state != NULL)
569             (*sc->sc_rcomp->decomp_stat)(sc->sc_rc_state, &pcp->d);
570         break;
571 #endif /* PPP_COMPRESS */
572
573     default:
574         error = EINVAL;
575     }
576     splx(s);
577     return (error);
578 }
579
580 /*
581  * Queue a packet.  Start transmission if not active.
582  * Packet is placed in Information field of PPP frame.
583  */
584 int
585 pppoutput(ifp, m0, dst, rtp)
586     struct ifnet *ifp;
587     struct mbuf *m0;
588     struct sockaddr *dst;
589     struct rtentry *rtp;
590 {
591     register struct ppp_softc *sc = &ppp_softc[ifp->if_unit];
592     int protocol, address, control;
593     u_char *cp;
594     int s, error;
595     struct ip *ip;
596     struct ifqueue *ifq;
597     enum NPmode mode;
598
599     if (sc->sc_devp == NULL || (ifp->if_flags & IFF_RUNNING) == 0
600         || ((ifp->if_flags & IFF_UP) == 0 && dst->sa_family != AF_UNSPEC)) {
601         error = ENETDOWN;       /* sort of */
602         goto bad;
603     }
604
605     /*
606      * Compute PPP header.
607      */
608     m0->m_flags &= ~M_HIGHPRI;
609     switch (dst->sa_family) {
610 #ifdef INET
611     case AF_INET:
612         address = PPP_ALLSTATIONS;
613         control = PPP_UI;
614         protocol = PPP_IP;
615         mode = sc->sc_npmode[NP_IP];
616
617         /*
618          * If this packet has the "low delay" bit set in the IP header,
619          * put it on the fastq instead.
620          */
621         ip = mtod(m0, struct ip *);
622         if (ip->ip_tos & IPTOS_LOWDELAY)
623             m0->m_flags |= M_HIGHPRI;
624         break;
625 #endif
626     case AF_UNSPEC:
627         address = PPP_ADDRESS(dst->sa_data);
628         control = PPP_CONTROL(dst->sa_data);
629         protocol = PPP_PROTOCOL(dst->sa_data);
630         mode = NPMODE_PASS;
631         break;
632     default:
633         printf("ppp%d: af%d not supported\n", ifp->if_unit, dst->sa_family);
634         error = EAFNOSUPPORT;
635         goto bad;
636     }
637
638     /*
639      * Drop this packet, or return an error, if necessary.
640      */
641     if (mode == NPMODE_ERROR) {
642         error = ENETDOWN;
643         goto bad;
644     }
645     if (mode == NPMODE_DROP) {
646         error = 0;
647         goto bad;
648     }
649
650     /*
651      * Add PPP header.  If no space in first mbuf, allocate another.
652      * (This assumes M_LEADINGSPACE is always 0 for a cluster mbuf.)
653      */
654     if (M_LEADINGSPACE(m0) < PPP_HDRLEN) {
655         m0 = m_prepend(m0, PPP_HDRLEN, M_DONTWAIT);
656         if (m0 == 0) {
657             error = ENOBUFS;
658             goto bad;
659         }
660         m0->m_len = 0;
661     } else
662         m0->m_data -= PPP_HDRLEN;
663
664     cp = mtod(m0, u_char *);
665     *cp++ = address;
666     *cp++ = control;
667     *cp++ = protocol >> 8;
668     *cp++ = protocol & 0xff;
669     m0->m_len += PPP_HDRLEN;
670
671     if (sc->sc_flags & SC_LOG_OUTPKT) {
672         printf("ppp%d output: ", ifp->if_unit);
673         pppdumpm(m0);
674     }
675
676     if ((protocol & 0x8000) == 0) {
677         /*
678          * Update the time we sent the most recent data packet.
679          */
680         sc->sc_last_sent = time.tv_sec;
681     }
682
683 #if NBPFILTER > 0
684     /*
685      * See if bpf wants to look at the packet.
686      */
687     if (sc->sc_bpf)
688         bpf_mtap(sc->sc_bpf, m0);
689 #endif
690
691     /*
692      * Put the packet on the appropriate queue.
693      */
694     s = splnet();
695     if (mode == NPMODE_QUEUE) {
696         /* XXX we should limit the number of packets on this queue */
697         *sc->sc_npqtail = m0;
698         m0->m_nextpkt = NULL;
699         sc->sc_npqtail = &m0->m_nextpkt;
700     } else {
701         ifq = (m0->m_flags & M_HIGHPRI)? &sc->sc_fastq: &ifp->if_snd;
702         if (IF_QFULL(ifq) && dst->sa_family != AF_UNSPEC) {
703             IF_DROP(ifq);
704             splx(s);
705             sc->sc_if.if_oerrors++;
706             sc->sc_stats.ppp_oerrors++;
707             error = ENOBUFS;
708             goto bad;
709         }
710         IF_ENQUEUE(ifq, m0);
711         (*sc->sc_start)(sc);
712     }
713     ifp->if_lastchange = time;
714     ifp->if_opackets++;
715     ifp->if_obytes += len;
716
717     splx(s);
718     return (0);
719
720 bad:
721     m_freem(m0);
722     return (error);
723 }
724
725 /*
726  * After a change in the NPmode for some NP, move packets from the
727  * npqueue to the send queue or the fast queue as appropriate.
728  * Should be called at splnet.
729  */
730 static void
731 ppp_requeue(sc)
732     struct ppp_softc *sc;
733 {
734     struct mbuf *m, **mpp;
735     struct ifqueue *ifq;
736     enum NPmode mode;
737
738     for (mpp = &sc->sc_npqueue; (m = *mpp) != NULL; ) {
739         switch (PPP_PROTOCOL(mtod(m, u_char *))) {
740         case PPP_IP:
741             mode = sc->sc_npmode[NP_IP];
742             break;
743         default:
744             mode = NPMODE_PASS;
745         }
746
747         switch (mode) {
748         case NPMODE_PASS:
749             /*
750              * This packet can now go on one of the queues to be sent.
751              */
752             *mpp = m->m_nextpkt;
753             m->m_nextpkt = NULL;
754             ifq = (m->m_flags & M_HIGHPRI)? &sc->sc_fastq: &sc->sc_if.if_snd;
755             if (IF_QFULL(ifq)) {
756                 IF_DROP(ifq);
757                 sc->sc_if.if_oerrors++;
758                 sc->sc_stats.ppp_oerrors++;
759             } else
760                 IF_ENQUEUE(ifq, m);
761             break;
762
763         case NPMODE_DROP:
764         case NPMODE_ERROR:
765             *mpp = m->m_nextpkt;
766             m_freem(m);
767             break;
768
769         case NPMODE_QUEUE:
770             mpp = &m->m_nextpkt;
771             break;
772         }
773     }
774     sc->sc_npqtail = mpp;
775 }
776
777 /*
778  * Transmitter has finished outputting some stuff;
779  * remember to call sc->sc_start later at splnet.
780  */
781 void
782 ppp_restart(sc)
783     struct ppp_softc *sc;
784 {
785     int s = splimp();
786
787     sc->sc_flags &= ~SC_TBUSY;
788     schednetisr(NETISR_PPP);
789     splx(s);
790 }
791
792 /*
793  * Get a packet to send.  This procedure is intended to be called at
794  * splnet, since it may involve time-consuming operations such as
795  * applying VJ compression, packet compression, address/control and/or
796  * protocol field compression to the packet.
797  */
798 struct mbuf *
799 ppp_dequeue(sc)
800     struct ppp_softc *sc;
801 {
802     struct mbuf *m, *mp;
803     u_char *cp;
804     int address, control, protocol;
805     int s;
806
807     /*
808      * Grab a packet to send: first try the fast queue, then the
809      * normal queue.
810      */
811     IF_DEQUEUE(&sc->sc_fastq, m);
812     if (m == NULL)
813         IF_DEQUEUE(&sc->sc_if.if_snd, m);
814     if (m == NULL)
815         return NULL;
816
817     ++sc->sc_stats.ppp_opackets;
818
819     /*
820      * Extract the ppp header of the new packet.
821      * The ppp header will be in one mbuf.
822      */
823     cp = mtod(m, u_char *);
824     address = PPP_ADDRESS(cp);
825     control = PPP_CONTROL(cp);
826     protocol = PPP_PROTOCOL(cp);
827
828     switch (protocol) {
829     case PPP_IP:
830 #ifdef VJC
831         /*
832          * If the packet is a TCP/IP packet, see if we can compress it.
833          */
834         if ((sc->sc_flags & SC_COMP_TCP) && sc->sc_comp != NULL) {
835             struct ip *ip;
836             int type;
837
838             mp = m;
839             ip = (struct ip *) (cp + PPP_HDRLEN);
840             if (mp->m_len <= PPP_HDRLEN) {
841                 mp = mp->m_next;
842                 if (mp == NULL)
843                     break;
844                 ip = mtod(mp, struct ip *);
845             }
846             /* this code assumes the IP/TCP header is in one non-shared mbuf */
847             if (ip->ip_p == IPPROTO_TCP) {
848                 type = vj_compress_tcp(mp, ip, sc->sc_comp,
849                                        !(sc->sc_flags & SC_NO_TCP_CCID));
850                 switch (type) {
851                 case TYPE_UNCOMPRESSED_TCP:
852                     protocol = PPP_VJC_UNCOMP;
853                     break;
854                 case TYPE_COMPRESSED_TCP:
855                     protocol = PPP_VJC_COMP;
856                     cp = mtod(m, u_char *);
857                     cp[0] = address;    /* header has moved */
858                     cp[1] = control;
859                     cp[2] = 0;
860                     break;
861                 }
862                 cp[3] = protocol;       /* update protocol in PPP header */
863             }
864         }
865 #endif  /* VJC */
866         break;
867
868 #ifdef PPP_COMPRESS
869     case PPP_CCP:
870         ppp_ccp(sc, m, 0);
871         break;
872 #endif  /* PPP_COMPRESS */
873     }
874
875 #ifdef PPP_COMPRESS
876     if (protocol != PPP_LCP && protocol != PPP_CCP
877         && sc->sc_xc_state && (sc->sc_flags & SC_COMP_RUN)) {
878         struct mbuf *mcomp = NULL;
879         int slen, clen;
880
881         slen = 0;
882         for (mp = m; mp != NULL; mp = mp->m_next)
883             slen += mp->m_len;
884         clen = (*sc->sc_xcomp->compress)
885             (sc->sc_xc_state, &mcomp, m, slen,
886              (sc->sc_flags & SC_CCP_UP? sc->sc_if.if_mtu: 0));
887         if (mcomp != NULL) {
888             m_freem(m);
889             m = mcomp;
890             cp = mtod(m, u_char *);
891             protocol = cp[3];
892         }
893     }
894 #endif  /* PPP_COMPRESS */
895
896     /*
897      * Compress the address/control and protocol, if possible.
898      */
899     if (sc->sc_flags & SC_COMP_AC && address == PPP_ALLSTATIONS &&
900         control == PPP_UI && protocol != PPP_ALLSTATIONS &&
901         protocol != PPP_LCP) {
902         /* can compress address/control */
903         m->m_data += 2;
904         m->m_len -= 2;
905     }
906     if (sc->sc_flags & SC_COMP_PROT && protocol < 0xFF) {
907         /* can compress protocol */
908         if (mtod(m, u_char *) == cp) {
909             cp[2] = cp[1];      /* move address/control up */
910             cp[1] = cp[0];
911         }
912         ++m->m_data;
913         --m->m_len;
914     }
915
916     return m;
917 }
918
919 /*
920  * Software interrupt routine, called at splnet.
921  */
922 void
923 pppintr()
924 {
925     struct ppp_softc *sc;
926     int i, s, s2;
927     struct mbuf *m;
928
929     sc = ppp_softc;
930     s = splnet();
931     for (i = 0; i < NPPP; ++i, ++sc) {
932         if (!(sc->sc_flags & SC_TBUSY)
933             && (sc->sc_if.if_snd.ifq_head || sc->sc_fastq.ifq_head)) {
934             s2 = splimp();
935             sc->sc_flags |= SC_TBUSY;
936             splx(s2);
937             (*sc->sc_start)(sc);
938         }
939         for (;;) {
940             s2 = splimp();
941             IF_DEQUEUE(&sc->sc_rawq, m);
942             splx(s2);
943             if (m == NULL)
944                 break;
945             ppp_inproc(sc, m);
946         }
947     }
948     splx(s);
949 }
950
951 #ifdef PPP_COMPRESS
952 /*
953  * Handle a CCP packet.  `rcvd' is 1 if the packet was received,
954  * 0 if it is about to be transmitted.
955  */
956 static void
957 ppp_ccp(sc, m, rcvd)
958     struct ppp_softc *sc;
959     struct mbuf *m;
960     int rcvd;
961 {
962     u_char *dp, *ep;
963     struct mbuf *mp;
964     int slen, s;
965
966     /*
967      * Get a pointer to the data after the PPP header.
968      */
969     if (m->m_len <= PPP_HDRLEN) {
970         mp = m->m_next;
971         if (mp == NULL)
972             return;
973         dp = (mp != NULL)? mtod(mp, u_char *): NULL;
974     } else {
975         mp = m;
976         dp = mtod(mp, u_char *) + PPP_HDRLEN;
977     }
978
979     ep = mtod(mp, u_char *) + mp->m_len;
980     if (dp + CCP_HDRLEN > ep)
981         return;
982     slen = CCP_LENGTH(dp);
983     if (dp + slen > ep) {
984         if (sc->sc_flags & SC_DEBUG)
985             printf("if_ppp/ccp: not enough data in mbuf (%p+%x > %p+%x)\n",
986                    dp, slen, mtod(mp, u_char *), mp->m_len);
987         return;
988     }
989
990     switch (CCP_CODE(dp)) {
991     case CCP_CONFREQ:
992     case CCP_TERMREQ:
993     case CCP_TERMACK:
994         /* CCP must be going down - disable compression */
995         if (sc->sc_flags & SC_CCP_UP) {
996             s = splimp();
997             sc->sc_flags &= ~(SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN);
998             splx(s);
999         }
1000         break;
1001
1002     case CCP_CONFACK:
1003         if (sc->sc_flags & SC_CCP_OPEN && !(sc->sc_flags & SC_CCP_UP)
1004             && slen >= CCP_HDRLEN + CCP_OPT_MINLEN
1005             && slen >= CCP_OPT_LENGTH(dp + CCP_HDRLEN) + CCP_HDRLEN) {
1006             if (!rcvd) {
1007                 /* we're agreeing to send compressed packets. */
1008                 if (sc->sc_xc_state != NULL
1009                     && (*sc->sc_xcomp->comp_init)
1010                         (sc->sc_xc_state, dp + CCP_HDRLEN, slen - CCP_HDRLEN,
1011                          sc->sc_if.if_unit, 0, sc->sc_flags & SC_DEBUG)) {
1012                     s = splimp();
1013                     sc->sc_flags |= SC_COMP_RUN;
1014                     splx(s);
1015                 }
1016             } else {
1017                 /* peer is agreeing to send compressed packets. */
1018                 if (sc->sc_rc_state != NULL
1019                     && (*sc->sc_rcomp->decomp_init)
1020                         (sc->sc_rc_state, dp + CCP_HDRLEN, slen - CCP_HDRLEN,
1021                          sc->sc_if.if_unit, 0, sc->sc_mru,
1022                          sc->sc_flags & SC_DEBUG)) {
1023                     s = splimp();
1024                     sc->sc_flags |= SC_DECOMP_RUN;
1025                     sc->sc_flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
1026                     splx(s);
1027                 }
1028             }
1029         }
1030         break;
1031
1032     case CCP_RESETACK:
1033         if (sc->sc_flags & SC_CCP_UP) {
1034             if (!rcvd) {
1035                 if (sc->sc_xc_state && (sc->sc_flags & SC_COMP_RUN))
1036                     (*sc->sc_xcomp->comp_reset)(sc->sc_xc_state);
1037             } else {
1038                 if (sc->sc_rc_state && (sc->sc_flags & SC_DECOMP_RUN)) {
1039                     (*sc->sc_rcomp->decomp_reset)(sc->sc_rc_state);
1040                     s = splimp();
1041                     sc->sc_flags &= ~SC_DC_ERROR;
1042                     splx(s);
1043                 }
1044             }
1045         }
1046         break;
1047     }
1048 }
1049
1050 /*
1051  * CCP is down; free (de)compressor state if necessary.
1052  */
1053 static void
1054 ppp_ccp_closed(sc)
1055     struct ppp_softc *sc;
1056 {
1057     if (sc->sc_xc_state) {
1058         (*sc->sc_xcomp->comp_free)(sc->sc_xc_state);
1059         sc->sc_xc_state = NULL;
1060     }
1061     if (sc->sc_rc_state) {
1062         (*sc->sc_rcomp->decomp_free)(sc->sc_rc_state);
1063         sc->sc_rc_state = NULL;
1064     }
1065 }
1066 #endif /* PPP_COMPRESS */
1067
1068 /*
1069  * PPP packet input routine.
1070  * The caller has checked and removed the FCS and has inserted
1071  * the address/control bytes and the protocol high byte if they
1072  * were omitted.
1073  */
1074 void
1075 ppppktin(sc, m, lost)
1076     struct ppp_softc *sc;
1077     struct mbuf *m;
1078     int lost;
1079 {
1080     int s = splimp();
1081
1082     if (lost)
1083         m->m_flags |= M_ERRMARK;
1084     IF_ENQUEUE(&sc->sc_rawq, m);
1085     schednetisr(NETISR_PPP);
1086     splx(s);
1087 }
1088
1089 /*
1090  * Process a received PPP packet, doing decompression as necessary.
1091  * Should be called at splnet.
1092  */
1093 #define COMPTYPE(proto) ((proto) == PPP_VJC_COMP? TYPE_COMPRESSED_TCP: \
1094                          TYPE_UNCOMPRESSED_TCP)
1095
1096 static void
1097 ppp_inproc(sc, m)
1098     struct ppp_softc *sc;
1099     struct mbuf *m;
1100 {
1101     struct ifnet *ifp = &sc->sc_if;
1102     struct ifqueue *inq;
1103     int s, ilen, xlen, proto, rv;
1104     u_char *cp, adrs, ctrl;
1105     struct mbuf *mp, *dmp = NULL;
1106     u_char *iphdr;
1107     u_int hlen;
1108
1109     sc->sc_stats.ppp_ipackets++;
1110
1111     if (sc->sc_flags & SC_LOG_INPKT) {
1112         ilen = 0;
1113         for (mp = m; mp != NULL; mp = mp->m_next)
1114             ilen += mp->m_len;
1115         printf("ppp%d: got %d bytes\n", ifp->if_unit, ilen);
1116         pppdumpm(m);
1117     }
1118
1119     cp = mtod(m, u_char *);
1120     adrs = PPP_ADDRESS(cp);
1121     ctrl = PPP_CONTROL(cp);
1122     proto = PPP_PROTOCOL(cp);
1123
1124     if (m->m_flags & M_ERRMARK) {
1125         m->m_flags &= ~M_ERRMARK;
1126         s = splimp();
1127         sc->sc_flags |= SC_VJ_RESET;
1128         splx(s);
1129     }
1130
1131 #ifdef PPP_COMPRESS
1132     /*
1133      * Decompress this packet if necessary, update the receiver's
1134      * dictionary, or take appropriate action on a CCP packet.
1135      */
1136     if (proto == PPP_COMP && sc->sc_rc_state && (sc->sc_flags & SC_DECOMP_RUN)
1137         && !(sc->sc_flags & SC_DC_ERROR) && !(sc->sc_flags & SC_DC_FERROR)) {
1138         /* decompress this packet */
1139         rv = (*sc->sc_rcomp->decompress)(sc->sc_rc_state, m, &dmp);
1140         if (rv == DECOMP_OK) {
1141             m_freem(m);
1142             if (dmp == NULL) {
1143                 /* no error, but no decompressed packet produced */
1144                 return;
1145             }
1146             m = dmp;
1147             cp = mtod(m, u_char *);
1148             proto = PPP_PROTOCOL(cp);
1149
1150         } else {
1151             /*
1152              * An error has occurred in decompression.
1153              * Pass the compressed packet up to pppd, which may take
1154              * CCP down or issue a Reset-Req.
1155              */
1156             if (sc->sc_flags & SC_DEBUG)
1157                 printf("ppp%d: decompress failed %d\n", ifp->if_unit, rv);
1158             s = splimp();
1159             sc->sc_flags |= SC_VJ_RESET;
1160             if (rv == DECOMP_ERROR)
1161                 sc->sc_flags |= SC_DC_ERROR;
1162             else
1163                 sc->sc_flags |= SC_DC_FERROR;
1164             splx(s);
1165         }
1166
1167     } else {
1168         if (sc->sc_rc_state && (sc->sc_flags & SC_DECOMP_RUN)) {
1169             (*sc->sc_rcomp->incomp)(sc->sc_rc_state, m);
1170         }
1171         if (proto == PPP_CCP) {
1172             ppp_ccp(sc, m, 1);
1173         }
1174     }
1175 #endif
1176
1177     ilen = 0;
1178     for (mp = m; mp != NULL; mp = mp->m_next)
1179         ilen += mp->m_len;
1180
1181 #ifdef VJC
1182     if (sc->sc_flags & SC_VJ_RESET) {
1183         /*
1184          * If we've missed a packet, we must toss subsequent compressed
1185          * packets which don't have an explicit connection ID.
1186          */
1187         if (sc->sc_comp)
1188             vj_uncompress_tcp(NULL, 0, TYPE_ERROR, sc->sc_comp);
1189         s = splimp();
1190         sc->sc_flags &= ~SC_VJ_RESET;
1191         splx(s);
1192     }
1193
1194     /*
1195      * See if we have a VJ-compressed packet to uncompress.
1196      */
1197     if (proto == PPP_VJC_COMP) {
1198         if ((sc->sc_flags & SC_REJ_COMP_TCP) || sc->sc_comp == 0)
1199             goto bad;
1200
1201         xlen = vj_uncompress_tcp_core(cp + PPP_HDRLEN, m->m_len - PPP_HDRLEN,
1202                                       ilen - PPP_HDRLEN, TYPE_COMPRESSED_TCP,
1203                                       sc->sc_comp, &iphdr, &hlen);
1204
1205         if (xlen <= 0) {
1206             if (sc->sc_flags & SC_DEBUG)
1207                 printf("ppp%d: VJ uncompress failed on type comp\n",
1208                         ifp->if_unit);
1209             goto bad;
1210         }
1211
1212         /* Copy the PPP and IP headers into a new mbuf. */
1213         MGETHDR(mp, M_DONTWAIT, MT_DATA);
1214         if (mp == NULL)
1215             goto bad;
1216         mp->m_len = 0;
1217         mp->m_next = NULL;
1218         if (hlen + PPP_HDRLEN > MHLEN) {
1219             MCLGET(mp, M_DONTWAIT);
1220             if (M_TRAILINGSPACE(mp) < hlen + PPP_HDRLEN) {
1221                 m_freem(mp);
1222                 goto bad;       /* lose if big headers and no clusters */
1223             }
1224         }
1225         cp = mtod(mp, u_char *);
1226         cp[0] = adrs;
1227         cp[1] = ctrl;
1228         cp[2] = 0;
1229         cp[3] = PPP_IP;
1230         proto = PPP_IP;
1231         bcopy(iphdr, cp + PPP_HDRLEN, hlen);
1232         mp->m_len = hlen + PPP_HDRLEN;
1233
1234         /*
1235          * Trim the PPP and VJ headers off the old mbuf
1236          * and stick the new and old mbufs together.
1237          */
1238         m->m_data += PPP_HDRLEN + xlen;
1239         m->m_len -= PPP_HDRLEN + xlen;
1240         if (m->m_len <= M_TRAILINGSPACE(mp)) {
1241             bcopy(mtod(m, u_char *), mtod(mp, u_char *) + mp->m_len, m->m_len);
1242             mp->m_len += m->m_len;
1243             MFREE(m, mp->m_next);
1244         } else
1245             mp->m_next = m;
1246         m = mp;
1247         ilen += hlen - xlen;
1248
1249     } else if (proto == PPP_VJC_UNCOMP) {
1250         if ((sc->sc_flags & SC_REJ_COMP_TCP) || sc->sc_comp == 0)
1251             goto bad;
1252
1253         xlen = vj_uncompress_tcp_core(cp + PPP_HDRLEN, m->m_len - PPP_HDRLEN,
1254                                       ilen - PPP_HDRLEN, TYPE_UNCOMPRESSED_TCP,
1255                                       sc->sc_comp, &iphdr, &hlen);
1256
1257         if (xlen < 0) {
1258             if (sc->sc_flags & SC_DEBUG)
1259                 printf("ppp%d: VJ uncompress failed on type uncomp\n",
1260                         ifp->if_unit);
1261             goto bad;
1262         }
1263
1264         proto = PPP_IP;
1265         cp[3] = PPP_IP;
1266     }
1267 #endif /* VJC */
1268
1269     /*
1270      * If the packet will fit in a header mbuf, don't waste a
1271      * whole cluster on it.
1272      */
1273     if (ilen <= MHLEN && M_IS_CLUSTER(m)) {
1274         MGETHDR(mp, M_DONTWAIT, MT_DATA);
1275         if (mp != NULL) {
1276             m_copydata(m, 0, ilen, mtod(mp, caddr_t));
1277             m_freem(m);
1278             m = mp;
1279             m->m_len = ilen;
1280         }
1281     }
1282     m->m_pkthdr.len = ilen;
1283     m->m_pkthdr.rcvif = ifp;
1284
1285     /*
1286      * Record the time that we received this packet.
1287      */
1288     if ((proto & 0x8000) == 0) {
1289         sc->sc_last_recv = time.tv_sec;
1290     }
1291
1292 #if NBPFILTER > 0
1293     /* See if bpf wants to look at the packet. */
1294     if (sc->sc_bpf)
1295         bpf_mtap(sc->sc_bpf, m);
1296 #endif
1297
1298     rv = 0;
1299     switch (proto) {
1300 #ifdef INET
1301     case PPP_IP:
1302         /*
1303          * IP packet - take off the ppp header and pass it up to IP.
1304          */
1305         if ((ifp->if_flags & IFF_UP) == 0
1306             || sc->sc_npmode[NP_IP] != NPMODE_PASS) {
1307             /* interface is down - drop the packet. */
1308             m_freem(m);
1309             return;
1310         }
1311         m->m_pkthdr.len -= PPP_HDRLEN;
1312         m->m_data += PPP_HDRLEN;
1313         m->m_len -= PPP_HDRLEN;
1314         schednetisr(NETISR_IP);
1315         inq = &ipintrq;
1316         break;
1317 #endif
1318
1319     default:
1320         /*
1321          * Some other protocol - place on input queue for read().
1322          */
1323         inq = &sc->sc_inq;
1324         rv = 1;
1325         break;
1326     }
1327
1328     /*
1329      * Put the packet on the appropriate input queue.
1330      */
1331     s = splimp();
1332     if (IF_QFULL(inq)) {
1333         IF_DROP(inq);
1334         splx(s);
1335         if (sc->sc_flags & SC_DEBUG)
1336             printf("ppp%d: input queue full\n", ifp->if_unit);
1337         ifp->if_iqdrops++;
1338         goto bad;
1339     }
1340     IF_ENQUEUE(inq, m);
1341     splx(s);
1342     ifp->if_ipackets++;
1343     ifp->if_ibytes += ilen;
1344     ifp->if_lastchange = time;
1345
1346     if (rv)
1347         (*sc->sc_ctlp)(sc);
1348
1349     return;
1350
1351  bad:
1352     m_freem(m);
1353     sc->sc_if.if_ierrors++;
1354     sc->sc_stats.ppp_ierrors++;
1355 }
1356
1357 #define MAX_DUMP_BYTES  128
1358
1359 static void
1360 pppdumpm(m0)
1361     struct mbuf *m0;
1362 {
1363     char buf[3*MAX_DUMP_BYTES+4];
1364     char *bp = buf;
1365     struct mbuf *m;
1366     static char digits[] = "0123456789abcdef";
1367
1368     for (m = m0; m; m = m->m_next) {
1369         int l = m->m_len;
1370         u_char *rptr = (u_char *)m->m_data;
1371
1372         while (l--) {
1373             if (bp > buf + sizeof(buf) - 4)
1374                 goto done;
1375             *bp++ = digits[*rptr >> 4]; /* convert byte to ascii hex */
1376             *bp++ = digits[*rptr++ & 0xf];
1377         }
1378
1379         if (m->m_next) {
1380             if (bp > buf + sizeof(buf) - 3)
1381                 goto done;
1382             *bp++ = '|';
1383         } else
1384             *bp++ = ' ';
1385     }
1386 done:
1387     if (m)
1388         *bp++ = '>';
1389     *bp = 0;
1390     printf("%s\n", buf);
1391 }
1392
1393 #endif  /* NPPP > 0 */