1 /*- 2 * Copyright (c) 2010 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 *conf; 54 55 conf = (const struct ng_patch_config *)(buf - 56 offsetof(struct ng_patch_config, ops)); 57 58 return (conf->count); 59 } 60 61 static const struct ng_parse_struct_field ng_patch_op_type_fields[] 62 = NG_PATCH_OP_TYPE_INFO; 63 static const struct ng_parse_type ng_patch_op_type = { 64 &ng_parse_struct_type, 65 &ng_patch_op_type_fields 66 }; 67 68 static const struct ng_parse_array_info ng_patch_ops_array_info = { 69 &ng_patch_op_type, 70 &ng_patch_config_getlen 71 }; 72 static const struct ng_parse_type ng_patch_ops_array_type = { 73 &ng_parse_array_type, 74 &ng_patch_ops_array_info 75 }; 76 77 static const struct ng_parse_struct_field ng_patch_config_type_fields[] 78 = NG_PATCH_CONFIG_TYPE_INFO; 79 static const struct ng_parse_type ng_patch_config_type = { 80 &ng_parse_struct_type, 81 &ng_patch_config_type_fields 82 }; 83 84 static const struct ng_parse_struct_field ng_patch_stats_fields[] 85 = NG_PATCH_STATS_TYPE_INFO; 86 static const struct ng_parse_type ng_patch_stats_type = { 87 &ng_parse_struct_type, 88 &ng_patch_stats_fields 89 }; 90 91 static const struct ng_cmdlist ng_patch_cmdlist[] = { 92 { 93 NGM_PATCH_COOKIE, 94 NGM_PATCH_GETCONFIG, 95 "getconfig", 96 NULL, 97 &ng_patch_config_type 98 }, 99 { 100 NGM_PATCH_COOKIE, 101 NGM_PATCH_SETCONFIG, 102 "setconfig", 103 &ng_patch_config_type, 104 NULL 105 }, 106 { 107 NGM_PATCH_COOKIE, 108 NGM_PATCH_GET_STATS, 109 "getstats", 110 NULL, 111 &ng_patch_stats_type 112 }, 113 { 114 NGM_PATCH_COOKIE, 115 NGM_PATCH_CLR_STATS, 116 "clrstats", 117 NULL, 118 NULL 119 }, 120 { 121 NGM_PATCH_COOKIE, 122 NGM_PATCH_GETCLR_STATS, 123 "getclrstats", 124 NULL, 125 &ng_patch_stats_type 126 }, 127 { 0 } 128 }; 129 130 static struct ng_type typestruct = { 131 .version = NG_ABI_VERSION, 132 .name = NG_PATCH_NODE_TYPE, 133 .constructor = ng_patch_constructor, 134 .rcvmsg = ng_patch_rcvmsg, 135 .shutdown = ng_patch_shutdown, 136 .newhook = ng_patch_newhook, 137 .rcvdata = ng_patch_rcvdata, 138 .disconnect = ng_patch_disconnect, 139 .cmdlist = ng_patch_cmdlist, 140 }; 141 142 NETGRAPH_INIT(patch, &typestruct); 143 144 union patch_val { 145 uint8_t v1; 146 uint16_t v2; 147 uint32_t v4; 148 uint64_t v8; 149 }; 150 151 /* private data */ 152 struct ng_patch_priv { 153 hook_p in; 154 hook_p out; 155 struct ng_patch_config *config; 156 union patch_val *val; 157 struct ng_patch_stats stats; 158 }; 159 typedef struct ng_patch_priv *priv_p; 160 161 #define NG_PATCH_CONF_SIZE(count) (sizeof(struct ng_patch_config) + \ 162 (count) * sizeof(struct ng_patch_op)) 163 164 static void do_patch(priv_p conf, struct mbuf *m); 165 166 static int 167 ng_patch_constructor(node_p node) 168 { 169 priv_p privdata; 170 171 privdata = malloc(sizeof(*privdata), M_NETGRAPH, M_WAITOK | M_ZERO); 172 NG_NODE_SET_PRIVATE(node, privdata); 173 privdata->in = NULL; 174 privdata->out = NULL; 175 privdata->config = NULL; 176 return (0); 177 } 178 179 static int 180 ng_patch_newhook(node_p node, hook_p hook, const char *name) 181 { 182 const priv_p privp = NG_NODE_PRIVATE(node); 183 184 if (strncmp(name, NG_PATCH_HOOK_IN, strlen(NG_PATCH_HOOK_IN)) == 0) { 185 privp->in = hook; 186 } else if (strncmp(name, NG_PATCH_HOOK_OUT, 187 strlen(NG_PATCH_HOOK_OUT)) == 0) { 188 privp->out = hook; 189 } else 190 return (EINVAL); 191 return(0); 192 } 193 194 static int 195 ng_patch_rcvmsg(node_p node, item_p item, hook_p lasthook) 196 { 197 const priv_p privp = NG_NODE_PRIVATE(node); 198 struct ng_patch_config *conf, *newconf; 199 union patch_val *newval; 200 struct ng_mesg *msg; 201 struct ng_mesg *resp; 202 int i, clear, error; 203 204 clear = error = 0; 205 resp = NULL; 206 NGI_GET_MSG(item, msg); 207 switch (msg->header.typecookie) { 208 case NGM_PATCH_COOKIE: 209 switch (msg->header.cmd) { 210 case NGM_PATCH_GETCONFIG: 211 if (privp->config == NULL) 212 break; 213 NG_MKRESPONSE(resp, msg, 214 NG_PATCH_CONF_SIZE(privp->config->count), 215 M_WAITOK); 216 bcopy(privp->config, resp->data, 217 NG_PATCH_CONF_SIZE(privp->config->count)); 218 break; 219 case NGM_PATCH_SETCONFIG: 220 { 221 if (msg->header.arglen < 222 sizeof(struct ng_patch_config)) { 223 error = EINVAL; 224 break; 225 } 226 227 conf = (struct ng_patch_config *)msg->data; 228 if (msg->header.arglen < 229 NG_PATCH_CONF_SIZE(conf->count)) { 230 error = EINVAL; 231 break; 232 } 233 234 for(i = 0; i < conf->count; i++) { 235 switch(conf->ops[i].length) { 236 case 1: 237 case 2: 238 case 4: 239 case 8: 240 break; 241 default: 242 error = EINVAL; 243 break; 244 } 245 if (error != 0) 246 break; 247 } 248 249 conf->csum_flags &= CSUM_IP | CSUM_TCP | CSUM_UDP | 250 CSUM_SCTP; 251 252 if (error == 0) { 253 newconf = malloc( 254 NG_PATCH_CONF_SIZE(conf->count), 255 M_NETGRAPH, M_WAITOK); 256 newval = malloc(conf->count * 257 sizeof(union patch_val), M_NETGRAPH, 258 M_WAITOK); 259 for(i = 0; i < conf->count; i++) { 260 switch (conf->ops[i].length) { 261 case 1: 262 newval[i].v1 = 263 conf->ops[i].value; 264 break; 265 case 2: 266 newval[i].v2 = 267 conf->ops[i].value; 268 break; 269 case 4: 270 newval[i].v4 = 271 conf->ops[i].value; 272 break; 273 case 8: 274 newval[i].v8 = 275 conf->ops[i].value; 276 break; 277 } 278 } 279 bcopy(conf, newconf, 280 NG_PATCH_CONF_SIZE(conf->count)); 281 if (privp->val != NULL) 282 free(privp->val, M_NETGRAPH); 283 privp->val = newval; 284 if (privp->config != NULL) 285 free(privp->config, M_NETGRAPH); 286 privp->config = newconf; 287 } 288 break; 289 } 290 case NGM_PATCH_GETCLR_STATS: 291 clear = 1; 292 /* FALLTHROUGH */ 293 case NGM_PATCH_GET_STATS: 294 NG_MKRESPONSE(resp, msg, sizeof(struct ng_patch_stats), 295 M_WAITOK); 296 bcopy(&(privp->stats), resp->data, 297 sizeof(struct ng_patch_stats)); 298 if (clear == 0) 299 break; 300 /* else FALLTHROUGH */ 301 case NGM_PATCH_CLR_STATS: 302 bzero(&(privp->stats), sizeof(struct ng_patch_stats)); 303 break; 304 default: 305 error = EINVAL; 306 break; 307 } 308 break; 309 default: 310 error = EINVAL; 311 break; 312 } 313 314 NG_RESPOND_MSG(error, node, item, resp); 315 NG_FREE_MSG(msg); 316 return(error); 317 } 318 319 static void 320 do_patch(priv_p privp, struct mbuf *m) 321 { 322 struct ng_patch_config *conf; 323 uint64_t buf; 324 int i, patched; 325 326 conf = privp->config; 327 patched = 0; 328 for(i = 0; i < conf->count; i++) { 329 if (conf->ops[i].offset + conf->ops[i].length > 330 m->m_pkthdr.len) 331 continue; 332 333 /* for "=" operation we don't need to copy data from mbuf */ 334 if (conf->ops[i].mode != NG_PATCH_MODE_SET) { 335 m_copydata(m, conf->ops[i].offset, 336 conf->ops[i].length, (caddr_t)&buf); 337 } 338 339 switch (conf->ops[i].length) { 340 case 1: 341 switch (conf->ops[i].mode) { 342 case NG_PATCH_MODE_SET: 343 *((uint8_t *)&buf) = privp->val[i].v1; 344 break; 345 case NG_PATCH_MODE_ADD: 346 *((uint8_t *)&buf) += privp->val[i].v1; 347 break; 348 case NG_PATCH_MODE_SUB: 349 *((uint8_t *)&buf) -= privp->val[i].v1; 350 break; 351 case NG_PATCH_MODE_MUL: 352 *((uint8_t *)&buf) *= privp->val[i].v1; 353 break; 354 case NG_PATCH_MODE_DIV: 355 *((uint8_t *)&buf) /= privp->val[i].v1; 356 break; 357 case NG_PATCH_MODE_NEG: 358 *((int8_t *)&buf) = - *((int8_t *)&buf); 359 break; 360 case NG_PATCH_MODE_AND: 361 *((uint8_t *)&buf) &= privp->val[i].v1; 362 break; 363 case NG_PATCH_MODE_OR: 364 *((uint8_t *)&buf) |= privp->val[i].v1; 365 break; 366 case NG_PATCH_MODE_XOR: 367 *((uint8_t *)&buf) ^= privp->val[i].v1; 368 break; 369 case NG_PATCH_MODE_SHL: 370 *((uint8_t *)&buf) <<= privp->val[i].v1; 371 break; 372 case NG_PATCH_MODE_SHR: 373 *((uint8_t *)&buf) >>= privp->val[i].v1; 374 break; 375 } 376 break; 377 case 2: 378 *((int16_t *)&buf) = ntohs(*((int16_t *)&buf)); 379 switch (conf->ops[i].mode) { 380 case NG_PATCH_MODE_SET: 381 *((uint16_t *)&buf) = privp->val[i].v2; 382 break; 383 case NG_PATCH_MODE_ADD: 384 *((uint16_t *)&buf) += privp->val[i].v2; 385 break; 386 case NG_PATCH_MODE_SUB: 387 *((uint16_t *)&buf) -= privp->val[i].v2; 388 break; 389 case NG_PATCH_MODE_MUL: 390 *((uint16_t *)&buf) *= privp->val[i].v2; 391 break; 392 case NG_PATCH_MODE_DIV: 393 *((uint16_t *)&buf) /= privp->val[i].v2; 394 break; 395 case NG_PATCH_MODE_NEG: 396 *((int16_t *)&buf) = - *((int16_t *)&buf); 397 break; 398 case NG_PATCH_MODE_AND: 399 *((uint16_t *)&buf) &= privp->val[i].v2; 400 break; 401 case NG_PATCH_MODE_OR: 402 *((uint16_t *)&buf) |= privp->val[i].v2; 403 break; 404 case NG_PATCH_MODE_XOR: 405 *((uint16_t *)&buf) ^= privp->val[i].v2; 406 break; 407 case NG_PATCH_MODE_SHL: 408 *((uint16_t *)&buf) <<= privp->val[i].v2; 409 break; 410 case NG_PATCH_MODE_SHR: 411 *((uint16_t *)&buf) >>= privp->val[i].v2; 412 break; 413 } 414 *((int16_t *)&buf) = htons(*((int16_t *)&buf)); 415 break; 416 case 4: 417 *((int32_t *)&buf) = ntohl(*((int32_t *)&buf)); 418 switch (conf->ops[i].mode) { 419 case NG_PATCH_MODE_SET: 420 *((uint32_t *)&buf) = privp->val[i].v4; 421 break; 422 case NG_PATCH_MODE_ADD: 423 *((uint32_t *)&buf) += privp->val[i].v4; 424 break; 425 case NG_PATCH_MODE_SUB: 426 *((uint32_t *)&buf) -= privp->val[i].v4; 427 break; 428 case NG_PATCH_MODE_MUL: 429 *((uint32_t *)&buf) *= privp->val[i].v4; 430 break; 431 case NG_PATCH_MODE_DIV: 432 *((uint32_t *)&buf) /= privp->val[i].v4; 433 break; 434 case NG_PATCH_MODE_NEG: 435 *((int32_t *)&buf) = - *((int32_t *)&buf); 436 break; 437 case NG_PATCH_MODE_AND: 438 *((uint32_t *)&buf) &= privp->val[i].v4; 439 break; 440 case NG_PATCH_MODE_OR: 441 *((uint32_t *)&buf) |= privp->val[i].v4; 442 break; 443 case NG_PATCH_MODE_XOR: 444 *((uint32_t *)&buf) ^= privp->val[i].v4; 445 break; 446 case NG_PATCH_MODE_SHL: 447 *((uint32_t *)&buf) <<= privp->val[i].v4; 448 break; 449 case NG_PATCH_MODE_SHR: 450 *((uint32_t *)&buf) >>= privp->val[i].v4; 451 break; 452 } 453 *((int32_t *)&buf) = htonl(*((int32_t *)&buf)); 454 break; 455 case 8: 456 *((int64_t *)&buf) = be64toh(*((int64_t *)&buf)); 457 switch (conf->ops[i].mode) { 458 case NG_PATCH_MODE_SET: 459 *((uint64_t *)&buf) = privp->val[i].v8; 460 break; 461 case NG_PATCH_MODE_ADD: 462 *((uint64_t *)&buf) += privp->val[i].v8; 463 break; 464 case NG_PATCH_MODE_SUB: 465 *((uint64_t *)&buf) -= privp->val[i].v8; 466 break; 467 case NG_PATCH_MODE_MUL: 468 *((uint64_t *)&buf) *= privp->val[i].v8; 469 break; 470 case NG_PATCH_MODE_DIV: 471 *((uint64_t *)&buf) /= privp->val[i].v8; 472 break; 473 case NG_PATCH_MODE_NEG: 474 *((int64_t *)&buf) = - *((int64_t *)&buf); 475 break; 476 case NG_PATCH_MODE_AND: 477 *((uint64_t *)&buf) &= privp->val[i].v8; 478 break; 479 case NG_PATCH_MODE_OR: 480 *((uint64_t *)&buf) |= privp->val[i].v8; 481 break; 482 case NG_PATCH_MODE_XOR: 483 *((uint64_t *)&buf) ^= privp->val[i].v8; 484 break; 485 case NG_PATCH_MODE_SHL: 486 *((uint64_t *)&buf) <<= privp->val[i].v8; 487 break; 488 case NG_PATCH_MODE_SHR: 489 *((uint64_t *)&buf) >>= privp->val[i].v8; 490 break; 491 } 492 *((int64_t *)&buf) = htobe64(*((int64_t *)&buf)); 493 break; 494 } 495 496 m_copyback(m, conf->ops[i].offset, conf->ops[i].length, 497 (caddr_t)&buf); 498 patched = 1; 499 } 500 if (patched > 0) 501 privp->stats.patched++; 502 } 503 504 static int 505 ng_patch_rcvdata(hook_p hook, item_p item) 506 { 507 const priv_p priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 508 struct mbuf *m; 509 hook_p target; 510 int error; 511 512 priv->stats.received++; 513 NGI_GET_M(item, m); 514 if (priv->config != NULL && hook == priv->in && 515 (m->m_flags & M_PKTHDR) != 0) { 516 m = m_unshare(m,M_NOWAIT); 517 if (m == NULL) { 518 priv->stats.dropped++; 519 NG_FREE_ITEM(item); 520 return (ENOMEM); 521 } 522 do_patch(priv, m); 523 m->m_pkthdr.csum_flags |= priv->config->csum_flags; 524 } 525 526 target = NULL; 527 if (hook == priv->in) { 528 /* return frames on 'in' hook if 'out' not connected */ 529 if (priv->out != NULL) 530 target = priv->out; 531 else 532 target = priv->in; 533 } 534 if (hook == priv->out && priv->in != NULL) 535 target = priv->in; 536 537 if (target == NULL) { 538 priv->stats.dropped++; 539 NG_FREE_ITEM(item); 540 NG_FREE_M(m); 541 return (0); 542 } 543 NG_FWD_NEW_DATA(error, item, target, m); 544 return (error); 545 } 546 547 static int 548 ng_patch_shutdown(node_p node) 549 { 550 const priv_p privdata = NG_NODE_PRIVATE(node); 551 552 if (privdata->val != NULL) 553 free(privdata->val, M_NETGRAPH); 554 if (privdata->config != NULL) 555 free(privdata->config, M_NETGRAPH); 556 NG_NODE_SET_PRIVATE(node, NULL); 557 NG_NODE_UNREF(node); 558 free(privdata, M_NETGRAPH); 559 return (0); 560 } 561 562 static int 563 ng_patch_disconnect(hook_p hook) 564 { 565 priv_p priv; 566 567 priv = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 568 if (hook == priv->in) { 569 priv->in = NULL; 570 } 571 if (hook == priv->out) { 572 priv->out = NULL; 573 } 574 if (NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0 && 575 NG_NODE_IS_VALID(NG_HOOK_NODE(hook))) /* already shutting down? */ 576 ng_rmnode_self(NG_HOOK_NODE(hook)); 577 return (0); 578 } 579 580