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/conf.h> 55 #include <sys/errno.h> 56 #include <sys/socket.h> 57 #include <sys/syslog.h> 58 59 #include <netgraph/ng_message.h> 60 #include <netgraph/netgraph.h> 61 #include <netgraph/ng_vjc.h> 62 63 #include <netinet/in.h> 64 #include <netinet/in_systm.h> 65 #include <netinet/ip.h> 66 #include <netinet/tcp.h> 67 68 #include <net/slcompress.h> 69 70 /* Check agreement with slcompress.c */ 71 #if MAX_STATES != NG_VJC_MAX_CHANNELS 72 #error NG_VJC_MAX_CHANNELS must be the same as MAX_STATES 73 #endif 74 75 #define MAX_VJHEADER 16 76 77 /* Node private data */ 78 struct private { 79 struct ngm_vjc_config conf; 80 struct slcompress slc; 81 hook_p ip; 82 hook_p vjcomp; 83 hook_p vjuncomp; 84 hook_p vjip; 85 }; 86 typedef struct private *priv_p; 87 88 #define ERROUT(x) do { error = (x); goto done; } while (0) 89 90 /* Netgraph node methods */ 91 static ng_constructor_t ng_vjc_constructor; 92 static ng_rcvmsg_t ng_vjc_rcvmsg; 93 static ng_shutdown_t ng_vjc_rmnode; 94 static ng_newhook_t ng_vjc_newhook; 95 static ng_rcvdata_t ng_vjc_rcvdata; 96 static ng_disconnect_t ng_vjc_disconnect; 97 98 /* Helper stuff */ 99 static struct mbuf *ng_vjc_pulluphdrs(struct mbuf *m); 100 101 /* Node type descriptor */ 102 static struct ng_type typestruct = { 103 NG_VERSION, 104 NG_VJC_NODE_TYPE, 105 NULL, 106 ng_vjc_constructor, 107 ng_vjc_rcvmsg, 108 ng_vjc_rmnode, 109 ng_vjc_newhook, 110 NULL, 111 NULL, 112 ng_vjc_rcvdata, 113 ng_vjc_rcvdata, 114 ng_vjc_disconnect 115 }; 116 NETGRAPH_INIT(vjc, &typestruct); 117 118 /************************************************************************ 119 NETGRAPH NODE METHODS 120 ************************************************************************/ 121 122 /* 123 * Create a new node 124 */ 125 static int 126 ng_vjc_constructor(node_p *nodep) 127 { 128 priv_p priv; 129 int error; 130 131 /* Allocate private structure */ 132 MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_WAITOK); 133 if (priv == NULL) 134 return (ENOMEM); 135 bzero(priv, sizeof(*priv)); 136 137 /* Call generic node constructor */ 138 if ((error = ng_make_node_common(&typestruct, nodep))) { 139 FREE(priv, M_NETGRAPH); 140 return (error); 141 } 142 (*nodep)->private = priv; 143 144 /* Done */ 145 return (0); 146 } 147 148 /* 149 * Add a new hook 150 */ 151 static int 152 ng_vjc_newhook(node_p node, hook_p hook, const char *name) 153 { 154 const priv_p priv = (priv_p) node->private; 155 hook_p *hookp; 156 157 /* Get hook */ 158 if (!strcmp(name, NG_VJC_HOOK_IP)) 159 hookp = &priv->ip; 160 else if (!strcmp(name, NG_VJC_HOOK_VJCOMP)) 161 hookp = &priv->vjcomp; 162 else if (!strcmp(name, NG_VJC_HOOK_VJUNCOMP)) 163 hookp = &priv->vjuncomp; 164 else if (!strcmp(name, NG_VJC_HOOK_VJIP)) 165 hookp = &priv->vjip; 166 else 167 return (EINVAL); 168 169 /* See if already connected */ 170 if (*hookp) 171 return (EISCONN); 172 173 /* OK */ 174 *hookp = hook; 175 return (0); 176 } 177 178 /* 179 * Receive a control message 180 */ 181 static int 182 ng_vjc_rcvmsg(node_p node, struct ng_mesg *msg, 183 const char *raddr, struct ng_mesg **rptr) 184 { 185 const priv_p priv = (priv_p) node->private; 186 struct ng_mesg *resp = NULL; 187 int error = 0; 188 189 /* Check type cookie */ 190 switch (msg->header.typecookie) { 191 case NGM_VJC_COOKIE: 192 switch (msg->header.cmd) { 193 case NGM_VJC_SET_CONFIG: 194 { 195 struct ngm_vjc_config *const c = 196 (struct ngm_vjc_config *) msg->data; 197 198 if (msg->header.arglen != sizeof(*c)) 199 ERROUT(EINVAL); 200 if ((priv->conf.enableComp || priv->conf.enableDecomp) 201 && (c->enableComp || c->enableDecomp)) 202 ERROUT(EALREADY); 203 if (c->enableComp) { 204 if (c->numChannels > NG_VJC_MAX_CHANNELS 205 || c->numChannels < NG_VJC_MIN_CHANNELS) 206 ERROUT(EINVAL); 207 } else { 208 c->numChannels = NG_VJC_MAX_CHANNELS; 209 } 210 if (c->enableComp != 0 || c->enableDecomp != 0) { 211 bzero(&priv->slc, sizeof(priv->slc)); 212 sl_compress_init(&priv->slc, c->numChannels); 213 } 214 priv->conf = *c; 215 break; 216 } 217 case NGM_VJC_GET_STATE: 218 NG_MKRESPONSE(resp, msg, sizeof(priv->slc), M_NOWAIT); 219 if (resp == NULL) 220 ERROUT(ENOMEM); 221 *((struct slcompress *) resp->data) = priv->slc; 222 break; 223 case NGM_VJC_CLR_STATS: 224 priv->slc.sls_packets = 0; 225 priv->slc.sls_compressed = 0; 226 priv->slc.sls_searches = 0; 227 priv->slc.sls_misses = 0; 228 priv->slc.sls_uncompressedin = 0; 229 priv->slc.sls_compressedin = 0; 230 priv->slc.sls_errorin = 0; 231 priv->slc.sls_tossed = 0; 232 break; 233 case NGM_VJC_RECV_ERROR: 234 priv->slc.flags |= SLF_TOSS; 235 break; 236 default: 237 error = EINVAL; 238 break; 239 } 240 break; 241 default: 242 error = EINVAL; 243 break; 244 } 245 if (rptr) 246 *rptr = resp; 247 else if (resp) 248 FREE(resp, M_NETGRAPH); 249 250 done: 251 FREE(msg, M_NETGRAPH); 252 return (error); 253 } 254 255 /* 256 * Receive data 257 */ 258 static int 259 ng_vjc_rcvdata(hook_p hook, struct mbuf *m, meta_p meta) 260 { 261 const node_p node = hook->node; 262 const priv_p priv = (priv_p) node->private; 263 int error = 0; 264 265 if (hook == priv->ip) { /* outgoing packet */ 266 u_int type; 267 268 if (!priv->conf.enableComp) /* compression not enabled */ 269 type = TYPE_IP; 270 else { 271 struct ip *ip; 272 273 if ((m = ng_vjc_pulluphdrs(m)) == NULL) 274 ERROUT(ENOBUFS); 275 ip = mtod(m, struct ip *); 276 type = (ip->ip_p == IPPROTO_TCP) ? 277 sl_compress_tcp(m, ip, 278 &priv->slc, priv->conf.compressCID) : TYPE_IP; 279 } 280 switch (type) { 281 case TYPE_IP: 282 hook = priv->vjip; 283 break; 284 case TYPE_UNCOMPRESSED_TCP: 285 hook = priv->vjuncomp; 286 break; 287 case TYPE_COMPRESSED_TCP: 288 hook = priv->vjcomp; 289 break; 290 default: 291 panic(__FUNCTION__); 292 } 293 } else if (hook == priv->vjcomp) { /* incoming compressed packet */ 294 int vjlen; 295 u_int hlen; 296 u_char *hdr; 297 struct mbuf *mp; 298 299 /* Are we decompressing? */ 300 if (!priv->conf.enableDecomp) { 301 m_freem(m); 302 m = NULL; 303 ERROUT(ENETDOWN); 304 } 305 306 /* Uncompress packet to reconstruct TCP/IP header */ 307 if (m->m_len < MAX_VJHEADER && !(m = m_pullup(m, MAX_VJHEADER))) 308 ERROUT(ENOBUFS); 309 vjlen = sl_uncompress_tcp_core(mtod(m, u_char *), 310 m->m_len, m->m_pkthdr.len, TYPE_COMPRESSED_TCP, 311 &priv->slc, &hdr, &hlen); 312 if (vjlen <= 0) { 313 m_freem(m); 314 m = NULL; 315 ERROUT(EINVAL); 316 } 317 318 /* Copy the reconstructed TCP/IP headers into a new mbuf */ 319 MGETHDR(mp, M_DONTWAIT, MT_DATA); 320 if (!mp) 321 goto compfailmem; 322 mp->m_len = 0; 323 mp->m_next = NULL; 324 if (hlen > MHLEN) { 325 MCLGET(mp, M_DONTWAIT); 326 if (M_TRAILINGSPACE(mp) < hlen) { 327 m_freem(mp); /* can't get a cluster, drop */ 328 compfailmem: 329 m_freem(m); 330 m = NULL; 331 ERROUT(ENOBUFS); 332 } 333 } 334 bcopy(hdr, mtod(mp, u_char *), hlen); 335 mp->m_len = hlen; 336 337 /* Stick header and rest of packet together */ 338 m->m_data += vjlen; 339 m->m_len -= vjlen; 340 if (m->m_len <= M_TRAILINGSPACE(mp)) { 341 bcopy(mtod(m, u_char *), 342 mtod(mp, u_char *) + mp->m_len, m->m_len); 343 mp->m_len += m->m_len; 344 MFREE(m, mp->m_next); 345 } else 346 mp->m_next = m; 347 m = mp; 348 hook = priv->ip; 349 } else if (hook == priv->vjuncomp) { /* incoming uncompressed pkt */ 350 u_char *hdr; 351 u_int hlen; 352 353 /* Are we decompressing? */ 354 if (!priv->conf.enableDecomp) { 355 m_freem(m); 356 m = NULL; 357 ERROUT(ENETDOWN); 358 } 359 360 /* Run packet through uncompressor */ 361 if ((m = ng_vjc_pulluphdrs(m)) == NULL) 362 ERROUT(ENOBUFS); 363 if (sl_uncompress_tcp_core(mtod(m, u_char *), 364 m->m_len, m->m_pkthdr.len, TYPE_UNCOMPRESSED_TCP, 365 &priv->slc, &hdr, &hlen) < 0) { 366 m_freem(m); 367 m = NULL; 368 ERROUT(EINVAL); 369 } 370 hook = priv->ip; 371 } else if (hook == priv->vjip) /* incoming regular packet (bypass) */ 372 hook = priv->ip; 373 else 374 panic(__FUNCTION__); 375 376 done: 377 if (m) 378 NG_SEND_DATA(error, hook, m, meta); 379 else 380 NG_FREE_META(meta); 381 return (error); 382 } 383 384 /* 385 * Shutdown node 386 */ 387 static int 388 ng_vjc_rmnode(node_p node) 389 { 390 const priv_p priv = (priv_p) node->private; 391 392 node->flags |= NG_INVALID; 393 ng_cutlinks(node); 394 ng_unname(node); 395 bzero(priv, sizeof(*priv)); 396 FREE(priv, M_NETGRAPH); 397 node->private = NULL; 398 ng_unref(node); 399 return (0); 400 } 401 402 /* 403 * Hook disconnection 404 */ 405 static int 406 ng_vjc_disconnect(hook_p hook) 407 { 408 if (hook->node->numhooks == 0) 409 ng_rmnode(hook->node); 410 return (0); 411 } 412 413 /************************************************************************ 414 HELPER STUFF 415 ************************************************************************/ 416 417 /* 418 * Pull up the full IP and TCP headers of a packet. If packet is not 419 * a TCP packet, just pull up the IP header. 420 */ 421 static struct mbuf * 422 ng_vjc_pulluphdrs(struct mbuf *m) 423 { 424 struct ip *ip; 425 struct tcphdr *tcp; 426 int ihlen, thlen; 427 428 if (m->m_len < sizeof(*ip) && !(m = m_pullup(m, sizeof(*ip)))) 429 return (NULL); 430 ip = mtod(m, struct ip *); 431 if (ip->ip_p != IPPROTO_TCP) 432 return (m); 433 ihlen = ip->ip_hl << 2; 434 if (m->m_len < ihlen + sizeof(*tcp)) { 435 if (!(m = m_pullup(m, ihlen + sizeof(*tcp)))) 436 return (NULL); 437 ip = mtod(m, struct ip *); 438 } 439 tcp = (struct tcphdr *) ((u_char *) ip + ihlen); 440 thlen = tcp->th_off << 2; 441 if (m->m_len < ihlen + thlen) 442 m = m_pullup(m, ihlen + thlen); 443 return (m); 444 } 445 446