ccp.c revision 1.1
1/*
2 * ccp.c - PPP Compression Control Protocol.
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
28#ifndef lint
29static char rcsid[] = "$Id: ccp.c,v 1.1 1995/10/18 08:47:58 deraadt Exp $";
30#endif
31
32#include <string.h>
33#include <syslog.h>
34#include <sys/ioctl.h>
35#include <net/ppp-comp.h>
36
37#include "pppd.h"
38#include "fsm.h"
39#include "ccp.h"
40
41fsm ccp_fsm[NUM_PPP];
42ccp_options ccp_wantoptions[NUM_PPP];	/* what to request the peer to use */
43ccp_options ccp_gotoptions[NUM_PPP];	/* what the peer agreed to do */
44ccp_options ccp_allowoptions[NUM_PPP];	/* what we'll agree to do */
45ccp_options ccp_hisoptions[NUM_PPP];	/* what we agreed to do */
46
47/*
48 * Callbacks for fsm code.
49 */
50static void ccp_resetci __P((fsm *));
51static int  ccp_cilen __P((fsm *));
52static void ccp_addci __P((fsm *, u_char *, int *));
53static int  ccp_ackci __P((fsm *, u_char *, int));
54static int  ccp_nakci __P((fsm *, u_char *, int));
55static int  ccp_rejci __P((fsm *, u_char *, int));
56static int  ccp_reqci __P((fsm *, u_char *, int *, int));
57static void ccp_up __P((fsm *));
58static void ccp_down __P((fsm *));
59static int  ccp_extcode __P((fsm *, int, int, u_char *, int));
60static void ccp_rack_timeout __P(());
61
62static fsm_callbacks ccp_callbacks = {
63    ccp_resetci,
64    ccp_cilen,
65    ccp_addci,
66    ccp_ackci,
67    ccp_nakci,
68    ccp_rejci,
69    ccp_reqci,
70    ccp_up,
71    ccp_down,
72    NULL,
73    NULL,
74    NULL,
75    NULL,
76    ccp_extcode,
77    "CCP"
78};
79
80/*
81 * Do we want / did we get any compression?
82 */
83#define ANY_COMPRESS(opt)	((opt).bsd_compress)
84
85/*
86 * Local state (mainly for handling reset-reqs and reset-acks
87 */
88static int ccp_localstate[NUM_PPP];
89#define RACK_PENDING	1	/* waiting for reset-ack */
90#define RREQ_REPEAT	2	/* send another reset-req if no reset-ack */
91
92#define RACKTIMEOUT	1	/* second */
93
94/*
95 * ccp_init - initialize CCP.
96 */
97void
98ccp_init(unit)
99    int unit;
100{
101    fsm *f = &ccp_fsm[unit];
102
103    f->unit = unit;
104    f->protocol = PPP_CCP;
105    f->callbacks = &ccp_callbacks;
106    fsm_init(f);
107
108    memset(&ccp_wantoptions[unit],  0, sizeof(ccp_options));
109    memset(&ccp_gotoptions[unit],   0, sizeof(ccp_options));
110    memset(&ccp_allowoptions[unit], 0, sizeof(ccp_options));
111    memset(&ccp_hisoptions[unit],   0, sizeof(ccp_options));
112
113    ccp_wantoptions[0].bsd_compress = 1;
114    ccp_wantoptions[0].bsd_bits = 12;	/* default value */
115
116    ccp_allowoptions[0].bsd_compress = 1;
117    ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS;
118}
119
120/*
121 * ccp_open - CCP is allowed to come up.
122 */
123void
124ccp_open(unit)
125    int unit;
126{
127    fsm *f = &ccp_fsm[unit];
128
129    if (f->state != OPENED)
130	ccp_flags_set(unit, 1, 0);
131    if (!ANY_COMPRESS(ccp_wantoptions[unit]))
132	f->flags |= OPT_SILENT;
133    fsm_open(f);
134}
135
136/*
137 * ccp_close - Terminate CCP.
138 */
139void
140ccp_close(unit)
141    int unit;
142{
143    ccp_flags_set(unit, 0, 0);
144    fsm_close(&ccp_fsm[unit]);
145}
146
147/*
148 * ccp_lowerup - we may now transmit CCP packets.
149 */
150void
151ccp_lowerup(unit)
152    int unit;
153{
154    fsm_lowerup(&ccp_fsm[unit]);
155}
156
157/*
158 * ccp_lowerdown - we may not transmit CCP packets.
159 */
160void
161ccp_lowerdown(unit)
162    int unit;
163{
164    fsm_lowerdown(&ccp_fsm[unit]);
165}
166
167/*
168 * ccp_input - process a received CCP packet.
169 */
170void
171ccp_input(unit, p, len)
172    int unit;
173    u_char *p;
174    int len;
175{
176    fsm *f = &ccp_fsm[unit];
177    int oldstate;
178
179    /*
180     * Check for a terminate-request so we can print a message.
181     */
182    oldstate = f->state;
183    fsm_input(f, p, len);
184    if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED)
185	syslog(LOG_NOTICE, "Compression disabled by peer.");
186
187    /*
188     * If we get a terminate-ack and we're not asking for compression,
189     * close CCP.
190     */
191    if (oldstate == REQSENT && p[0] == TERMACK
192	&& !ANY_COMPRESS(ccp_gotoptions[unit]))
193	ccp_close(unit);
194}
195
196/*
197 * Handle a CCP-specific code.
198 */
199static int
200ccp_extcode(f, code, id, p, len)
201    fsm *f;
202    int code, id;
203    u_char *p;
204    int len;
205{
206    switch (code) {
207    case CCP_RESETREQ:
208	if (f->state != OPENED)
209	    break;
210	/* send a reset-ack, which the transmitter will see and
211	   reset its compression state. */
212	fsm_sdata(f, CCP_RESETACK, id, NULL, 0);
213	break;
214
215    case CCP_RESETACK:
216	if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) {
217	    ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT);
218	    UNTIMEOUT(ccp_rack_timeout, (caddr_t) f);
219	}
220	break;
221
222    default:
223	return 0;
224    }
225
226    return 1;
227}
228
229/*
230 * ccp_protrej - peer doesn't talk CCP.
231 */
232void
233ccp_protrej(unit)
234    int unit;
235{
236    ccp_flags_set(unit, 0, 0);
237    fsm_lowerdown(&ccp_fsm[unit]);
238}
239
240/*
241 * ccp_resetci - initialize at start of negotiation.
242 */
243static void
244ccp_resetci(f)
245    fsm *f;
246{
247    ccp_options *go = &ccp_gotoptions[f->unit];
248    u_char opt_buf[16];
249
250    *go = ccp_wantoptions[f->unit];
251    if (go->bsd_compress) {
252	opt_buf[0] = CI_BSD_COMPRESS;
253	opt_buf[1] = CILEN_BSD_COMPRESS;
254	opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
255	if (!ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0))
256	    go->bsd_compress = 0;
257    }
258}
259
260/*
261 * ccp_cilen - Return total length of our configuration info.
262 */
263static int
264ccp_cilen(f)
265    fsm *f;
266{
267    ccp_options *go = &ccp_gotoptions[f->unit];
268
269    return (go->bsd_compress? CILEN_BSD_COMPRESS: 0);
270}
271
272/*
273 * ccp_addci - put our requests in a packet.
274 */
275static void
276ccp_addci(f, p, lenp)
277    fsm *f;
278    u_char *p;
279    int *lenp;
280{
281    ccp_options *go = &ccp_gotoptions[f->unit];
282    u_char *p0 = p;
283
284    if (go->bsd_compress) {
285	p[0] = CI_BSD_COMPRESS;
286	p[1] = CILEN_BSD_COMPRESS;
287	p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
288	if (ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0))
289	    p += CILEN_BSD_COMPRESS;
290	else
291	    go->bsd_compress = 0;
292    }
293    *lenp = p - p0;
294}
295
296/*
297 * ccp_ackci - process a received configure-ack, and return
298 * 1 iff the packet was OK.
299 */
300static int
301ccp_ackci(f, p, len)
302    fsm *f;
303    u_char *p;
304    int len;
305{
306    ccp_options *go = &ccp_gotoptions[f->unit];
307
308    if (go->bsd_compress) {
309	if (len < CILEN_BSD_COMPRESS
310	    || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS
311	    || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
312	    return 0;
313	p += CILEN_BSD_COMPRESS;
314	len -= CILEN_BSD_COMPRESS;
315    }
316    if (len != 0)
317	return 0;
318    return 1;
319}
320
321/*
322 * ccp_nakci - process received configure-nak.
323 * Returns 1 iff the nak was OK.
324 */
325static int
326ccp_nakci(f, p, len)
327    fsm *f;
328    u_char *p;
329    int len;
330{
331    ccp_options *go = &ccp_gotoptions[f->unit];
332    ccp_options no;		/* options we've seen already */
333    ccp_options try;		/* options to ask for next time */
334
335    memset(&no, 0, sizeof(no));
336    try = *go;
337
338    if (go->bsd_compress && !no.bsd_compress && len >= CILEN_BSD_COMPRESS
339	&& p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
340	no.bsd_compress = 1;
341	/*
342	 * Peer wants us to use a different number of bits
343	 * or a different version.
344	 */
345	if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION)
346	    try.bsd_compress = 0;
347	else if (BSD_NBITS(p[2]) < go->bsd_bits)
348	    try.bsd_bits = BSD_NBITS(p[2]);
349	p += CILEN_BSD_COMPRESS;
350	len -= CILEN_BSD_COMPRESS;
351    }
352
353    /*
354     * Have a look at any remaining options...???
355     */
356
357    if (len != 0)
358	return 0;
359
360    if (f->state != OPENED)
361	*go = try;
362    return 1;
363}
364
365/*
366 * ccp_rejci - reject some of our suggested compression methods.
367 */
368static int
369ccp_rejci(f, p, len)
370    fsm *f;
371    u_char *p;
372    int len;
373{
374    ccp_options *go = &ccp_gotoptions[f->unit];
375    ccp_options try;		/* options to request next time */
376
377    try = *go;
378
379    if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
380	&& p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
381	if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
382	    return 0;
383	try.bsd_compress = 0;
384	p += CILEN_BSD_COMPRESS;
385	len -= CILEN_BSD_COMPRESS;
386    }
387
388    if (len != 0)
389	return 0;
390
391    if (f->state != OPENED)
392	*go = try;
393
394    return 1;
395}
396
397/*
398 * ccp_reqci - processed a received configure-request.
399 * Returns CONFACK, CONFNAK or CONFREJ and the packet modified
400 * appropriately.
401 */
402static int
403ccp_reqci(f, p, lenp, dont_nak)
404    fsm *f;
405    u_char *p;
406    int *lenp;
407    int dont_nak;
408{
409    int ret, newret;
410    u_char *p0, *retp;
411    int len, clen, type, nb;
412    ccp_options *ho = &ccp_hisoptions[f->unit];
413    ccp_options *ao = &ccp_allowoptions[f->unit];
414
415    ret = CONFACK;
416    retp = p0 = p;
417    len = *lenp;
418
419    memset(ho, 0, sizeof(ccp_options));
420
421    while (len > 0) {
422	newret = CONFACK;
423	if (len < 2 || p[1] < 2 || p[1] > len) {
424	    /* length is bad */
425	    clen = len;
426	    newret = CONFREJ;
427
428	} else {
429	    type = p[0];
430	    clen = p[1];
431
432	    switch (type) {
433	    case CI_BSD_COMPRESS:
434		if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) {
435		    newret = CONFREJ;
436		    break;
437		}
438
439		ho->bsd_compress = 1;
440		ho->bsd_bits = nb = BSD_NBITS(p[2]);
441		if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION
442		    || nb > ao->bsd_bits) {
443		    newret = CONFNAK;
444		    nb = ao->bsd_bits;
445		} else if (nb < BSD_MIN_BITS) {
446		    newret = CONFREJ;
447		} else if (!ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 1)) {
448		    if (nb > BSD_MIN_BITS) {
449			--nb;
450			newret = CONFNAK;
451		    } else
452			newret = CONFREJ;
453		}
454		if (newret == CONFNAK && !dont_nak) {
455		    p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb);
456		}
457
458		break;
459
460	    default:
461		newret = CONFREJ;
462	    }
463	}
464
465	if (newret == CONFNAK && dont_nak)
466	    newret = CONFREJ;
467	if (!(newret == CONFACK || newret == CONFNAK && ret == CONFREJ)) {
468	    /* we're returning this option */
469	    if (newret == CONFREJ && ret == CONFNAK)
470		retp = p0;
471	    ret = newret;
472	    if (p != retp)
473		BCOPY(p, retp, clen);
474	    retp += clen;
475	}
476
477	p += clen;
478	len -= clen;
479    }
480
481    if (ret != CONFACK)
482	*lenp = retp - p0;
483    return ret;
484}
485
486/*
487 * CCP has come up - inform the kernel driver.
488 */
489static void
490ccp_up(f)
491    fsm *f;
492{
493    ccp_options *go = &ccp_gotoptions[f->unit];
494    ccp_options *ho = &ccp_hisoptions[f->unit];
495
496    ccp_flags_set(f->unit, 1, 1);
497    if (go->bsd_compress || ho->bsd_compress)
498	syslog(LOG_NOTICE, "%s enabled",
499	       go->bsd_compress? ho->bsd_compress? "Compression":
500	       "Receive compression": "Transmit compression");
501}
502
503/*
504 * CCP has gone down - inform the kernel driver.
505 */
506static void
507ccp_down(f)
508    fsm *f;
509{
510    if (ccp_localstate[f->unit] & RACK_PENDING)
511	UNTIMEOUT(ccp_rack_timeout, (caddr_t) f);
512    ccp_localstate[f->unit] = 0;
513    ccp_flags_set(f->unit, 1, 0);
514}
515
516/*
517 * Print the contents of a CCP packet.
518 */
519char *ccp_codenames[] = {
520    "ConfReq", "ConfAck", "ConfNak", "ConfRej",
521    "TermReq", "TermAck", "CodeRej",
522    NULL, NULL, NULL, NULL, NULL, NULL,
523    "ResetReq", "ResetAck",
524};
525
526int
527ccp_printpkt(p, plen, printer, arg)
528    u_char *p;
529    int plen;
530    void (*printer) __P((void *, char *, ...));
531    void *arg;
532{
533    u_char *p0, *optend;
534    int code, id, len;
535    int optlen;
536
537    p0 = p;
538    if (plen < HEADERLEN)
539	return 0;
540    code = p[0];
541    id = p[1];
542    len = (p[2] << 8) + p[3];
543    if (len < HEADERLEN || len > plen)
544	return 0;
545
546    if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *)
547	&& ccp_codenames[code-1] != NULL)
548	printer(arg, " %s", ccp_codenames[code-1]);
549    else
550	printer(arg, " code=0x%x", code);
551    printer(arg, " id=0x%x", id);
552    len -= HEADERLEN;
553    p += HEADERLEN;
554
555    switch (code) {
556    case CONFREQ:
557    case CONFACK:
558    case CONFNAK:
559    case CONFREJ:
560	/* print list of possible compression methods */
561	while (len >= 2) {
562	    code = p[0];
563	    optlen = p[1];
564	    if (optlen < 2 || optlen > len)
565		break;
566	    printer(arg, " <");
567	    len -= optlen;
568	    optend = p + optlen;
569	    switch (code) {
570	    case CI_BSD_COMPRESS:
571		if (optlen >= CILEN_BSD_COMPRESS) {
572		    printer(arg, "bsd v%d %d", BSD_VERSION(p[2]),
573			    BSD_NBITS(p[2]));
574		    p += CILEN_BSD_COMPRESS;
575		}
576		break;
577	    }
578	    while (p < optend)
579		printer(arg, " %.2x", *p++);
580	    printer(arg, ">");
581	}
582	break;
583    }
584
585    /* dump out the rest of the packet in hex */
586    while (--len >= 0)
587	printer(arg, " %.2x", *p++);
588
589    return p - p0;
590}
591
592/*
593 * We have received a packet that the decompressor failed to
594 * decompress.  Here we would expect to issue a reset-request, but
595 * Motorola has a patent on resetting the compressor as a result of
596 * detecting an error in the decompressed data after decompression.
597 * (See US patent 5,130,993; international patent publication number
598 * WO 91/10289; Australian patent 73296/91.)
599 *
600 * So we ask the kernel whether the error was detected after
601 * decompression; if it was, we take CCP down, thus disabling
602 * compression :-(, otherwise we issue the reset-request.
603 */
604void
605ccp_datainput(unit, pkt, len)
606    int unit;
607    u_char *pkt;
608    int len;
609{
610    fsm *f;
611
612    f = &ccp_fsm[unit];
613    if (f->state == OPENED) {
614	if (ccp_fatal_error(unit)) {
615	    /*
616	     * Disable compression by taking CCP down.
617	     */
618	    syslog(LOG_ERR, "Lost compression sync: disabling compression");
619	    ccp_close(unit);
620	} else {
621	    /*
622	     * Send a reset-request to reset the peer's compressor.
623	     * We don't do that if we are still waiting for an
624	     * acknowledgement to a previous reset-request.
625	     */
626	    if (!(ccp_localstate[f->unit] & RACK_PENDING)) {
627		fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
628		TIMEOUT(ccp_rack_timeout, (caddr_t) f, RACKTIMEOUT);
629		ccp_localstate[f->unit] |= RACK_PENDING;
630	    } else
631		ccp_localstate[f->unit] |= RREQ_REPEAT;
632	}
633    }
634}
635
636/*
637 * Timeout waiting for reset-ack.
638 */
639static void
640ccp_rack_timeout(arg)
641    caddr_t arg;
642{
643    fsm *f = (fsm *) arg;
644
645    if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) {
646	fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0);
647	TIMEOUT(ccp_rack_timeout, (caddr_t) f, RACKTIMEOUT);
648	ccp_localstate[f->unit] &= ~RREQ_REPEAT;
649    } else
650	ccp_localstate[f->unit] &= ~RACK_PENDING;
651}
652
653