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