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) 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 { 259 const node_p node = hook->node; 260 const priv_p priv = (priv_p) node->private; 261 int error = 0; 262 263 if (hook == priv->ip) { /* outgoing packet */ 264 u_int type = TYPE_IP; 265 266 /* Compress packet if enabled and proto is TCP */ 267 if (priv->conf.enableComp) { 268 struct ip *ip; 269 270 if ((m = ng_vjc_pulluphdrs(m, 0)) == NULL) { 271 NG_FREE_META(meta); 272 return (ENOBUFS); 273 } 274 ip = mtod(m, struct ip *); 275 if (ip->ip_p == IPPROTO_TCP) { 276 const int origLen = m->m_len; 277 278 type = sl_compress_tcp(m, ip, 279 &priv->slc, priv->conf.compressCID); 280 m->m_pkthdr.len += m->m_len - origLen; 281 } 282 } 283 284 /* Dispatch to the appropriate outgoing hook */ 285 switch (type) { 286 case TYPE_IP: 287 hook = priv->vjip; 288 break; 289 case TYPE_UNCOMPRESSED_TCP: 290 hook = priv->vjuncomp; 291 break; 292 case TYPE_COMPRESSED_TCP: 293 hook = priv->vjcomp; 294 break; 295 default: 296 panic("%s: type=%d", __FUNCTION__, type); 297 } 298 } else if (hook == priv->vjcomp) { /* incoming compressed packet */ 299 int vjlen, need2pullup; 300 struct mbuf *hm; 301 u_char *hdr; 302 u_int hlen; 303 304 /* Are we decompressing? */ 305 if (!priv->conf.enableDecomp) { 306 NG_FREE_DATA(m, meta); 307 return (ENXIO); 308 } 309 310 /* Pull up the necessary amount from the mbuf */ 311 need2pullup = MAX_VJHEADER; 312 if (need2pullup > m->m_pkthdr.len) 313 need2pullup = m->m_pkthdr.len; 314 if (m->m_len < need2pullup 315 && (m = m_pullup(m, need2pullup)) == NULL) { 316 priv->slc.sls_errorin++; 317 NG_FREE_META(meta); 318 return (ENOBUFS); 319 } 320 321 /* Uncompress packet to reconstruct TCP/IP header */ 322 vjlen = sl_uncompress_tcp_core(mtod(m, u_char *), 323 m->m_len, m->m_pkthdr.len, TYPE_COMPRESSED_TCP, 324 &priv->slc, &hdr, &hlen); 325 if (vjlen <= 0) { 326 NG_FREE_DATA(m, meta); 327 return (EINVAL); 328 } 329 m_adj(m, vjlen); 330 331 /* Copy the reconstructed TCP/IP headers into a new mbuf */ 332 MGETHDR(hm, M_DONTWAIT, MT_DATA); 333 if (hm == NULL) { 334 priv->slc.sls_errorin++; 335 NG_FREE_DATA(m, meta); 336 return (ENOBUFS); 337 } 338 hm->m_len = 0; 339 hm->m_pkthdr.rcvif = NULL; 340 if (hlen > MHLEN) { /* unlikely, but can happen */ 341 MCLGET(hm, M_DONTWAIT); 342 if ((hm->m_flags & M_EXT) == 0) { 343 m_freem(hm); 344 priv->slc.sls_errorin++; 345 NG_FREE_DATA(m, meta); 346 return (ENOBUFS); 347 } 348 } 349 bcopy(hdr, mtod(hm, u_char *), hlen); 350 hm->m_len = hlen; 351 352 /* Glue TCP/IP headers and rest of packet together */ 353 hm->m_next = m; 354 hm->m_pkthdr.len = hlen + m->m_pkthdr.len; 355 m = hm; 356 hook = priv->ip; 357 } else if (hook == priv->vjuncomp) { /* incoming uncompressed pkt */ 358 u_char *hdr; 359 u_int hlen; 360 361 /* Are we decompressing? */ 362 if (!priv->conf.enableDecomp) { 363 NG_FREE_DATA(m, meta); 364 return (ENXIO); 365 } 366 367 /* Pull up IP+TCP headers */ 368 if ((m = ng_vjc_pulluphdrs(m, 1)) == NULL) { 369 NG_FREE_META(meta); 370 return (ENOBUFS); 371 } 372 373 /* Run packet through uncompressor */ 374 if (sl_uncompress_tcp_core(mtod(m, u_char *), 375 m->m_len, m->m_pkthdr.len, TYPE_UNCOMPRESSED_TCP, 376 &priv->slc, &hdr, &hlen) < 0) { 377 NG_FREE_DATA(m, meta); 378 return (EINVAL); 379 } 380 hook = priv->ip; 381 } else if (hook == priv->vjip) /* incoming regular packet (bypass) */ 382 hook = priv->ip; 383 else 384 panic("%s: unknown hook", __FUNCTION__); 385 386 /* Send result back out */ 387 NG_SEND_DATA(error, hook, m, meta); 388 return (error); 389 } 390 391 /* 392 * Shutdown node 393 */ 394 static int 395 ng_vjc_rmnode(node_p node) 396 { 397 const priv_p priv = (priv_p) node->private; 398 399 node->flags |= NG_INVALID; 400 ng_cutlinks(node); 401 ng_unname(node); 402 bzero(priv, sizeof(*priv)); 403 FREE(priv, M_NETGRAPH); 404 node->private = NULL; 405 ng_unref(node); 406 return (0); 407 } 408 409 /* 410 * Hook disconnection 411 */ 412 static int 413 ng_vjc_disconnect(hook_p hook) 414 { 415 const node_p node = hook->node; 416 const priv_p priv = node->private; 417 418 /* Zero out hook pointer */ 419 if (hook == priv->ip) 420 priv->ip = NULL; 421 else if (hook == priv->vjcomp) 422 priv->vjcomp = NULL; 423 else if (hook == priv->vjuncomp) 424 priv->vjuncomp = NULL; 425 else if (hook == priv->vjip) 426 priv->vjip = NULL; 427 else 428 panic("%s: unknown hook", __FUNCTION__); 429 430 /* Go away if no hooks left */ 431 if (node->numhooks == 0) 432 ng_rmnode(node); 433 return (0); 434 } 435 436 /************************************************************************ 437 HELPER STUFF 438 ************************************************************************/ 439 440 /* 441 * Pull up the full IP and TCP headers of a packet. If packet is not 442 * a TCP packet, just pull up the IP header. 443 */ 444 static struct mbuf * 445 ng_vjc_pulluphdrs(struct mbuf *m, int knownTCP) 446 { 447 struct ip *ip; 448 struct tcphdr *tcp; 449 int ihlen, thlen; 450 451 if (m->m_len < sizeof(*ip) && (m = m_pullup(m, sizeof(*ip))) == NULL) 452 return (NULL); 453 ip = mtod(m, struct ip *); 454 if (!knownTCP && ip->ip_p != IPPROTO_TCP) 455 return (m); 456 ihlen = ip->ip_hl << 2; 457 if (m->m_len < ihlen + sizeof(*tcp)) { 458 if ((m = m_pullup(m, ihlen + sizeof(*tcp))) == NULL) 459 return (NULL); 460 ip = mtod(m, struct ip *); 461 } 462 tcp = (struct tcphdr *)((u_char *)ip + ihlen); 463 thlen = tcp->th_off << 2; 464 if (m->m_len < ihlen + thlen) 465 m = m_pullup(m, ihlen + thlen); 466 return (m); 467 } 468 469