1/* 2 * ppp_comp.c - STREAMS module for kernel-level compression and CCP support. 3 * 4 * Copyright (c) 1994 Paul Mackerras. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * 3. The name(s) of the authors of this software must not be used to 19 * endorse or promote products derived from this software without 20 * prior written permission. 21 * 22 * 4. Redistributions of any form whatsoever must retain the following 23 * acknowledgment: 24 * "This product includes software developed by Paul Mackerras 25 * <paulus@samba.org>". 26 * 27 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO 28 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 29 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 30 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 31 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 32 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 33 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 34 * 35 * $Id: ppp_comp.c,v 1.3 2004/01/17 05:47:55 carlsonj Exp $ 36 */ 37 38/* 39 * This file is used under SVR4, Solaris 2, SunOS 4, and Digital UNIX. 40 */ 41 42#include <sys/types.h> 43#include <sys/param.h> 44#include <sys/errno.h> 45#include <sys/stream.h> 46 47#ifdef SVR4 48#include <sys/conf.h> 49#include <sys/cmn_err.h> 50#include <sys/ddi.h> 51#else 52#include <sys/user.h> 53#ifdef __osf__ 54#include <sys/cmn_err.h> 55#endif 56#endif /* SVR4 */ 57 58#include <net/ppp_defs.h> 59#include <net/pppio.h> 60#include "ppp_mod.h" 61 62#ifdef __osf__ 63#include <sys/mbuf.h> 64#include <sys/protosw.h> 65#endif 66 67#include <netinet/in.h> 68#include <netinet/in_systm.h> 69#include <netinet/ip.h> 70#include <net/vjcompress.h> 71 72#define PACKETPTR mblk_t * 73#include <net/ppp-comp.h> 74 75MOD_OPEN_DECL(ppp_comp_open); 76MOD_CLOSE_DECL(ppp_comp_close); 77static int ppp_comp_rput __P((queue_t *, mblk_t *)); 78static int ppp_comp_rsrv __P((queue_t *)); 79static int ppp_comp_wput __P((queue_t *, mblk_t *)); 80static int ppp_comp_wsrv __P((queue_t *)); 81static void ppp_comp_ccp __P((queue_t *, mblk_t *, int)); 82static int msg_byte __P((mblk_t *, unsigned int)); 83 84/* Extract byte i of message mp. */ 85#define MSG_BYTE(mp, i) ((i) < (mp)->b_wptr - (mp)->b_rptr? (mp)->b_rptr[i]: \ 86 msg_byte((mp), (i))) 87 88/* Is this LCP packet one we have to transmit using LCP defaults? */ 89#define LCP_USE_DFLT(mp) (1 <= (code = MSG_BYTE((mp), 4)) && code <= 7) 90 91#define PPP_COMP_ID 0xbadf 92static struct module_info minfo = { 93#ifdef PRIOQ 94 PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16512, 16384, 95#else 96 PPP_COMP_ID, "ppp_comp", 0, INFPSZ, 16384, 4096, 97#endif 98}; 99 100static struct qinit r_init = { 101 ppp_comp_rput, ppp_comp_rsrv, ppp_comp_open, ppp_comp_close, 102 NULL, &minfo, NULL 103}; 104 105static struct qinit w_init = { 106 ppp_comp_wput, ppp_comp_wsrv, NULL, NULL, NULL, &minfo, NULL 107}; 108 109#if defined(SVR4) && !defined(SOL2) 110int pcmpdevflag = 0; 111#define ppp_compinfo pcmpinfo 112#endif 113struct streamtab ppp_compinfo = { 114 &r_init, &w_init, NULL, NULL 115}; 116 117int ppp_comp_count; /* number of module instances in use */ 118 119#ifdef __osf__ 120 121static void ppp_comp_alloc __P((comp_state_t *)); 122typedef struct memreq { 123 unsigned char comp_opts[20]; 124 int cmd; 125 int thread_status; 126 char *returned_mem; 127} memreq_t; 128 129#endif 130 131typedef struct comp_state { 132 int flags; 133 int mru; 134 int mtu; 135 int unit; 136 struct compressor *xcomp; 137 void *xstate; 138 struct compressor *rcomp; 139 void *rstate; 140 struct vjcompress vj_comp; 141 int vj_last_ierrors; 142 struct pppstat stats; 143#ifdef __osf__ 144 memreq_t memreq; 145 thread_t thread; 146#endif 147} comp_state_t; 148 149 150#ifdef __osf__ 151extern task_t first_task; 152#endif 153 154/* Bits in flags are as defined in pppio.h. */ 155#define CCP_ERR (CCP_ERROR | CCP_FATALERROR) 156#define LAST_MOD 0x1000000 /* no ppp modules below us */ 157#define DBGLOG 0x2000000 /* log debugging stuff */ 158 159#define MAX_IPHDR 128 /* max TCP/IP header size */ 160#define MAX_VJHDR 20 /* max VJ compressed header size (?) */ 161 162#undef MIN /* just in case */ 163#define MIN(a, b) ((a) < (b)? (a): (b)) 164 165/* 166 * List of compressors we know about. 167 */ 168 169#if DO_BSD_COMPRESS 170extern struct compressor ppp_bsd_compress; 171#endif 172#if DO_DEFLATE 173extern struct compressor ppp_deflate, ppp_deflate_draft; 174#endif 175 176struct compressor *ppp_compressors[] = { 177#if DO_BSD_COMPRESS 178 &ppp_bsd_compress, 179#endif 180#if DO_DEFLATE 181 &ppp_deflate, 182 &ppp_deflate_draft, 183#endif 184 NULL 185}; 186 187/* 188 * STREAMS module entry points. 189 */ 190MOD_OPEN(ppp_comp_open) 191{ 192 comp_state_t *cp; 193#ifdef __osf__ 194 thread_t thread; 195#endif 196 197 if (q->q_ptr == NULL) { 198 cp = (comp_state_t *) ALLOC_SLEEP(sizeof(comp_state_t)); 199 if (cp == NULL) 200 OPEN_ERROR(ENOSR); 201 bzero((caddr_t)cp, sizeof(comp_state_t)); 202 WR(q)->q_ptr = q->q_ptr = (caddr_t) cp; 203 cp->mru = PPP_MRU; 204 cp->mtu = PPP_MTU; 205 cp->xstate = NULL; 206 cp->rstate = NULL; 207 vj_compress_init(&cp->vj_comp, -1); 208#ifdef __osf__ 209 if (!(thread = kernel_thread_w_arg(first_task, ppp_comp_alloc, (void *)cp))) 210 OPEN_ERROR(ENOSR); 211 cp->thread = thread; 212#endif 213 ++ppp_comp_count; 214 qprocson(q); 215 } 216 return 0; 217} 218 219MOD_CLOSE(ppp_comp_close) 220{ 221 comp_state_t *cp; 222 223 qprocsoff(q); 224 cp = (comp_state_t *) q->q_ptr; 225 if (cp != NULL) { 226 if (cp->xstate != NULL) 227 (*cp->xcomp->comp_free)(cp->xstate); 228 if (cp->rstate != NULL) 229 (*cp->rcomp->decomp_free)(cp->rstate); 230#ifdef __osf__ 231 if (!cp->thread) 232 printf("ppp_comp_close: NULL thread!\n"); 233 else 234 thread_terminate(cp->thread); 235#endif 236 FREE(cp, sizeof(comp_state_t)); 237 q->q_ptr = NULL; 238 OTHERQ(q)->q_ptr = NULL; 239 --ppp_comp_count; 240 } 241 return 0; 242} 243 244#ifdef __osf__ 245 246/* thread for calling back to a compressor's memory allocator 247 * Needed for Digital UNIX since it's VM can't handle requests 248 * for large amounts of memory without blocking. The thread 249 * provides a context in which we can call a memory allocator 250 * that may block. 251 */ 252static void 253ppp_comp_alloc(comp_state_t *cp) 254{ 255 int len, cmd; 256 unsigned char *compressor_options; 257 thread_t thread; 258 void *(*comp_allocator)(); 259 260 261#if defined(MAJOR_VERSION) && (MAJOR_VERSION <= 2) 262 263 /* In 2.x and earlier the argument gets passed 264 * in the thread structure itself. Yuck. 265 */ 266 thread = current_thread(); 267 cp = thread->reply_port; 268 thread->reply_port = PORT_NULL; 269 270#endif 271 272 for (;;) { 273 assert_wait((vm_offset_t)&cp->memreq.thread_status, TRUE); 274 thread_block(); 275 276 if (thread_should_halt(current_thread())) 277 thread_halt_self(); 278 cmd = cp->memreq.cmd; 279 compressor_options = &cp->memreq.comp_opts[0]; 280 len = compressor_options[1]; 281 if (cmd == PPPIO_XCOMP) { 282 cp->memreq.returned_mem = cp->xcomp->comp_alloc(compressor_options, len); 283 if (!cp->memreq.returned_mem) { 284 cp->memreq.thread_status = ENOSR; 285 } else { 286 cp->memreq.thread_status = 0; 287 } 288 } else { 289 cp->memreq.returned_mem = cp->rcomp->decomp_alloc(compressor_options, len); 290 if (!cp->memreq.returned_mem) { 291 cp->memreq.thread_status = ENOSR; 292 } else { 293 cp->memreq.thread_status = 0; 294 } 295 } 296 } 297} 298 299#endif /* __osf__ */ 300 301/* here's the deal with memory allocation under Digital UNIX. 302 * Some other may also benefit from this... 303 * We can't ask for huge chunks of memory in a context where 304 * the caller can't be put to sleep (like, here.) The alloc 305 * is likely to fail. Instead we do this: the first time we 306 * get called, kick off a thread to do the allocation. Return 307 * immediately to the caller with EAGAIN, as an indication that 308 * they should send down the ioctl again. By the time the 309 * second call comes in it's likely that the memory allocation 310 * thread will have returned with the requested memory. We will 311 * continue to return EAGAIN however until the thread has completed. 312 * When it has, we return zero (and the memory) if the allocator 313 * was successful and ENOSR otherwise. 314 * 315 * Callers of the RCOMP and XCOMP ioctls are encouraged (but not 316 * required) to loop for some number of iterations with a small 317 * delay in the loop body (for instance a 1/10-th second "sleep" 318 * via select.) 319 */ 320static int 321ppp_comp_wput(q, mp) 322 queue_t *q; 323 mblk_t *mp; 324{ 325 struct iocblk *iop; 326 comp_state_t *cp; 327 int error, len, n; 328 int flags, mask; 329 mblk_t *np; 330 struct compressor **comp; 331 struct ppp_stats *psp; 332 struct ppp_comp_stats *csp; 333 unsigned char *opt_data; 334 int nxslots, nrslots; 335 336 cp = (comp_state_t *) q->q_ptr; 337 if (cp == 0) { 338 DPRINT("cp == 0 in ppp_comp_wput\n"); 339 freemsg(mp); 340 return 0; 341 } 342 343 switch (mp->b_datap->db_type) { 344 345 case M_DATA: 346 putq(q, mp); 347 break; 348 349 case M_IOCTL: 350 iop = (struct iocblk *) mp->b_rptr; 351 error = EINVAL; 352 switch (iop->ioc_cmd) { 353 354 case PPPIO_CFLAGS: 355 /* set/get CCP state */ 356 if (iop->ioc_count != 2 * sizeof(int)) 357 break; 358 if (mp->b_cont == 0) { 359 DPRINT1("ppp_comp_wput/%d: PPPIO_CFLAGS b_cont = 0!\n", cp->unit); 360 break; 361 } 362 flags = ((int *) mp->b_cont->b_rptr)[0]; 363 mask = ((int *) mp->b_cont->b_rptr)[1]; 364 cp->flags = (cp->flags & ~mask) | (flags & mask); 365 if ((mask & CCP_ISOPEN) && (flags & CCP_ISOPEN) == 0) { 366 if (cp->xstate != NULL) { 367 (*cp->xcomp->comp_free)(cp->xstate); 368 cp->xstate = NULL; 369 } 370 if (cp->rstate != NULL) { 371 (*cp->rcomp->decomp_free)(cp->rstate); 372 cp->rstate = NULL; 373 } 374 cp->flags &= ~CCP_ISUP; 375 } 376 error = 0; 377 iop->ioc_count = sizeof(int); 378 ((int *) mp->b_cont->b_rptr)[0] = cp->flags; 379 mp->b_cont->b_wptr = mp->b_cont->b_rptr + sizeof(int); 380 break; 381 382 case PPPIO_VJINIT: 383 /* 384 * Initialize VJ compressor/decompressor 385 */ 386 if (iop->ioc_count != 2) 387 break; 388 if (mp->b_cont == 0) { 389 DPRINT1("ppp_comp_wput/%d: PPPIO_VJINIT b_cont = 0!\n", cp->unit); 390 break; 391 } 392 nxslots = mp->b_cont->b_rptr[0] + 1; 393 nrslots = mp->b_cont->b_rptr[1] + 1; 394 if (nxslots > MAX_STATES || nrslots > MAX_STATES) 395 break; 396 vj_compress_init(&cp->vj_comp, nxslots); 397 cp->vj_last_ierrors = cp->stats.ppp_ierrors; 398 error = 0; 399 iop->ioc_count = 0; 400 break; 401 402 case PPPIO_XCOMP: 403 case PPPIO_RCOMP: 404 if (iop->ioc_count <= 0) 405 break; 406 if (mp->b_cont == 0) { 407 DPRINT1("ppp_comp_wput/%d: PPPIO_[XR]COMP b_cont = 0!\n", cp->unit); 408 break; 409 } 410 opt_data = mp->b_cont->b_rptr; 411 len = mp->b_cont->b_wptr - opt_data; 412 if (len > iop->ioc_count) 413 len = iop->ioc_count; 414 if (opt_data[1] < 2 || opt_data[1] > len) 415 break; 416 for (comp = ppp_compressors; *comp != NULL; ++comp) 417 if ((*comp)->compress_proto == opt_data[0]) { 418 /* here's the handler! */ 419 error = 0; 420#ifndef __osf__ 421 if (iop->ioc_cmd == PPPIO_XCOMP) { 422 /* A previous call may have fetched memory for a compressor 423 * that's now being retired or reset. Free it using it's 424 * mechanism for freeing stuff. 425 */ 426 if (cp->xstate != NULL) { 427 (*cp->xcomp->comp_free)(cp->xstate); 428 cp->xstate = NULL; 429 } 430 cp->xcomp = *comp; 431 cp->xstate = (*comp)->comp_alloc(opt_data, len); 432 if (cp->xstate == NULL) 433 error = ENOSR; 434 } else { 435 if (cp->rstate != NULL) { 436 (*cp->rcomp->decomp_free)(cp->rstate); 437 cp->rstate = NULL; 438 } 439 cp->rcomp = *comp; 440 cp->rstate = (*comp)->decomp_alloc(opt_data, len); 441 if (cp->rstate == NULL) 442 error = ENOSR; 443 } 444#else 445 if ((error = cp->memreq.thread_status) != EAGAIN) 446 if (iop->ioc_cmd == PPPIO_XCOMP) { 447 if (cp->xstate) { 448 (*cp->xcomp->comp_free)(cp->xstate); 449 cp->xstate = 0; 450 } 451 /* sanity check for compressor options 452 */ 453 if (sizeof (cp->memreq.comp_opts) < len) { 454 printf("can't handle options for compressor %d (%d)\n", opt_data[0], 455 opt_data[1]); 456 cp->memreq.thread_status = ENOSR; 457 cp->memreq.returned_mem = 0; 458 } 459 /* fill in request for the thread and kick it off 460 */ 461 if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) { 462 bcopy(opt_data, cp->memreq.comp_opts, len); 463 cp->memreq.cmd = PPPIO_XCOMP; 464 cp->xcomp = *comp; 465 error = cp->memreq.thread_status = EAGAIN; 466 thread_wakeup((vm_offset_t)&cp->memreq.thread_status); 467 } else { 468 cp->xstate = cp->memreq.returned_mem; 469 cp->memreq.returned_mem = 0; 470 cp->memreq.thread_status = 0; 471 } 472 } else { 473 if (cp->rstate) { 474 (*cp->rcomp->decomp_free)(cp->rstate); 475 cp->rstate = NULL; 476 } 477 if (sizeof (cp->memreq.comp_opts) < len) { 478 printf("can't handle options for compressor %d (%d)\n", opt_data[0], 479 opt_data[1]); 480 cp->memreq.thread_status = ENOSR; 481 cp->memreq.returned_mem = 0; 482 } 483 if (cp->memreq.thread_status == 0 && !cp->memreq.returned_mem) { 484 bcopy(opt_data, cp->memreq.comp_opts, len); 485 cp->memreq.cmd = PPPIO_RCOMP; 486 cp->rcomp = *comp; 487 error = cp->memreq.thread_status = EAGAIN; 488 thread_wakeup((vm_offset_t)&cp->memreq.thread_status); 489 } else { 490 cp->rstate = cp->memreq.returned_mem; 491 cp->memreq.returned_mem = 0; 492 cp->memreq.thread_status = 0; 493 } 494 } 495#endif 496 break; 497 } 498 iop->ioc_count = 0; 499 break; 500 501 case PPPIO_GETSTAT: 502 if ((cp->flags & LAST_MOD) == 0) { 503 error = -1; /* let the ppp_ahdl module handle it */ 504 break; 505 } 506 np = allocb(sizeof(struct ppp_stats), BPRI_HI); 507 if (np == 0) { 508 error = ENOSR; 509 break; 510 } 511 if (mp->b_cont != 0) 512 freemsg(mp->b_cont); 513 mp->b_cont = np; 514 psp = (struct ppp_stats *) np->b_wptr; 515 np->b_wptr += sizeof(struct ppp_stats); 516 iop->ioc_count = sizeof(struct ppp_stats); 517 psp->p = cp->stats; 518 psp->vj = cp->vj_comp.stats; 519 error = 0; 520 break; 521 522 case PPPIO_GETCSTAT: 523 np = allocb(sizeof(struct ppp_comp_stats), BPRI_HI); 524 if (np == 0) { 525 error = ENOSR; 526 break; 527 } 528 if (mp->b_cont != 0) 529 freemsg(mp->b_cont); 530 mp->b_cont = np; 531 csp = (struct ppp_comp_stats *) np->b_wptr; 532 np->b_wptr += sizeof(struct ppp_comp_stats); 533 iop->ioc_count = sizeof(struct ppp_comp_stats); 534 bzero((caddr_t)csp, sizeof(struct ppp_comp_stats)); 535 if (cp->xstate != 0) 536 (*cp->xcomp->comp_stat)(cp->xstate, &csp->c); 537 if (cp->rstate != 0) 538 (*cp->rcomp->decomp_stat)(cp->rstate, &csp->d); 539 error = 0; 540 break; 541 542 case PPPIO_DEBUG: 543 if (iop->ioc_count != sizeof(int)) 544 break; 545 if (mp->b_cont == 0) { 546 DPRINT1("ppp_comp_wput/%d: PPPIO_DEBUG b_cont = 0!\n", cp->unit); 547 break; 548 } 549 n = *(int *)mp->b_cont->b_rptr; 550 if (n == PPPDBG_LOG + PPPDBG_COMP) { 551 DPRINT1("ppp_comp%d: debug log enabled\n", cp->unit); 552 cp->flags |= DBGLOG; 553 error = 0; 554 iop->ioc_count = 0; 555 } else { 556 error = -1; 557 } 558 break; 559 560 case PPPIO_LASTMOD: 561 cp->flags |= LAST_MOD; 562 error = 0; 563 break; 564 565 default: 566 error = -1; 567 break; 568 } 569 570 if (error < 0) 571 putnext(q, mp); 572 else if (error == 0) { 573 mp->b_datap->db_type = M_IOCACK; 574 qreply(q, mp); 575 } else { 576 mp->b_datap->db_type = M_IOCNAK; 577 iop->ioc_error = error; 578 iop->ioc_count = 0; 579 qreply(q, mp); 580 } 581 break; 582 583 case M_CTL: 584 switch (*mp->b_rptr) { 585 case PPPCTL_MTU: 586 cp->mtu = ((unsigned short *)mp->b_rptr)[1]; 587 break; 588 case PPPCTL_MRU: 589 cp->mru = ((unsigned short *)mp->b_rptr)[1]; 590 break; 591 case PPPCTL_UNIT: 592 cp->unit = mp->b_rptr[1]; 593 break; 594 } 595 putnext(q, mp); 596 break; 597 598 default: 599 putnext(q, mp); 600 } 601 602 return 0; 603} 604 605static int 606ppp_comp_wsrv(q) 607 queue_t *q; 608{ 609 mblk_t *mp, *cmp = NULL; 610 comp_state_t *cp; 611 int len, proto, type, hlen, code; 612 struct ip *ip; 613 unsigned char *vjhdr, *dp; 614 615 cp = (comp_state_t *) q->q_ptr; 616 if (cp == 0) { 617 DPRINT("cp == 0 in ppp_comp_wsrv\n"); 618 return 0; 619 } 620 621 while ((mp = getq(q)) != 0) { 622 /* assert(mp->b_datap->db_type == M_DATA) */ 623#ifdef PRIOQ 624 if (!bcanputnext(q,mp->b_band)) 625#else 626 if (!canputnext(q)) 627#endif /* PRIOQ */ 628 { 629 putbq(q, mp); 630 break; 631 } 632 633 /* 634 * First check the packet length and work out what the protocol is. 635 */ 636 len = msgdsize(mp); 637 if (len < PPP_HDRLEN) { 638 DPRINT1("ppp_comp_wsrv: bogus short packet (%d)\n", len); 639 freemsg(mp); 640 cp->stats.ppp_oerrors++; 641 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR); 642 continue; 643 } 644 proto = (MSG_BYTE(mp, 2) << 8) + MSG_BYTE(mp, 3); 645 646 /* 647 * Make sure we've got enough data in the first mblk 648 * and that we are its only user. 649 */ 650 if (proto == PPP_CCP) 651 hlen = len; 652 else if (proto == PPP_IP) 653 hlen = PPP_HDRLEN + MAX_IPHDR; 654 else 655 hlen = PPP_HDRLEN; 656 if (hlen > len) 657 hlen = len; 658 if (mp->b_wptr < mp->b_rptr + hlen || mp->b_datap->db_ref > 1) { 659 PULLUP(mp, hlen); 660 if (mp == 0) { 661 DPRINT1("ppp_comp_wsrv: pullup failed (%d)\n", hlen); 662 cp->stats.ppp_oerrors++; 663 putctl1(RD(q)->q_next, M_CTL, PPPCTL_OERROR); 664 continue; 665 } 666 } 667 668 /* 669 * Do VJ compression if requested. 670 */ 671 if (proto == PPP_IP && (cp->flags & COMP_VJC)) { 672 ip = (struct ip *) (mp->b_rptr + PPP_HDRLEN); 673 if (ip->ip_p == IPPROTO_TCP) { 674 type = vj_compress_tcp(ip, len - PPP_HDRLEN, &cp->vj_comp, 675 (cp->flags & COMP_VJCCID), &vjhdr); 676 switch (type) { 677 case TYPE_UNCOMPRESSED_TCP: 678 mp->b_rptr[3] = proto = PPP_VJC_UNCOMP; 679 break; 680 case TYPE_COMPRESSED_TCP: 681 dp = vjhdr - PPP_HDRLEN; 682 dp[1] = mp->b_rptr[1]; /* copy control field */ 683 dp[0] = mp->b_rptr[0]; /* copy address field */ 684 dp[2] = 0; /* set protocol field */ 685 dp[3] = proto = PPP_VJC_COMP; 686 mp->b_rptr = dp; 687 break; 688 } 689 } 690 } 691 692 /* 693 * Do packet compression if enabled. 694 */ 695 if (proto == PPP_CCP) 696 ppp_comp_ccp(q, mp, 0); 697 else if (proto != PPP_LCP && (cp->flags & CCP_COMP_RUN) 698 && cp->xstate != NULL) { 699 len = msgdsize(mp); 700 (*cp->xcomp->compress)(cp->xstate, &cmp, mp, len, 701 (cp->flags & CCP_ISUP? cp->mtu + PPP_HDRLEN: 0)); 702 if (cmp != NULL) { 703#ifdef PRIOQ 704 cmp->b_band=mp->b_band; 705#endif /* PRIOQ */ 706 freemsg(mp); 707 mp = cmp; 708 } 709 } 710 711 /* 712 * Do address/control and protocol compression if enabled. 713 */ 714 if ((cp->flags & COMP_AC) 715 && !(proto == PPP_LCP && LCP_USE_DFLT(mp))) { 716 mp->b_rptr += 2; /* drop the address & ctrl fields */ 717 if (proto < 0x100 && (cp->flags & COMP_PROT)) 718 ++mp->b_rptr; /* drop the high protocol byte */ 719 } else if (proto < 0x100 && (cp->flags & COMP_PROT)) { 720 /* shuffle up the address & ctrl fields */ 721 mp->b_rptr[2] = mp->b_rptr[1]; 722 mp->b_rptr[1] = mp->b_rptr[0]; 723 ++mp->b_rptr; 724 } 725 726 cp->stats.ppp_opackets++; 727 cp->stats.ppp_obytes += msgdsize(mp); 728 putnext(q, mp); 729 } 730 731 return 0; 732} 733 734static int 735ppp_comp_rput(q, mp) 736 queue_t *q; 737 mblk_t *mp; 738{ 739 comp_state_t *cp; 740 struct iocblk *iop; 741 struct ppp_stats *psp; 742 743 cp = (comp_state_t *) q->q_ptr; 744 if (cp == 0) { 745 DPRINT("cp == 0 in ppp_comp_rput\n"); 746 freemsg(mp); 747 return 0; 748 } 749 750 switch (mp->b_datap->db_type) { 751 752 case M_DATA: 753 putq(q, mp); 754 break; 755 756 case M_IOCACK: 757 iop = (struct iocblk *) mp->b_rptr; 758 switch (iop->ioc_cmd) { 759 case PPPIO_GETSTAT: 760 /* 761 * Catch this on the way back from the ppp_ahdl module 762 * so we can fill in the VJ stats. 763 */ 764 if (mp->b_cont == 0 || iop->ioc_count != sizeof(struct ppp_stats)) 765 break; 766 psp = (struct ppp_stats *) mp->b_cont->b_rptr; 767 psp->vj = cp->vj_comp.stats; 768 break; 769 } 770 putnext(q, mp); 771 break; 772 773 case M_CTL: 774 switch (mp->b_rptr[0]) { 775 case PPPCTL_IERROR: 776 ++cp->stats.ppp_ierrors; 777 break; 778 case PPPCTL_OERROR: 779 ++cp->stats.ppp_oerrors; 780 break; 781 } 782 putnext(q, mp); 783 break; 784 785 default: 786 putnext(q, mp); 787 } 788 789 return 0; 790} 791 792static int 793ppp_comp_rsrv(q) 794 queue_t *q; 795{ 796 int proto, rv, i; 797 mblk_t *mp, *dmp = NULL, *np; 798 uchar_t *dp, *iphdr; 799 comp_state_t *cp; 800 int len, hlen, vjlen; 801 u_int iphlen; 802 803 cp = (comp_state_t *) q->q_ptr; 804 if (cp == 0) { 805 DPRINT("cp == 0 in ppp_comp_rsrv\n"); 806 return 0; 807 } 808 809 while ((mp = getq(q)) != 0) { 810 /* assert(mp->b_datap->db_type == M_DATA) */ 811 if (!canputnext(q)) { 812 putbq(q, mp); 813 break; 814 } 815 816 len = msgdsize(mp); 817 cp->stats.ppp_ibytes += len; 818 cp->stats.ppp_ipackets++; 819 820 /* 821 * First work out the protocol and where the PPP header ends. 822 */ 823 i = 0; 824 proto = MSG_BYTE(mp, 0); 825 if (proto == PPP_ALLSTATIONS) { 826 i = 2; 827 proto = MSG_BYTE(mp, 2); 828 } 829 if ((proto & 1) == 0) { 830 ++i; 831 proto = (proto << 8) + MSG_BYTE(mp, i); 832 } 833 hlen = i + 1; 834 835 /* 836 * Now reconstruct a complete, contiguous PPP header at the 837 * start of the packet. 838 */ 839 if (hlen < ((cp->flags & DECOMP_AC)? 0: 2) 840 + ((cp->flags & DECOMP_PROT)? 1: 2)) { 841 /* count these? */ 842 goto bad; 843 } 844 if (mp->b_rptr + hlen > mp->b_wptr) { 845 adjmsg(mp, hlen); /* XXX check this call */ 846 hlen = 0; 847 } 848 if (hlen != PPP_HDRLEN) { 849 /* 850 * We need to put some bytes on the front of the packet 851 * to make a full-length PPP header. 852 * If we can put them in *mp, we do, otherwise we 853 * tack another mblk on the front. 854 * XXX we really shouldn't need to carry around 855 * the address and control at this stage. 856 */ 857 dp = mp->b_rptr + hlen - PPP_HDRLEN; 858 if (dp < mp->b_datap->db_base || mp->b_datap->db_ref > 1) { 859 np = allocb(PPP_HDRLEN, BPRI_MED); 860 if (np == 0) 861 goto bad; 862 np->b_cont = mp; 863 mp->b_rptr += hlen; 864 mp = np; 865 dp = mp->b_wptr; 866 mp->b_wptr += PPP_HDRLEN; 867 } else 868 mp->b_rptr = dp; 869 870 dp[0] = PPP_ALLSTATIONS; 871 dp[1] = PPP_UI; 872 dp[2] = proto >> 8; 873 dp[3] = proto; 874 } 875 876 /* 877 * Now see if we have a compressed packet to decompress, 878 * or a CCP packet to take notice of. 879 */ 880 proto = PPP_PROTOCOL(mp->b_rptr); 881 if (proto == PPP_CCP) { 882 len = msgdsize(mp); 883 if (mp->b_wptr < mp->b_rptr + len) { 884 PULLUP(mp, len); 885 if (mp == 0) 886 goto bad; 887 } 888 ppp_comp_ccp(q, mp, 1); 889 } else if (proto == PPP_COMP) { 890 if ((cp->flags & CCP_ISUP) 891 && (cp->flags & CCP_DECOMP_RUN) && cp->rstate 892 && (cp->flags & CCP_ERR) == 0) { 893 rv = (*cp->rcomp->decompress)(cp->rstate, mp, &dmp); 894 switch (rv) { 895 case DECOMP_OK: 896 freemsg(mp); 897 mp = dmp; 898 if (mp == NULL) { 899 /* no error, but no packet returned either. */ 900 continue; 901 } 902 break; 903 case DECOMP_ERROR: 904 cp->flags |= CCP_ERROR; 905 ++cp->stats.ppp_ierrors; 906 putctl1(q->q_next, M_CTL, PPPCTL_IERROR); 907 break; 908 case DECOMP_FATALERROR: 909 cp->flags |= CCP_FATALERROR; 910 ++cp->stats.ppp_ierrors; 911 putctl1(q->q_next, M_CTL, PPPCTL_IERROR); 912 break; 913 } 914 } 915 } else if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) { 916 (*cp->rcomp->incomp)(cp->rstate, mp); 917 } 918 919 /* 920 * Now do VJ decompression. 921 */ 922 proto = PPP_PROTOCOL(mp->b_rptr); 923 if (proto == PPP_VJC_COMP || proto == PPP_VJC_UNCOMP) { 924 len = msgdsize(mp) - PPP_HDRLEN; 925 if ((cp->flags & DECOMP_VJC) == 0 || len <= 0) 926 goto bad; 927 928 /* 929 * Advance past the ppp header. 930 * Here we assume that the whole PPP header is in the first mblk. 931 */ 932 np = mp; 933 dp = np->b_rptr + PPP_HDRLEN; 934 if (dp >= mp->b_wptr) { 935 np = np->b_cont; 936 dp = np->b_rptr; 937 } 938 939 /* 940 * Make sure we have sufficient contiguous data at this point. 941 */ 942 hlen = (proto == PPP_VJC_COMP)? MAX_VJHDR: MAX_IPHDR; 943 if (hlen > len) 944 hlen = len; 945 if (np->b_wptr < dp + hlen || np->b_datap->db_ref > 1) { 946 PULLUP(mp, hlen + PPP_HDRLEN); 947 if (mp == 0) 948 goto bad; 949 np = mp; 950 dp = np->b_rptr + PPP_HDRLEN; 951 } 952 953 if (proto == PPP_VJC_COMP) { 954 /* 955 * Decompress VJ-compressed packet. 956 * First reset compressor if an input error has occurred. 957 */ 958 if (cp->stats.ppp_ierrors != cp->vj_last_ierrors) { 959 if (cp->flags & DBGLOG) 960 DPRINT1("ppp%d: resetting VJ\n", cp->unit); 961 vj_uncompress_err(&cp->vj_comp); 962 cp->vj_last_ierrors = cp->stats.ppp_ierrors; 963 } 964 965 vjlen = vj_uncompress_tcp(dp, np->b_wptr - dp, len, 966 &cp->vj_comp, &iphdr, &iphlen); 967 if (vjlen < 0) { 968 if (cp->flags & DBGLOG) 969 DPRINT2("ppp%d: vj_uncomp_tcp failed, pkt len %d\n", 970 cp->unit, len); 971 ++cp->vj_last_ierrors; /* so we don't reset next time */ 972 goto bad; 973 } 974 975 /* drop ppp and vj headers off */ 976 if (mp != np) { 977 freeb(mp); 978 mp = np; 979 } 980 mp->b_rptr = dp + vjlen; 981 982 /* allocate a new mblk for the ppp and ip headers */ 983 if ((np = allocb(iphlen + PPP_HDRLEN + 4, BPRI_MED)) == 0) 984 goto bad; 985 dp = np->b_rptr; /* prepend mblk with TCP/IP hdr */ 986 dp[0] = PPP_ALLSTATIONS; /* reconstruct PPP header */ 987 dp[1] = PPP_UI; 988 dp[2] = PPP_IP >> 8; 989 dp[3] = PPP_IP; 990 bcopy((caddr_t)iphdr, (caddr_t)dp + PPP_HDRLEN, iphlen); 991 np->b_wptr = dp + iphlen + PPP_HDRLEN; 992 np->b_cont = mp; 993 994 /* XXX there seems to be a bug which causes panics in strread 995 if we make an mbuf with only the IP header in it :-( */ 996 if (mp->b_wptr - mp->b_rptr > 4) { 997 bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, 4); 998 mp->b_rptr += 4; 999 np->b_wptr += 4; 1000 } else { 1001 bcopy((caddr_t)mp->b_rptr, (caddr_t)np->b_wptr, 1002 mp->b_wptr - mp->b_rptr); 1003 np->b_wptr += mp->b_wptr - mp->b_rptr; 1004 np->b_cont = mp->b_cont; 1005 freeb(mp); 1006 } 1007 1008 mp = np; 1009 1010 } else { 1011 /* 1012 * "Decompress" a VJ-uncompressed packet. 1013 */ 1014 cp->vj_last_ierrors = cp->stats.ppp_ierrors; 1015 if (!vj_uncompress_uncomp(dp, hlen, &cp->vj_comp)) { 1016 if (cp->flags & DBGLOG) 1017 DPRINT2("ppp%d: vj_uncomp_uncomp failed, pkt len %d\n", 1018 cp->unit, len); 1019 ++cp->vj_last_ierrors; /* don't need to reset next time */ 1020 goto bad; 1021 } 1022 mp->b_rptr[3] = PPP_IP; /* fix up the PPP protocol field */ 1023 } 1024 } 1025 1026 putnext(q, mp); 1027 continue; 1028 1029 bad: 1030 if (mp != 0) 1031 freemsg(mp); 1032 cp->stats.ppp_ierrors++; 1033 putctl1(q->q_next, M_CTL, PPPCTL_IERROR); 1034 } 1035 1036 return 0; 1037} 1038 1039/* 1040 * Handle a CCP packet being sent or received. 1041 * Here all the data in the packet is in a single mbuf. 1042 */ 1043static void 1044ppp_comp_ccp(q, mp, rcvd) 1045 queue_t *q; 1046 mblk_t *mp; 1047 int rcvd; 1048{ 1049 int len, clen; 1050 comp_state_t *cp; 1051 unsigned char *dp; 1052 1053 len = msgdsize(mp); 1054 if (len < PPP_HDRLEN + CCP_HDRLEN) 1055 return; 1056 1057 cp = (comp_state_t *) q->q_ptr; 1058 dp = mp->b_rptr + PPP_HDRLEN; 1059 len -= PPP_HDRLEN; 1060 clen = CCP_LENGTH(dp); 1061 if (clen > len) 1062 return; 1063 1064 switch (CCP_CODE(dp)) { 1065 case CCP_CONFREQ: 1066 case CCP_TERMREQ: 1067 case CCP_TERMACK: 1068 cp->flags &= ~CCP_ISUP; 1069 break; 1070 1071 case CCP_CONFACK: 1072 if ((cp->flags & (CCP_ISOPEN | CCP_ISUP)) == CCP_ISOPEN 1073 && clen >= CCP_HDRLEN + CCP_OPT_MINLEN 1074 && clen >= CCP_HDRLEN + CCP_OPT_LENGTH(dp + CCP_HDRLEN)) { 1075 if (!rcvd) { 1076 if (cp->xstate != NULL 1077 && (*cp->xcomp->comp_init) 1078 (cp->xstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN, 1079 cp->unit, 0, ((cp->flags & DBGLOG) != 0))) 1080 cp->flags |= CCP_COMP_RUN; 1081 } else { 1082 if (cp->rstate != NULL 1083 && (*cp->rcomp->decomp_init) 1084 (cp->rstate, dp + CCP_HDRLEN, clen - CCP_HDRLEN, 1085 cp->unit, 0, cp->mru, ((cp->flags & DBGLOG) != 0))) 1086 cp->flags = (cp->flags & ~CCP_ERR) | CCP_DECOMP_RUN; 1087 } 1088 } 1089 break; 1090 1091 case CCP_RESETACK: 1092 if (cp->flags & CCP_ISUP) { 1093 if (!rcvd) { 1094 if (cp->xstate && (cp->flags & CCP_COMP_RUN)) 1095 (*cp->xcomp->comp_reset)(cp->xstate); 1096 } else { 1097 if (cp->rstate && (cp->flags & CCP_DECOMP_RUN)) { 1098 (*cp->rcomp->decomp_reset)(cp->rstate); 1099 cp->flags &= ~CCP_ERROR; 1100 } 1101 } 1102 } 1103 break; 1104 } 1105} 1106 1107#if 0 1108dump_msg(mp) 1109 mblk_t *mp; 1110{ 1111 dblk_t *db; 1112 1113 while (mp != 0) { 1114 db = mp->b_datap; 1115 DPRINT2("mp=%x cont=%x ", mp, mp->b_cont); 1116 DPRINT3("rptr=%x wptr=%x datap=%x\n", mp->b_rptr, mp->b_wptr, db); 1117 DPRINT2(" base=%x lim=%x", db->db_base, db->db_lim); 1118 DPRINT2(" ref=%d type=%d\n", db->db_ref, db->db_type); 1119 mp = mp->b_cont; 1120 } 1121} 1122#endif 1123 1124static int 1125msg_byte(mp, i) 1126 mblk_t *mp; 1127 unsigned int i; 1128{ 1129 while (mp != 0 && i >= mp->b_wptr - mp->b_rptr) 1130 mp = mp->b_cont; 1131 if (mp == 0) 1132 return -1; 1133 return mp->b_rptr[i]; 1134} 1135