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