1/* 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2002 Ericsson Research & Pekka Nikander 5 * Copyright (c) 2020 Nick Hibma <n_hibma@FreeBSD.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice unmodified, this list of conditions, and the following 13 * disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31/* 32 * MACFILTER NETGRAPH NODE TYPE 33 * 34 * This node type routes packets from the ether hook to either the default hook 35 * if sender MAC address is not in the MAC table, or out over the specified 36 * hook if it is. 37 * 38 * Other node types can then be used to apply specific processing to the 39 * packets on each hook. 40 * 41 * If compiled with NG_MACFILTER_DEBUG the flow and resizing of the MAC table 42 * are logged to the console. 43 * 44 * If compiled with NG_MACFILTER_DEBUG_RECVDATA every packet handled is logged 45 * on the console. 46 */ 47 48#include <sys/param.h> 49#include <sys/ctype.h> 50#include <sys/systm.h> 51 52#include <sys/lock.h> 53#include <sys/mbuf.h> 54#include <sys/mutex.h> 55 56#include <sys/kernel.h> 57#include <sys/malloc.h> 58 59#include <sys/socket.h> 60#include <net/ethernet.h> 61 62#include <netgraph/ng_message.h> 63#include <netgraph/netgraph.h> 64#include <netgraph/ng_parse.h> 65 66#include "ng_macfilter.h" 67 68#ifdef NG_SEPARATE_MALLOC 69MALLOC_DEFINE(M_NETGRAPH_MACFILTER, "netgraph_macfilter", "netgraph macfilter node "); 70#else 71#define M_NETGRAPH_MACFILTER M_NETGRAPH 72#endif 73 74#define MACTABLE_BLOCKSIZE 128 /* block size for incrementing table */ 75 76#ifdef NG_MACFILTER_DEBUG 77#define MACFILTER_DEBUG(fmt, ...) printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, __VA_ARGS__) 78#else 79#define MACFILTER_DEBUG(fmt, ...) 80#endif 81#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" 82#define MAC_S_ARGS(v) (v)[0], (v)[1], (v)[2], (v)[3], (v)[4], (v)[5] 83 84/* 85 * Parse type for struct ngm_macfilter_direct 86 */ 87 88static const struct ng_parse_struct_field macfilter_direct_fields[] 89 = NGM_MACFILTER_DIRECT_FIELDS; 90static const struct ng_parse_type ng_macfilter_direct_type = { 91 &ng_parse_struct_type, 92 &macfilter_direct_fields 93}; 94 95/* 96 * Parse type for struct ngm_macfilter_direct_hookid. 97 */ 98 99static const struct ng_parse_struct_field macfilter_direct_ndx_fields[] 100 = NGM_MACFILTER_DIRECT_NDX_FIELDS; 101static const struct ng_parse_type ng_macfilter_direct_hookid_type = { 102 &ng_parse_struct_type, 103 &macfilter_direct_ndx_fields 104}; 105 106/* 107 * Parse types for struct ngm_macfilter_get_macs. 108 */ 109static int 110macfilter_get_macs_count(const struct ng_parse_type *type, 111 const u_char *start, const u_char *buf) 112{ 113 const struct ngm_macfilter_macs *const ngm_macs = 114 (const struct ngm_macfilter_macs *)(buf - OFFSETOF(struct ngm_macfilter_macs, macs)); 115 116 return ngm_macs->n; 117} 118static const struct ng_parse_struct_field ng_macfilter_mac_fields[] 119 = NGM_MACFILTER_MAC_FIELDS; 120static const struct ng_parse_type ng_macfilter_mac_type = { 121 &ng_parse_struct_type, 122 ng_macfilter_mac_fields, 123}; 124static const struct ng_parse_array_info ng_macfilter_macs_array_info = { 125 &ng_macfilter_mac_type, 126 macfilter_get_macs_count 127}; 128static const struct ng_parse_type ng_macfilter_macs_array_type = { 129 &ng_parse_array_type, 130 &ng_macfilter_macs_array_info 131}; 132static const struct ng_parse_struct_field ng_macfilter_macs_fields[] 133 = NGM_MACFILTER_MACS_FIELDS; 134static const struct ng_parse_type ng_macfilter_macs_type = { 135 &ng_parse_struct_type, 136 &ng_macfilter_macs_fields 137}; 138 139/* 140 * Parse types for struct ngm_macfilter_get_hooks. 141 */ 142static int 143macfilter_get_upper_hook_count(const struct ng_parse_type *type, 144 const u_char *start, const u_char *buf) 145{ 146 const struct ngm_macfilter_hooks *const ngm_hooks = 147 (const struct ngm_macfilter_hooks *)(buf - OFFSETOF(struct ngm_macfilter_hooks, hooks)); 148 149 MACFILTER_DEBUG("buf %p, ngm_hooks %p, n %d", buf, ngm_hooks, ngm_hooks->n); 150 151 return ngm_hooks->n; 152} 153 154static const struct ng_parse_struct_field ng_macfilter_hook_fields[] 155 = NGM_MACFILTER_HOOK_FIELDS; 156static const struct ng_parse_type ng_macfilter_hook_type = { 157 &ng_parse_struct_type, 158 ng_macfilter_hook_fields, 159}; 160static const struct ng_parse_array_info ng_macfilter_hooks_array_info = { 161 &ng_macfilter_hook_type, 162 macfilter_get_upper_hook_count 163}; 164static const struct ng_parse_type ng_macfilter_hooks_array_type = { 165 &ng_parse_array_type, 166 &ng_macfilter_hooks_array_info 167}; 168static const struct ng_parse_struct_field ng_macfilter_hooks_fields[] 169 = NGM_MACFILTER_HOOKS_FIELDS; 170static const struct ng_parse_type ng_macfilter_hooks_type = { 171 &ng_parse_struct_type, 172 &ng_macfilter_hooks_fields 173}; 174 175/* 176 * List of commands and how to convert arguments to/from ASCII 177 */ 178static const struct ng_cmdlist ng_macfilter_cmdlist[] = { 179 { 180 NGM_MACFILTER_COOKIE, 181 NGM_MACFILTER_RESET, 182 "reset", 183 NULL, 184 NULL 185 }, 186 { 187 NGM_MACFILTER_COOKIE, 188 NGM_MACFILTER_DIRECT, 189 "direct", 190 &ng_macfilter_direct_type, 191 NULL 192 }, 193 { 194 NGM_MACFILTER_COOKIE, 195 NGM_MACFILTER_DIRECT_HOOKID, 196 "directi", 197 &ng_macfilter_direct_hookid_type, 198 NULL 199 }, 200 { 201 NGM_MACFILTER_COOKIE, 202 NGM_MACFILTER_GET_MACS, 203 "getmacs", 204 NULL, 205 &ng_macfilter_macs_type 206 }, 207 { 208 NGM_MACFILTER_COOKIE, 209 NGM_MACFILTER_GETCLR_MACS, 210 "getclrmacs", 211 NULL, 212 &ng_macfilter_macs_type 213 }, 214 { 215 NGM_MACFILTER_COOKIE, 216 NGM_MACFILTER_CLR_MACS, 217 "clrmacs", 218 NULL, 219 NULL, 220 }, 221 { 222 NGM_MACFILTER_COOKIE, 223 NGM_MACFILTER_GET_HOOKS, 224 "gethooks", 225 NULL, 226 &ng_macfilter_hooks_type 227 }, 228 { 0 } 229}; 230 231/* 232 * Netgraph node type descriptor 233 */ 234static ng_constructor_t ng_macfilter_constructor; 235static ng_rcvmsg_t ng_macfilter_rcvmsg; 236static ng_shutdown_t ng_macfilter_shutdown; 237static ng_newhook_t ng_macfilter_newhook; 238static ng_rcvdata_t ng_macfilter_rcvdata; 239static ng_disconnect_t ng_macfilter_disconnect; 240 241static struct ng_type typestruct = { 242 .version = NG_ABI_VERSION, 243 .name = NG_MACFILTER_NODE_TYPE, 244 .constructor = ng_macfilter_constructor, 245 .rcvmsg = ng_macfilter_rcvmsg, 246 .shutdown = ng_macfilter_shutdown, 247 .newhook = ng_macfilter_newhook, 248 .rcvdata = ng_macfilter_rcvdata, 249 .disconnect = ng_macfilter_disconnect, 250 .cmdlist = ng_macfilter_cmdlist 251}; 252NETGRAPH_INIT(macfilter, &typestruct); 253 254/* 255 * Per MAC address info: the hook where to send to, the address 256 * Note: We use the same struct as in the netgraph message, so we can bcopy the 257 * array. 258 */ 259typedef struct ngm_macfilter_mac *mf_mac_p; 260 261/* 262 * Node info 263 */ 264typedef struct { 265 hook_p mf_ether_hook; /* Ethernet hook */ 266 267 hook_p *mf_upper; /* Upper hooks */ 268 u_int mf_upper_cnt; /* Allocated # of upper slots */ 269 270 struct mtx mtx; /* Mutex for MACs table */ 271 mf_mac_p mf_macs; /* MAC info: dynamically allocated */ 272 u_int mf_mac_allocated;/* Allocated # of MAC slots */ 273 u_int mf_mac_used; /* Used # of MAC slots */ 274} *macfilter_p; 275 276/* 277 * Resize the MAC table to accommodate at least mfp->mf_mac_used + 1 entries. 278 * 279 * Note: mtx already held 280 */ 281static int 282macfilter_mactable_resize(macfilter_p mfp) 283{ 284 int error = 0; 285 286 int n = mfp->mf_mac_allocated; 287 if (mfp->mf_mac_used < 2*MACTABLE_BLOCKSIZE-1) /* minimum size */ 288 n = 2*MACTABLE_BLOCKSIZE-1; 289 else if (mfp->mf_mac_used + 2*MACTABLE_BLOCKSIZE < mfp->mf_mac_allocated) /* reduce size */ 290 n = mfp->mf_mac_allocated - MACTABLE_BLOCKSIZE; 291 else if (mfp->mf_mac_used == mfp->mf_mac_allocated) /* increase size */ 292 n = mfp->mf_mac_allocated + MACTABLE_BLOCKSIZE; 293 294 if (n != mfp->mf_mac_allocated) { 295 MACFILTER_DEBUG("used=%d allocated=%d->%d", 296 mfp->mf_mac_used, mfp->mf_mac_allocated, n); 297 298 mf_mac_p mfp_new = realloc(mfp->mf_macs, 299 sizeof(mfp->mf_macs[0])*n, 300 M_NETGRAPH, M_NOWAIT | M_ZERO); 301 if (mfp_new == NULL) { 302 error = -1; 303 } else { 304 mfp->mf_macs = mfp_new; 305 mfp->mf_mac_allocated = n; 306 } 307 } 308 309 return error; 310} 311 312/* 313 * Resets the macfilter to pass all received packets 314 * to the default hook. 315 * 316 * Note: mtx already held 317 */ 318static void 319macfilter_reset(macfilter_p mfp) 320{ 321 mfp->mf_mac_used = 0; 322 323 macfilter_mactable_resize(mfp); 324} 325 326/* 327 * Resets the counts for each MAC address. 328 * 329 * Note: mtx already held 330 */ 331static void 332macfilter_reset_stats(macfilter_p mfp) 333{ 334 int i; 335 336 for (i = 0; i < mfp->mf_mac_used; i++) { 337 mf_mac_p p = &mfp->mf_macs[i]; 338 p->packets_in = p->packets_out = 0; 339 p->bytes_in = p->bytes_out = 0; 340 } 341} 342 343/* 344 * Count the number of matching macs routed to this hook. 345 * 346 * Note: mtx already held 347 */ 348static int 349macfilter_mac_count(macfilter_p mfp, int hookid) 350{ 351 int i; 352 int cnt = 0; 353 354 for (i = 0; i < mfp->mf_mac_used; i++) 355 if (mfp->mf_macs[i].hookid == hookid) 356 cnt++; 357 358 return cnt; 359} 360 361/* 362 * Find a MAC address in the mac table. 363 * 364 * Returns 0 on failure with *ri set to index before which to insert a new 365 * element. Or returns 1 on success with *ri set to the index of the element 366 * that matches. 367 * 368 * Note: mtx already held. 369 */ 370static u_int 371macfilter_find_mac(macfilter_p mfp, const u_char *ether, u_int *ri) 372{ 373 mf_mac_p mf_macs = mfp->mf_macs; 374 375 u_int base = 0; 376 u_int range = mfp->mf_mac_used; 377 while (range > 0) { 378 u_int middle = base + (range >> 1); /* middle */ 379 int d = bcmp(ether, mf_macs[middle].ether, ETHER_ADDR_LEN); 380 if (d == 0) { /* match */ 381 *ri = middle; 382 return 1; 383 } else if (d > 0) { /* move right */ 384 range -= middle - base + 1; 385 base = middle + 1; 386 } else { /* move left */ 387 range = middle - base; 388 } 389 } 390 391 *ri = base; 392 return 0; 393} 394 395/* 396 * Change the upper hook for the given MAC address. If the hook id is zero (the 397 * default hook), the MAC address is removed from the table. Otherwise it is 398 * inserted to the table at a proper location, and the id of the hook is 399 * marked. 400 * 401 * Note: mtx already held. 402 */ 403static int 404macfilter_mactable_change(macfilter_p mfp, u_char *ether, int hookid) 405{ 406 u_int i; 407 int found = macfilter_find_mac(mfp, ether, &i); 408 409 mf_mac_p mf_macs = mfp->mf_macs; 410 411 MACFILTER_DEBUG("ether=" MAC_FMT " found=%d i=%d ether=" MAC_FMT " hookid=%d->%d used=%d allocated=%d", 412 MAC_S_ARGS(ether), found, i, MAC_S_ARGS(mf_macs[i].ether), 413 (found? mf_macs[i].hookid:NG_MACFILTER_HOOK_DEFAULT_ID), hookid, 414 mfp->mf_mac_used, mfp->mf_mac_allocated); 415 416 if (found) { 417 if (hookid == NG_MACFILTER_HOOK_DEFAULT_ID) { /* drop */ 418 /* Compress table */ 419 mfp->mf_mac_used--; 420 size_t len = (mfp->mf_mac_used - i) * sizeof(mf_macs[0]); 421 if (len > 0) 422 bcopy(&mf_macs[i+1], &mf_macs[i], len); 423 424 macfilter_mactable_resize(mfp); 425 } else { /* modify */ 426 mf_macs[i].hookid = hookid; 427 } 428 } else { 429 if (hookid == NG_MACFILTER_HOOK_DEFAULT_ID) { /* not found */ 430 /* not present and not inserted */ 431 return 0; 432 } else { /* add */ 433 if (macfilter_mactable_resize(mfp) == -1) { 434 return ENOMEM; 435 } else { 436 mf_macs = mfp->mf_macs; /* reassign; might have moved during resize */ 437 438 /* make room for new entry, unless appending */ 439 size_t len = (mfp->mf_mac_used - i) * sizeof(mf_macs[0]); 440 if (len > 0) 441 bcopy(&mf_macs[i], &mf_macs[i+1], len); 442 443 mf_macs[i].hookid = hookid; 444 bcopy(ether, mf_macs[i].ether, ETHER_ADDR_LEN); 445 446 mfp->mf_mac_used++; 447 } 448 } 449 } 450 451 return 0; 452} 453 454static int 455macfilter_mactable_remove_by_hookid(macfilter_p mfp, int hookid) 456{ 457 int i, j; 458 459 for (i = 0, j = 0; i < mfp->mf_mac_used; i++) { 460 if (mfp->mf_macs[i].hookid != hookid) { 461 if (i != j) 462 bcopy(&mfp->mf_macs[i], &mfp->mf_macs[j], sizeof(mfp->mf_macs[0])); 463 j++; 464 } 465 } 466 467 int removed = i - j; 468 mfp->mf_mac_used = j; 469 macfilter_mactable_resize(mfp); 470 471 return removed; 472} 473 474static int 475macfilter_find_hook(macfilter_p mfp, const char *hookname) 476{ 477 int hookid; 478 479 for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++) { 480 if (mfp->mf_upper[hookid]) { 481 if (strncmp(NG_HOOK_NAME(mfp->mf_upper[hookid]), 482 hookname, NG_HOOKSIZ) == 0) { 483 return hookid; 484 } 485 } 486 } 487 488 return 0; 489} 490 491static int 492macfilter_direct(macfilter_p mfp, struct ngm_macfilter_direct *md) 493{ 494 MACFILTER_DEBUG("ether=" MAC_FMT " hook=%s", 495 MAC_S_ARGS(md->ether), md->hookname); 496 497 int hookid = macfilter_find_hook(mfp, md->hookname); 498 if (hookid < 0) 499 return ENOENT; 500 501 return macfilter_mactable_change(mfp, md->ether, hookid); 502} 503 504static int 505macfilter_direct_hookid(macfilter_p mfp, struct ngm_macfilter_direct_hookid *mdi) 506{ 507 MACFILTER_DEBUG("ether=" MAC_FMT " hookid=%d", 508 MAC_S_ARGS(mdi->ether), mdi->hookid); 509 510 if (mdi->hookid >= mfp->mf_upper_cnt) 511 return EINVAL; 512 else if (mfp->mf_upper[mdi->hookid] == NULL) 513 return EINVAL; 514 515 return macfilter_mactable_change(mfp, mdi->ether, mdi->hookid); 516} 517 518/* 519 * Packet handling 520 */ 521 522/* 523 * Pass packets received from any upper hook to 524 * a lower hook 525 */ 526static int 527macfilter_ether_output(hook_p hook, macfilter_p mfp, struct mbuf *m, hook_p *next_hook) 528{ 529 struct ether_header *ether_header = mtod(m, struct ether_header *); 530 u_char *ether = ether_header->ether_dhost; 531 532 *next_hook = mfp->mf_ether_hook; 533 534 mtx_lock(&mfp->mtx); 535 536 u_int i; 537 int found = macfilter_find_mac(mfp, ether, &i); 538 if (found) { 539 mf_mac_p mf_macs = mfp->mf_macs; 540 541 mf_macs[i].packets_out++; 542 if (m->m_len > ETHER_HDR_LEN) 543 mf_macs[i].bytes_out += m->m_len - ETHER_HDR_LEN; 544 545#ifdef NG_MACFILTER_DEBUG_RECVDATA 546 MACFILTER_DEBUG("ether=" MAC_FMT " len=%db->%lldb: bytes: %s -> %s", 547 MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN, mf_macs[i].bytes_out, 548 NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook)); 549#endif 550 } else { 551#ifdef NG_MACFILTER_DEBUG_RECVDATA 552 MACFILTER_DEBUG("ether=" MAC_FMT " len=%db->?b: bytes: %s->%s", 553 MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN, 554 NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook)); 555#endif 556 } 557 558 mtx_unlock(&mfp->mtx); 559 560 return 0; 561} 562 563/* 564 * Search for the right upper hook, based on the source ethernet 565 * address. If not found, pass to the default upper hook. 566 */ 567static int 568macfilter_ether_input(hook_p hook, macfilter_p mfp, struct mbuf *m, hook_p *next_hook) 569{ 570 struct ether_header *ether_header = mtod(m, struct ether_header *); 571 u_char *ether = ether_header->ether_shost; 572 int hookid = NG_MACFILTER_HOOK_DEFAULT_ID; 573 574 mtx_lock(&mfp->mtx); 575 576 u_int i; 577 int found = macfilter_find_mac(mfp, ether, &i); 578 if (found) { 579 mf_mac_p mf_macs = mfp->mf_macs; 580 581 mf_macs[i].packets_in++; 582 if (m->m_len > ETHER_HDR_LEN) 583 mf_macs[i].bytes_in += m->m_len - ETHER_HDR_LEN; 584 585 hookid = mf_macs[i].hookid; 586 587#ifdef NG_MACFILTER_DEBUG_RECVDATA 588 MACFILTER_DEBUG("ether=" MAC_FMT " len=%db->%lldb: bytes: %s->%s", 589 MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN, mf_macs[i].bytes_in, 590 NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook)); 591#endif 592 } else { 593#ifdef NG_MACFILTER_DEBUG_RECVDATA 594 MACFILTER_DEBUG("ether=" MAC_FMT " len=%db->?b: bytes: %s->%s", 595 MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN, 596 NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook)); 597#endif 598 } 599 600 if (hookid >= mfp->mf_upper_cnt) 601 *next_hook = NULL; 602 else 603 *next_hook = mfp->mf_upper[hookid]; 604 605 mtx_unlock(&mfp->mtx); 606 607 return 0; 608} 609 610/* 611 * ====================================================================== 612 * Netgraph hooks 613 * ====================================================================== 614 */ 615 616/* 617 * See basic netgraph code for comments on the individual functions. 618 */ 619 620static int 621ng_macfilter_constructor(node_p node) 622{ 623 macfilter_p mfp = malloc(sizeof(*mfp), M_NETGRAPH, M_NOWAIT | M_ZERO); 624 if (mfp == NULL) 625 return ENOMEM; 626 627 int error = macfilter_mactable_resize(mfp); 628 if (error) 629 return error; 630 631 NG_NODE_SET_PRIVATE(node, mfp); 632 633 mtx_init(&mfp->mtx, "Macfilter table", NULL, MTX_DEF); 634 635 return (0); 636} 637 638static int 639ng_macfilter_newhook(node_p node, hook_p hook, const char *hookname) 640{ 641 const macfilter_p mfp = NG_NODE_PRIVATE(node); 642 643 MACFILTER_DEBUG("%s", hookname); 644 645 if (strcmp(hookname, NG_MACFILTER_HOOK_ETHER) == 0) { 646 mfp->mf_ether_hook = hook; 647 } else { 648 int hookid; 649 if (strcmp(hookname, NG_MACFILTER_HOOK_DEFAULT) == 0) { 650 hookid = NG_MACFILTER_HOOK_DEFAULT_ID; 651 } else { 652 for (hookid = 1; hookid < mfp->mf_upper_cnt; hookid++) 653 if (mfp->mf_upper[hookid] == NULL) 654 break; 655 } 656 657 if (hookid >= mfp->mf_upper_cnt) { 658 MACFILTER_DEBUG("upper cnt %d -> %d", mfp->mf_upper_cnt, hookid + 1); 659 660 mfp->mf_upper_cnt = hookid + 1; 661 mfp->mf_upper = realloc(mfp->mf_upper, 662 sizeof(mfp->mf_upper[0])*mfp->mf_upper_cnt, 663 M_NETGRAPH, M_NOWAIT | M_ZERO); 664 } 665 666 mfp->mf_upper[hookid] = hook; 667 } 668 669 return(0); 670} 671 672static int 673ng_macfilter_rcvmsg(node_p node, item_p item, hook_p lasthook) 674{ 675 const macfilter_p mfp = NG_NODE_PRIVATE(node); 676 struct ng_mesg *resp = NULL; 677 struct ng_mesg *msg; 678 int error = 0; 679 struct ngm_macfilter_macs *ngm_macs; 680 struct ngm_macfilter_hooks *ngm_hooks; 681 struct ngm_macfilter_direct *md; 682 struct ngm_macfilter_direct_hookid *mdi; 683 int n = 0, i = 0; 684 int hookid = 0; 685 int resplen; 686 687 NGI_GET_MSG(item, msg); 688 689 mtx_lock(&mfp->mtx); 690 691 switch (msg->header.typecookie) { 692 case NGM_MACFILTER_COOKIE: 693 switch (msg->header.cmd) { 694 695 case NGM_MACFILTER_RESET: 696 macfilter_reset(mfp); 697 break; 698 699 case NGM_MACFILTER_DIRECT: 700 if (msg->header.arglen != sizeof(struct ngm_macfilter_direct)) { 701 MACFILTER_DEBUG("direct: wrong type length (%d, expected %zu)", 702 msg->header.arglen, sizeof(struct ngm_macfilter_direct)); 703 error = EINVAL; 704 break; 705 } 706 md = (struct ngm_macfilter_direct *)msg->data; 707 error = macfilter_direct(mfp, md); 708 break; 709 case NGM_MACFILTER_DIRECT_HOOKID: 710 if (msg->header.arglen != sizeof(struct ngm_macfilter_direct_hookid)) { 711 MACFILTER_DEBUG("direct hookid: wrong type length (%d, expected %zu)", 712 msg->header.arglen, sizeof(struct ngm_macfilter_direct)); 713 error = EINVAL; 714 break; 715 } 716 mdi = (struct ngm_macfilter_direct_hookid *)msg->data; 717 error = macfilter_direct_hookid(mfp, mdi); 718 break; 719 720 case NGM_MACFILTER_GET_MACS: 721 case NGM_MACFILTER_GETCLR_MACS: 722 n = mfp->mf_mac_used; 723 resplen = sizeof(struct ngm_macfilter_macs) + n * sizeof(struct ngm_macfilter_mac); 724 NG_MKRESPONSE(resp, msg, resplen, M_NOWAIT); 725 if (resp == NULL) { 726 error = ENOMEM; 727 break; 728 } 729 ngm_macs = (struct ngm_macfilter_macs *)resp->data; 730 ngm_macs->n = n; 731 bcopy(mfp->mf_macs, &ngm_macs->macs[0], n * sizeof(struct ngm_macfilter_mac)); 732 733 if (msg->header.cmd == NGM_MACFILTER_GETCLR_MACS) 734 macfilter_reset_stats(mfp); 735 break; 736 737 case NGM_MACFILTER_CLR_MACS: 738 macfilter_reset_stats(mfp); 739 break; 740 741 case NGM_MACFILTER_GET_HOOKS: 742 for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++) 743 if (mfp->mf_upper[hookid] != NULL) 744 n++; 745 resplen = sizeof(struct ngm_macfilter_hooks) + n * sizeof(struct ngm_macfilter_hook); 746 NG_MKRESPONSE(resp, msg, resplen, M_NOWAIT | M_ZERO); 747 if (resp == NULL) { 748 error = ENOMEM; 749 break; 750 } 751 752 ngm_hooks = (struct ngm_macfilter_hooks *)resp->data; 753 ngm_hooks->n = n; 754 for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++) { 755 if (mfp->mf_upper[hookid] != NULL) { 756 struct ngm_macfilter_hook *ngm_hook = &ngm_hooks->hooks[i++]; 757 strlcpy(ngm_hook->hookname, 758 NG_HOOK_NAME(mfp->mf_upper[hookid]), 759 NG_HOOKSIZ); 760 ngm_hook->hookid = hookid; 761 ngm_hook->maccnt = macfilter_mac_count(mfp, hookid); 762 } 763 } 764 break; 765 766 default: 767 error = EINVAL; /* unknown command */ 768 break; 769 } 770 break; 771 772 default: 773 error = EINVAL; /* unknown cookie type */ 774 break; 775 } 776 777 mtx_unlock(&mfp->mtx); 778 779 NG_RESPOND_MSG(error, node, item, resp); 780 NG_FREE_MSG(msg); 781 782 return error; 783} 784 785static int 786ng_macfilter_rcvdata(hook_p hook, item_p item) 787{ 788 const macfilter_p mfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 789 int error; 790 hook_p next_hook = NULL; 791 struct mbuf *m; 792 793 m = NGI_M(item); /* 'item' still owns it. We are peeking */ 794 MACFILTER_DEBUG("%s", NG_HOOK_NAME(hook)); 795 796 if (hook == mfp->mf_ether_hook) 797 error = macfilter_ether_input(hook, mfp, m, &next_hook); 798 else if (mfp->mf_ether_hook != NULL) 799 error = macfilter_ether_output(hook, mfp, m, &next_hook); 800 801 if (next_hook == NULL) { 802 NG_FREE_ITEM(item); 803 return (0); 804 } 805 806 NG_FWD_ITEM_HOOK(error, item, next_hook); 807 808 return error; 809} 810 811static int 812ng_macfilter_disconnect(hook_p hook) 813{ 814 const macfilter_p mfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 815 816 mtx_lock(&mfp->mtx); 817 818 if (mfp->mf_ether_hook == hook) { 819 mfp->mf_ether_hook = NULL; 820 821 MACFILTER_DEBUG("%s", NG_HOOK_NAME(hook)); 822 } else { 823 int hookid; 824 825 for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++) { 826 if (mfp->mf_upper[hookid] == hook) { 827 mfp->mf_upper[hookid] = NULL; 828 829#ifndef NG_MACFILTER_DEBUG 830 macfilter_mactable_remove_by_hookid(mfp, hookid); 831#else 832 int cnt = macfilter_mactable_remove_by_hookid(mfp, hookid); 833 834 MACFILTER_DEBUG("%s: removed %d MACs", NG_HOOK_NAME(hook), cnt); 835#endif 836 break; 837 } 838 } 839 840 if (hookid == mfp->mf_upper_cnt - 1) { 841 /* Reduce the size of the array when the last element was removed */ 842 for (--hookid; hookid >= 0 && mfp->mf_upper[hookid] == NULL; hookid--) 843 ; 844 845 MACFILTER_DEBUG("upper cnt %d -> %d", mfp->mf_upper_cnt, hookid + 1); 846 mfp->mf_upper_cnt = hookid + 1; 847 mfp->mf_upper = realloc(mfp->mf_upper, 848 sizeof(mfp->mf_upper[0])*mfp->mf_upper_cnt, 849 M_NETGRAPH, M_NOWAIT | M_ZERO); 850 } 851 } 852 853 mtx_unlock(&mfp->mtx); 854 855 if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 856 && (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) { 857 ng_rmnode_self(NG_HOOK_NODE(hook)); 858 } 859 860 return (0); 861} 862 863static int 864ng_macfilter_shutdown(node_p node) 865{ 866 const macfilter_p mfp = NG_NODE_PRIVATE(node); 867 868 mtx_destroy(&mfp->mtx); 869 free(mfp->mf_upper, M_NETGRAPH); 870 free(mfp->mf_macs, M_NETGRAPH); 871 free(mfp, M_NETGRAPH); 872 873 NG_NODE_UNREF(node); 874 875 return (0); 876} 877