1 2 /* 3 * ng_vjc.c 4 * 5 * Copyright (c) 1996-1999 Whistle Communications, Inc. 6 * All rights reserved. 7 * 8 * Subject to the following obligations and disclaimer of warranty, use and 9 * redistribution of this software, in source or object code forms, with or 10 * without modifications are expressly permitted by Whistle Communications; 11 * provided, however, that: 12 * 1. Any and all reproductions of the source or object code must include the 13 * copyright notice above and the following disclaimer of warranties; and 14 * 2. No rights are granted, in any manner or form, to use Whistle 15 * Communications, Inc. trademarks, including the mark "WHISTLE 16 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 17 * such appears in the above copyright notice or in the software. 18 * 19 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 20 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 21 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 22 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 23 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 24 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 25 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 26 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 27 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 28 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 29 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 30 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 31 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 35 * OF SUCH DAMAGE. 36 * 37 * Author: Archie Cobbs <archie@whistle.com> 38 * 39 * $FreeBSD$ 40 * $Whistle: ng_vjc.c,v 1.17 1999/11/01 09:24:52 julian Exp $ 41 */ 42 43 /* 44 * This node performs Van Jacobsen IP header (de)compression. 45 * You must have included net/slcompress.c in your kernel compilation. 46 */ 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/errno.h> 51 #include <sys/kernel.h> 52 #include <sys/mbuf.h> 53 #include <sys/malloc.h> 54 #include <sys/errno.h> 55 56 #include <netgraph/ng_message.h> 57 #include <netgraph/netgraph.h> 58 #include <netgraph/ng_vjc.h> 59 60 #include <netinet/in.h> 61 #include <netinet/in_systm.h> 62 #include <netinet/ip.h> 63 #include <netinet/tcp.h> 64 65 #include <net/slcompress.h> 66 67 /* Check agreement with slcompress.c */ 68 #if MAX_STATES != NG_VJC_MAX_CHANNELS 69 #error NG_VJC_MAX_CHANNELS must be the same as MAX_STATES 70 #endif 71 72 /* Maximum length of a compressed TCP VJ header */ 73 #define MAX_VJHEADER 19 74 75 /* Node private data */ 76 struct ng_vjc_private { 77 struct ngm_vjc_config conf; 78 struct slcompress slc; 79 hook_p ip; 80 hook_p vjcomp; 81 hook_p vjuncomp; 82 hook_p vjip; 83 }; 84 typedef struct ng_vjc_private *priv_p; 85 86 #define ERROUT(x) do { error = (x); goto done; } while (0) 87 88 /* Netgraph node methods */ 89 static ng_constructor_t ng_vjc_constructor; 90 static ng_rcvmsg_t ng_vjc_rcvmsg; 91 static ng_shutdown_t ng_vjc_rmnode; 92 static ng_newhook_t ng_vjc_newhook; 93 static ng_rcvdata_t ng_vjc_rcvdata; 94 static ng_disconnect_t ng_vjc_disconnect; 95 96 /* Helper stuff */ 97 static struct mbuf *ng_vjc_pulluphdrs(struct mbuf *m, int knownTCP); 98 99 /* Node type descriptor */ 100 static struct ng_type typestruct = { 101 NG_VERSION, 102 NG_VJC_NODE_TYPE, 103 NULL, 104 ng_vjc_constructor, 105 ng_vjc_rcvmsg, 106 ng_vjc_rmnode, 107 ng_vjc_newhook, 108 NULL, 109 NULL, 110 ng_vjc_rcvdata, 111 ng_vjc_rcvdata, 112 ng_vjc_disconnect, 113 NULL 114 }; 115 NETGRAPH_INIT(vjc, &typestruct); 116 117 /************************************************************************ 118 NETGRAPH NODE METHODS 119 ************************************************************************/ 120 121 /* 122 * Create a new node 123 */ 124 static int 125 ng_vjc_constructor(node_p *nodep) 126 { 127 priv_p priv; 128 int error; 129 130 /* Allocate private structure */ 131 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); 132 if (priv == NULL) 133 return (ENOMEM); 134 bzero(priv, sizeof(*priv)); 135 136 /* Call generic node constructor */ 137 if ((error = ng_make_node_common(&typestruct, nodep))) { 138 FREE(priv, M_NETGRAPH); 139 return (error); 140 } 141 (*nodep)->private = priv; 142 143 /* Done */ 144 return (0); 145 } 146 147 /* 148 * Add a new hook 149 */ 150 static int 151 ng_vjc_newhook(node_p node, hook_p hook, const char *name) 152 { 153 const priv_p priv = (priv_p) node->private; 154 hook_p *hookp; 155 156 /* Get hook */ 157 if (strcmp(name, NG_VJC_HOOK_IP) == 0) 158 hookp = &priv->ip; 159 else if (strcmp(name, NG_VJC_HOOK_VJCOMP) == 0) 160 hookp = &priv->vjcomp; 161 else if (strcmp(name, NG_VJC_HOOK_VJUNCOMP) == 0) 162 hookp = &priv->vjuncomp; 163 else if (strcmp(name, NG_VJC_HOOK_VJIP) == 0) 164 hookp = &priv->vjip; 165 else 166 return (EINVAL); 167 168 /* See if already connected */ 169 if (*hookp) 170 return (EISCONN); 171 172 /* OK */ 173 *hookp = hook; 174 return (0); 175 } 176 177 /* 178 * Receive a control message 179 */ 180 static int 181 ng_vjc_rcvmsg(node_p node, struct ng_mesg *msg, 182 const char *raddr, struct ng_mesg **rptr, hook_p lasthook) 183 { 184 const priv_p priv = (priv_p) node->private; 185 struct ng_mesg *resp = NULL; 186 int error = 0; 187 188 /* Check type cookie */ 189 switch (msg->header.typecookie) { 190 case NGM_VJC_COOKIE: 191 switch (msg->header.cmd) { 192 case NGM_VJC_SET_CONFIG: 193 { 194 struct ngm_vjc_config *const c = 195 (struct ngm_vjc_config *) msg->data; 196 197 if (msg->header.arglen != sizeof(*c)) 198 ERROUT(EINVAL); 199 if ((priv->conf.enableComp || priv->conf.enableDecomp) 200 && (c->enableComp || c->enableDecomp)) 201 ERROUT(EALREADY); 202 if (c->enableComp) { 203 if (c->maxChannel > NG_VJC_MAX_CHANNELS - 1 204 || c->maxChannel < NG_VJC_MIN_CHANNELS - 1) 205 ERROUT(EINVAL); 206 } else 207 c->maxChannel = NG_VJC_MAX_CHANNELS - 1; 208 if (c->enableComp != 0 || c->enableDecomp != 0) { 209 bzero(&priv->slc, sizeof(priv->slc)); 210 sl_compress_init(&priv->slc, c->maxChannel); 211 } 212 priv->conf = *c; 213 break; 214 } 215 case NGM_VJC_GET_STATE: 216 NG_MKRESPONSE(resp, msg, sizeof(priv->slc), M_NOWAIT); 217 if (resp == NULL) 218 ERROUT(ENOMEM); 219 *((struct slcompress *) resp->data) = priv->slc; 220 break; 221 case NGM_VJC_CLR_STATS: 222 priv->slc.sls_packets = 0; 223 priv->slc.sls_compressed = 0; 224 priv->slc.sls_searches = 0; 225 priv->slc.sls_misses = 0; 226 priv->slc.sls_uncompressedin = 0; 227 priv->slc.sls_compressedin = 0; 228 priv->slc.sls_errorin = 0; 229 priv->slc.sls_tossed = 0; 230 break; 231 case NGM_VJC_RECV_ERROR: 232 sl_uncompress_tcp(NULL, 0, TYPE_ERROR, &priv->slc); 233 break; 234 default: 235 error = EINVAL; 236 break; 237 } 238 break; 239 default: 240 error = EINVAL; 241 break; 242 } 243 if (rptr) 244 *rptr = resp; 245 else if (resp) 246 FREE(resp, M_NETGRAPH); 247 248 done: 249 FREE(msg, M_NETGRAPH); 250 return (error); 251 } 252 253 /* 254 * Receive data 255 */ 256 static int 257 ng_vjc_rcvdata(hook_p hook, struct mbuf *m, meta_p meta, 258 struct mbuf **ret_m, meta_p *ret_meta) 259 { 260 const node_p node = hook->node; 261 const priv_p priv = (priv_p) node->private; 262 int error = 0; 263 264 if (hook == priv->ip) { /* outgoing packet */ 265 u_int type = TYPE_IP; 266 267 /* Compress packet if enabled and proto is TCP */ 268 if (priv->conf.enableComp) { 269 struct ip *ip; 270 271 if ((m = ng_vjc_pulluphdrs(m, 0)) == NULL) { 272 NG_FREE_META(meta); 273 return (ENOBUFS); 274 } 275 ip = mtod(m, struct ip *); 276 if (ip->ip_p == IPPROTO_TCP) { 277 const int origLen = m->m_len; 278 279 type = sl_compress_tcp(m, ip, 280 &priv->slc, priv->conf.compressCID); 281 m->m_pkthdr.len += m->m_len - origLen; 282 } 283 } 284 285 /* Dispatch to the appropriate outgoing hook */ 286 switch (type) { 287 case TYPE_IP: 288 hook = priv->vjip; 289 break; 290 case TYPE_UNCOMPRESSED_TCP: 291 hook = priv->vjuncomp; 292 break; 293 case TYPE_COMPRESSED_TCP: 294 hook = priv->vjcomp; 295 break; 296 default: 297 panic("%s: type=%d", __FUNCTION__, type); 298 } 299 } else if (hook == priv->vjcomp) { /* incoming compressed packet */ 300 int vjlen, need2pullup; 301 struct mbuf *hm; 302 u_char *hdr; 303 u_int hlen; 304 305 /* Are we decompressing? */ 306 if (!priv->conf.enableDecomp) { 307 NG_FREE_DATA(m, meta); 308 return (ENXIO); 309 } 310 311 /* Pull up the necessary amount from the mbuf */ 312 need2pullup = MAX_VJHEADER; 313 if (need2pullup > m->m_pkthdr.len) 314 need2pullup = m->m_pkthdr.len; 315 if (m->m_len < need2pullup 316 && (m = m_pullup(m, need2pullup)) == NULL) { 317 priv->slc.sls_errorin++; 318 NG_FREE_META(meta); 319 return (ENOBUFS); 320 } 321 322 /* Uncompress packet to reconstruct TCP/IP header */ 323 vjlen = sl_uncompress_tcp_core(mtod(m, u_char *), 324 m->m_len, m->m_pkthdr.len, TYPE_COMPRESSED_TCP, 325 &priv->slc, &hdr, &hlen); 326 if (vjlen <= 0) { 327 NG_FREE_DATA(m, meta); 328 return (EINVAL); 329 } 330 m_adj(m, vjlen); 331 332 /* Copy the reconstructed TCP/IP headers into a new mbuf */ 333 MGETHDR(hm, M_DONTWAIT, MT_DATA); 334 if (hm == NULL) { 335 priv->slc.sls_errorin++; 336 NG_FREE_DATA(m, meta); 337 return (ENOBUFS); 338 } 339 hm->m_len = 0; 340 hm->m_pkthdr.rcvif = NULL; 341 if (hlen > MHLEN) { /* unlikely, but can happen */ 342 MCLGET(hm, M_DONTWAIT); 343 if ((hm->m_flags & M_EXT) == 0) { 344 m_freem(hm); 345 priv->slc.sls_errorin++; 346 NG_FREE_DATA(m, meta); 347 return (ENOBUFS); 348 } 349 } 350 bcopy(hdr, mtod(hm, u_char *), hlen); 351 hm->m_len = hlen; 352 353 /* Glue TCP/IP headers and rest of packet together */ 354 hm->m_next = m; 355 hm->m_pkthdr.len = hlen + m->m_pkthdr.len; 356 m = hm; 357 hook = priv->ip; 358 } else if (hook == priv->vjuncomp) { /* incoming uncompressed pkt */ 359 u_char *hdr; 360 u_int hlen; 361 362 /* Are we decompressing? */ 363 if (!priv->conf.enableDecomp) { 364 NG_FREE_DATA(m, meta); 365 return (ENXIO); 366 } 367 368 /* Pull up IP+TCP headers */ 369 if ((m = ng_vjc_pulluphdrs(m, 1)) == NULL) { 370 NG_FREE_META(meta); 371 return (ENOBUFS); 372 } 373 374 /* Run packet through uncompressor */ 375 if (sl_uncompress_tcp_core(mtod(m, u_char *), 376 m->m_len, m->m_pkthdr.len, TYPE_UNCOMPRESSED_TCP, 377 &priv->slc, &hdr, &hlen) < 0) { 378 NG_FREE_DATA(m, meta); 379 return (EINVAL); 380 } 381 hook = priv->ip; 382 } else if (hook == priv->vjip) /* incoming regular packet (bypass) */ 383 hook = priv->ip; 384 else 385 panic("%s: unknown hook", __FUNCTION__); 386 387 /* Send result back out */ 388 NG_SEND_DATA(error, hook, m, meta); 389 return (error); 390 } 391 392 /* 393 * Shutdown node 394 */ 395 static int 396 ng_vjc_rmnode(node_p node) 397 { 398 const priv_p priv = (priv_p) node->private; 399 400 node->flags |= NG_INVALID; 401 ng_cutlinks(node); 402 ng_unname(node); 403 bzero(priv, sizeof(*priv)); 404 FREE(priv, M_NETGRAPH); 405 node->private = NULL; 406 ng_unref(node); 407 return (0); 408 } 409 410 /* 411 * Hook disconnection 412 */ 413 static int 414 ng_vjc_disconnect(hook_p hook) 415 { 416 const node_p node = hook->node; 417 const priv_p priv = node->private; 418 419 /* Zero out hook pointer */ 420 if (hook == priv->ip) 421 priv->ip = NULL; 422 else if (hook == priv->vjcomp) 423 priv->vjcomp = NULL; 424 else if (hook == priv->vjuncomp) 425 priv->vjuncomp = NULL; 426 else if (hook == priv->vjip) 427 priv->vjip = NULL; 428 else 429 panic("%s: unknown hook", __FUNCTION__); 430 431 /* Go away if no hooks left */ 432 if (node->numhooks == 0) 433 ng_rmnode(node); 434 return (0); 435 } 436 437 /************************************************************************ 438 HELPER STUFF 439 ************************************************************************/ 440 441 /* 442 * Pull up the full IP and TCP headers of a packet. If packet is not 443 * a TCP packet, just pull up the IP header. 444 */ 445 static struct mbuf * 446 ng_vjc_pulluphdrs(struct mbuf *m, int knownTCP) 447 { 448 struct ip *ip; 449 struct tcphdr *tcp; 450 int ihlen, thlen; 451 452 if (m->m_len < sizeof(*ip) && (m = m_pullup(m, sizeof(*ip))) == NULL) 453 return (NULL); 454 ip = mtod(m, struct ip *); 455 if (!knownTCP && ip->ip_p != IPPROTO_TCP) 456 return (m); 457 ihlen = ip->ip_hl << 2; 458 if (m->m_len < ihlen + sizeof(*tcp)) { 459 if ((m = m_pullup(m, ihlen + sizeof(*tcp))) == NULL) 460 return (NULL); 461 ip = mtod(m, struct ip *); 462 } 463 tcp = (struct tcphdr *)((u_char *)ip + ihlen); 464 thlen = tcp->th_off << 2; 465 if (m->m_len < ihlen + thlen) 466 m = m_pullup(m, ihlen + thlen); 467 return (m); 468 } 469 470