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