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