1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 ppp_comp.c - Compression protocol for ppp.
26
27 based on if_ppp.c from bsd
28
29 *
30 * Copyright (c) 1989 Carnegie Mellon University.
31 * All rights reserved.
32 *
33 * Redistribution and use in source and binary forms are permitted
34 * provided that the above copyright notice and this paragraph are
35 * duplicated in all such forms and that any documentation,
36 * advertising materials, and other materials related to such
37 * distribution and use acknowledge that the software was developed
38 * by Carnegie Mellon University.  The name of the
39 * University may not be used to endorse or promote products derived
40 * from this software without specific prior written permission.
41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS ORpppall
42 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
43 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
44 *
45 * Drew D. Perkins
46 * Carnegie Mellon University
47 * 4910 Forbes Ave.
48 * Pittsburgh, PA 15213
49 * (412) 268-8576
50 * ddp@andrew.cmu.edu
51 *
52 * Based on:
53 *	@(#)if_sl.c	7.6.1.2 (Berkeley) 2/15/89
54 *
55 * Copyright (c) 1987 Regents of the University of California.
56 * All rights reserved.
57 *
58 * Redistribution and use in source and binary forms are permitted
59 * provided that the above copyright notice and this paragraph are
60 * duplicated in all such forms and that any documentation,
61 * advertising materials, and other materials related to such
62 * distribution and use acknowledge that the software was developed
63 * by the University of California, Berkeley.  The name of the
64 * University may not be used to endorse or promote products derived
65 * from this software without specific prior written permission.
66 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
67 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
68 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
69 *
70 */
71
72
73#include <sys/types.h>
74#include <sys/param.h>
75#include <sys/systm.h>
76#include <sys/proc.h>
77#include <sys/mbuf.h>
78#include <sys/socket.h>
79#include <net/if.h>
80#include <sys/vm.h>
81#if 0
82#include <net/netisr.h>
83#endif
84#include <sys/syslog.h>
85#include <netinet/in.h>
86
87
88#include "ppp_defs.h"		// public ppp values
89#include "ppp_ip.h"
90#include "if_ppplink.h"		// public link API
91#include "ppp_domain.h"
92#include "ppp_if.h"
93#include "ppp_domain.h"
94#include "ppp_comp.h"
95
96/* -----------------------------------------------------------------------------
97Definitions
98----------------------------------------------------------------------------- */
99
100struct ppp_comp {
101
102    TAILQ_ENTRY(ppp_comp) next;
103
104    /* compressor identifier */
105    u_int32_t	protocol;				/* CCP compression protocol number */
106
107    void 	*userdata;			/* user data to pass to the compressor */
108
109    /* compression call back functions */
110
111    void	*(*comp_alloc)
112                (u_char *options, int opt_len);	/* Allocate space for a compressor (transmit side) */
113    void	(*comp_free)
114                (void *state);			/* Free space used by a compressor */
115    int		(*comp_init) 			/* Initialize a compressor */
116                (void *state, u_char *options, int opt_len,
117                int unit, int hdrlen, int mtu, int debug);
118    void	(*comp_reset)
119                (void *state);			/* Reset a compressor */
120    int		(*compress) 			/* Compress a packet */
121                (void *state, mbuf_t *m);
122    void	(*comp_stat) 			/* Return compression statistics */
123                (void *state, struct compstat *stats);
124
125
126    /* decompression call back functions */
127
128    void	*(*decomp_alloc) 		/* Allocate space for a decompressor (receive side) */
129                (u_char *options, int opt_len);
130    void	(*decomp_free)
131                (void *state);			/* Free space used by a decompressor */
132    int		(*decomp_init) 			/* Initialize a decompressor */
133                (void *state, u_char *options, int opt_len,
134                int unit, int hdrlen, int mru, int debug);
135    void	(*decomp_reset) 		/* Reset a decompressor */
136                (void *state);
137    int		(*decompress) 			/* Decompress a packet. */
138                (void *state, mbuf_t *m);
139    void	(*incomp) 			/* Update state for an incompressible packet received */
140                (void *state, mbuf_t m);
141    void	(*decomp_stat) 			/* Return decompression statistics */
142                (void *state, struct compstat *stats);
143};
144
145
146/* -----------------------------------------------------------------------------
147Forward declarations
148----------------------------------------------------------------------------- */
149
150struct ppp_comp *ppp_comp_find(u_int32_t proto);
151
152/* -----------------------------------------------------------------------------
153Globals
154----------------------------------------------------------------------------- */
155static TAILQ_HEAD(, ppp_comp) 	ppp_comp_head;
156
157/* -----------------------------------------------------------------------------
158----------------------------------------------------------------------------- */
159int ppp_comp_init()
160{
161    TAILQ_INIT(&ppp_comp_head);
162    return 0;
163}
164
165/* -----------------------------------------------------------------------------
166----------------------------------------------------------------------------- */
167int ppp_comp_dispose()
168{
169    struct ppp_comp  	*comp;
170
171    while ((comp = TAILQ_FIRST(&ppp_comp_head))) {
172        TAILQ_REMOVE(&ppp_comp_head, comp, next);
173    	FREE(comp, M_TEMP);
174    }
175
176    return 0;
177}
178
179/* -----------------------------------------------------------------------------
180----------------------------------------------------------------------------- */
181struct ppp_comp *ppp_comp_find(u_int32_t proto)
182{
183    struct ppp_comp  	*comp;
184
185    TAILQ_FOREACH(comp, &ppp_comp_head, next)
186        if (comp->protocol == proto)
187            return comp;
188
189    return 0;
190}
191
192/* -----------------------------------------------------------------------------
193----------------------------------------------------------------------------- */
194int ppp_comp_register(struct ppp_comp_reg *compreg, ppp_comp_ref *compref)
195{
196    struct ppp_comp *comp;
197
198    /* sanity check */
199    if (compreg == NULL
200        || compreg->comp_alloc == NULL
201        || compreg->comp_free == NULL
202        || compreg->comp_init == NULL
203        || compreg->comp_reset == NULL
204        || compreg->compress == NULL
205        || compreg->comp_stat == NULL
206        || compreg->decomp_alloc == NULL
207        || compreg->decomp_free == NULL
208        || compreg->decomp_init == NULL
209        || compreg->decomp_reset == NULL
210        || compreg->decompress == NULL
211        || compreg->incomp == NULL
212        || compreg->decomp_stat == NULL)
213        return(EINVAL);
214
215    comp = ppp_comp_find(compreg->compress_proto);
216    if (comp != NULL)
217        return(EEXIST);
218
219    MALLOC(comp, struct ppp_comp *, sizeof(*comp), M_TEMP, M_WAITOK);
220    if (comp == NULL)
221        return(ENOMEM);
222
223    bzero((char *)comp, sizeof(*comp));
224
225    comp->protocol = compreg->compress_proto;
226    comp->comp_alloc = compreg->comp_alloc;
227    comp->comp_free = compreg->comp_free;
228    comp->comp_init = compreg->comp_init;
229    comp->comp_reset = compreg->comp_reset;
230    comp->compress = compreg->compress;
231    comp->comp_stat = compreg->comp_stat;
232    comp->decomp_alloc = compreg->decomp_alloc;
233    comp->decomp_free = compreg->decomp_free;
234    comp->decomp_init = compreg->decomp_init;
235    comp->decomp_reset = compreg->decomp_reset;
236    comp->decompress = compreg->decompress;
237    comp->incomp = compreg->incomp;
238    comp->decomp_stat = compreg->decomp_stat;
239
240    TAILQ_INSERT_TAIL(&ppp_comp_head, comp, next);
241
242    *compref = comp;
243    return 0;
244}
245
246/* -----------------------------------------------------------------------------
247----------------------------------------------------------------------------- */
248int ppp_comp_deregister(ppp_comp_ref *compref)
249{
250    struct ppp_comp	*comp = (struct ppp_comp *)compref;
251
252    if (comp == NULL)	/* sanity check */
253        return(EINVAL);
254
255    TAILQ_REMOVE(&ppp_comp_head, comp, next);
256
257    FREE(comp, M_TEMP);
258    return(0);
259}
260
261/* -----------------------------------------------------------------------------
262----------------------------------------------------------------------------- */
263int ppp_comp_setcompressor(struct ppp_if *wan, struct ppp_option_data *odp)
264{
265    int 			error = 0;
266	u_int32_t		nb;
267    struct ppp_comp *cp;
268    u_char 			ccp_option[CCP_MAX_OPTION_LENGTH];
269    user_addr_t		ptr;
270    int				transmit;
271
272    if (proc_is64bit(current_proc())) {
273        struct ppp_option_data64 *odp64 = (struct ppp_option_data64 *)odp;
274
275        nb = odp64->length;
276        ptr = odp64->ptr;
277        transmit = odp64->transmit;
278    } else {
279        struct ppp_option_data32 *odp32 = (struct ppp_option_data32 *)odp;
280
281        nb = odp32->length;
282        ptr = CAST_USER_ADDR_T(odp32->ptr);
283        transmit = odp32->transmit;
284    }
285
286    if (nb > sizeof(ccp_option))
287        nb = sizeof(ccp_option);
288
289    if ((error = copyin(ptr, ccp_option, nb)))
290        return (error);
291
292    if (ccp_option[1] < 2)	/* preliminary check on the length byte */
293        return (EINVAL);
294
295    cp = ppp_comp_find(ccp_option[0]);
296    if (cp == 0) {
297        LOGDBG(wan->net, ("ppp%d: no compressor for [%x %x %x], %x\n",
298                ifnet_unit(wan->net), ccp_option[0], ccp_option[1],
299                ccp_option[2], nb));
300
301        return EINVAL;	/* no handler found */
302    }
303
304    if (transmit) {
305        if (wan->xc_state)
306            (*wan->xcomp->comp_free)(wan->xc_state);
307        wan->xcomp = cp;
308        wan->xc_state = cp->comp_alloc(ccp_option, nb);
309        if (!wan->xc_state) {
310            error = ENOMEM;
311            LOGDBG(wan->net, ("ppp%d: comp_alloc failed\n", ifnet_unit(wan->net)));
312        }
313        wan->sc_flags &= ~SC_COMP_RUN;
314    }
315    else {
316        if (wan->rc_state)
317            (*wan->rcomp->decomp_free)(wan->rc_state);
318        wan->rcomp = cp;
319        wan->rc_state = cp->decomp_alloc(ccp_option, nb);
320        if (!wan->rc_state) {
321            error = ENOMEM;
322            LOGDBG(wan->net, ("ppp%d: decomp_alloc failed\n", ifnet_unit(wan->net)));
323        }
324        wan->sc_flags &= ~SC_DECOMP_RUN;
325    }
326
327    return error;
328}
329
330
331/* -----------------------------------------------------------------------------
332----------------------------------------------------------------------------- */
333void ppp_comp_getstats(struct ppp_if *wan, struct ppp_comp_stats *stats)
334{
335
336    bzero(stats, sizeof(struct ppp_comp_stats));
337    if (wan->xc_state)
338        (*wan->xcomp->comp_stat)(wan->xc_state, &stats->c);
339    if (wan->rc_state)
340        (*wan->rcomp->decomp_stat)(wan->rc_state, &stats->d);
341}
342
343/* -----------------------------------------------------------------------------
344Handle a CCP packet.  `rcvd' is 1 if the packet was received,
3450 if it is about to be transmitted.
346mbuf points to the ccp payload (doesn't include FF03 and 80FD)
347----------------------------------------------------------------------------- */
348void ppp_comp_ccp(struct ppp_if *wan, mbuf_t m, int rcvd)
349{
350    u_char 	*p = mbuf_data(m);	// no alignment issue as p is *u_char.
351    int 	slen;
352
353    slen = CCP_LENGTH(p);
354    if (slen > mbuf_pkthdr_len(m)) {
355        LOGDBG(wan->net, ("ppp_comp_ccp: not enough data in mbuf (expected = %d, got = %d)\n",
356		   slen, mbuf_pkthdr_len(m)));
357	return;
358    }
359
360    switch (CCP_CODE(p)) {
361    case CCP_CONFREQ:
362    case CCP_TERMREQ:
363    case CCP_TERMACK:
364	/* CCP must be going down - disable compression */
365        wan->sc_flags &= ~(rcvd ? SC_COMP_RUN : SC_DECOMP_RUN);
366	break;
367
368    case CCP_CONFACK:
369	if (wan->sc_flags & SC_CCP_OPEN && !(wan->sc_flags & SC_CCP_UP)
370	    && slen >= CCP_HDRLEN + CCP_OPT_MINLEN
371	    && slen >= CCP_OPT_LENGTH(p + CCP_HDRLEN) + CCP_HDRLEN) {
372	    if (rcvd) {
373		/* peer is agreeing to send compressed packets. */
374		if (wan->rc_state
375		    && (*wan->rcomp->decomp_init)
376			(wan->rc_state, p + CCP_HDRLEN, slen - CCP_HDRLEN,
377			 ifnet_unit(wan->net), 0, wan->mru, ifnet_flags(wan->net) & IFF_DEBUG)) {
378		    wan->sc_flags |= SC_DECOMP_RUN;
379		    wan->sc_flags &= ~(SC_DC_ERROR | SC_DC_FERROR);
380		}
381	    } else {
382		/* we're agreeing to send compressed packets. */
383		if (wan->xc_state
384		    && (*wan->xcomp->comp_init)
385			(wan->xc_state, p + CCP_HDRLEN, slen - CCP_HDRLEN,
386			 ifnet_unit(wan->net), 0, ifnet_mtu(wan->net), ifnet_flags(wan->net) & IFF_DEBUG)) {
387		    wan->sc_flags |= SC_COMP_RUN;
388		}
389	    }
390	}
391	break;
392
393    case CCP_RESETACK:
394	if (wan->sc_flags & SC_CCP_UP) {
395	    if (rcvd) {
396		if (wan->rc_state && (wan->sc_flags & SC_DECOMP_RUN)) {
397		    (*wan->rcomp->decomp_reset)(wan->rc_state);
398		    wan->sc_flags &= ~SC_DC_ERROR;
399		}
400	    } else {
401		if (wan->xc_state && (wan->sc_flags & SC_COMP_RUN))
402		    (*wan->xcomp->comp_reset)(wan->xc_state);
403	    }
404	}
405	break;
406    }
407}
408
409/* -----------------------------------------------------------------------------
410CCP is down; free (de)compressor state if necessary.
411----------------------------------------------------------------------------- */
412void ppp_comp_close(struct ppp_if *wan)
413{
414
415    wan->sc_flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN);
416    if (wan->xc_state) {
417	(*wan->xcomp->comp_free)(wan->xc_state);
418	wan->xc_state = NULL;
419    }
420    if (wan->rc_state) {
421	(*wan->rcomp->decomp_free)(wan->rc_state);
422	wan->rc_state = NULL;
423    }
424}
425
426/* -----------------------------------------------------------------------------
427----------------------------------------------------------------------------- */
428void ppp_comp_logmbuf(char *msg, mbuf_t m)
429{
430    int 	i, lcount, copycount, count;
431    char 	lbuf[16], *data;
432
433    if (m == NULL)
434        return;
435
436    IOLog("%s: \n", msg);
437
438    for (count = mbuf_len(m), data = mbuf_data(m); m != NULL; ) {
439        /* build a line of output */
440        for(lcount = 0; lcount < sizeof(lbuf); lcount += copycount) {
441            if (!count) {
442                m = mbuf_next(m);
443                if (m == NULL)
444                    break;
445                count = mbuf_len(m);
446                data  = mbuf_data(m);
447            }
448            copycount = (count > sizeof(lbuf) - lcount) ? sizeof(lbuf) - lcount : count;
449            bcopy(data, &lbuf[lcount], copycount);
450            data  += copycount;
451            count -= copycount;
452        }
453
454        /* output line (hex 1st, then ascii) */
455        IOLog("%s:  0x  ", msg);
456        for(i = 0; i < lcount; i++) {
457            if (i == 8) IOLog("  ");
458            IOLog("%02x ", (u_char)lbuf[i]);
459        }
460        for( ; i < sizeof(lbuf); i++) {
461            if (i == 8) IOLog("  ");
462            IOLog("   ");
463        }
464        IOLog("  '");
465        for(i = 0; i < lcount; i++)
466            IOLog("%c",(lbuf[i]>=040 && lbuf[i]<=0176)?lbuf[i]:'.');
467        IOLog("'\n");
468    }
469}
470
471/* -----------------------------------------------------------------------------
472return codes :
473> 0 : compression done, buffer has changed, return new lenght
4740 : compression not done, buffer has not changed, lenght is unchanged
475< 0 : compression not done because of error, return -error
476----------------------------------------------------------------------------- */
477int ppp_comp_compress(struct ppp_if *wan, mbuf_t *m)
478{
479    if (wan->xc_state == 0 || (wan->sc_flags & SC_CCP_UP) == 0)
480        return COMP_NOTDONE;
481
482    return wan->xcomp->compress(wan->xc_state, m);
483}
484
485/* -----------------------------------------------------------------------------
486----------------------------------------------------------------------------- */
487int ppp_comp_incompress(struct ppp_if *wan, mbuf_t m)
488{
489
490    if ((wan->rc_state == 0) || (wan->sc_flags & (SC_DC_ERROR | SC_DC_FERROR)))
491        return 0;
492
493    /* Uncompressed frame - pass to decompressor so it can update its dictionary if necessary. */
494    wan->rcomp->incomp(wan->rc_state, m);
495
496    return 0;
497}
498
499/* -----------------------------------------------------------------------------
500----------------------------------------------------------------------------- */
501int ppp_comp_decompress(struct ppp_if *wan, mbuf_t *m)
502{
503    int err;
504
505    if ((wan->rc_state == 0) || (wan->sc_flags & (SC_DC_ERROR | SC_DC_FERROR)))
506        return DECOMP_ERROR;
507
508    err = wan->rcomp->decompress(wan->rc_state, m);
509    if (err != DECOMP_OK) {
510        if (err == DECOMP_FATALERROR)
511            wan->sc_flags |= SC_DC_FERROR;
512        wan->sc_flags |= SC_DC_ERROR;
513        ppp_if_error(wan->net);
514    }
515
516    return err;
517}
518