Initial revision
[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.1 1994/08/11 01:44:32 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     fsm_open(f);
113 }
114
115 /*
116  * ccp_close - Terminate CCP.
117  */
118 void
119 ccp_close(unit)
120     int unit;
121 {
122     ccp_flags_set(unit, 0, 0);
123     fsm_close(&ccp_fsm[unit]);
124 }
125
126 /*
127  * ccp_lowerup - we may now transmit CCP packets.
128  */
129 void
130 ccp_lowerup(unit)
131     int unit;
132 {
133     fsm_lowerup(&ccp_fsm[unit]);
134 }
135
136 /*
137  * ccp_lowerdown - we may not transmit CCP packets.
138  */
139 void
140 ccp_lowerdown(unit)
141     int unit;
142 {
143     fsm_lowerdown(&ccp_fsm[unit]);
144 }
145
146 /*
147  * ccp_input - process a received CCP packet.
148  */
149 void
150 ccp_input(unit, p, len)
151     int unit;
152     u_char *p;
153     int len;
154 {
155     fsm_input(&ccp_fsm[unit], p, len);
156 }
157
158 /*
159  * Handle a CCP-specific code.
160  */
161 static int
162 ccp_extcode(f, code, id, p, len)
163     fsm *f;
164     int code, id;
165     u_char *p;
166     int len;
167 {
168     switch (code) {
169     case RESETREQ:
170         if (f->state != OPENED)
171             break;
172         /* send a reset-ack, which the transmitter will see and
173            reset its compression state. */
174         fsm_sdata(f, RESETACK, id, NULL, 0);
175         break;
176
177     case RESETACK:
178         if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) {
179             ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT);
180             UNTIMEOUT(ccp_rack_timeout, (caddr_t) f);
181         }
182         break;
183
184     default:
185         return 0;
186     }
187
188     return 1;
189 }
190
191 /*
192  * ccp_protrej - peer doesn't talk CCP.
193  */
194 void
195 ccp_protrej(unit)
196     int unit;
197 {
198     ccp_flags_set(unit, 0, 0);
199     fsm_lowerdown(&ccp_fsm[unit]);
200 }
201
202 /*
203  * ccp_resetci - initialize at start of negotiation.
204  */
205 static void
206 ccp_resetci(f)
207     fsm *f;
208 {
209     ccp_gotoptions[f->unit] = ccp_wantoptions[f->unit];
210 }
211
212 /*
213  * ccp_cilen - Return total length of our configuration info.
214  */
215 static int
216 ccp_cilen(f)
217     fsm *f;
218 {
219     ccp_options *go = &ccp_gotoptions[f->unit];
220
221     return (go->bsd_compress? CILEN_BSD: 0);
222 }
223
224 /*
225  * ccp_addci - put our requests in a packet.
226  */
227 static void
228 ccp_addci(f, p, lenp)
229     fsm *f;
230     u_char *p;
231     int *lenp;
232 {
233     ccp_options *go = &ccp_gotoptions[f->unit];
234     u_char *p0 = p;
235
236     if (go->bsd_compress) {
237         p[0] = CI_BSD_COMPRESS;
238         p[1] = CILEN_BSD;
239         p[2] = go->bsd_bits;
240         p += 3;
241     }
242     *lenp = p - p0;
243 }
244
245 /*
246  * ccp_ackci - process a received configure-ack, and return
247  * 1 iff the packet was OK.
248  */
249 static int
250 ccp_ackci(f, p, len)
251     fsm *f;
252     u_char *p;
253     int len;
254 {
255     ccp_options *go = &ccp_gotoptions[f->unit];
256
257     if (go->bsd_compress) {
258         if (len != 3 || p[0] != CI_BSD_COMPRESS
259             || p[1] != CILEN_BSD || p[2] != go->bsd_bits)
260             return 0;
261         p += 3;
262         len -= 3;
263     }
264     if (len != 0)
265         return 0;
266     return 1;
267 }
268
269 /*
270  * ccp_nakci - process received configure-nak.
271  * Returns 1 iff the nak was OK.
272  */
273 static int
274 ccp_nakci(f, p, len)
275     fsm *f;
276     u_char *p;
277     int len;
278 {
279     ccp_options *go = &ccp_gotoptions[f->unit];
280     ccp_options no;             /* options we've seen already */
281     ccp_options try;            /* options to ask for next time */
282
283     memset(&no, 0, sizeof(no));
284     try = *go;
285
286     if (go->bsd_compress && len >= CILEN_BSD && p[0] == CI_BSD_COMPRESS
287         && p[1] == CILEN_BSD) {
288         no.bsd_compress = 1;
289         /*
290          * Peer wants us to use a different number of bits.
291          */
292         if (p[2] < go->bsd_bits)
293             try.bsd_bits = p[2];
294         p += CILEN_BSD;
295         len -= CILEN_BSD;
296     }
297
298     /*
299      * Have a look at any remaining options...???
300      */
301
302     if (len != 0)
303         return 0;
304
305     if (f->state != OPENED)
306         *go = try;
307     return 1;
308 }
309
310 /*
311  * ccp_rejci - reject some of our suggested compression methods.
312  */
313 static int
314 ccp_rejci(f, p, len)
315     fsm *f;
316     u_char *p;
317     int len;
318 {
319     ccp_options *go = &ccp_gotoptions[f->unit];
320     ccp_options try;            /* options to request next time */
321
322     try = *go;
323
324     if (go->bsd_compress && len >= CILEN_BSD && p[0] == CI_BSD_COMPRESS
325         && p[1] == CILEN_BSD) {
326         if (p[2] != go->bsd_bits)
327             return 0;
328         try.bsd_compress = 0;
329         p += CILEN_BSD;
330         len -= CILEN_BSD;
331     }
332
333     if (len != 0)
334         return 0;
335
336     if (f->state != OPENED)
337         *go = try;
338
339     return 1;
340 }
341
342 /*
343  * ccp_reqci - processed a received configure-request.
344  * Returns CONFACK, CONFNAK or CONFREJ and the packet modified
345  * appropriately.
346  */
347 static int
348 ccp_reqci(f, p, lenp, dont_nak)
349     fsm *f;
350     u_char *p;
351     int *lenp;
352     int dont_nak;
353 {
354     int ret, newret;
355     u_char *p0, *retp;
356     int len, clen, type;
357     ccp_options *ho = &ccp_hisoptions[f->unit];
358     ccp_options *ao = &ccp_allowoptions[f->unit];
359
360     ret = CONFACK;
361     p0 = p;
362     len = *lenp;
363
364     memset(ho, 0, sizeof(ccp_options));
365
366     while (len > 0) {
367         newret = CONFACK;
368         if (len < 2 || p[1] < 2 || p[1] > len) {
369             /* length is bad */
370             clen = len;
371             newret = CONFREJ;
372
373         } else {
374             type = p[0];
375             clen = p[1];
376
377             switch (type) {
378             case CI_BSD_COMPRESS:
379                 if (!ao->bsd_compress || clen != CILEN_BSD) {
380                     newret = CONFREJ;
381                     break;
382                 }
383
384                 ho->bsd_compress = 1;
385                 ho->bsd_bits = p[2];
386                 if (ho->bsd_bits < MIN_BSD_BITS
387                     || ho->bsd_bits > ao->bsd_bits) {
388                     newret = CONFNAK;
389                     if (!dont_nak)
390                         p[2] = (ho->bsd_bits < MIN_BSD_BITS? MIN_BSD_BITS:
391                                 ao->bsd_bits);
392                 }
393
394                 break;
395
396             default:
397                 newret = CONFREJ;
398             }
399         }
400
401         if (!(newret == CONFACK || newret == CONFNAK && ret == CONFREJ)) {
402             /* we're returning this option */
403             if (newret != ret) {
404                 retp = p0;
405                 ret = newret;
406             }
407             if (p != retp)
408                 BCOPY(p, retp, clen);
409             retp += clen;
410         }
411
412         p += clen;
413         len -= clen;
414     }
415
416     if (ret != CONFACK)
417         *lenp = retp - p0;
418     return ret;
419 }
420
421 /*
422  * CCP has come up - inform the kernel driver.
423  */
424 static void
425 ccp_up(f)
426     fsm *f;
427 {
428     ccp_flags_set(f->unit, 1, 1);
429 }
430
431 /*
432  * CCP has gone down - inform the kernel driver.
433  */
434 static void
435 ccp_down(f)
436     fsm *f;
437 {
438     if (ccp_localstate[f->unit] & RACK_PENDING)
439         UNTIMEOUT(ccp_rack_timeout, (caddr_t) f);
440     ccp_localstate[f->unit] = 0;
441     ccp_flags_set(f->unit, 1, 0);
442 }
443
444 /*
445  * Print the contents of a CCP packet.
446  */
447 char *ccp_codenames[] = {
448     "ConfReq", "ConfAck", "ConfNak", "ConfRej",
449     "TermReq", "TermAck", "CodeRej",
450     NULL, NULL, NULL, NULL, NULL, NULL,
451     "ResetReq", "ResetAck",
452 };
453
454 int
455 ccp_printpkt(p, plen, printer, arg)
456     u_char *p;
457     int plen;
458     void (*printer) __ARGS((void *, char *, ...));
459     void *arg;
460 {
461     u_char *p0, *optend;
462     int code, id, len;
463     int optlen;
464
465     p0 = p;
466     if (plen < HEADERLEN)
467         return 0;
468     code = p[0];
469     id = p[1];
470     len = (p[2] << 8) + p[3];
471     if (len < HEADERLEN || len > plen)
472         return 0;
473
474     if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *)
475         && ccp_codenames[code-1] != NULL)
476         printer(arg, " %s", ccp_codenames[code-1]);
477     else
478         printer(arg, " code=0x%x", code);
479     printer(arg, " id=0x%x", id);
480     len -= HEADERLEN;
481     p += HEADERLEN;
482
483     switch (code) {
484     case CONFREQ:
485     case CONFACK:
486     case CONFNAK:
487     case CONFREJ:
488         /* print list of possible compression methods */
489         while (len >= 2) {
490             code = p[0];
491             optlen = p[1];
492             if (optlen < 2 || optlen > len)
493                 break;
494             printer(arg, " <");
495             len -= optlen;
496             optend = p + optlen;
497             switch (code) {
498             case CI_BSD_COMPRESS:
499                 if (optlen >= CILEN_BSD) {
500                     printer(arg, "bsd %d", p[2]);
501                     p += CILEN_BSD;
502                 }
503                 break;
504             }
505             while (p < optend)
506                 printer(arg, " %.2x", *p++);
507             printer(arg, ">");
508         }
509         break;
510     }
511
512     /* dump out the rest of the packet in hex */
513     while (--len >= 0)
514         printer(arg, " %.2x", *p++);
515
516     return p - p0;
517 }
518
519 /*
520  * We have received a packet that the decompressor failed to decompress.
521  * Issue a reset-req (if we haven't issued one recently).
522  */
523 void
524 ccp_datainput(unit, pkt, len)
525     int unit;
526     u_char *pkt;
527     int len;
528 {
529     fsm *f;
530
531     f = &ccp_fsm[unit];
532     if (f->state == OPENED) {
533         if (!(ccp_localstate[unit] & RACK_PENDING)) {
534             fsm_sdata(f, RESETREQ, f->reqid = ++f->id, NULL, 0);
535             TIMEOUT(ccp_rack_timeout, (caddr_t) f, RACKTIMEOUT);
536             ccp_localstate[unit] |= RACK_PENDING;
537         } else
538             ccp_localstate[unit] |= RREQ_REPEAT;
539     }
540 }
541
542 /*
543  * Timeout waiting for reset-ack.
544  */
545 static void
546 ccp_rack_timeout(arg)
547     caddr_t arg;
548 {
549     fsm *f = (fsm *) arg;
550
551     if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) {
552         fsm_sdata(f, RESETREQ, f->reqid, NULL, 0);
553         TIMEOUT(ccp_rack_timeout, (caddr_t) f, RACKTIMEOUT);
554         ccp_localstate[f->unit] &= ~RREQ_REPEAT;
555     } else
556         ccp_localstate[f->unit] &= ~RACK_PENDING;
557 }
558