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