added 0 hdrlen param to comp_init
[ppp.git] / aix4 / 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.4 1995/04/28 06:13:05 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     &ppp_bsd_compress,
90     NULL
91 };
92
93 strconf_t pppcompconf = {
94     "pppcomp", &ppp_compinfo, STR_NEW_OPEN, 0, SQLVL_DEFAULT, (void *) 0
95 };
96
97 int pppcomp_load(int cmd, struct uio *uiop)
98 {
99     int rc = 0;
100
101     switch (cmd) {
102         case CFG_INIT:
103             rc = str_install(STR_LOAD_MOD, &pppcompconf);
104             break;
105         case CFG_TERM:
106             rc = str_install(STR_UNLOAD_MOD, &pppcompconf);
107             break;
108         default:
109             rc = EINVAL;
110             break;
111     }
112     return(rc);
113 }
114
115 static int
116 ppp_comp_open(q, dev, flag, sflag)
117     queue_t *q;
118     dev_t dev;
119     int flag;
120     int sflag;
121 {
122     struct ppp_comp_state *cp;
123
124     if (q->q_ptr == NULL) {
125         cp = (struct ppp_comp_state *)
126             xmalloc(sizeof(struct ppp_comp_state), 0, pinned_heap);
127         if (cp == NULL) {
128             return(ENOSR);
129         }
130         bzero(cp, sizeof(struct ppp_comp_state));
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         xmfree(cp, pinned_heap);
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;
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                 *(u_char *) mp->b_wptr = IF_CSTATS;
184                 mp->b_wptr += sizeof(u_long); /* should be enough alignment */
185                 pcp = (struct ppp_comp_stats *) mp->b_wptr;
186                 mp->b_wptr += sizeof(struct ppp_comp_stats);
187                 bzero(pcp, sizeof(struct ppp_comp_stats));
188                 if (cp->xstate != NULL)
189                     (*cp->xcomp->comp_stat)(cp->xstate, &pcp->c);
190                 if (cp->rstate != NULL)
191                     (*cp->rcomp->decomp_stat)(cp->rstate, &pcp->d);
192                 qreply(q, mp);
193             }
194             break;
195         default:
196             putnext(q, mp);
197         }
198         break;
199
200     case M_DATA:
201         /* first find out what the protocol is */
202         if (mp->b_wptr - mp->b_rptr >= PPP_HDRLEN
203             || pullupmsg(mp, PPP_HDRLEN)) {
204             proto = PPP_PROTOCOL(mp->b_rptr);
205             if (proto == PPP_CCP)
206                 ppp_comp_ccp(q, mp, 0);
207             else if (proto != PPP_LCP && (cp->ccp_state & CCP_COMP_RUN)
208                      && cp->xstate != NULL) {
209                 len = msgdsize(mp);
210                 (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len,
211                                        (cp->ccp_state & CCP_ISUP? len: 0));
212                 /* XXX we really want the MTU here, not len */
213                 if (cmp != NULL) {
214                     freemsg(mp);
215                     mp = cmp;
216                 }
217             }
218         }
219         putnext(q, mp);
220         break;
221
222     case M_IOCTL:
223         iop = (struct iocblk *) mp->b_rptr;
224         error = -1;
225         switch ((unsigned int)iop->ioc_cmd) {
226
227         case SIOCSIFCOMP:
228             /* set CCP state */
229             if ((iop->ioc_count != sizeof(int)) &&
230                 (iop->ioc_count != TRANSPARENT)) {
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                 bsdlog(LOG_INFO, "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             break;
263
264         case SIOCSCOMPRESS:
265             error = EINVAL;
266             if (iop->ioc_count != TRANSPARENT)
267                 break;
268             odp = *((struct ppp_option_data **) mp->b_cont->b_rptr);
269             len = sizeof(odp->opt_data);
270             if (len > odp->length)
271                 len = odp->length;
272             if (odp->opt_data[1] < 2 || odp->opt_data[1] > len)
273                 break;
274             for (comp = ppp_compressors; *comp != NULL; ++comp)
275                 if ((*comp)->compress_proto == odp->opt_data[0]) {
276                     /* here's the handler! */
277                     error = 0;
278                     if (odp->transmit) {
279                         if (cp->xstate != NULL)
280                             (*cp->xcomp->comp_free)(cp->xstate);
281                         cp->xcomp = *comp;
282                         cp->xstate = (*comp)->comp_alloc(odp->opt_data, len);
283                         if (cp->xstate == NULL)
284                             error = ENOSR;
285                     } else {
286                         if (cp->rstate != NULL)
287                             (*cp->rcomp->decomp_free)(cp->rstate);
288                         cp->rcomp = *comp;
289                         cp->rstate = (*comp)->decomp_alloc(odp->opt_data, len);
290                         if (cp->rstate == NULL)
291                             error = ENOSR;
292                     }
293                     if (cp->debug)
294                         bsdlog(LOG_INFO, "SIOCSCOMPRESS %s len=%d\n",
295                             odp->transmit? "xmit": "recv", len);
296                     break;
297                 }
298             iop->ioc_count = 0;
299             break;
300
301         case SIOCSIFDEBUG:
302             /* set our debug flag from this */
303             if ((iop->ioc_count == TRANSPARENT) ||
304                 (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 == TRANSPARENT) ||
312                 (iop->ioc_count == sizeof(int))) {
313                 cp->mru = *(int *) mp->b_cont->b_rptr;
314             }
315             break;
316
317         }
318
319         if (error < 0)
320             putnext(q, mp);
321         else if (error == 0) {
322             mp->b_datap->db_type = M_IOCACK;
323             qreply(q, mp);
324         } else {
325             mp->b_datap->db_type = M_IOCNAK;
326             iop->ioc_count = 0;
327             qreply(q, mp);
328         }
329         break;
330
331     default:
332         putnext(q, mp);
333     }
334 }
335
336 static int
337 ppp_comp_rput(q, mp)
338     queue_t *q;
339     mblk_t *mp;
340 {
341     int proto, rv;
342     mblk_t *dmp;
343     struct ppp_comp_state *cp;
344
345     cp = (struct ppp_comp_state *) q->q_ptr;
346     switch (mp->b_datap->db_type) {
347
348     case M_DATA:
349         /* possibly a compressed packet to decompress,
350            or a CCP packet to take notice of. */
351         if (mp->b_wptr - mp->b_rptr >= PPP_HDRLEN
352             || pullupmsg(mp, PPP_HDRLEN)) {
353             proto = PPP_PROTOCOL(mp->b_rptr);
354             if (proto == PPP_CCP)
355                 ppp_comp_ccp(q, mp, 1);
356             else if (proto == PPP_COMP) {
357                 if ((cp->ccp_state & CCP_ISUP)
358                     && (cp->ccp_state & CCP_DECOMP_RUN) && cp->rstate
359                     && (cp->ccp_state & CCP_ERR) == 0) {
360                     rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp);
361                     if (dmp != NULL) {
362                         freemsg(mp);
363                         mp = dmp;
364                     } else {
365                         switch (rv) {
366                         case DECOMP_OK:
367                             /* no error, but no packet returned */
368                             freemsg(mp);
369                             mp = NULL;
370                             break;
371                         case DECOMP_ERROR:
372                             cp->ccp_state |= CCP_ERROR;
373                             break;
374                         case DECOMP_FATALERROR:
375                             cp->ccp_state |= CCP_FATALERROR;
376                             break;
377                         }
378                     }
379                 }
380             } else if (cp->rstate && (cp->ccp_state & CCP_DECOMP_RUN)) {
381                 (*cp->rcomp->incomp)(cp->rstate, mp);
382             }
383         }
384         if (mp != NULL)
385             putnext(q, mp);
386         break;
387
388     default:
389         putnext(q, mp);
390     }
391 }
392
393 static void
394 ppp_comp_ccp(q, mp, rcvd)
395     queue_t *q;
396     mblk_t *mp;
397     int rcvd;
398 {
399     int len, clen;
400     struct ppp_comp_state *cp;
401     unsigned char *dp;
402
403     len = msgdsize(mp);
404     if (len < PPP_HDRLEN + CCP_HDRLEN || !pullupmsg(mp, len))
405         return;
406     cp = (struct ppp_comp_state *) q->q_ptr;
407     dp = mp->b_rptr + PPP_HDRLEN;
408     len -= PPP_HDRLEN;
409     clen = CCP_LENGTH(dp);
410     if (clen > len)
411         return;
412     if (cp->debug)
413         bsdlog(LOG_INFO, "CCP %s: code=%x len=%d\n", rcvd? "rcvd": "sent",
414             CCP_CODE(dp), clen);
415
416     switch (CCP_CODE(dp)) {
417     case CCP_CONFREQ:
418     case CCP_TERMREQ:
419     case CCP_TERMACK:
420         cp->ccp_state &= ~CCP_ISUP;
421         break;
422
423     case CCP_CONFACK:
424         if ((cp->ccp_state & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN
425             && clen >= CCP_HDRLEN + CCP_OPT_MINLEN
426             && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) {
427             if (!rcvd) {
428                 if (cp->xstate != NULL
429                     && (*cp->xcomp->comp_init)
430                         (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
431                          0, /* XXX: should be unit */ 0,
432                          cp->debug))
433                     cp->ccp_state |= CCP_COMP_RUN;
434             } else {
435                 if (cp->rstate != NULL
436                     && (*cp->rcomp->decomp_init)
437                         (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN,
438                          0/* unit */, 0, cp->mru, cp->debug))
439                     cp->ccp_state = (cp->ccp_state & ~CCP_ERR)
440                         | CCP_DECOMP_RUN;
441             }
442         }
443         break;
444
445     case CCP_RESETACK:
446         if (cp->ccp_state & CCP_ISUP) {
447             if (!rcvd) {
448                 if (cp->xstate && (cp->ccp_state & CCP_COMP_RUN))
449                     (*cp->xcomp->comp_reset)(cp->xstate);
450             } else {
451                 if (cp->rstate && (cp->ccp_state & CCP_DECOMP_RUN)) {
452                     (*cp->rcomp->decomp_reset)(cp->rstate);
453                     cp->ccp_state &= ~CCP_ERROR;
454                 }
455             }
456         }
457         break;
458     }
459
460     if (cp->debug)
461         bsdlog(LOG_INFO, "ccp_state = %x\n", cp->ccp_state);
462 }