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