7032d9af1b25c4ef3c092cc991b081aa82819eb7
[ppp.git] / 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.6 1995/06/23 01:42:54 paulus Exp $
28  */
29
30 #include <net/net_globals.h>
31 #include <sys/types.h>
32 #include <sys/param.h>
33 #include <sys/errno.h>
34 #include <sys/user.h>
35 #include <sys/stream.h>
36 #include <sys/stropts.h>
37 #include <sys/strconf.h>
38 #include <sys/device.h>
39 #include <sys/syslog.h>
40 #include <sys/socket.h>
41 #include <net/if.h>
42 #include <net/ppp_defs.h>
43 #include <net/ppp_str.h>
44
45 #define PACKETPTR       mblk_t *
46 #include <net/ppp-comp.h>
47
48 static int ppp_comp_open(), ppp_comp_close();
49 static int ppp_comp_rput(), ppp_comp_wput();
50 static void ppp_comp_ccp();
51
52 static struct module_info minfo = {
53     0xbadf, "ppp_compress", 0, INFPSZ, 16384, 4096,
54 };
55
56 static struct qinit r_init = {
57     ppp_comp_rput, NULL, ppp_comp_open, ppp_comp_close,
58     NULL, &minfo, NULL
59 };
60
61 static struct qinit w_init = {
62     ppp_comp_wput, NULL, NULL, NULL, NULL, &minfo, NULL
63 };
64
65 struct streamtab ppp_compinfo = {
66     &r_init, &w_init, NULL, NULL
67 };
68
69 struct ppp_comp_state {
70     int         ccp_state;
71     int         debug;
72     int         mru;
73     struct compressor *xcomp;
74     void        *xstate;
75     struct compressor *rcomp;
76     void        *rstate;
77 };
78
79 /* Bits in ccp_state are as defined in ppp_str.h. */
80 #define CCP_ERR         (CCP_ERROR | CCP_FATALERROR)
81
82 /*
83  * List of compressors we know about.
84  */
85
86 extern struct compressor ppp_bsd_compress;
87
88 struct compressor *ppp_compressors[] = {
89 #if DO_BSD_COMPRESS
90     &ppp_bsd_compress,
91 #endif
92     NULL
93 };
94
95 strconf_t pppcompconf = {
96     "pppcomp", &ppp_compinfo, STR_NEW_OPEN, 0, SQLVL_DEFAULT, (void *) 0
97 };
98
99 int pppcomp_load(int cmd, struct uio *uiop)
100 {
101     int rc = 0;
102
103     switch (cmd) {
104         case CFG_INIT:
105             rc = str_install(STR_LOAD_MOD, &pppcompconf);
106             break;
107         case CFG_TERM:
108             rc = str_install(STR_UNLOAD_MOD, &pppcompconf);
109             break;
110         default:
111             rc = EINVAL;
112             break;
113     }
114     return(rc);
115 }
116
117 static int
118 ppp_comp_open(q, dev, flag, sflag)
119     queue_t *q;
120     dev_t dev;
121     int flag;
122     int sflag;
123 {
124     struct ppp_comp_state *cp;
125
126     if (q->q_ptr == NULL) {
127         cp = (struct ppp_comp_state *)
128             xmalloc(sizeof(struct ppp_comp_state), 0, pinned_heap);
129         if (cp == NULL) {
130             return(ENOSR);
131         }
132         bzero(cp, sizeof(struct ppp_comp_state));
133         OTHERQ(q)->q_ptr = q->q_ptr = (caddr_t) cp;
134         cp->ccp_state = 0;
135         cp->debug = 0;
136         cp->mru = PPP_MRU;
137         cp->xstate = NULL;
138         cp->rstate = NULL;
139     }
140     return 0;
141 }
142
143 static int
144 ppp_comp_close(q)
145     queue_t *q;
146 {
147     struct ppp_comp_state *cp;
148
149     cp = (struct ppp_comp_state *) q->q_ptr;
150     if (cp != NULL) {
151         if (cp->xstate != NULL)
152             (*cp->xcomp->comp_free)(cp->xstate);
153         if (cp->rstate != NULL)
154             (*cp->rcomp->decomp_free)(cp->rstate);
155         xmfree(cp, pinned_heap);
156         q->q_ptr = NULL;
157         OTHERQ(q)->q_ptr = NULL;
158     }
159     return 0;
160 }
161
162 static int
163 ppp_comp_wput(q, mp)
164     queue_t *q;
165     mblk_t *mp;
166 {
167     struct iocblk *iop;
168     struct ppp_comp_state *cp;
169     mblk_t *cmp;
170     int error, len, proto, state;
171     struct ppp_option_data *odp;
172     struct compressor **comp;
173     struct ppp_comp_stats *pcp;
174
175     cp = (struct ppp_comp_state *) q->q_ptr;
176     switch (mp->b_datap->db_type) {
177
178     case M_CTL:
179         switch (*(u_char *) mp->b_rptr) {
180         case IF_GET_CSTATS:
181             freemsg(mp);
182             mp = allocb(sizeof(struct ppp_comp_stats) + sizeof(u_long),
183                         BPRI_HI);
184             if (mp != NULL) {
185                 mp->b_datap->db_type = M_CTL;
186                 *(u_char *) mp->b_wptr = IF_CSTATS;
187                 mp->b_wptr += sizeof(u_long); /* should be enough alignment */
188                 pcp = (struct ppp_comp_stats *) mp->b_wptr;
189                 mp->b_wptr += sizeof(struct ppp_comp_stats);
190                 bzero(pcp, sizeof(struct ppp_comp_stats));
191                 if (cp->xstate != NULL)
192                     (*cp->xcomp->comp_stat)(cp->xstate, &pcp->c);
193                 if (cp->rstate != NULL)
194                     (*cp->rcomp->decomp_stat)(cp->rstate, &pcp->d);
195                 qreply(q, mp);
196             }
197             break;
198         default:
199             putnext(q, mp);
200         }
201         break;
202
203     case M_DATA:
204         /* first find out what the protocol is */
205         if (mp->b_wptr - mp->b_rptr >= PPP_HDRLEN
206             || pullupmsg(mp, PPP_HDRLEN)) {
207             proto = PPP_PROTOCOL(mp->b_rptr);
208             if (proto == PPP_CCP)
209                 ppp_comp_ccp(q, mp, 0);
210             else if (proto != PPP_LCP && (cp->ccp_state & CCP_COMP_RUN)
211                      && cp->xstate != NULL) {
212                 len = msgdsize(mp);
213                 (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len,
214                                        (cp->ccp_state & CCP_ISUP? len: 0));
215                 /* XXX we really want the MTU here, not len */
216                 if (cmp != NULL) {
217                     freemsg(mp);
218                     mp = cmp;
219                 }
220             }
221         }
222         putnext(q, mp);
223         break;
224
225     case M_IOCTL:
226         iop = (struct iocblk *) mp->b_rptr;
227         error = -1;
228         switch ((unsigned int)iop->ioc_cmd) {
229
230         case SIOCSIFCOMP:
231             /* set CCP state */
232             if ((iop->ioc_count != sizeof(int)) &&
233                 (iop->ioc_count != TRANSPARENT)) {
234                 error = EINVAL;
235                 break;
236             }
237             state = (*(int *) mp->b_cont->b_rptr) & (CCP_ISUP | CCP_ISOPEN);
238             if ((state & CCP_ISOPEN) == 0) {
239                 if (cp->xstate != NULL) {
240                     (*cp->xcomp->comp_free)(cp->xstate);
241                     cp->xstate = NULL;
242                 }
243                 if (cp->rstate != NULL) {
244                     (*cp->rcomp->decomp_free)(cp->rstate);
245                     cp->rstate = NULL;
246                 }
247                 cp->ccp_state = 0;
248             } else {
249                 cp->ccp_state = (cp->ccp_state & ~CCP_ISUP) | state;
250             }
251             if (cp->debug)
252                 bsdlog(LOG_INFO, "SIOCSIFCOMP %x, state = %x\n",
253                     *(int *) mp->b_cont->b_rptr, cp->ccp_state);
254             error = 0;
255             iop->ioc_count = 0;
256             break;
257
258         case SIOCGIFCOMP:
259             if ((mp->b_cont = allocb(sizeof(int), BPRI_MED)) == NULL) {
260                 error = ENOSR;
261                 break;
262             }
263             *(int *)mp->b_cont->b_wptr = cp->ccp_state;
264             mp->b_cont->b_wptr += iop->ioc_count = sizeof(int);
265             break;
266
267         case SIOCSCOMPRESS:
268             error = EINVAL;
269             if (iop->ioc_count != TRANSPARENT)
270                 break;
271             odp = *((struct ppp_option_data **) mp->b_cont->b_rptr);
272             len = sizeof(odp->opt_data);
273             if (len > odp->length)
274                 len = odp->length;
275             if (odp->opt_data[1] < 2 || odp->opt_data[1] > len)
276                 break;
277             for (comp = ppp_compressors; *comp != NULL; ++comp)
278                 if ((*comp)->compress_proto == odp->opt_data[0]) {
279                     /* here's the handler! */
280                     error = 0;
281                     if (odp->transmit) {
282                         if (cp->xstate != NULL)
283                             (*cp->xcomp->comp_free)(cp->xstate);
284                         cp->xcomp = *comp;
285                         cp->xstate = (*comp)->comp_alloc(odp->opt_data, len);
286                         if (cp->xstate == NULL)
287                             error = ENOSR;
288                     } else {
289                         if (cp->rstate != NULL)
290                             (*cp->rcomp->decomp_free)(cp->rstate);
291                         cp->rcomp = *comp;
292                         cp->rstate = (*comp)->decomp_alloc(odp->opt_data, len);
293                         if (cp->rstate == NULL)
294                             error = ENOSR;
295                     }
296                     if (cp->debug)
297                         bsdlog(LOG_INFO, "SIOCSCOMPRESS %s len=%d\n",
298                             odp->transmit? "xmit": "recv", len);
299                     break;
300                 }
301             iop->ioc_count = 0;
302             break;
303
304         case SIOCSIFDEBUG:
305             /* set our debug flag from this */
306             if ((iop->ioc_count == TRANSPARENT) ||
307                 (iop->ioc_count == sizeof(int))) {
308                 cp->debug = *(int *) mp->b_cont->b_rptr & 1;
309             }
310             break;
311
312         case SIOCSIFMRU:
313             /* remember this value */
314             if ((iop->ioc_count == TRANSPARENT) ||
315                 (iop->ioc_count == sizeof(int))) {
316                 cp->mru = *(int *) mp->b_cont->b_rptr;
317             }
318             break;
319
320         }
321
322         if (error < 0)
323             putnext(q, mp);
324         else if (error == 0) {
325             mp->b_datap->db_type = M_IOCACK;
326             qreply(q, mp);
327         } else {
328             mp->b_datap->db_type = M_IOCNAK;
329             iop->ioc_count = 0;
330             qreply(q, mp);
331         }
332         break;
333
334     default:
335         putnext(q, mp);
336     }
337 }
338
339 static int
340 ppp_comp_rput(q, mp)
341     queue_t *q;
342     mblk_t *mp;
343 {
344     int proto, rv;
345     mblk_t *dmp;
346     struct ppp_comp_state *cp;
347
348     cp = (struct ppp_comp_state *) q->q_ptr;
349     switch (mp->b_datap->db_type) {
350
351     case M_DATA:
352         /* possibly a compressed packet to decompress,
353            or a CCP packet to take notice of. */
354         if (mp->b_wptr - mp->b_rptr >= PPP_HDRLEN
355             || pullupmsg(mp, PPP_HDRLEN)) {
356             proto = PPP_PROTOCOL(mp->b_rptr);
357             if (proto == PPP_CCP)
358                 ppp_comp_ccp(q, mp, 1);
359             else if (proto == PPP_COMP) {
360                 if ((cp->ccp_state & CCP_ISUP)
361                     && (cp->ccp_state & CCP_DECOMP_RUN) && cp->rstate
362                     && (cp->ccp_state & CCP_ERR) == 0) {
363                     rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp);
364                     if (dmp != NULL) {
365                         freemsg(mp);
366                         mp = dmp;
367                     } else {
368                         switch (rv) {
369                         case DECOMP_OK:
370                             /* no error, but no packet returned */
371                             freemsg(mp);
372                             mp = NULL;
373                             break;
374                         case DECOMP_ERROR:
375                             cp->ccp_state |= CCP_ERROR;
376                             break;
377                         case DECOMP_FATALERROR:
378                             cp->ccp_state |= CCP_FATALERROR;
379                             break;
380                         }
381                     }
382                 }
383             } else if (cp->rstate && (cp->ccp_state & CCP_DECOMP_RUN)) {
384                 (*cp->rcomp->incomp)(cp->rstate, mp);
385             }
386         }
387         if (mp != NULL)
388             putnext(q, mp);
389         break;
390
391     default:
392         putnext(q, mp);
393     }
394 }
395
396 static void
397 ppp_comp_ccp(q, mp, rcvd)
398     queue_t *q;
399     mblk_t *mp;
400     int rcvd;
401 {
402     int len, clen;
403     struct ppp_comp_state *cp;
404     unsigned char *dp;
405
406     len = msgdsize(mp);
407     if (len < PPP_HDRLEN + CCP_HDRLEN || !pullupmsg(mp, len))
408         return;
409     cp = (struct ppp_comp_state *) q->q_ptr;
410     dp = mp->b_rptr + PPP_HDRLEN;
411     len -= PPP_HDRLEN;
412     clen = CCP_LENGTH(dp);
413     if (clen > len)
414         return;
415     if (cp->debug)
416         bsdlog(LOG_INFO, "CCP %s: code=%x len=%d\n", rcvd? "rcvd": "sent",
417             CCP_CODE(dp), clen);
418
419     switch (CCP_CODE(dp)) {
420     case CCP_CONFREQ:
421     case CCP_TERMREQ:
422     case CCP_TERMACK:
423         cp->ccp_state &= ~CCP_ISUP;
424         break;
425
426     case CCP_CONFACK:
427         if ((cp->ccp_state & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN
428             && clen >= CCP_HDRLEN + CCP_OPT_MINLEN
429             && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) {
430             if (!rcvd) {
431                 if (cp->xstate != NULL
432                     && (*cp->xcomp->comp_init)
433                         (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
434                          0, /* XXX: should be unit */ 0,
435                          cp->debug))
436                     cp->ccp_state |= CCP_COMP_RUN;
437             } else {
438                 if (cp->rstate != NULL
439                     && (*cp->rcomp->decomp_init)
440                         (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
441                          0/* unit */, 0, cp->mru, cp->debug))
442                     cp->ccp_state = (cp->ccp_state & ~CCP_ERR)
443                         | CCP_DECOMP_RUN;
444             }
445         }
446         break;
447
448     case CCP_RESETACK:
449         if (cp->ccp_state & CCP_ISUP) {
450             if (!rcvd) {
451                 if (cp->xstate && (cp->ccp_state & CCP_COMP_RUN))
452                     (*cp->xcomp->comp_reset)(cp->xstate);
453             } else {
454                 if (cp->rstate && (cp->ccp_state & CCP_DECOMP_RUN)) {
455                     (*cp->rcomp->decomp_reset)(cp->rstate);
456                     cp->ccp_state &= ~CCP_ERROR;
457                 }
458             }
459         }
460         break;
461     }
462
463     if (cp->debug)
464         bsdlog(LOG_INFO, "ccp_state = %x\n", cp->ccp_state);
465 }