1/* 2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 ppp_comp.c - Compression protocol for ppp. 26 27 based on if_ppp.c from bsd 28 29 * 30 * Copyright (c) 1989 Carnegie Mellon University. 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms are permitted 34 * provided that the above copyright notice and this paragraph are 35 * duplicated in all such forms and that any documentation, 36 * advertising materials, and other materials related to such 37 * distribution and use acknowledge that the software was developed 38 * by Carnegie Mellon University. The name of the 39 * University may not be used to endorse or promote products derived 40 * from this software without specific prior written permission. 41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS ORpppall 42 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 43 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 44 * 45 * Drew D. Perkins 46 * Carnegie Mellon University 47 * 4910 Forbes Ave. 48 * Pittsburgh, PA 15213 49 * (412) 268-8576 50 * ddp@andrew.cmu.edu 51 * 52 * Based on: 53 * @(#)if_sl.c 7.6.1.2 (Berkeley) 2/15/89 54 * 55 * Copyright (c) 1987 Regents of the University of California. 56 * All rights reserved. 57 * 58 * Redistribution and use in source and binary forms are permitted 59 * provided that the above copyright notice and this paragraph are 60 * duplicated in all such forms and that any documentation, 61 * advertising materials, and other materials related to such 62 * distribution and use acknowledge that the software was developed 63 * by the University of California, Berkeley. The name of the 64 * University may not be used to endorse or promote products derived 65 * from this software without specific prior written permission. 66 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 67 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 68 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 69 * 70 */ 71 72 73#include <sys/types.h> 74#include <sys/param.h> 75#include <sys/systm.h> 76#include <sys/proc.h> 77#include <sys/mbuf.h> 78#include <sys/socket.h> 79#include <net/if.h> 80#include <sys/vm.h> 81#if 0 82#include <net/netisr.h> 83#endif 84#include <sys/syslog.h> 85#include <netinet/in.h> 86 87 88#include "ppp_defs.h" // public ppp values 89#include "ppp_ip.h" 90#include "if_ppplink.h" // public link API 91#include "ppp_domain.h" 92#include "ppp_if.h" 93#include "ppp_domain.h" 94#include "ppp_comp.h" 95 96/* ----------------------------------------------------------------------------- 97Definitions 98----------------------------------------------------------------------------- */ 99 100struct ppp_comp { 101 102 TAILQ_ENTRY(ppp_comp) next; 103 104 /* compressor identifier */ 105 u_int32_t protocol; /* CCP compression protocol number */ 106 107 void *userdata; /* user data to pass to the compressor */ 108 109 /* compression call back functions */ 110 111 void *(*comp_alloc) 112 (u_char *options, int opt_len); /* Allocate space for a compressor (transmit side) */ 113 void (*comp_free) 114 (void *state); /* Free space used by a compressor */ 115 int (*comp_init) /* Initialize a compressor */ 116 (void *state, u_char *options, int opt_len, 117 int unit, int hdrlen, int mtu, int debug); 118 void (*comp_reset) 119 (void *state); /* Reset a compressor */ 120 int (*compress) /* Compress a packet */ 121 (void *state, mbuf_t *m); 122 void (*comp_stat) /* Return compression statistics */ 123 (void *state, struct compstat *stats); 124 125 126 /* decompression call back functions */ 127 128 void *(*decomp_alloc) /* Allocate space for a decompressor (receive side) */ 129 (u_char *options, int opt_len); 130 void (*decomp_free) 131 (void *state); /* Free space used by a decompressor */ 132 int (*decomp_init) /* Initialize a decompressor */ 133 (void *state, u_char *options, int opt_len, 134 int unit, int hdrlen, int mru, int debug); 135 void (*decomp_reset) /* Reset a decompressor */ 136 (void *state); 137 int (*decompress) /* Decompress a packet. */ 138 (void *state, mbuf_t *m); 139 void (*incomp) /* Update state for an incompressible packet received */ 140 (void *state, mbuf_t m); 141 void (*decomp_stat) /* Return decompression statistics */ 142 (void *state, struct compstat *stats); 143}; 144 145 146/* ----------------------------------------------------------------------------- 147Forward declarations 148----------------------------------------------------------------------------- */ 149 150struct ppp_comp *ppp_comp_find(u_int32_t proto); 151 152/* ----------------------------------------------------------------------------- 153Globals 154----------------------------------------------------------------------------- */ 155static TAILQ_HEAD(, ppp_comp) ppp_comp_head; 156 157/* ----------------------------------------------------------------------------- 158----------------------------------------------------------------------------- */ 159int ppp_comp_init() 160{ 161 TAILQ_INIT(&ppp_comp_head); 162 return 0; 163} 164 165/* ----------------------------------------------------------------------------- 166----------------------------------------------------------------------------- */ 167int ppp_comp_dispose() 168{ 169 struct ppp_comp *comp; 170 171 while ((comp = TAILQ_FIRST(&ppp_comp_head))) { 172 TAILQ_REMOVE(&ppp_comp_head, comp, next); 173 FREE(comp, M_TEMP); 174 } 175 176 return 0; 177} 178 179/* ----------------------------------------------------------------------------- 180----------------------------------------------------------------------------- */ 181struct ppp_comp *ppp_comp_find(u_int32_t proto) 182{ 183 struct ppp_comp *comp; 184 185 TAILQ_FOREACH(comp, &ppp_comp_head, next) 186 if (comp->protocol == proto) 187 return comp; 188 189 return 0; 190} 191 192/* ----------------------------------------------------------------------------- 193----------------------------------------------------------------------------- */ 194int ppp_comp_register(struct ppp_comp_reg *compreg, ppp_comp_ref *compref) 195{ 196 struct ppp_comp *comp; 197 198 /* sanity check */ 199 if (compreg == NULL 200 || compreg->comp_alloc == NULL 201 || compreg->comp_free == NULL 202 || compreg->comp_init == NULL 203 || compreg->comp_reset == NULL 204 || compreg->compress == NULL 205 || compreg->comp_stat == NULL 206 || compreg->decomp_alloc == NULL 207 || compreg->decomp_free == NULL 208 || compreg->decomp_init == NULL 209 || compreg->decomp_reset == NULL 210 || compreg->decompress == NULL 211 || compreg->incomp == NULL 212 || compreg->decomp_stat == NULL) 213 return(EINVAL); 214 215 comp = ppp_comp_find(compreg->compress_proto); 216 if (comp != NULL) 217 return(EEXIST); 218 219 MALLOC(comp, struct ppp_comp *, sizeof(*comp), M_TEMP, M_WAITOK); 220 if (comp == NULL) 221 return(ENOMEM); 222 223 bzero((char *)comp, sizeof(*comp)); 224 225 comp->protocol = compreg->compress_proto; 226 comp->comp_alloc = compreg->comp_alloc; 227 comp->comp_free = compreg->comp_free; 228 comp->comp_init = compreg->comp_init; 229 comp->comp_reset = compreg->comp_reset; 230 comp->compress = compreg->compress; 231 comp->comp_stat = compreg->comp_stat; 232 comp->decomp_alloc = compreg->decomp_alloc; 233 comp->decomp_free = compreg->decomp_free; 234 comp->decomp_init = compreg->decomp_init; 235 comp->decomp_reset = compreg->decomp_reset; 236 comp->decompress = compreg->decompress; 237 comp->incomp = compreg->incomp; 238 comp->decomp_stat = compreg->decomp_stat; 239 240 TAILQ_INSERT_TAIL(&ppp_comp_head, comp, next); 241 242 *compref = comp; 243 return 0; 244} 245 246/* ----------------------------------------------------------------------------- 247----------------------------------------------------------------------------- */ 248int ppp_comp_deregister(ppp_comp_ref *compref) 249{ 250 struct ppp_comp *comp = (struct ppp_comp *)compref; 251 252 if (comp == NULL) /* sanity check */ 253 return(EINVAL); 254 255 TAILQ_REMOVE(&ppp_comp_head, comp, next); 256 257 FREE(comp, M_TEMP); 258 return(0); 259} 260 261/* ----------------------------------------------------------------------------- 262----------------------------------------------------------------------------- */ 263int ppp_comp_setcompressor(struct ppp_if *wan, struct ppp_option_data *odp) 264{ 265 int error = 0; 266 u_int32_t nb; 267 struct ppp_comp *cp; 268 u_char ccp_option[CCP_MAX_OPTION_LENGTH]; 269 user_addr_t ptr; 270 int transmit; 271 272 if (proc_is64bit(current_proc())) { 273 struct ppp_option_data64 *odp64 = (struct ppp_option_data64 *)odp; 274 275 nb = odp64->length; 276 ptr = odp64->ptr; 277 transmit = odp64->transmit; 278 } else { 279 struct ppp_option_data32 *odp32 = (struct ppp_option_data32 *)odp; 280 281 nb = odp32->length; 282 ptr = CAST_USER_ADDR_T(odp32->ptr); 283 transmit = odp32->transmit; 284 } 285 286 if (nb > sizeof(ccp_option)) 287 nb = sizeof(ccp_option); 288 289 if ((error = copyin(ptr, ccp_option, nb))) 290 return (error); 291 292 if (ccp_option[1] < 2) /* preliminary check on the length byte */ 293 return (EINVAL); 294 295 cp = ppp_comp_find(ccp_option[0]); 296 if (cp == 0) { 297 LOGDBG(wan->net, ("ppp%d: no compressor for [%x %x %x], %x\n", 298 ifnet_unit(wan->net), ccp_option[0], ccp_option[1], 299 ccp_option[2], nb)); 300 301 return EINVAL; /* no handler found */ 302 } 303 304 if (transmit) { 305 if (wan->xc_state) 306 (*wan->xcomp->comp_free)(wan->xc_state); 307 wan->xcomp = cp; 308 wan->xc_state = cp->comp_alloc(ccp_option, nb); 309 if (!wan->xc_state) { 310 error = ENOMEM; 311 LOGDBG(wan->net, ("ppp%d: comp_alloc failed\n", ifnet_unit(wan->net))); 312 } 313 wan->sc_flags &= ~SC_COMP_RUN; 314 } 315 else { 316 if (wan->rc_state) 317 (*wan->rcomp->decomp_free)(wan->rc_state); 318 wan->rcomp = cp; 319 wan->rc_state = cp->decomp_alloc(ccp_option, nb); 320 if (!wan->rc_state) { 321 error = ENOMEM; 322 LOGDBG(wan->net, ("ppp%d: decomp_alloc failed\n", ifnet_unit(wan->net))); 323 } 324 wan->sc_flags &= ~SC_DECOMP_RUN; 325 } 326 327 return error; 328} 329 330 331/* ----------------------------------------------------------------------------- 332----------------------------------------------------------------------------- */ 333void ppp_comp_getstats(struct ppp_if *wan, struct ppp_comp_stats *stats) 334{ 335 336 bzero(stats, sizeof(struct ppp_comp_stats)); 337 if (wan->xc_state) 338 (*wan->xcomp->comp_stat)(wan->xc_state, &stats->c); 339 if (wan->rc_state) 340 (*wan->rcomp->decomp_stat)(wan->rc_state, &stats->d); 341} 342 343/* ----------------------------------------------------------------------------- 344Handle a CCP packet. `rcvd' is 1 if the packet was received, 3450 if it is about to be transmitted. 346mbuf points to the ccp payload (doesn't include FF03 and 80FD) 347----------------------------------------------------------------------------- */ 348void ppp_comp_ccp(struct ppp_if *wan, mbuf_t m, int rcvd) 349{ 350 u_char *p = mbuf_data(m); // no alignment issue as p is *u_char. 351 int slen; 352 353 slen = CCP_LENGTH(p); 354 if (slen > mbuf_pkthdr_len(m)) { 355 LOGDBG(wan->net, ("ppp_comp_ccp: not enough data in mbuf (expected = %d, got = %d)\n", 356 slen, mbuf_pkthdr_len(m))); 357 return; 358 } 359 360 switch (CCP_CODE(p)) { 361 case CCP_CONFREQ: 362 case CCP_TERMREQ: 363 case CCP_TERMACK: 364 /* CCP must be going down - disable compression */ 365 wan->sc_flags &= ~(rcvd ? SC_COMP_RUN : SC_DECOMP_RUN); 366 break; 367 368 case CCP_CONFACK: 369 if (wan->sc_flags & SC_CCP_OPEN && !(wan->sc_flags & SC_CCP_UP) 370 && slen >= CCP_HDRLEN + CCP_OPT_MINLEN 371 && slen >= CCP_OPT_LENGTH(p + CCP_HDRLEN) + CCP_HDRLEN) { 372 if (rcvd) { 373 /* peer is agreeing to send compressed packets. */ 374 if (wan->rc_state 375 && (*wan->rcomp->decomp_init) 376 (wan->rc_state, p + CCP_HDRLEN, slen - CCP_HDRLEN, 377 ifnet_unit(wan->net), 0, wan->mru, ifnet_flags(wan->net) & IFF_DEBUG)) { 378 wan->sc_flags |= SC_DECOMP_RUN; 379 wan->sc_flags &= ~(SC_DC_ERROR | SC_DC_FERROR); 380 } 381 } else { 382 /* we're agreeing to send compressed packets. */ 383 if (wan->xc_state 384 && (*wan->xcomp->comp_init) 385 (wan->xc_state, p + CCP_HDRLEN, slen - CCP_HDRLEN, 386 ifnet_unit(wan->net), 0, ifnet_mtu(wan->net), ifnet_flags(wan->net) & IFF_DEBUG)) { 387 wan->sc_flags |= SC_COMP_RUN; 388 } 389 } 390 } 391 break; 392 393 case CCP_RESETACK: 394 if (wan->sc_flags & SC_CCP_UP) { 395 if (rcvd) { 396 if (wan->rc_state && (wan->sc_flags & SC_DECOMP_RUN)) { 397 (*wan->rcomp->decomp_reset)(wan->rc_state); 398 wan->sc_flags &= ~SC_DC_ERROR; 399 } 400 } else { 401 if (wan->xc_state && (wan->sc_flags & SC_COMP_RUN)) 402 (*wan->xcomp->comp_reset)(wan->xc_state); 403 } 404 } 405 break; 406 } 407} 408 409/* ----------------------------------------------------------------------------- 410CCP is down; free (de)compressor state if necessary. 411----------------------------------------------------------------------------- */ 412void ppp_comp_close(struct ppp_if *wan) 413{ 414 415 wan->sc_flags &= ~(SC_CCP_OPEN | SC_CCP_UP | SC_COMP_RUN | SC_DECOMP_RUN); 416 if (wan->xc_state) { 417 (*wan->xcomp->comp_free)(wan->xc_state); 418 wan->xc_state = NULL; 419 } 420 if (wan->rc_state) { 421 (*wan->rcomp->decomp_free)(wan->rc_state); 422 wan->rc_state = NULL; 423 } 424} 425 426/* ----------------------------------------------------------------------------- 427----------------------------------------------------------------------------- */ 428void ppp_comp_logmbuf(char *msg, mbuf_t m) 429{ 430 int i, lcount, copycount, count; 431 char lbuf[16], *data; 432 433 if (m == NULL) 434 return; 435 436 IOLog("%s: \n", msg); 437 438 for (count = mbuf_len(m), data = mbuf_data(m); m != NULL; ) { 439 /* build a line of output */ 440 for(lcount = 0; lcount < sizeof(lbuf); lcount += copycount) { 441 if (!count) { 442 m = mbuf_next(m); 443 if (m == NULL) 444 break; 445 count = mbuf_len(m); 446 data = mbuf_data(m); 447 } 448 copycount = (count > sizeof(lbuf) - lcount) ? sizeof(lbuf) - lcount : count; 449 bcopy(data, &lbuf[lcount], copycount); 450 data += copycount; 451 count -= copycount; 452 } 453 454 /* output line (hex 1st, then ascii) */ 455 IOLog("%s: 0x ", msg); 456 for(i = 0; i < lcount; i++) { 457 if (i == 8) IOLog(" "); 458 IOLog("%02x ", (u_char)lbuf[i]); 459 } 460 for( ; i < sizeof(lbuf); i++) { 461 if (i == 8) IOLog(" "); 462 IOLog(" "); 463 } 464 IOLog(" '"); 465 for(i = 0; i < lcount; i++) 466 IOLog("%c",(lbuf[i]>=040 && lbuf[i]<=0176)?lbuf[i]:'.'); 467 IOLog("'\n"); 468 } 469} 470 471/* ----------------------------------------------------------------------------- 472return codes : 473> 0 : compression done, buffer has changed, return new lenght 4740 : compression not done, buffer has not changed, lenght is unchanged 475< 0 : compression not done because of error, return -error 476----------------------------------------------------------------------------- */ 477int ppp_comp_compress(struct ppp_if *wan, mbuf_t *m) 478{ 479 if (wan->xc_state == 0 || (wan->sc_flags & SC_CCP_UP) == 0) 480 return COMP_NOTDONE; 481 482 return wan->xcomp->compress(wan->xc_state, m); 483} 484 485/* ----------------------------------------------------------------------------- 486----------------------------------------------------------------------------- */ 487int ppp_comp_incompress(struct ppp_if *wan, mbuf_t m) 488{ 489 490 if ((wan->rc_state == 0) || (wan->sc_flags & (SC_DC_ERROR | SC_DC_FERROR))) 491 return 0; 492 493 /* Uncompressed frame - pass to decompressor so it can update its dictionary if necessary. */ 494 wan->rcomp->incomp(wan->rc_state, m); 495 496 return 0; 497} 498 499/* ----------------------------------------------------------------------------- 500----------------------------------------------------------------------------- */ 501int ppp_comp_decompress(struct ppp_if *wan, mbuf_t *m) 502{ 503 int err; 504 505 if ((wan->rc_state == 0) || (wan->sc_flags & (SC_DC_ERROR | SC_DC_FERROR))) 506 return DECOMP_ERROR; 507 508 err = wan->rcomp->decompress(wan->rc_state, m); 509 if (err != DECOMP_OK) { 510 if (err == DECOMP_FATALERROR) 511 wan->sc_flags |= SC_DC_FERROR; 512 wan->sc_flags |= SC_DC_ERROR; 513 ppp_if_error(wan->net); 514 } 515 516 return err; 517} 518