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