fix stats stuff
[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.9 1995/05/19 03:17:09 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 == CONFNAK && dont_nak)
456             newret = CONFREJ;
457         if (!(newret == CONFACK || newret == CONFNAK && ret == CONFREJ)) {
458             /* we're returning this option */
459             if (newret == CONFREJ && ret == CONFNAK)
460                 retp = p0;
461             ret = newret;
462             if (p != retp)
463                 BCOPY(p, retp, clen);
464             retp += clen;
465         }
466
467         p += clen;
468         len -= clen;
469     }
470
471     if (ret != CONFACK)
472         *lenp = retp - p0;
473     return ret;
474 }
475
476 /*
477  * CCP has come up - inform the kernel driver.
478  */
479 static void
480 ccp_up(f)
481     fsm *f;
482 {
483     ccp_options *go = &ccp_gotoptions[f->unit];
484     ccp_options *ho = &ccp_hisoptions[f->unit];
485
486     ccp_flags_set(f->unit, 1, 1);
487     if (go->bsd_compress || ho->bsd_compress)
488         syslog(LOG_NOTICE, "%s enabled",
489                go->bsd_compress? ho->bsd_compress? "Compression":
490                "Receive compression": "Transmit compression");
491 }
492
493 /*
494  * CCP has gone down - inform the kernel driver.
495  */
496 static void
497 ccp_down(f)
498     fsm *f;
499 {
500     if (ccp_localstate[f->unit] & RACK_PENDING)
501         UNTIMEOUT(ccp_rack_timeout, (caddr_t) f);
502     ccp_localstate[f->unit] = 0;
503     ccp_flags_set(f->unit, 1, 0);
504 }
505
506 /*
507  * Print the contents of a CCP packet.
508  */
509 char *ccp_codenames[] = {
510     "ConfReq", "ConfAck", "ConfNak", "ConfRej",
511     "TermReq", "TermAck", "CodeRej",
512     NULL, NULL, NULL, NULL, NULL, NULL,
513     "ResetReq", "ResetAck",
514 };
515
516 int
517 ccp_printpkt(p, plen, printer, arg)
518     u_char *p;
519     int plen;
520     void (*printer) __P((void *, char *, ...));
521     void *arg;
522 {
523     u_char *p0, *optend;
524     int code, id, len;
525     int optlen;
526
527     p0 = p;
528     if (plen < HEADERLEN)
529         return 0;
530     code = p[0];
531     id = p[1];
532     len = (p[2] << 8) + p[3];
533     if (len < HEADERLEN || len > plen)
534         return 0;
535
536     if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *)
537         && ccp_codenames[code-1] != NULL)
538         printer(arg, " %s", ccp_codenames[code-1]);
539     else
540         printer(arg, " code=0x%x", code);
541     printer(arg, " id=0x%x", id);
542     len -= HEADERLEN;
543     p += HEADERLEN;
544
545     switch (code) {
546     case CONFREQ:
547     case CONFACK:
548     case CONFNAK:
549     case CONFREJ:
550         /* print list of possible compression methods */
551         while (len >= 2) {
552             code = p[0];
553             optlen = p[1];
554             if (optlen < 2 || optlen > len)
555                 break;
556             printer(arg, " <");
557             len -= optlen;
558             optend = p + optlen;
559             switch (code) {
560             case CI_BSD_COMPRESS:
561                 if (optlen >= CILEN_BSD_COMPRESS) {
562                     printer(arg, "bsd v%d %d", BSD_VERSION(p[2]),
563                             BSD_NBITS(p[2]));
564                     p += CILEN_BSD_COMPRESS;
565                 }
566                 break;
567             }
568             while (p < optend)
569                 printer(arg, " %.2x", *p++);
570             printer(arg, ">");
571         }
572         break;
573     }
574
575     /* dump out the rest of the packet in hex */
576     while (--len >= 0)
577         printer(arg, " %.2x", *p++);
578
579     return p - p0;
580 }
581
582 /*
583  * We have received a packet that the decompressor failed to
584  * decompress.  Here we would expect to issue a reset-request, but
585  * Motorola has a patent on resetting the compressor as a result of
586  * detecting an error in the decompressed data after decompression.
587  * (See US patent 5,130,993; international patent publication number
588  * WO 91/10289; Australian patent 73296/91.)
589  *
590  * So we ask the kernel whether the error was detected after
591  * decompression; if it was, we take CCP down, thus disabling
592  * compression :-(, otherwise we issue the reset-request.
593  */
594 void
595 ccp_datainput(unit, pkt, len)
596     int unit;
597     u_char *pkt;
598     int len;
599 {
600     fsm *f;
601
602     f = &ccp_fsm[unit];
603     if (f->state == OPENED) {
604         if (ccp_fatal_error(unit)) {
605             /*
606              * Disable compression by taking CCP down.
607              */
608             syslog(LOG_ERR, "Lost compression sync: disabling compression");
609             ccp_close(unit);
610         } else {
611             /*
612              * Send a reset-request to reset the peer's compressor.
613              * We don't do that if we are still waiting for an
614              * acknowledgement to a previous reset-request.
615              */
616             if (!(ccp_localstate[f->unit] & RACK_PENDING)) {
617                 fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
618                 TIMEOUT(ccp_rack_timeout, (caddr_t) f, RACKTIMEOUT);
619                 ccp_localstate[f->unit] |= RACK_PENDING;
620             } else
621                 ccp_localstate[f->unit] |= RREQ_REPEAT;
622         }
623     }
624 }
625
626 /*
627  * Timeout waiting for reset-ack.
628  */
629 static void
630 ccp_rack_timeout(arg)
631     caddr_t arg;
632 {
633     fsm *f = (fsm *) arg;
634
635     if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) {
636         fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0);
637         TIMEOUT(ccp_rack_timeout, (caddr_t) f, RACKTIMEOUT);
638         ccp_localstate[f->unit] &= ~RREQ_REPEAT;
639     } else
640         ccp_localstate[f->unit] &= ~RACK_PENDING;
641 }
642