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