]> git.ozlabs.org Git - ppp.git/blob - modules/ppp_comp.c
5336e770dabaebc461a4a5ffdc4cd0b7569fd13f
[ppp.git] / modules / ppp_comp.c
1 /*
2  * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
3  *
4  * Copyright (c) 1994 The Australian National University.
5  * All rights reserved.
6  *
7  * Permission to use, copy, modify, and distribute this software and its
8  * documentation is hereby granted, provided that the above copyright
9  * notice appears in all copies.  This software is provided without any
10  * warranty, express or implied. The Australian National University
11  * makes no representations about the suitability of this software for
12  * any purpose.
13  *
14  * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
15  * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
17  * THE AUSTRALIAN NATIONAL UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY
18  * OF SUCH DAMAGE.
19  *
20  * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
21  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
23  * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
24  * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
25  * OR MODIFICATIONS.
26  *
27  * $Id: ppp_comp.c,v 1.6 1996/09/14 05:19:18 paulus Exp $
28  */
29
30 /*
31  * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX.
32  */
33
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/errno.h>
37 #include <sys/stream.h>
38
39 #ifdef SVR4
40 #include <sys/conf.h>
41 #include <sys/cmn_err.h>
42 #include <sys/ddi.h>
43 #else
44 #include <sys/user.h>
45 #endif /* SVR4 */
46
47 #include <net/ppp_defs.h>
48 #include <net/pppio.h>
49 #include "ppp_mod.h"
50
51 #ifdef __osf__
52 #include <sys/mbuf.h>
53 #include <sys/protosw.h>
54 #endif
55
56 #include <netinet/in.h>
57 #include <netinet/in_systm.h>
58 #include <netinet/ip.h>
59 #include <net/vjcompress.h>
60
61 #define PACKETPTR       mblk_t *
62 #include <net/ppp-comp.h>
63
64 MOD_OPEN_DECL(ppp_comp_open);
65 MOD_CLOSE_DECL(ppp_comp_close);
66 static int ppp_comp_rput __P((queue_t *, mblk_t *));
67 static int ppp_comp_rsrv __P((queue_t *));
68 static int ppp_comp_wput __P((queue_t *, mblk_t *));
69 static int ppp_comp_wsrv __P((queue_t *));
70 static void ppp_comp_ccp __P((queue_t *, mblk_t *, int));
71 static int msg_byte __P((mblk_t *, unsigned int));
72
73 /* Extract byte i of message mp. */
74 #define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \
75                          msg_byte((mp), (i)))
76
77 /* Is this LCP packet one we have to transmit using LCP defaults? */
78 #define LCP_USE_DFLT(mp)        (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7)
79
80 #define PPP_COMP_ID 0xbadf
81 static struct module_info minfo = {
82     PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16384, 4096,
83 };
84
85 static struct qinit r_init = {
86     ppp_comp_rput, ppp_comp_rsrv, ppp_comp_open, ppp_comp_close,
87     NULL, &minfo, NULL
88 };
89
90 static struct qinit w_init = {
91     ppp_comp_wput, ppp_comp_wsrv, NULL, NULL, NULL, &minfo, NULL
92 };
93
94 #if defined(SVR4) && !defined(SOL2)
95 int pcmpdevflag = 0;
96 #define ppp_compinfo pcmpinfo
97 #endif
98 struct streamtab ppp_compinfo = {
99     &r_init, &w_init, NULL, NULL
100 };
101
102 int ppp_comp_count;             /* number of module instances in use */
103
104 #ifdef __osf__
105
106 static void ppp_comp_alloc __P((comp_state_t *));
107 typedef struct memreq {
108     unsigned char *comp;
109     void *mem;
110     int len;
111     int cmd;
112     int ret;
113 } memreq_t;
114
115 #endif
116
117 typedef struct comp_state {
118     int         flags;
119     int         mru;
120     int         mtu;
121     int         unit;
122     struct compressor *xcomp;
123     void        *xstate;
124     struct compressor *rcomp;
125     void        *rstate;
126     struct vjcompress vj_comp;
127     int         vj_last_ierrors;
128     struct pppstat stats;
129 #ifdef __osf__
130     memreq_t    memreq;
131     thread_t    thread;
132 #endif
133 } comp_state_t;
134
135
136 #ifdef __osf__
137 extern task_t first_task;
138 #endif
139
140 /* Bits in flags are as defined in pppio.h. */
141 #define CCP_ERR         (CCP_ERROR | CCP_FATALERROR)
142 #define LAST_MOD        0x1000000       /* no ppp modules below us */
143 #define DBGLOG          0x2000000       /* log debugging stuff */
144
145 #define MAX_IPHDR       128     /* max TCP/IP header size */
146 #define MAX_VJHDR       20      /* max VJ compressed header size (?) */
147
148 #undef MIN              /* just in case */
149 #define MIN(a, b)       ((a) < (b)? (a): (b))
150
151 /*
152  * List of compressors we know about.
153  */
154
155 #if DO_BSD_COMPRESS
156 extern struct compressor ppp_bsd_compress;
157 #endif
158 #if DO_DEFLATE
159 extern struct compressor ppp_deflate;
160 #endif
161
162 struct compressor *ppp_compressors[] = {
163 #if DO_BSD_COMPRESS
164     &ppp_bsd_compress,
165 #endif
166 #if DO_DEFLATE
167     &ppp_deflate,
168 #endif
169     NULL
170 };
171
172 /*
173  * STREAMS module entry points.
174  */
175 MOD_OPEN(ppp_comp_open)
176 {
177     comp_state_t *cp;
178 #ifdef __osf__
179     thread_t thread;
180 #endif
181
182     if (q->q_ptr == NULL) {
183         cp = (comp_state_t *) ALLOC_SLEEP(sizeof(comp_state_t));
184         if (cp == NULL)
185             OPEN_ERROR(ENOSR);
186         WR(q)->q_ptr = q->q_ptr = (caddr_t) cp;
187         bzero((caddr_t)cp, sizeof(comp_state_t));
188         cp->mru = PPP_MRU;
189         cp->mtu = PPP_MRU;
190         cp->xstate = NULL;
191         cp->rstate = NULL;
192         vj_compress_init(&cp->vj_comp, -1);
193         ++ppp_comp_count;
194         qprocson(q);
195 #ifdef __osf__
196         if (!(thread = kernel_thread_w_arg(first_task, ppp_comp_alloc, (void *)cp)))
197                 OPEN_ERROR(ENOSR);
198         cp->thread = thread;
199 #endif
200     }
201     return 0;
202 }
203
204 MOD_CLOSE(ppp_comp_close)
205 {
206     comp_state_t *cp;
207
208     qprocsoff(q);
209     cp = (comp_state_t *) q->q_ptr;
210     if (cp != NULL) {
211         if (cp->xstate != NULL)
212             (*cp->xcomp->comp_free)(cp->xstate);
213         if (cp->rstate != NULL)
214             (*cp->rcomp->decomp_free)(cp->rstate);
215 #ifdef __osf__
216         if (!cp->thread)
217             printf("ppp_comp_close: NULL thread!\n");
218         else
219             thread_deallocate(cp->thread);
220 #endif
221         FREE(cp, sizeof(comp_state_t));
222         q->q_ptr = NULL;
223         OTHERQ(q)->q_ptr = NULL;
224         --ppp_comp_count;
225     }
226     return 0;
227 }
228
229 #ifdef __osf__
230
231 /* thread for calling back to a compressor's memory allocator
232  * Needed for Digital UNIX since it's VM can't handle requests
233  * for large amounts of memory without blocking.  The thread
234  * provides a context in which we can call a memory allocator
235  * that may block.
236  */
237 static void
238 ppp_comp_alloc(comp_state_t *cp)
239 {
240     unsigned char *opt_data;
241     int len, cmd;
242     struct compressor *comp;
243     thread_t thread;
244
245 #if (MAJOR_VERSION <= 2)
246
247     /* In 2.x and earlier the argument gets passed
248      * in the thread structure itself.  Yuck.
249      */
250     thread = current_thread();
251     cp = thread->reply_port;
252     thread->reply_port = PORT_NULL;
253
254 #endif
255
256     for (;;) {
257         assert_wait((vm_offset_t)&cp->memreq.comp, TRUE);
258         thread_block();
259         opt_data = cp->memreq.comp;
260         len = cp->memreq.len;
261         cmd = cp->memreq.cmd;
262
263         if (cmd == PPPIO_XCOMP) {
264             comp = cp->xcomp;
265             cp->memreq.mem = (*comp->comp_alloc)(opt_data, len);
266         } else {
267             comp = cp->rcomp;
268             cp->memreq.mem = (*comp->decomp_alloc)(opt_data, len);
269         }
270         if (!cp->memreq.mem)
271             cp->memreq.ret = ENOSR;
272         else
273             bcopy(opt_data, cp->memreq.mem, len);
274
275         /* have to free thunk here, since there's
276          * no guarantee that the user will call the ioctl
277          * again if we've taken a long time to complete
278          */
279         FREE(opt_data, len);
280         cp->memreq.ret = 0;
281     }
282 }
283
284 #endif /* __osf__ */
285
286 /* here's the deal with memory allocation under Digital UNIX.
287  * Some other may also benefit from this...
288  * We can't ask for huge chunks of memory in a context where
289  * the caller can't be put to sleep (like, here.)  The alloc
290  * is likely to fail.  Instead we do this: the first time we
291  * get called, kick off a thread to do the allocation.  Return
292  * immediately to the caller with EAGAIN, as an indication that
293  * they should send down the ioctl again.  By the time the
294  * second call comes in it's likely that the memory allocation
295  * thread will have returned with the requested memory.  We will
296  * continue to return EAGAIN however until the thread has completed.
297  * When it has, we return zero (and the memory) if the allocator
298  * was successful and ENOSR otherwise.
299  *
300  * Callers of the RCOMP and XCOMP ioctls are encouraged (but not
301  * required) to loop for some number of iterations with a small
302  * delay in the loop body (for instance a 1/10-th second "sleep"
303  * via select.)
304  */
305 static int
306 ppp_comp_wput(q, mp)
307     queue_t *q;
308     mblk_t *mp;
309 {
310     struct iocblk *iop;
311     comp_state_t *cp;
312     int error, len, n;
313     int flags, mask;
314     mblk_t *np;
315     struct compressor **comp;
316     struct ppp_stats *psp;
317     struct ppp_comp_stats *csp;
318     unsigned char *opt_data;
319     int nxslots, nrslots;
320
321     cp = (comp_state_t *) q->q_ptr;
322     switch (mp->b_datap->db_type) {
323
324     case M_DATA:
325         putq(q, mp);
326         break;
327
328     case M_IOCTL:
329         iop = (struct iocblk *) mp->b_rptr;
330         error = EINVAL;
331         switch (iop->ioc_cmd) {
332
333         case PPPIO_CFLAGS:
334             /* set/get CCP state */
335             if (iop->ioc_count != 2 * sizeof(int))
336                 break;
337             flags = ((int *) mp->b_cont->b_rptr)[0];
338             mask = ((int *) mp->b_cont->b_rptr)[1];
339             cp->flags = (cp->flags & ~mask) | (flags & mask);
340             if ((mask & CCP_ISOPEN) && (flags & CCP_ISOPEN) == 0) {
341                 if (cp->xstate != NULL) {
342                     (*cp->xcomp->comp_free)(cp->xstate);
343                     cp->xstate = NULL;
344                 }
345                 if (cp->rstate != NULL) {
346                     (*cp->rcomp->decomp_free)(cp->rstate);
347                     cp->rstate = NULL;
348                 }
349                 cp->flags &= ~CCP_ISUP;
350             }
351             error = 0;
352             iop->ioc_count = sizeof(int);
353             ((int *) mp->b_cont->b_rptr)[0] = cp->flags;
354             mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int);
355             break;
356
357         case PPPIO_VJINIT:
358             /*
359              * Initialize VJ compressor/decompressor
360              */
361             if (iop->ioc_count != 2)
362                 break;
363             nxslots = mp->b_cont->b_rptr[0] + 1;
364             nrslots = mp->b_cont->b_rptr[1] + 1;
365             if (nxslots > MAX_STATES || nrslots > MAX_STATES)
366                 break;
367             vj_compress_init(&cp->vj_comp, nxslots);
368             cp->vj_last_ierrors = cp->stats.ppp_ierrors;
369             error = 0;
370             iop->ioc_count = 0;
371             break;
372
373         case PPPIO_XCOMP:
374         case PPPIO_RCOMP:
375             if (iop->ioc_count <= 0)
376                 break;
377             opt_data = mp->b_cont->b_rptr;
378             len = mp->b_cont->b_wptr - opt_data;
379             if (len > iop->ioc_count)
380                 len = iop->ioc_count;
381             if (opt_data[1] < 2 || opt_data[1] > len)
382                 break;
383             for (comp = ppp_compressors; *comp != NULL; ++comp)
384                 if ((*comp)->compress_proto == opt_data[0]) {
385                     /* here's the handler! */
386                     error = 0;
387                     if (iop->ioc_cmd == PPPIO_XCOMP) {
388
389                         /* A previous call may have fetched memory for a compressor
390                          * that's now being retired or reset.  Free it using it's
391                          * mechanism for freeing stuff.
392                          */
393                         if (cp->xstate != NULL) {
394                             (*cp->xcomp->comp_free)(cp->xstate);
395                             cp->xstate = NULL;
396                         }
397
398 #ifdef __osf__
399                         /* Account for an orpahned call to get memory.
400                          * Free that memory up and go on.
401                          *
402                          * The trick is that we need to be able to tell
403                          * the difference between an old call that kicked
404                          * off a thread where the memory was subsequently
405                          * orphaned, and the memory we're really interested
406                          * in.  The thread helps by stamping the memory it
407                          * allocated with the parameters for the compressor
408                          * it belongs to.  If the parameters match then this
409                          * is the memory we want (whether it was an actual
410                          * orphan or not we don't care.)  If the parameters
411                          * don't match then this is an orphan.
412                          *
413                          * Note that cp->memreq.ret is the synchronization
414                          * point: we set it to EAGAIN in this function, then
415                          * wait for the thread to set it to something other
416                          * than EAGAIN before we fool with the data structure
417                          * again.  Basically, if ret != EAGAIN then the thread
418                          * is working and it owns the memreq struture.
419                          */
420                         if (cp->memreq.ret == 0 && cp->memreq.mem != NULL &&
421                             bcmp(cp->memreq.mem, opt_data, len)) {
422                             (*cp->xcomp->comp_free)(cp->memreq.mem);
423                             cp->memreq.mem = 0;
424                         }
425
426                         /* First time through, prime the pump and kick
427                          * off the thread.  Subsequent times though, the thread
428                          * is either busy (ret == EAGAIN) or finsihed (mem != NULL)
429                          * || (ret != 0)
430                          */
431                         if (cp->memreq.ret == 0 && cp->memreq.mem == NULL) {
432                             cp->memreq.comp = ALLOC_NOSLEEP(len);
433                             if (!cp->memreq.comp) {
434                                 printf("gack! can't get memory for thunk\n");
435                                 return ENOSR;
436                             }
437                             bcopy(opt_data, cp->memreq.comp, len);
438                             cp->memreq.len = len;
439                             cp->memreq.cmd = PPPIO_XCOMP;
440                             cp->xcomp = *comp;
441                             cp->memreq.ret = EAGAIN;
442                             thread_wakeup((vm_offset_t)&cp->memreq.comp);
443                         }
444
445                         /* Collect results from the thread, and reset the
446                          * mechanism for the next attempt to allocate memory
447                          * If the thread isn't finished (ret == EAGAIN) then
448                          * don't reset and just return.
449                          */
450                         if ((error = cp->memreq.ret) != EAGAIN) {
451                             cp->xstate = cp->memreq.mem;
452                             cp->memreq.mem = 0;
453                             cp->memreq.ret = 0;
454                         }
455 #else
456                         cp->xcomp = *comp;
457                         cp->xstate = (*comp)->comp_alloc(opt_data, len);
458                         if (cp->xstate == NULL)
459                             error = ENOSR;
460 #endif
461                     } else {
462                         if (cp->rstate != NULL) {
463                             (*cp->rcomp->decomp_free)(cp->rstate);
464                             cp->rstate = NULL;
465                         }
466 #ifdef __osf__
467                         if (cp->memreq.ret == 0 && cp->memreq.mem != NULL &&
468                             bcmp(cp->memreq.mem, opt_data, len)) {
469                             (*cp->rcomp->comp_free)(cp->memreq.mem);
470                             cp->memreq.mem = 0;
471                         }
472                         if (cp->memreq.ret == 0 && cp->memreq.mem == NULL) {
473                             cp->memreq.comp = ALLOC_NOSLEEP(len);
474                             if (!cp->memreq.comp) {
475                                 printf("gack! can't get memory for thunk\n");
476                                 return ENOSR;
477                             }
478                             bcopy(opt_data, cp->memreq.comp, len);
479                             cp->memreq.len = len;
480                             cp->memreq.cmd = PPPIO_RCOMP;
481                             cp->rcomp = *comp;
482                             cp->memreq.ret = EAGAIN;
483                             thread_wakeup((vm_offset_t)&cp->memreq.comp);
484                         }
485                         if ((error = cp->memreq.ret) != EAGAIN) {
486                             cp->rstate = cp->memreq.mem;
487                             cp->memreq.mem = 0;
488                             cp->memreq.ret = 0;
489                         }
490 #else
491                         cp->rcomp = *comp;
492                         cp->rstate = (*comp)->decomp_alloc(opt_data, len);
493                         if (cp->rstate == NULL)
494                             error = ENOSR;
495 #endif
496                     }
497                     break;
498                 }
499             iop->ioc_count = 0;
500             break;
501
502         case PPPIO_GETSTAT:
503             if ((cp->flags & LAST_MOD) == 0) {
504                 error = -1;     /* let the ppp_ahdl module handle it */
505                 break;
506             }
507             np = allocb(sizeof(struct ppp_stats), BPRI_HI);
508             if (np == 0) {
509                 error = ENOSR;
510                 break;
511             }
512             if (mp->b_cont != 0)
513                 freemsg(mp->b_cont);
514             mp->b_cont = np;
515             psp = (struct ppp_stats *) np->b_wptr;
516             np->b_wptr += sizeof(struct ppp_stats);
517             iop->ioc_count = sizeof(struct ppp_stats);
518             psp->p = cp->stats;
519             psp->vj = cp->vj_comp.stats;
520             error = 0;
521             break;
522
523         case PPPIO_GETCSTAT:
524             np = allocb(sizeof(struct ppp_comp_stats), BPRI_HI);
525             if (np == 0) {
526                 error = ENOSR;
527                 break;
528             }
529             if (mp->b_cont != 0)
530                 freemsg(mp->b_cont);
531             mp->b_cont = np;
532             csp = (struct ppp_comp_stats *) np->b_wptr;
533             np->b_wptr += sizeof(struct ppp_comp_stats);
534             iop->ioc_count = sizeof(struct ppp_comp_stats);
535             bzero((caddr_t)csp, sizeof(struct ppp_comp_stats));
536             if (cp->xstate != 0)
537                 (*cp->xcomp->comp_stat)(cp->xstate, &csp->c);
538             if (cp->rstate != 0)
539                 (*cp->rcomp->decomp_stat)(cp->rstate, &csp->d);
540             error = 0;
541             break;
542
543         case PPPIO_DEBUG:
544             if (iop->ioc_count != sizeof(int))
545                 break;
546             n = *(int *)mp->b_cont->b_rptr;
547             if (n == PPPDBG_LOG + PPPDBG_COMP) {
548                 DPRINT1("ppp_comp%d: debug log enabled\n", cp->unit);
549                 cp->flags |= DBGLOG;
550                 error = 0;
551                 iop->ioc_count = 0;
552             } else {
553                 error = -1;
554             }
555             break;
556
557         case PPPIO_LASTMOD:
558             cp->flags |= LAST_MOD;
559             error = 0;
560             break;
561
562         default:
563             error = -1;
564             break;
565         }
566
567         if (error < 0)
568             putnext(q, mp);
569         else if (error == 0) {
570             mp->b_datap->db_type = M_IOCACK;
571             qreply(q, mp);
572         } else {
573             mp->b_datap->db_type = M_IOCNAK;
574             iop->ioc_error = error;
575             iop->ioc_count = 0;
576             qreply(q, mp);
577         }
578         break;
579
580     case M_CTL:
581         switch (*mp->b_rptr) {
582         case PPPCTL_MTU:
583             cp->mtu = ((unsigned short *)mp->b_rptr)[1];
584             break;
585         case PPPCTL_MRU:
586             cp->mru = ((unsigned short *)mp->b_rptr)[1];
587             break;
588         case PPPCTL_UNIT:
589             cp->unit = mp->b_rptr[1];
590             break;
591         }
592         putnext(q, mp);
593         break;
594
595     default:
596         putnext(q, mp);
597     }
598 }
599
600 static int
601 ppp_comp_wsrv(q)
602     queue_t *q;
603 {
604     mblk_t *mp, *cmp = NULL, *np;
605     comp_state_t *cp;
606     int len, proto, type, hlen, code;
607     struct ip *ip;
608     unsigned char *vjhdr, *dp;
609
610     cp = (comp_state_t *) q->q_ptr;
611     while ((mp = getq(q)) != 0) {
612         /* assert(mp->b_datap->db_type == M_DATA) */
613         if (!canputnext(q)) {
614             putbq(q, mp);
615             return;
616         }
617
618         /*
619          * First check the packet length and work out what the protocol is.
620          */
621         len = msgdsize(mp);
622         if (len < PPP_HDRLEN) {
623             DPRINT1("ppp_comp_wsrv: bogus short packet (%d)\n", len);
624             freemsg(mp);
625             cp->stats.ppp_oerrors++;
626             putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
627             continue;
628         }
629         proto = (MSG_BYTE(mp, 2) << 8) + MSG_BYTE(mp, 3);
630
631         /*
632          * Make sure we've got enough data in the first mblk
633          * and that we are its only user.
634          */
635         if (proto == PPP_CCP)
636             hlen = len;
637         else if (proto == PPP_IP)
638             hlen = PPP_HDRLEN + MAX_IPHDR;
639         else
640             hlen = PPP_HDRLEN;
641         if (hlen > len)
642             hlen = len;
643         if (mp->b_wptr < mp->b_rptr + hlen || mp->b_datap->db_ref > 1) {
644             PULLUP(mp, hlen);
645             if (mp == 0) {
646                 DPRINT1("ppp_comp_wsrv: pullup failed (%d)\n", hlen);
647                 cp->stats.ppp_oerrors++;
648                 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR);
649                 continue;
650             }
651         }
652
653         /*
654          * Do VJ compression if requested.
655          */
656         if (proto == PPP_IP && (cp->flags & COMP_VJC)) {
657             ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN);
658             if (ip->ip_p == IPPROTO_TCP) {
659                 type = vj_compress_tcp(ip, len - PPP_HDRLEN, &cp->vj_comp,
660                                        (cp->flags & COMP_VJCCID), &vjhdr);
661                 switch (type) {
662                 case TYPE_UNCOMPRESSED_TCP:
663                     mp->b_rptr[3] = proto = PPP_VJC_UNCOMP;
664                     break;
665                 case TYPE_COMPRESSED_TCP:
666                     dp = vjhdr - PPP_HDRLEN;
667                     dp[1] = mp->b_rptr[1]; /* copy control field */
668                     dp[0] = mp->b_rptr[0]; /* copy address field */
669                     dp[2] = 0;             /* set protocol field */
670                     dp[3] = proto = PPP_VJC_COMP;
671                     mp->b_rptr = dp;
672                     break;
673                 }
674             }
675         }
676
677         /*
678          * Do packet compression if enabled.
679          */
680         if (proto == PPP_CCP)
681             ppp_comp_ccp(q, mp, 0);
682         else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN)
683                  && cp->xstate != NULL) {
684             len = msgdsize(mp);
685             (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len,
686                                    (cp->flags & CCP_ISUP? cp->mtu: 0));
687             if (cmp != NULL) {
688                 freemsg(mp);
689                 mp = cmp;
690             }
691         }
692
693         /*
694          * Do address/control and protocol compression if enabled.
695          */
696         if ((cp->flags & COMP_AC)
697             && !(proto == PPP_LCP && LCP_USE_DFLT(mp))) {
698             mp->b_rptr += 2;    /* drop the address & ctrl fields */
699             if (proto < 0x100 && (cp->flags & COMP_PROT))
700                 ++mp->b_rptr;   /* drop the high protocol byte */
701         } else if (proto < 0x100 && (cp->flags & COMP_PROT)) {
702             /* shuffle up the address & ctrl fields */
703             mp->b_rptr[2] = mp->b_rptr[1];
704             mp->b_rptr[1] = mp->b_rptr[0];
705             ++mp->b_rptr;
706         }
707
708         cp->stats.ppp_opackets++;
709         cp->stats.ppp_obytes += msgdsize(mp);
710         putnext(q, mp);
711     }
712 }
713
714 static int
715 ppp_comp_rput(q, mp)
716     queue_t *q;
717     mblk_t *mp;
718 {
719     comp_state_t *cp;
720     struct iocblk *iop;
721     struct ppp_stats *psp;
722
723     cp = (comp_state_t *) q->q_ptr;
724     switch (mp->b_datap->db_type) {
725
726     case M_DATA:
727         putq(q, mp);
728         break;
729
730     case M_IOCACK:
731         iop = (struct iocblk *) mp->b_rptr;
732         switch (iop->ioc_cmd) {
733         case PPPIO_GETSTAT:
734             /*
735              * Catch this on the way back from the ppp_ahdl module
736              * so we can fill in the VJ stats.
737              */
738             if (mp->b_cont == 0 || iop->ioc_count != sizeof(struct ppp_stats))
739                 break;
740             psp = (struct ppp_stats *) mp->b_cont->b_rptr;
741             psp->vj = cp->vj_comp.stats;
742             break;
743         }
744         putnext(q, mp);
745         break;
746
747     case M_CTL:
748         switch (mp->b_rptr[0]) {
749         case PPPCTL_IERROR:
750             ++cp->stats.ppp_ierrors;
751             break;
752         case PPPCTL_OERROR:
753             ++cp->stats.ppp_oerrors;
754             break;
755         }
756         putnext(q, mp);
757         break;
758
759     default:
760         putnext(q, mp);
761     }
762 }
763
764 static int
765 ppp_comp_rsrv(q)
766     queue_t *q;
767 {
768     int proto, rv, i;
769     mblk_t *mp, *dmp = NULL, *np;
770     uchar_t *dp, *iphdr;
771     comp_state_t *cp;
772     int len, hlen, vjlen;
773     u_int iphlen;
774
775     cp = (comp_state_t *) q->q_ptr;
776     while ((mp = getq(q)) != 0) {
777         /* assert(mp->b_datap->db_type == M_DATA) */
778         if (!canputnext(q)) {
779             putbq(q, mp);
780             return;
781         }
782
783         len = msgdsize(mp);
784         cp->stats.ppp_ibytes += len;
785         cp->stats.ppp_ipackets++;
786
787         /*
788          * First work out the protocol and where the PPP header ends.
789          */
790         i = 0;
791         proto = MSG_BYTE(mp, 0);
792         if (proto == PPP_ALLSTATIONS) {
793             i = 2;
794             proto = MSG_BYTE(mp, 2);
795         }
796         if ((proto & 1) == 0) {
797             ++i;
798             proto = (proto << 8) + MSG_BYTE(mp, i);
799         }
800         hlen = i + 1;
801
802         /*
803          * Now reconstruct a complete, contiguous PPP header at the
804          * start of the packet.
805          */
806         if (hlen < ((cp->flags & DECOMP_AC)? 0: 2)
807                    + ((cp->flags & DECOMP_PROT)? 1: 2)) {
808             /* count these? */
809             goto bad;
810         }
811         if (mp->b_rptr + hlen > mp->b_wptr) {
812             adjmsg(mp, hlen);   /* XXX check this call */
813             hlen = 0;
814         }
815         if (hlen != PPP_HDRLEN) {
816             /*
817              * We need to put some bytes on the front of the packet
818              * to make a full-length PPP header.
819              * If we can put them in *mp, we do, otherwise we
820              * tack another mblk on the front.
821              * XXX we really shouldn't need to carry around
822              * the address and control at this stage.
823              */
824             dp = mp->b_rptr + hlen - PPP_HDRLEN;
825             if (dp < mp->b_datap->db_base || mp->b_datap->db_ref > 1) {
826                 np = allocb(PPP_HDRLEN, BPRI_MED);
827                 if (np == 0)
828                     goto bad;
829                 np->b_cont = mp;
830                 mp->b_rptr += hlen;
831                 mp = np;
832                 dp = mp->b_wptr;
833                 mp->b_wptr += PPP_HDRLEN;
834             } else
835                 mp->b_rptr = dp;
836
837             dp[0] = PPP_ALLSTATIONS;
838             dp[1] = PPP_UI;
839             dp[2] = proto >> 8;
840             dp[3] = proto;
841         }
842
843         /*
844          * Now see if we have a compressed packet to decompress,
845          * or a CCP packet to take notice of.
846          */
847         proto = PPP_PROTOCOL(mp->b_rptr);
848         if (proto == PPP_CCP) {
849             len = msgdsize(mp);
850             if (mp->b_wptr < mp->b_rptr + len) {
851                 PULLUP(mp, len);
852                 if (mp == 0)
853                     goto bad;
854             }
855             ppp_comp_ccp(q, mp, 1);
856         } else if (proto == PPP_COMP) {
857             if ((cp->flags & CCP_ISUP)
858                 && (cp->flags & CCP_DECOMP_RUN) && cp->rstate
859                 && (cp->flags & CCP_ERR) == 0) {
860                 rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp);
861                 switch (rv) {
862                 case DECOMP_OK:
863                     freemsg(mp);
864                     mp = dmp;
865                     if (mp == NULL) {
866                         /* no error, but no packet returned either. */
867                         continue;
868                     }
869                     break;
870                 case DECOMP_ERROR:
871                     cp->flags |= CCP_ERROR;
872                     ++cp->stats.ppp_ierrors;
873                     putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
874                     break;
875                 case DECOMP_FATALERROR:
876                     cp->flags |= CCP_FATALERROR;
877                     ++cp->stats.ppp_ierrors;
878                     putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
879                     break;
880                 }
881             }
882         } else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
883             (*cp->rcomp->incomp)(cp->rstate, mp);
884         }
885
886         /*
887          * Now do VJ decompression.
888          */
889         proto = PPP_PROTOCOL(mp->b_rptr);
890         if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) {
891             len = msgdsize(mp) - PPP_HDRLEN;
892             if ((cp->flags & DECOMP_VJC) == 0 || len <= 0)
893                 goto bad;
894
895             /*
896              * Advance past the ppp header.
897              * Here we assume that the whole PPP header is in the first mblk.
898              */
899             np = mp;
900             dp = np->b_rptr + PPP_HDRLEN;
901             if (dp >= mp->b_wptr) {
902                 np = np->b_cont;
903                 dp = np->b_rptr;
904             }
905
906             /*
907              * Make sure we have sufficient contiguous data at this point.
908              */
909             hlen = (proto == PPP_VJC_COMP)? MAX_VJHDR: MAX_IPHDR;
910             if (hlen > len)
911                 hlen = len;
912             if (np->b_wptr < dp + hlen || np->b_datap->db_ref > 1) {
913                 PULLUP(mp, hlen + PPP_HDRLEN);
914                 if (mp == 0)
915                     goto bad;
916                 np = mp;
917                 dp = np->b_rptr + PPP_HDRLEN;
918             }
919
920             if (proto == PPP_VJC_COMP) {
921                 /*
922                  * Decompress VJ-compressed packet.
923                  * First reset compressor if an input error has occurred.
924                  */
925                 if (cp->stats.ppp_ierrors != cp->vj_last_ierrors) {
926                     if (cp->flags & DBGLOG)
927                         DPRINT1("ppp%d: resetting VJ\n", cp->unit);
928                     vj_uncompress_err(&cp->vj_comp);
929                     cp->vj_last_ierrors = cp->stats.ppp_ierrors;
930                 }
931
932                 vjlen = vj_uncompress_tcp(dp, np->b_wptr - dp, len,
933                                           &cp->vj_comp, &iphdr, &iphlen);
934                 if (vjlen < 0) {
935                     if (cp->flags & DBGLOG)
936                         DPRINT2("ppp%d: vj_uncomp_tcp failed, pkt len %d\n",
937                                 cp->unit, len);
938                     ++cp->vj_last_ierrors;  /* so we don't reset next time */
939                     goto bad;
940                 }
941
942                 /* drop ppp and vj headers off */
943                 if (mp != np) {
944                     freeb(mp);
945                     mp = np;
946                 }
947                 mp->b_rptr = dp + vjlen;
948
949                 /* allocate a new mblk for the ppp and ip headers */
950                 if ((np = allocb(iphlen + PPP_HDRLEN + 4, BPRI_MED)) == 0)
951                     goto bad;
952                 dp = np->b_rptr;        /* prepend mblk with TCP/IP hdr */
953                 dp[0] = PPP_ALLSTATIONS; /* reconstruct PPP header */
954                 dp[1] = PPP_UI;
955                 dp[2] = PPP_IP >> 8;
956                 dp[3] = PPP_IP;
957                 bcopy((caddr_t)iphdr, (caddr_t)dp + PPP_HDRLEN, iphlen);
958                 np->b_wptr = dp + iphlen + PPP_HDRLEN;
959                 np->b_cont = mp;
960
961                 /* XXX there seems to be a bug which causes panics in strread
962                    if we make an mbuf with only the IP header in it :-( */
963                 if (mp->b_wptr - mp->b_rptr > 4) {
964                     bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, 4);
965                     mp->b_rptr += 4;
966                     np->b_wptr += 4;
967                 } else {
968                     bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr,
969                           mp->b_wptr - mp->b_rptr);
970                     np->b_wptr += mp->b_wptr - mp->b_rptr;
971                     np->b_cont = mp->b_cont;
972                     freeb(mp);
973                 }
974
975                 mp = np;
976
977             } else {
978                 /*
979                  * "Decompress" a VJ-uncompressed packet.
980                  */
981                 cp->vj_last_ierrors = cp->stats.ppp_ierrors;
982                 if (!vj_uncompress_uncomp(dp, hlen, &cp->vj_comp)) {
983                     if (cp->flags & DBGLOG)
984                         DPRINT2("ppp%d: vj_uncomp_uncomp failed, pkt len %d\n",
985                                 cp->unit, len);
986                     ++cp->vj_last_ierrors;  /* don't need to reset next time */
987                     goto bad;
988                 }
989                 mp->b_rptr[3] = PPP_IP; /* fix up the PPP protocol field */
990             }
991         }
992
993         putnext(q, mp);
994         continue;
995
996     bad:
997         if (mp != 0)
998             freemsg(mp);
999         cp->stats.ppp_ierrors++;
1000         putctl1(q->q_next, M_CTL, PPPCTL_IERROR);
1001     }
1002 }
1003
1004 /*
1005  * Handle a CCP packet being sent or received.
1006  * Here all the data in the packet is in a single mbuf.
1007  */
1008 static void
1009 ppp_comp_ccp(q, mp, rcvd)
1010     queue_t *q;
1011     mblk_t *mp;
1012     int rcvd;
1013 {
1014     int len, clen;
1015     comp_state_t *cp;
1016     unsigned char *dp;
1017
1018     len = msgdsize(mp);
1019     if (len < PPP_HDRLEN + CCP_HDRLEN)
1020         return;
1021
1022     cp = (comp_state_t *) q->q_ptr;
1023     dp = mp->b_rptr + PPP_HDRLEN;
1024     len -= PPP_HDRLEN;
1025     clen = CCP_LENGTH(dp);
1026     if (clen > len)
1027         return;
1028
1029     switch (CCP_CODE(dp)) {
1030     case CCP_CONFREQ:
1031     case CCP_TERMREQ:
1032     case CCP_TERMACK:
1033         cp->flags &= ~CCP_ISUP;
1034         break;
1035
1036     case CCP_CONFACK:
1037         if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN
1038             && clen >= CCP_HDRLEN + CCP_OPT_MINLEN
1039             && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) {
1040             if (!rcvd) {
1041                 if (cp->xstate != NULL
1042                     && (*cp->xcomp->comp_init)
1043                         (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
1044                          cp->unit, 0, ((cp->flags & DBGLOG) != 0)))
1045                     cp->flags |= CCP_COMP_RUN;
1046             } else {
1047                 if (cp->rstate != NULL
1048                     && (*cp->rcomp->decomp_init)
1049                         (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
1050                          cp->unit, 0, cp->mru, ((cp->flags & DBGLOG) != 0)))
1051                     cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN;
1052             }
1053         }
1054         break;
1055
1056     case CCP_RESETACK:
1057         if (cp->flags & CCP_ISUP) {
1058             if (!rcvd) {
1059                 if (cp->xstate && (cp->flags & CCP_COMP_RUN))
1060                     (*cp->xcomp->comp_reset)(cp->xstate);
1061             } else {
1062                 if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
1063                     (*cp->rcomp->decomp_reset)(cp->rstate);
1064                     cp->flags &= ~CCP_ERROR;
1065                 }
1066             }
1067         }
1068         break;
1069     }
1070 }
1071
1072 #if 0
1073 dump_msg(mp)
1074     mblk_t *mp;
1075 {
1076     dblk_t *db;
1077
1078     while (mp != 0) {
1079         db = mp->b_datap;
1080         DPRINT2("mp=%x cont=%x ", mp, mp->b_cont);
1081         DPRINT3("rptr=%x wptr=%x datap=%x\n", mp->b_rptr, mp->b_wptr, db);
1082         DPRINT2("  base=%x lim=%x", db->db_base, db->db_lim);
1083         DPRINT2(" ref=%d type=%d\n", db->db_ref, db->db_type);
1084         mp = mp->b_cont;
1085     }
1086 }
1087 #endif
1088
1089 static int
1090 msg_byte(mp, i)
1091     mblk_t *mp;
1092     unsigned int i;
1093 {
1094     while (mp != 0 && i >= mp->b_wptr - mp->b_rptr)
1095         mp = mp->b_cont;
1096     if (mp == 0)
1097         return -1;
1098     return mp->b_rptr[i];
1099 }