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