2 * ppp_comp.c - STREAMS module for kernel-level compression and CCP support.
4 * Copyright (c) 1994 The Australian National University.
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
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 HAVE BEEN ADVISED OF THE POSSIBILITY
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,
27 * $Id: ppp_comp.c,v 1.2 1995/05/19 02:18:11 paulus Exp $
31 * This file is used under Solaris 2.
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/errno.h>
37 #include <sys/stream.h>
38 #include <sys/modctl.h>
42 #include <sys/sunddi.h>
43 #include <sys/cmn_err.h>
44 #include <net/ppp_defs.h>
45 #include <net/pppio.h>
46 #include <netinet/in.h>
47 #include <netinet/in_systm.h>
48 #include <netinet/ip.h>
49 #include <net/vjcompress.h>
51 #define ALLOCATE(n) kmem_alloc((n), KM_NOSLEEP)
52 #define FREE(p, n) kmem_free((p), (n))
54 #define PACKETPTR mblk_t *
55 #include <net/ppp-comp.h>
57 static int ppp_comp_open __P((queue_t *, dev_t *, int, int, cred_t *));
58 static int ppp_comp_close __P((queue_t *, int, cred_t *));
59 static int ppp_comp_rput __P((queue_t *, mblk_t *));
60 static int ppp_comp_rsrv __P((queue_t *));
61 static int ppp_comp_wput __P((queue_t *, mblk_t *));
62 static int ppp_comp_wsrv __P((queue_t *));
63 static void ppp_comp_ccp __P((queue_t *, mblk_t *, int));
65 static struct module_info minfo = {
66 0xbadf, "ppp_comp", 0, INFPSZ, 16384, 4096,
69 static struct qinit r_init = {
70 ppp_comp_rput, ppp_comp_rsrv, ppp_comp_open, ppp_comp_close,
74 static struct qinit w_init = {
75 ppp_comp_wput, ppp_comp_wsrv, NULL, NULL, NULL, &minfo, NULL
78 static struct streamtab ppp_compinfo = {
79 &r_init, &w_init, NULL, NULL
82 static struct fmodsw fsw = {
85 D_NEW | D_MP | D_MTQPAIR
88 extern struct mod_ops mod_strmodops;
90 static struct modlstrmod modlstrmod = {
92 "PPP compression module",
96 static struct modlinkage modlinkage = {
102 struct ppp_comp_state {
108 struct compressor *xcomp;
110 struct compressor *rcomp;
112 struct vjcompress vj_comp;
116 /* Bits in flags are as defined in pppio.h. */
117 #define CCP_ERR (CCP_ERROR | CCP_FATALERROR)
119 #define MAX_IPHDR 128 /* max TCP/IP header size */
120 #define MAX_VJHDR 20 /* max VJ compressed header size (?) */
123 * List of compressors we know about.
126 extern struct compressor ppp_bsd_compress;
128 struct compressor *ppp_compressors[] = {
136 * Entry points for modloading.
141 return mod_install(&modlinkage);
147 return mod_remove(&modlinkage);
154 return mod_info(&modlinkage, mip);
158 * STREAMS module entry points.
161 ppp_comp_open(q, devp, flag, sflag, credp)
167 struct ppp_comp_state *cp;
169 if (q->q_ptr == NULL) {
170 cp = (struct ppp_comp_state *) ALLOCATE(sizeof(struct ppp_comp_state));
173 WR(q)->q_ptr = q->q_ptr = cp;
174 bzero((caddr_t)cp, sizeof(struct ppp_comp_state));
179 vj_compress_init(&cp->vj_comp, -1);
186 ppp_comp_close(q, flag, credp)
191 struct ppp_comp_state *cp;
194 cp = (struct ppp_comp_state *) q->q_ptr;
196 if (cp->xstate != NULL)
197 (*cp->xcomp->comp_free)(cp->xstate);
198 if (cp->rstate != NULL)
199 (*cp->rcomp->decomp_free)(cp->rstate);
200 FREE(cp, sizeof(struct ppp_comp_state));
202 OTHERQ(q)->q_ptr = NULL;
213 struct ppp_comp_state *cp;
216 struct compressor **comp;
217 struct ppp_comp_stats *pcp;
218 unsigned char *opt_data;
219 int nxslots, nrslots;
221 cp = (struct ppp_comp_state *) q->q_ptr;
222 switch (mp->b_datap->db_type) {
229 iop = (struct iocblk *) mp->b_rptr;
231 switch (iop->ioc_cmd) {
234 /* set/get CCP state */
235 if (iop->ioc_count != 2 * sizeof(int))
237 flags = ((int *) mp->b_cont->b_rptr)[0];
238 mask = ((int *) mp->b_cont->b_rptr)[1];
239 cp->flags = (cp->flags & ~mask) | (flags & mask);
240 if ((mask & CCP_ISOPEN) && (flags & CCP_ISOPEN) == 0) {
241 if (cp->xstate != NULL) {
242 (*cp->xcomp->comp_free)(cp->xstate);
245 if (cp->rstate != NULL) {
246 (*cp->rcomp->decomp_free)(cp->rstate);
249 cp->flags &= ~CCP_ISUP;
252 iop->ioc_count = sizeof(int);
253 ((int *) mp->b_cont->b_rptr)[0] = cp->flags;
254 mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int);
259 * Initialize VJ compressor/decompressor
261 if (iop->ioc_count != 2)
263 nxslots = mp->b_cont->b_rptr[0] + 1;
264 nrslots = mp->b_cont->b_rptr[1] + 1;
265 if (nxslots > MAX_STATES || nrslots > MAX_STATES)
267 vj_compress_init(&cp->vj_comp, nxslots);
268 cp->vj_last_ierrors = cp->ierrors;
275 if (iop->ioc_count <= 0)
277 opt_data = mp->b_cont->b_rptr;
278 len = mp->b_cont->b_wptr - opt_data;
279 if (len > iop->ioc_count)
280 len = iop->ioc_count;
281 if (opt_data[1] < 2 || opt_data[1] > len)
283 for (comp = ppp_compressors; *comp != NULL; ++comp)
284 if ((*comp)->compress_proto == opt_data[0]) {
285 /* here's the handler! */
287 if (iop->ioc_cmd == PPPIO_XCOMP) {
288 if (cp->xstate != NULL)
289 (*cp->xcomp->comp_free)(cp->xstate);
291 cp->xstate = (*comp)->comp_alloc(opt_data, len);
292 if (cp->xstate == NULL)
295 if (cp->rstate != NULL)
296 (*cp->rcomp->decomp_free)(cp->rstate);
298 cp->rstate = (*comp)->decomp_alloc(opt_data, len);
299 if (cp->rstate == NULL)
308 /* remember this value */
309 if (iop->ioc_count == sizeof(int)) {
310 cp->mru = *(int *) mp->b_cont->b_rptr;
322 else if (error == 0) {
323 mp->b_datap->db_type = M_IOCACK;
326 mp->b_datap->db_type = M_IOCNAK;
327 iop->ioc_error = error;
334 switch (*mp->b_rptr) {
336 cp->mtu = ((unsigned short *)mp->b_rptr)[1];
339 cp->mru = ((unsigned short *)mp->b_rptr)[1];
342 cp->unit = mp->b_rptr[1];
358 struct ppp_comp_state *cp;
359 int len, proto, type;
361 unsigned char *vjhdr, *dp;
363 cp = (struct ppp_comp_state *) q->q_ptr;
364 while ((mp = getq(q)) != 0) {
365 /* assert(mp->b_datap->db_type == M_DATA) */
366 if (!canputnext(q)) {
371 /* first find out what the protocol is */
372 if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN
373 && !pullupmsg(mp, PPP_HDRLEN)) {
374 freemsg(mp); /* give up on it */
377 proto = PPP_PROTOCOL(mp->b_rptr);
380 * Do VJ compression if requested.
382 if (proto == PPP_IP && (cp->flags & COMP_VJC)) {
384 if (len > MAX_IPHDR + PPP_HDRLEN)
385 len = MAX_IPHDR + PPP_HDRLEN;
386 if (mp->b_wptr - mp->b_rptr >= len || pullupmsg(mp, len)) {
387 ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN);
388 if (ip->ip_p == IPPROTO_TCP) {
389 type = vj_compress_tcp(ip, len - PPP_HDRLEN,
390 &cp->vj_comp, (cp->flags & COMP_VJCCID),
393 case TYPE_UNCOMPRESSED_TCP:
394 mp->b_rptr[3] = proto = PPP_VJC_UNCOMP;
396 case TYPE_COMPRESSED_TCP:
397 dp = vjhdr - PPP_HDRLEN;
398 dp[1] = mp->b_rptr[1]; /* copy control field */
399 dp[0] = mp->b_rptr[0]; /* copy address field */
400 dp[2] = 0; /* set protocol field */
401 dp[3] = proto = PPP_VJC_COMP;
410 * Do packet compression if enabled.
412 if (proto == PPP_CCP)
413 ppp_comp_ccp(q, mp, 0);
414 else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN)
415 && cp->xstate != NULL) {
417 (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len,
418 (cp->flags & CCP_ISUP? cp->mtu: 0));
426 * Do address/control and protocol compression if enabled.
428 if (proto != PPP_LCP && (cp->flags & COMP_AC)) {
429 mp->b_rptr += 2; /* drop the address & ctrl fields */
430 if (proto < 0x100 && (cp->flags & COMP_PROT))
431 ++mp->b_rptr; /* drop the high protocol byte */
432 } else if (proto < 0x100 && (cp->flags & COMP_PROT)) {
433 /* shuffle up the address & ctrl fields */
434 mp->b_rptr[2] = mp->b_rptr[1];
435 mp->b_rptr[1] = mp->b_rptr[0];
448 struct ppp_comp_state *cp;
450 cp = (struct ppp_comp_state *) q->q_ptr;
451 switch (mp->b_datap->db_type) {
458 switch (mp->b_rptr[0]) {
476 mblk_t *mp, *dmp, *np;
477 unsigned char *dp, *iphdr;
478 struct ppp_comp_state *cp;
479 int len, hlen, vjlen, iphlen;
482 cp = (struct ppp_comp_state *) q->q_ptr;
483 oldierrors = cp->ierrors;
484 while ((mp = getq(q)) != 0) {
485 /* assert(mp->b_datap->db_type == M_DATA) */
486 if (!canputnext(q)) {
492 * First do address/control and protocol "decompression".
495 if (len > PPP_HDRLEN)
497 if (mp->b_wptr - mp->b_rptr < len && !pullupmsg(mp, len)) {
503 if (PPP_ADDRESS(dp) == PPP_ALLSTATIONS && PPP_CONTROL(dp) == PPP_UI)
504 dp += 2; /* skip address/control */
506 if ((dp[0] & 1) == 0)
507 proto = *dp++ << 8; /* grab high byte of protocol */
508 proto += *dp++; /* grab low byte of protocol */
509 if (dp > mp->b_wptr) {
510 ++cp->ierrors; /* short/bogus packet */
514 if ((dp -= PPP_HDRLEN) < mp->b_datap->db_base) {
515 /* yucko, need a new message block */
517 np = allocb(PPP_HDRLEN, BPRI_MED);
526 mp->b_wptr = dp + PPP_HDRLEN;
529 dp[0] = PPP_ALLSTATIONS;
535 * Now see if we have a compressed packet to decompress,
536 * or a CCP packet to take notice of.
538 proto = PPP_PROTOCOL(mp->b_rptr);
539 if (proto == PPP_CCP)
540 ppp_comp_ccp(q, mp, 1);
541 else if (proto == PPP_COMP) {
542 if ((cp->flags & CCP_ISUP)
543 && (cp->flags & CCP_DECOMP_RUN) && cp->rstate
544 && (cp->flags & CCP_ERR) == 0) {
545 rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp);
552 /* no error, but no packet returned */
556 cp->flags |= CCP_ERROR;
559 case DECOMP_FATALERROR:
560 cp->flags |= CCP_FATALERROR;
566 } else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
567 (*cp->rcomp->incomp)(cp->rstate, mp);
571 * Now do VJ decompression.
573 proto = PPP_PROTOCOL(mp->b_rptr);
574 if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) {
575 if ((cp->flags & DECOMP_VJC) == 0) {
576 ++cp->ierrors; /* ? */
580 if (cp->ierrors != cp->vj_last_ierrors) {
581 vj_uncompress_err(&cp->vj_comp);
582 cp->vj_last_ierrors = cp->ierrors;
585 hlen = (proto == PPP_VJC_COMP? MAX_VJHDR: MAX_IPHDR) + PPP_HDRLEN;
588 if (mp->b_wptr - mp->b_rptr < hlen && !pullupmsg(mp, hlen)) {
594 if (proto == PPP_VJC_COMP) {
595 mp->b_rptr += PPP_HDRLEN;
596 vjlen = vj_uncompress_tcp(mp->b_rptr, mp->b_wptr - mp->b_rptr,
597 len - PPP_HDRLEN, &cp->vj_comp,
600 || (np = allocb(iphlen + PPP_HDRLEN + 4, BPRI_MED)) == 0) {
606 mp->b_rptr += vjlen; /* drop off VJ header */
607 dp = np->b_rptr; /* prepend mblk with TCP/IP hdr */
608 dp[0] = PPP_ALLSTATIONS; /* reconstruct PPP header */
612 bcopy(iphdr, dp + PPP_HDRLEN, iphlen);
613 np->b_wptr = dp + iphlen + PPP_HDRLEN;
616 /* XXX there seems to be a bug which causes panics in strread
617 if we make an mbuf with only the IP header in it :-( */
618 if (mp->b_wptr - mp->b_rptr > 4) {
619 bcopy(mp->b_rptr, np->b_wptr, 4);
623 bcopy(mp->b_rptr, np->b_wptr, mp->b_wptr - mp->b_rptr);
624 np->b_wptr += mp->b_wptr - mp->b_rptr;
625 np->b_cont = mp->b_cont;
632 if (!vj_uncompress_uncomp(mp->b_rptr + PPP_HDRLEN,
638 mp->b_rptr[3] = PPP_IP; /* fix up the PPP protocol field */
645 if (cp->ierrors != oldierrors)
646 cmn_err(CE_CONT, "ppp_comp_rsrv ierrors now %d\n", cp->ierrors);
651 * Handle a CCP packet being sent or received.
654 ppp_comp_ccp(q, mp, rcvd)
660 struct ppp_comp_state *cp;
664 if (len < PPP_HDRLEN + CCP_HDRLEN || !pullupmsg(mp, len))
666 cp = (struct ppp_comp_state *) q->q_ptr;
667 dp = mp->b_rptr + PPP_HDRLEN;
669 clen = CCP_LENGTH(dp);
673 switch (CCP_CODE(dp)) {
677 cp->flags &= ~CCP_ISUP;
681 if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN
682 && clen >= CCP_HDRLEN + CCP_OPT_MINLEN
683 && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) {
685 if (cp->xstate != NULL
686 && (*cp->xcomp->comp_init)
687 (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
689 cp->flags |= CCP_COMP_RUN;
691 if (cp->rstate != NULL
692 && (*cp->rcomp->decomp_init)
693 (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
694 cp->unit, 0, cp->mru, 0))
695 cp->flags = (cp->flags & ~CCP_ERR)
702 if (cp->flags & CCP_ISUP) {
704 if (cp->xstate && (cp->flags & CCP_COMP_RUN))
705 (*cp->xcomp->comp_reset)(cp->xstate);
707 if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
708 (*cp->rcomp->decomp_reset)(cp->rstate);
709 cp->flags &= ~CCP_ERROR;
726 cmn_err(CE_CONT, "mp=%x cont=%x rptr=%x wptr=%x datap=%x\n",
727 mp, mp->b_cont, mp->b_rptr, mp->b_wptr, db);
728 cmn_err(CE_CONT, " base=%x lim=%x ref=%d type=%d struioflag=%d\n",
729 db->db_base, db->db_lim, db->db_ref, db->db_type,