1 /*- 2 * Copyright (C) 2010 by Maxim Ignatenko <gelraen.ua@gmail.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/endian.h> 35 #include <sys/malloc.h> 36 #include <sys/mbuf.h> 37 #include <netgraph/ng_message.h> 38 #include <netgraph/ng_parse.h> 39 #include <netgraph/ng_patch.h> 40 #include <netgraph/netgraph.h> 41 42 static ng_constructor_t ng_patch_constructor; 43 static ng_rcvmsg_t ng_patch_rcvmsg; 44 static ng_shutdown_t ng_patch_shutdown; 45 static ng_newhook_t ng_patch_newhook; 46 static ng_rcvdata_t ng_patch_rcvdata; 47 static ng_disconnect_t ng_patch_disconnect; 48 49 static int 50 ng_patch_config_getlen(const struct ng_parse_type *type, 51 const u_char *start, const u_char *buf) 52 { 53 const struct ng_patch_config *p; 54 55 p = (const struct ng_patch_config *)(buf - 56 offsetof(struct ng_patch_config, ops)); 57 return (p->count); 58 } 59 60 static const struct ng_parse_struct_field ng_patch_op_type_fields[] 61 = NG_PATCH_OP_TYPE_INFO; 62 static const struct ng_parse_type ng_patch_op_type = { 63 &ng_parse_struct_type, 64 &ng_patch_op_type_fields 65 }; 66 67 static const struct ng_parse_array_info ng_patch_confarr_info = { 68 &ng_patch_op_type, 69 &ng_patch_config_getlen 70 }; 71 static const struct ng_parse_type ng_patch_confarr_type = { 72 &ng_parse_array_type, 73 &ng_patch_confarr_info 74 }; 75 76 static const struct ng_parse_struct_field ng_patch_config_type_fields[] 77 = NG_PATCH_CONFIG_TYPE_INFO; 78 static const struct ng_parse_type ng_patch_config_type = { 79 &ng_parse_struct_type, 80 &ng_patch_config_type_fields 81 }; 82 83 static const struct ng_parse_struct_field ng_patch_stats_fields[] 84 = NG_PATCH_STATS_TYPE_INFO; 85 static const struct ng_parse_type ng_patch_stats_type = { 86 &ng_parse_struct_type, 87 &ng_patch_stats_fields 88 }; 89 90 static const struct ng_cmdlist ng_patch_cmdlist[] = { 91 { 92 NGM_PATCH_COOKIE, 93 NGM_PATCH_GETCONFIG, 94 "getconfig", 95 NULL, 96 &ng_patch_config_type 97 }, 98 { 99 NGM_PATCH_COOKIE, 100 NGM_PATCH_SETCONFIG, 101 "setconfig", 102 &ng_patch_config_type, 103 NULL 104 }, 105 { 106 NGM_PATCH_COOKIE, 107 NGM_PATCH_GET_STATS, 108 "getstats", 109 NULL, 110 &ng_patch_stats_type 111 }, 112 { 113 NGM_PATCH_COOKIE, 114 NGM_PATCH_CLR_STATS, 115 "clrstats", 116 NULL, 117 NULL 118 }, 119 { 120 NGM_PATCH_COOKIE, 121 NGM_PATCH_GETCLR_STATS, 122 "getclrstats", 123 NULL, 124 &ng_patch_stats_type 125 }, 126 { 0 } 127 }; 128 129 static struct ng_type typestruct = { 130 .version = NG_ABI_VERSION, 131 .name = NG_PATCH_NODE_TYPE, 132 .constructor = ng_patch_constructor, 133 .rcvmsg = ng_patch_rcvmsg, 134 .shutdown = ng_patch_shutdown, 135 .newhook = ng_patch_newhook, 136 .rcvdata = ng_patch_rcvdata, 137 .disconnect = ng_patch_disconnect, 138 .cmdlist = ng_patch_cmdlist, 139 }; 140 NETGRAPH_INIT(patch, &typestruct); 141 142 union patch_val { 143 uint8_t v1; 144 uint16_t v2; 145 uint32_t v4; 146 uint64_t v8; 147 }; 148 149 struct ng_patch_priv { 150 hook_p in; 151 hook_p out; 152 struct ng_patch_config *config; 153 union patch_val *val; 154 struct ng_patch_stats stats; 155 }; 156 typedef struct ng_patch_priv *priv_p; 157 158 #define NG_PATCH_CONF_SIZE(count) (sizeof(struct ng_patch_config) + \ 159 (count) * sizeof(struct ng_patch_op)) 160 161 static void do_patch(priv_p conf, struct mbuf *m); 162 163 static int 164 ng_patch_constructor(node_p node) 165 { 166 priv_p privdata; 167 168 privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO); 169 NG_NODE_SET_PRIVATE(node, privdata); 170 privdata->in = NULL; 171 privdata->out = NULL; 172 privdata->config = NULL; 173 return (0); 174 } 175 176 static int 177 ng_patch_newhook(node_p node, hook_p hook, const char *name) 178 { 179 const priv_p privp = NG_NODE_PRIVATE(node); 180 181 if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) { 182 privp->in = hook; 183 } else if (strncmp(name, NG_PATCH_HOOK_OUT, 184 strlen(NG_PATCH_HOOK_OUT)) == 0) { 185 privp->out = hook; 186 } else 187 return (EINVAL); 188 return(0); 189 } 190 191 static int 192 ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook) 193 { 194 const priv_p privp = NG_NODE_PRIVATE(node); 195 struct ng_patch_config *conf, *newconf; 196 union patch_val *newval; 197 struct ng_mesg *msg; 198 struct ng_mesg *resp; 199 int i, clear, error; 200 201 clear = error = 0; 202 resp = NULL; 203 NGI_GET_MSG(item, msg); 204 switch (msg->header.typecookie) { 205 case NGM_PATCH_COOKIE: 206 switch (msg->header.cmd) { 207 case NGM_PATCH_GETCONFIG: 208 if (privp->config == NULL) 209 break; 210 NG_MKRESPONSE(resp, msg, 211 NG_PATCH_CONF_SIZE(privp->config->count), 212 M_WAITOK); 213 bcopy(privp->config, resp->data, 214 NG_PATCH_CONF_SIZE(privp->config->count)); 215 break; 216 case NGM_PATCH_SETCONFIG: 217 { 218 if (msg->header.arglen < 219 sizeof(struct ng_patch_config)) { 220 error = EINVAL; 221 break; 222 } 223 224 conf = (struct ng_patch_config *)msg->data; 225 if (msg->header.arglen < 226 NG_PATCH_CONF_SIZE(conf->count)) { 227 error = EINVAL; 228 break; 229 } 230 231 for(i = 0; i < conf->count; i++) { 232 switch(conf->ops[i].length) { 233 case 1: 234 case 2: 235 case 4: 236 case 8: 237 break; 238 default: 239 error = EINVAL; 240 break; 241 } 242 if (error != 0) 243 break; 244 } 245 246 conf->csum_flags &= CSUM_IP | CSUM_TCP | CSUM_UDP | 247 CSUM_SCTP; 248 249 if (error == 0) { 250 newconf = malloc( 251 NG_PATCH_CONF_SIZE(conf->count), 252 M_NETGRAPH, M_WAITOK); 253 newval = malloc(conf->count * 254 sizeof(union patch_val), M_NETGRAPH, 255 M_WAITOK); 256 for(i = 0; i < conf->count; i++) { 257 switch (conf->ops[i].length) { 258 case 1: 259 newval[i].v1 = 260 conf->ops[i].value; 261 break; 262 case 2: 263 newval[i].v2 = 264 conf->ops[i].value; 265 break; 266 case 4: 267 newval[i].v4 = 268 conf->ops[i].value; 269 break; 270 case 8: 271 newval[i].v8 = 272 conf->ops[i].value; 273 break; 274 } 275 } 276 bcopy(conf, newconf, 277 NG_PATCH_CONF_SIZE(conf->count)); 278 if (privp->val != NULL) 279 free(privp->val, M_NETGRAPH); 280 privp->val = newval; 281 if (privp->config != NULL) 282 free(privp->config, M_NETGRAPH); 283 privp->config = newconf; 284 } 285 break; 286 } 287 case NGM_PATCH_GETCLR_STATS: 288 clear = 1; 289 /* FALLTHROUGH */ 290 case NGM_PATCH_GET_STATS: 291 NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats), 292 M_WAITOK); 293 bcopy(&(privp->stats), resp->data, 294 sizeof(struct ng_patch_stats)); 295 if (clear == 0) 296 break; 297 /* else FALLTHROUGH */ 298 case NGM_PATCH_CLR_STATS: 299 bzero(&(privp->stats), sizeof(struct ng_patch_stats)); 300 break; 301 default: 302 error = EINVAL; 303 break; 304 } 305 break; 306 default: 307 error = EINVAL; 308 break; 309 } 310 311 NG_RESPOND_MSG(error, node, item, resp); 312 NG_FREE_MSG(msg); 313 return(error); 314 } 315 316 static void 317 do_patch(priv_p privp, struct mbuf *m) 318 { 319 struct ng_patch_config *conf; 320 uint64_t buf; 321 int i, patched; 322 323 conf = privp->config; 324 patched = 0; 325 for(i = 0; i < conf->count; i++) { 326 if (conf->ops[i].offset + conf->ops[i].length > 327 m->m_pkthdr.len) 328 continue; 329 330 /* for "=" operation we don't need to copy data from mbuf */ 331 if (conf->ops[i].mode != NG_PATCH_MODE_SET) { 332 m_copydata(m, conf->ops[i].offset, 333 conf->ops[i].length, (caddr_t)&buf); 334 } 335 336 switch (conf->ops[i].length) { 337 case 1: 338 switch (conf->ops[i].mode) { 339 case NG_PATCH_MODE_SET: 340 *((uint8_t *)&buf) = privp->val[i].v1; 341 break; 342 case NG_PATCH_MODE_ADD: 343 *((uint8_t *)&buf) += privp->val[i].v1; 344 break; 345 case NG_PATCH_MODE_SUB: 346 *((uint8_t *)&buf) -= privp->val[i].v1; 347 break; 348 case NG_PATCH_MODE_MUL: 349 *((uint8_t *)&buf) *= privp->val[i].v1; 350 break; 351 case NG_PATCH_MODE_DIV: 352 *((uint8_t *)&buf) /= privp->val[i].v1; 353 break; 354 case NG_PATCH_MODE_NEG: 355 *((int8_t *)&buf) = - *((int8_t *)&buf); 356 break; 357 case NG_PATCH_MODE_AND: 358 *((uint8_t *)&buf) &= privp->val[i].v1; 359 break; 360 case NG_PATCH_MODE_OR: 361 *((uint8_t *)&buf) |= privp->val[i].v1; 362 break; 363 case NG_PATCH_MODE_XOR: 364 *((uint8_t *)&buf) ^= privp->val[i].v1; 365 break; 366 case NG_PATCH_MODE_SHL: 367 *((uint8_t *)&buf) <<= privp->val[i].v1; 368 break; 369 case NG_PATCH_MODE_SHR: 370 *((uint8_t *)&buf) >>= privp->val[i].v1; 371 break; 372 } 373 break; 374 case 2: 375 *((int16_t *)&buf) = ntohs(*((int16_t *)&buf)); 376 switch (conf->ops[i].mode) { 377 case NG_PATCH_MODE_SET: 378 *((uint16_t *)&buf) = privp->val[i].v2; 379 break; 380 case NG_PATCH_MODE_ADD: 381 *((uint16_t *)&buf) += privp->val[i].v2; 382 break; 383 case NG_PATCH_MODE_SUB: 384 *((uint16_t *)&buf) -= privp->val[i].v2; 385 break; 386 case NG_PATCH_MODE_MUL: 387 *((uint16_t *)&buf) *= privp->val[i].v2; 388 break; 389 case NG_PATCH_MODE_DIV: 390 *((uint16_t *)&buf) /= privp->val[i].v2; 391 break; 392 case NG_PATCH_MODE_NEG: 393 *((int16_t *)&buf) = - *((int16_t *)&buf); 394 break; 395 case NG_PATCH_MODE_AND: 396 *((uint16_t *)&buf) &= privp->val[i].v2; 397 break; 398 case NG_PATCH_MODE_OR: 399 *((uint16_t *)&buf) |= privp->val[i].v2; 400 break; 401 case NG_PATCH_MODE_XOR: 402 *((uint16_t *)&buf) ^= privp->val[i].v2; 403 break; 404 case NG_PATCH_MODE_SHL: 405 *((uint16_t *)&buf) <<= privp->val[i].v2; 406 break; 407 case NG_PATCH_MODE_SHR: 408 *((uint16_t *)&buf) >>= privp->val[i].v2; 409 break; 410 } 411 *((int16_t *)&buf) = htons(*((int16_t *)&buf)); 412 break; 413 case 4: 414 *((int32_t *)&buf) = ntohl(*((int32_t *)&buf)); 415 switch (conf->ops[i].mode) { 416 case NG_PATCH_MODE_SET: 417 *((uint32_t *)&buf) = privp->val[i].v4; 418 break; 419 case NG_PATCH_MODE_ADD: 420 *((uint32_t *)&buf) += privp->val[i].v4; 421 break; 422 case NG_PATCH_MODE_SUB: 423 *((uint32_t *)&buf) -= privp->val[i].v4; 424 break; 425 case NG_PATCH_MODE_MUL: 426 *((uint32_t *)&buf) *= privp->val[i].v4; 427 break; 428 case NG_PATCH_MODE_DIV: 429 *((uint32_t *)&buf) /= privp->val[i].v4; 430 break; 431 case NG_PATCH_MODE_NEG: 432 *((int32_t *)&buf) = - *((int32_t *)&buf); 433 break; 434 case NG_PATCH_MODE_AND: 435 *((uint32_t *)&buf) &= privp->val[i].v4; 436 break; 437 case NG_PATCH_MODE_OR: 438 *((uint32_t *)&buf) |= privp->val[i].v4; 439 break; 440 case NG_PATCH_MODE_XOR: 441 *((uint32_t *)&buf) ^= privp->val[i].v4; 442 break; 443 case NG_PATCH_MODE_SHL: 444 *((uint32_t *)&buf) <<= privp->val[i].v4; 445 break; 446 case NG_PATCH_MODE_SHR: 447 *((uint32_t *)&buf) >>= privp->val[i].v4; 448 break; 449 } 450 *((int32_t *)&buf) = htonl(*((int32_t *)&buf)); 451 break; 452 case 8: 453 *((int64_t *)&buf) = be64toh(*((int64_t *)&buf)); 454 switch (conf->ops[i].mode) { 455 case NG_PATCH_MODE_SET: 456 *((uint64_t *)&buf) = privp->val[i].v8; 457 break; 458 case NG_PATCH_MODE_ADD: 459 *((uint64_t *)&buf) += privp->val[i].v8; 460 break; 461 case NG_PATCH_MODE_SUB: 462 *((uint64_t *)&buf) -= privp->val[i].v8; 463 break; 464 case NG_PATCH_MODE_MUL: 465 *((uint64_t *)&buf) *= privp->val[i].v8; 466 break; 467 case NG_PATCH_MODE_DIV: 468 *((uint64_t *)&buf) /= privp->val[i].v8; 469 break; 470 case NG_PATCH_MODE_NEG: 471 *((int64_t *)&buf) = - *((int64_t *)&buf); 472 break; 473 case NG_PATCH_MODE_AND: 474 *((uint64_t *)&buf) &= privp->val[i].v8; 475 break; 476 case NG_PATCH_MODE_OR: 477 *((uint64_t *)&buf) |= privp->val[i].v8; 478 break; 479 case NG_PATCH_MODE_XOR: 480 *((uint64_t *)&buf) ^= privp->val[i].v8; 481 break; 482 case NG_PATCH_MODE_SHL: 483 *((uint64_t *)&buf) <<= privp->val[i].v8; 484 break; 485 case NG_PATCH_MODE_SHR: 486 *((uint64_t *)&buf) >>= privp->val[i].v8; 487 break; 488 } 489 *((int64_t *)&buf) = htobe64(*((int64_t *)&buf)); 490 break; 491 } 492 493 m_copyback(m, conf->ops[i].offset, conf->ops[i].length, 494 (caddr_t)&buf); 495 patched = 1; 496 } 497 if (patched > 0) 498 privp->stats.patched++; 499 } 500 501 static int 502 ng_patch_rcvdata(hook_p hook, item_p item) 503 { 504 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 505 struct mbuf *m; 506 hook_p target; 507 int error; 508 509 priv->stats.received++; 510 NGI_GET_M(item, m); 511 if (priv->config != NULL && hook == priv->in && 512 (m->m_flags & M_PKTHDR) != 0) { 513 m = m_unshare(m,M_NOWAIT); 514 if (m == NULL) { 515 priv->stats.dropped++; 516 NG_FREE_ITEM(item); 517 return (ENOMEM); 518 } 519 do_patch(priv, m); 520 m->m_pkthdr.csum_flags |= priv->config->csum_flags; 521 } 522 523 target = NULL; 524 if (hook == priv->in) { 525 /* return frames on 'in' hook if 'out' not connected */ 526 if (priv->out != NULL) 527 target = priv->out; 528 else 529 target = priv->in; 530 } 531 if (hook == priv->out && priv->in != NULL) 532 target = priv->in; 533 534 if (target == NULL) { 535 priv->stats.dropped++; 536 NG_FREE_ITEM(item); 537 NG_FREE_M(m); 538 return (0); 539 } 540 NG_FWD_NEW_DATA(error, item, target, m); 541 return (error); 542 } 543 544 static int 545 ng_patch_shutdown(node_p node) 546 { 547 const priv_p privdata = NG_NODE_PRIVATE(node); 548 549 if (privdata->val != NULL) 550 free(privdata->val, M_NETGRAPH); 551 if (privdata->config != NULL) 552 free(privdata->config, M_NETGRAPH); 553 NG_NODE_SET_PRIVATE(node, NULL); 554 NG_NODE_UNREF(node); 555 free(privdata, M_NETGRAPH); 556 return (0); 557 } 558 559 static int 560 ng_patch_disconnect(hook_p hook) 561 { 562 priv_p priv; 563 564 priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 565 if (hook == priv->in) { 566 priv->in = NULL; 567 } 568 if (hook == priv->out) { 569 priv->out = NULL; 570 } 571 if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 && 572 NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */ 573 ng_rmnode_self(NG_HOOK_NODE(hook)); 574 return (0); 575 } 576 577