]> git.ozlabs.org Git - ppp.git/blob - svr4/ppp_comp.c
254a9358890fb00279cc2448ef62199786ecc8d1
[ppp.git] / svr4 / ppp_comp.c
1 /*
2  * ppp_comp.c - STREAMS module for kernel-level 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 HAVE 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.1 1995/05/10 01:38:47 paulus Exp $
28  */
29
30 /*
31  * This file is used under SunOS 4.x, and OSF/1 on DEC Alpha.
32  *
33  * Beware that under OSF/1, the ioctl constants (SIOC*) end up
34  * as 64-bit (long) values, so an ioctl constant should be cast to
35  * int (32 bits) before being compared with the ioc_cmd field of
36  * an iocblk structure.
37  */
38
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/stream.h>
43 #include <sys/modctl.h>
44 #include <sys/conf.h>
45 #include <sys/kmem.h>
46 #include <sys/ddi.h>
47 #include <sys/sunddi.h>
48 #include <net/ppp_defs.h>
49 #include <net/ppp_str.h>
50
51 #define ALLOCATE(n)     kmem_zalloc((n), KM_NOSLEEP)
52 #define FREE(p, n)      kmem_free((p), (n))
53
54 #define PACKETPTR       mblk_t *
55 #include <net/ppp-comp.h>
56
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_wput __P((queue_t *, mblk_t *));
61 static void ppp_comp_ccp __P((queue_t *, mblk_t *, int));
62
63 static struct module_info minfo = {
64     0xbadf, "ppp_compress", 0, INFPSZ, 16384, 4096,
65 };
66
67 static struct qinit r_init = {
68     ppp_comp_rput, NULL, ppp_comp_open, ppp_comp_close,
69     NULL, &minfo, NULL
70 };
71
72 static struct qinit w_init = {
73     ppp_comp_wput, NULL, NULL, NULL, NULL, &minfo, NULL
74 };
75
76 static struct streamtab ppp_compinfo = {
77     &r_init, &w_init, NULL, NULL
78 };
79
80 static struct fmodsw fsw = {
81     "ppp_comp",
82     &ppp_compinfo,
83     D_NEW | D_MP | D_MTQPAIR
84 };
85
86 extern struct mod_ops mod_strmodops;
87
88 static struct modlstrmod modlstrmod = {
89     &mod_strmodops,
90     "PPP compression module",
91     &fsw
92 };
93
94 static struct modlinkage modlinkage = {
95     MODREV_1,
96     (void *) &modlstrmod,
97     NULL
98 };
99
100 struct ppp_comp_state {
101     int         flags;
102     int         mru;
103     int         mtu;
104     struct compressor *xcomp;
105     void        *xstate;
106     struct compressor *rcomp;
107     void        *rstate;
108     struct vjcompress vj_comp;
109 };
110
111 /* Bits in flags are as defined in pppio.h. */
112 #define CCP_ERR         (CCP_ERROR | CCP_FATALERROR)
113
114 #define MAX_IPHDR       128     /* max TCP/IP header size */
115 #define MAX_VJHDR       20      /* max VJ compressed header size (?) */
116
117 /*
118  * List of compressors we know about.
119  */
120
121 extern struct compressor ppp_bsd_compress;
122
123 struct compressor *ppp_compressors[] = {
124 #if DO_BSD_COMPRESS
125     &ppp_bsd_compress,
126 #endif
127     NULL
128 };
129
130 /*
131  * Entry points for modloading.
132  */
133 int
134 _init(void)
135 {
136     return mod_install(&modlinkage);
137 }
138
139 int
140 _fini(void)
141 {
142     return mod_remove(&modlinkage);
143 }
144
145 int
146 _info(mip)
147     struct modinfo *mip;
148 {
149     return mod_info(&modlinkage, mip);
150 }
151
152 /*
153  * STREAMS module entry points.
154  */
155 static int
156 ppp_comp_open(q, dev, flag, sflag, credp)
157     queue_t *q;
158     dev_t dev;
159     int flag, sflag;
160     cred_t *credp;
161 {
162     struct ppp_comp_state *cp;
163
164     if (q->q_ptr == NULL) {
165         cp = (struct ppp_comp_state *) ALLOCATE(sizeof(struct ppp_comp_state));
166         if (cp == NULL)
167             return ENOSR;
168         OTHERQ(q)->q_ptr = q->q_ptr = cp;
169         cp->flags = 0;
170         cp->mru = PPP_MRU;
171         cp->xstate = NULL;
172         cp->rstate = NULL;
173     }
174     return 0;
175 }
176
177 static int
178 ppp_comp_close(q)
179     queue_t *q;
180 {
181     struct ppp_comp_state *cp;
182
183     cp = (struct ppp_comp_state *) q->q_ptr;
184     if (cp != NULL) {
185         if (cp->xstate != NULL)
186             (*cp->xcomp->comp_free)(cp->xstate);
187         if (cp->rstate != NULL)
188             (*cp->rcomp->decomp_free)(cp->rstate);
189         FREE(cp, sizeof(struct ppp_comp_state));
190         q->q_ptr = NULL;
191         OTHERQ(q)->q_ptr = NULL;
192     }
193     return 0;
194 }
195
196 static int
197 ppp_comp_wput(q, mp)
198     queue_t *q;
199     mblk_t *mp;
200 {
201     struct iocblk *iop;
202     struct ppp_comp_state *cp;
203     mblk_t *cmp;
204     int error, len, proto, state;
205     struct ppp_option_data *odp;
206     struct compressor **comp;
207     struct ppp_comp_stats *pcp;
208
209     cp = (struct ppp_comp_state *) q->q_ptr;
210     switch (mp->b_datap->db_type) {
211
212     case M_DATA:
213         /* first find out what the protocol is */
214         if (mp->b_wptr - mp->b_rptr < PPP_HDRLEN
215             && !pullupmsg(mp, PPP_HDRLEN)) {
216             freemsg(mp);        /* give up on it */
217             break;
218         }
219         proto = PPP_PROTOCOL(mp->b_rptr);
220
221         /*
222          * Do VJ compression if requested.
223          */
224         if (proto == PPP_IP && (cp->flags & COMP_VJC)) {
225             len = msgdsize(mp);
226             if (len > MAX_IPHDR + PPP_HDRLEN)
227                 len = MAX_IPHDR + PPP_HDRLEN;
228             if (mp->b_wptr - mp->b_rptr >= len || pullupmsg(mp, len)) {
229                 ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN);
230                 if (ip->ip_p == IPPROTO_TCP) {
231                     type = vj_compress_tcp(ip, len - PPP_HDRLEN,
232                                 cp->vj_comp, (cp->flags & COMP_VJCCID),
233                                 &vjhdr);
234                     switch (type) {
235                     case TYPE_UNCOMPRESSED_TCP:
236                         mp->b_rptr[3] = proto = PPP_VJC_UNCOMP;
237                         break;
238                     case TYPE_COMPRESSED_TCP:
239                         dp = vjhdr - PPP_HDRLEN;
240                         dp[1] = mp->b_rptr[1]; /* copy control field */
241                         dp[0] = mp->b_rptr[0]; /* copy address field */
242                         dp[2] = 0;                 /* set protocol field */
243                         dp[3] = proto = PPP_VJC_COMP;
244                         mp->b_rptr = dp;
245                         break;
246                     }
247                 }
248             }
249         }
250
251         /*
252          * Do packet compression if enabled.
253          */
254         if (proto == PPP_CCP)
255             ppp_comp_ccp(q, mp, 0);
256         else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN)
257                  && cp->xstate != NULL) {
258             len = msgdsize(mp);
259             (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len,
260                                    (cp->flags & CCP_ISUP? cp->mtu: 0));
261             if (cmp != NULL) {
262                 freemsg(mp);
263                 mp = cmp;
264             }
265         }
266
267         /*
268          * Do address/control and protocol compression if enabled.
269          */
270         if (proto != PPP_LCP && (cp->flags & COMP_AC)) {
271             mp->b_rptr += 2;    /* drop the address & ctrl fields */
272             if (proto < 0x100 && (cp->flags & COMP_PROT))
273                 ++mp->b_rptr;   /* drop the high protocol byte */
274         } else if (proto < 0x100 && (cp->flags & COMP_PROT)) {
275             /* shuffle up the address & ctrl fields */
276             mp->b_rptr[2] = mp->b_rptr[1];
277             mp->b_rptr[1] = mp->b_rptr[0];
278             ++mp->b_rptr;
279         }
280
281         putnext(q, mp);
282         break;
283
284     case M_IOCTL:
285         iop = (struct iocblk *) mp->b_rptr;
286         error = -1;
287         switch (iop->ioc_cmd) {
288
289         case PPPIO_CFLAGS:
290             /* set CCP state */
291             if (iop->ioc_count != sizeof(int)) {
292                 error = EINVAL;
293                 break;
294             }
295             state = (*(int *) mp->b_cont->b_rptr) & (CCP_ISUP | CCP_ISOPEN);
296             if ((state & CCP_ISOPEN) == 0) {
297                 if (cp->xstate != NULL) {
298                     (*cp->xcomp->comp_free)(cp->xstate);
299                     cp->xstate = NULL;
300                 }
301                 if (cp->rstate != NULL) {
302                     (*cp->rcomp->decomp_free)(cp->rstate);
303                     cp->rstate = NULL;
304                 }
305                 cp->flags = 0;
306             } else {
307                 cp->flags = (cp->flags & ~CCP_ISUP) | state;
308             }
309             error = 0;
310             iop->ioc_count = 0;
311             break;
312
313         case SIOCGIFCOMP:
314             if ((mp->b_cont = allocb(sizeof(int), BPRI_MED)) == NULL) {
315                 error = ENOSR;
316                 break;
317             }
318             *(int *)mp->b_cont->b_wptr = cp->flags;
319             mp->b_cont->b_wptr += iop->ioc_count = sizeof(int);
320             break;
321
322         case PPPIO_COMPRESS:
323             error = EINVAL;
324             if (iop->ioc_count != sizeof(struct ppp_option_data))
325                 break;
326             odp = (struct ppp_option_data *) mp->b_cont->b_rptr;
327             len = mp->b_cont->b_wptr - (unsigned char *) odp->opt_data;
328             if (len > odp->length)
329                 len = odp->length;
330             if (odp->opt_data[1] < 2 || odp->opt_data[1] > len)
331                 break;
332             for (comp = ppp_compressors; *comp != NULL; ++comp)
333                 if ((*comp)->compress_proto == odp->opt_data[0]) {
334                     /* here's the handler! */
335                     error = 0;
336                     if (odp->transmit) {
337                         if (cp->xstate != NULL)
338                             (*cp->xcomp->comp_free)(cp->xstate);
339                         cp->xcomp = *comp;
340                         cp->xstate = (*comp)->comp_alloc(odp->opt_data, len);
341                         if (cp->xstate == NULL)
342                             error = ENOSR;
343                     } else {
344                         if (cp->rstate != NULL)
345                             (*cp->rcomp->decomp_free)(cp->rstate);
346                         cp->rcomp = *comp;
347                         cp->rstate = (*comp)->decomp_alloc(odp->opt_data, len);
348                         if (cp->rstate == NULL)
349                             error = ENOSR;
350                     }
351                     break;
352                 }
353             iop->ioc_count = 0;
354             break;
355
356         case PPPIO_MRU:
357             /* remember this value */
358             if (iop->ioc_count == sizeof(int)) {
359                 cp->mru = *(int *) mp->b_cont->b_rptr;
360             }
361             break;
362
363         }
364
365         if (error < 0)
366             putnext(q, mp);
367         else if (error == 0) {
368             mp->b_datap->db_type = M_IOCACK;
369             qreply(q, mp);
370         } else {
371             mp->b_datap->db_type = M_IOCNAK;
372             iop->ioc_count = 0;
373             qreply(q, mp);
374         }
375         break;
376
377     default:
378         putnext(q, mp);
379     }
380 }
381
382 static int
383 ppp_comp_rput(q, mp)
384     queue_t *q;
385     mblk_t *mp;
386 {
387     int proto, rv;
388     mblk_t *dmp;
389     struct ppp_comp_state *cp;
390
391     cp = (struct ppp_comp_state *) q->q_ptr;
392     switch (mp->b_datap->db_type) {
393
394     case M_DATA:
395         /*
396          * First do address/control and protocol "decompression".
397          */
398         len = msgdsize(mp);
399         if (len > PPP_HDRLEN)
400             len = PPP_HDRLEN;
401         if (mp->b_wptr - mp->b_rptr < len && !pullupmsg(mp, len)) {
402             /* XXX reset VJ */
403             freemsg(mp);
404             break;
405         }
406         dp = mp->b_rptr;
407         if (PPP_ADDRESS(dp) == PPP_ALLSTATIONS && PPP_CONTROL(dp) == PPP_UI)
408             dp += 2;                    /* skip address/control */
409         proto = 0;
410         if ((dp[0] & 1) == 0)
411             proto = *dp++ << 8;         /* grab high byte of protocol */
412         proto += *dp++;                 /* grab low byte of protocol */
413         if (dp > mp->b_wptr) {
414             freemsg(mp);        /* short/bogus packet */
415             break;
416         }
417         if ((dp -= PPP_HDRLEN) < mp->b_datap->db_base) {
418             /* yucko, need a new message block */
419             mp->b_rptr = dp;
420             np = allocb(PPP_HDRLEN, BPRI_MED);
421             if (np == 0) {
422                 freemsg(mp);
423                 break;
424             }
425             linkb(np, mp);
426             mp = np;
427             dp = mp->b_rptr;
428             mp->b_wptr = dp + PPP_HDRLEN;
429         } else
430             mp->b_rptr = dp;
431         dp[0] = PPP_ALLSTATIONS;
432         dp[1] = PPP_UI;
433         dp[2] = proto >> 8;
434         dp[3] = proto;
435
436         /*
437          * Now see if we have a compressed packet to decompress,
438          * or a CCP packet to take notice of.
439          */
440         proto = PPP_PROTOCOL(mp->b_rptr);
441         if (proto == PPP_CCP)
442             ppp_comp_ccp(q, mp, 1);
443         else if (proto == PPP_COMP) {
444             if ((cp->flags & CCP_ISUP)
445                 && (cp->flags & CCP_DECOMP_RUN) && cp->rstate
446                 && (cp->flags & CCP_ERR) == 0) {
447                 rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp);
448                 if (dmp != NULL) {
449                     freemsg(mp);
450                     mp = dmp;
451                 } else {
452                     switch (rv) {
453                     case DECOMP_OK:
454                         /* no error, but no packet returned */
455                         freemsg(mp);
456                         mp = NULL;
457                         break;
458                     case DECOMP_ERROR:
459                         cp->flags |= CCP_ERROR;
460                         break;
461                     case DECOMP_FATALERROR:
462                         cp->flags |= CCP_FATALERROR;
463                         break;
464                     }
465                 }
466             }
467         } else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
468             (*cp->rcomp->incomp)(cp->rstate, mp);
469         }
470
471         /*
472          * Now do VJ decompression.
473          */
474
475         if (mp != NULL)
476             putnext(q, mp);
477         break;
478
479     default:
480         putnext(q, mp);
481     }
482 }
483
484 /*
485  * Handle a CCP packet being sent or received.
486  */
487 static void
488 ppp_comp_ccp(q, mp, rcvd)
489     queue_t *q;
490     mblk_t *mp;
491     int rcvd;
492 {
493     int len, clen;
494     struct ppp_comp_state *cp;
495     unsigned char *dp;
496
497     len = msgdsize(mp);
498     if (len < PPP_HDRLEN + CCP_HDRLEN || !pullupmsg(mp, len))
499         return;
500     cp = (struct ppp_comp_state *) q->q_ptr;
501     dp = mp->b_rptr + PPP_HDRLEN;
502     len -= PPP_HDRLEN;
503     clen = CCP_LENGTH(dp);
504     if (clen > len)
505         return;
506
507     switch (CCP_CODE(dp)) {
508     case CCP_CONFREQ:
509     case CCP_TERMREQ:
510     case CCP_TERMACK:
511         cp->flags &= ~CCP_ISUP;
512         break;
513
514     case CCP_CONFACK:
515         if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN
516             && clen >= CCP_HDRLEN + CCP_OPT_MINLEN
517             && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) {
518             if (!rcvd) {
519                 if (cp->xstate != NULL
520                     && (*cp->xcomp->comp_init)
521                         (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
522                          0, /* XXX: should be unit */ 0, 0))
523                     cp->flags |= CCP_COMP_RUN;
524             } else {
525                 if (cp->rstate != NULL
526                     && (*cp->rcomp->decomp_init)
527                         (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
528                          0/* unit */, 0, cp->mru, 0))
529                     cp->flags = (cp->flags & ~CCP_ERR)
530                         | CCP_DECOMP_RUN;
531             }
532         }
533         break;
534
535     case CCP_RESETACK:
536         if (cp->flags & CCP_ISUP) {
537             if (!rcvd) {
538                 if (cp->xstate && (cp->flags & CCP_COMP_RUN))
539                     (*cp->xcomp->comp_reset)(cp->xstate);
540             } else {
541                 if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) {
542                     (*cp->rcomp->decomp_reset)(cp->rstate);
543                     cp->flags &= ~CCP_ERROR;
544                 }
545             }
546         }
547         break;
548     }
549
550 }