Changeset View
Changeset View
Standalone View
Standalone View
sys/netgraph/ng_macfilter.c
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/* | |||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||||
* | |||||
* Copyright (c) 2002 Ericsson Research & Pekka Nikander | |||||
* Copyright (c) 2020 Nick Hibma <n_hibma@FreeBSD.org> | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice unmodified, this list of conditions, and the following | |||||
* disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||||
* SUCH DAMAGE. | |||||
* | |||||
* $FreeBSD$ | |||||
*/ | |||||
/* | |||||
* MACFILTER NETGRAPH NODE TYPE | |||||
* | |||||
* This node type routes packets from the ether hook to either the default hook | |||||
* if sender MAC address is not in the MAC table, or out over the specified | |||||
* hook if it is. | |||||
* | |||||
* Other node types can then be used to apply specific processing to the | |||||
* packets on each hook. | |||||
* | |||||
* If compiled with NG_MACFILTER_DEBUG the flow and resizing of the MAC table | |||||
* are logged to the console. | |||||
* | |||||
* If compiled with NG_MACFILTER_DEBUG_RECVDATA every packet handled is logged | |||||
* on the console. | |||||
*/ | |||||
#include <sys/param.h> | |||||
#include <sys/ctype.h> | |||||
#include <sys/systm.h> | |||||
#include <sys/lock.h> | |||||
#include <sys/mbuf.h> | |||||
#include <sys/mutex.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/socket.h> | |||||
#include <net/ethernet.h> | |||||
#include <net/if.h> | |||||
#include <net/if_var.h> | |||||
#include <net/if_dl.h> | |||||
#include <net/if_types.h> | |||||
afedorov: These 'include's are unnecessary:
#include <net/if.h>
#include <net/if_var.h>
#include… | |||||
#include <netgraph/ng_message.h> | |||||
#include <netgraph/ng_parse.h> | |||||
#include <netgraph/netgraph.h> | |||||
#include <netgraph/ng_ether.h> | |||||
Done Inline ActionsDitto. afedorov: Ditto. | |||||
#include "ng_macfilter.h" | |||||
#ifdef NG_SEPARATE_MALLOC | |||||
MALLOC_DEFINE(M_NETGRAPH_MACFILTER, "netgraph_macfilter", "netgraph macfilter node "); | |||||
#else | |||||
#define M_NETGRAPH_MACFILTER M_NETGRAPH | |||||
#endif | |||||
#define MACTABLE_BLOCKSIZE 128 /* block size for incrementing table */ | |||||
#ifdef NG_MACFILTER_DEBUG | |||||
#define DEBUG(fmt, ...) printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, __VA_ARGS__) | |||||
#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" | |||||
#define MAC_S_ARGS(v) (v)[0], (v)[1], (v)[2], (v)[3], (v)[4], (v)[5] | |||||
const u_char first_eth[ETHER_ADDR_LEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; | |||||
const u_char last_eth[ETHER_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | |||||
/* These printfs cost space and time, so enable them only when really needed. */ | |||||
Done Inline ActionsThese two variables are not used. afedorov: These two variables are not used. | |||||
#ifndef INVARIANTS | |||||
#define INVARIANTS 1 | |||||
#endif | |||||
Done Inline ActionsThese define also unused. afedorov: These define also unused. | |||||
#else | |||||
#define DEBUG(a...) | |||||
#endif | |||||
/* | |||||
* Parse type for struct ngm_macfilter_direct | |||||
*/ | |||||
static const struct ng_parse_struct_field macfilter_direct_fields[] | |||||
= NGM_MACFILTER_DIRECT_FIELDS; | |||||
static const struct ng_parse_type ng_macfilter_direct_type = { | |||||
&ng_parse_struct_type, | |||||
&macfilter_direct_fields | |||||
}; | |||||
/* | |||||
* Parse type for struct ngm_macfilter_direct_hookid. | |||||
*/ | |||||
static const struct ng_parse_struct_field macfilter_direct_ndx_fields[] | |||||
= NGM_MACFILTER_DIRECT_NDX_FIELDS; | |||||
static const struct ng_parse_type ng_macfilter_direct_hookid_type = { | |||||
&ng_parse_struct_type, | |||||
&macfilter_direct_ndx_fields | |||||
}; | |||||
/* | |||||
* Parse types for struct ngm_macfilter_get_macs. | |||||
*/ | |||||
static int | |||||
macfilter_get_macs_count(const struct ng_parse_type *type, | |||||
const u_char *start, const u_char *buf) | |||||
{ | |||||
const struct ngm_macfilter_macs *const macs = | |||||
(const struct ngm_macfilter_macs *)(buf - sizeof(u_int32_t)); | |||||
return macs->n; | |||||
} | |||||
static const struct ng_parse_struct_field ng_macfilter_mac_fields[] | |||||
= NGM_MACFILTER_MAC_FIELDS; | |||||
static const struct ng_parse_type ng_macfilter_mac_type = { | |||||
&ng_parse_struct_type, | |||||
ng_macfilter_mac_fields, | |||||
}; | |||||
static const struct ng_parse_array_info ng_macfilter_macs_array_info = { | |||||
&ng_macfilter_mac_type, | |||||
macfilter_get_macs_count | |||||
}; | |||||
static const struct ng_parse_type ng_macfilter_macs_array_type = { | |||||
&ng_parse_array_type, | |||||
&ng_macfilter_macs_array_info | |||||
}; | |||||
static const struct ng_parse_struct_field ng_macfilter_macs_fields[] | |||||
= NGM_MACFILTER_MACS_FIELDS; | |||||
static const struct ng_parse_type ng_macfilter_macs_type = { | |||||
&ng_parse_struct_type, | |||||
&ng_macfilter_macs_fields | |||||
}; | |||||
/* | |||||
* Parse types for struct ngm_macfilter_get_hooks. | |||||
*/ | |||||
static const struct ng_parse_struct_field ng_macfilter_hook_fields[] | |||||
= NGM_MACFILTER_HOOK_FIELDS; | |||||
static const struct ng_parse_type ng_macfilter_hook_type = { | |||||
&ng_parse_struct_type, | |||||
ng_macfilter_hook_fields, | |||||
}; | |||||
static const struct ng_parse_fixedarray_info ng_macfilter_hooks_array_info = { | |||||
&ng_macfilter_hook_type, | |||||
NG_MACFILTER_UPPER_NUM | |||||
}; | |||||
static const struct ng_parse_type ng_macfilter_hooks_array_type = { | |||||
&ng_parse_fixedarray_type, | |||||
&ng_macfilter_hooks_array_info | |||||
}; | |||||
/* | |||||
* List of commands and how to convert arguments to/from ASCII | |||||
*/ | |||||
static const struct ng_cmdlist ng_macfilter_cmdlist[] = { | |||||
{ | |||||
NGM_MACFILTER_COOKIE, | |||||
NGM_MACFILTER_RESET, | |||||
"reset", | |||||
NULL, | |||||
NULL | |||||
}, | |||||
{ | |||||
NGM_MACFILTER_COOKIE, | |||||
NGM_MACFILTER_DIRECT, | |||||
"direct", | |||||
&ng_macfilter_direct_type, | |||||
NULL | |||||
}, | |||||
{ | |||||
NGM_MACFILTER_COOKIE, | |||||
NGM_MACFILTER_DIRECT_HOOKID, | |||||
"directi", | |||||
&ng_macfilter_direct_hookid_type, | |||||
NULL | |||||
}, | |||||
{ | |||||
NGM_MACFILTER_COOKIE, | |||||
NGM_MACFILTER_GET_MACS, | |||||
"getmacs", | |||||
NULL, | |||||
&ng_macfilter_macs_type | |||||
}, | |||||
{ | |||||
NGM_MACFILTER_COOKIE, | |||||
NGM_MACFILTER_GETCLR_MACS, | |||||
"getclrmacs", | |||||
NULL, | |||||
&ng_macfilter_macs_type | |||||
}, | |||||
{ | |||||
NGM_MACFILTER_COOKIE, | |||||
NGM_MACFILTER_CLR_MACS, | |||||
"clrmacs", | |||||
NULL, | |||||
NULL, | |||||
}, | |||||
{ | |||||
NGM_MACFILTER_COOKIE, | |||||
NGM_MACFILTER_GET_HOOKS, | |||||
"gethooks", | |||||
NULL, | |||||
&ng_macfilter_hooks_array_type | |||||
}, | |||||
{ 0 } | |||||
}; | |||||
/* | |||||
* Netgraph node type descriptor | |||||
*/ | |||||
static ng_constructor_t ng_macfilter_constructor; | |||||
static ng_rcvmsg_t ng_macfilter_rcvmsg; | |||||
static ng_shutdown_t ng_macfilter_shutdown; | |||||
static ng_newhook_t ng_macfilter_newhook; | |||||
static ng_rcvdata_t ng_macfilter_rcvdata; | |||||
static ng_disconnect_t ng_macfilter_disconnect; | |||||
static struct ng_type typestruct = { | |||||
.version = NG_ABI_VERSION, | |||||
.name = NG_MACFILTER_NODE_TYPE, | |||||
.constructor = ng_macfilter_constructor, | |||||
.rcvmsg = ng_macfilter_rcvmsg, | |||||
.shutdown = ng_macfilter_shutdown, | |||||
.newhook = ng_macfilter_newhook, | |||||
.rcvdata = ng_macfilter_rcvdata, | |||||
.disconnect = ng_macfilter_disconnect, | |||||
.cmdlist = ng_macfilter_cmdlist | |||||
}; | |||||
NETGRAPH_INIT(macfilter, &typestruct); | |||||
/* | |||||
* Per MAC address info: the hook where to send to, the address | |||||
* Note: We use the same struct as in the netgraph message, so we can bcopy the | |||||
* array. | |||||
*/ | |||||
typedef struct ngm_macfilter_mac mf_mac_t, *mf_mac_p; | |||||
/* | |||||
* Per upper hook info | |||||
*/ | |||||
typedef struct mf_upper_s { | |||||
hook_p mfu_hook; /* Hook */ | |||||
} *mf_upper_p; | |||||
/* | |||||
* Node info | |||||
*/ | |||||
typedef struct { | |||||
hook_p mf_ether_hook; /* Ethernet hook */ | |||||
struct mf_upper_s mf_upper[NG_MACFILTER_UPPER_NUM]; /* Upper hooks */ | |||||
struct mtx mtx; /* Mutex for MACs table */ | |||||
mf_mac_t *mf_macs; /* MAC info: dynamically allocated */ | |||||
u_int mf_mac_allocated;/* Allocated # of MAC slots */ | |||||
u_int mf_mac_used; /* Used # of MAC slots */ | |||||
} *macfilter_p; | |||||
/* | |||||
* Resize the MAC table to accommodate at least mfp->mf_mac_used + 1 entries. | |||||
* | |||||
* Note: mtx already held | |||||
*/ | |||||
static int | |||||
macfilter_mactable_resize(macfilter_p mfp) | |||||
{ | |||||
int error = 0; | |||||
int n = mfp->mf_mac_allocated; | |||||
if (mfp->mf_mac_used < 2*MACTABLE_BLOCKSIZE-1) /* minimum size */ | |||||
n = 2*MACTABLE_BLOCKSIZE-1; | |||||
else if (mfp->mf_mac_used + 2*MACTABLE_BLOCKSIZE < mfp->mf_mac_allocated) /* reduce size */ | |||||
n = mfp->mf_mac_allocated - MACTABLE_BLOCKSIZE; | |||||
else if (mfp->mf_mac_used == mfp->mf_mac_allocated) /* increase size */ | |||||
n = mfp->mf_mac_allocated + MACTABLE_BLOCKSIZE; | |||||
if (n != mfp->mf_mac_allocated) { | |||||
DEBUG("used=%d allocated=%d->%d", | |||||
mfp->mf_mac_used, mfp->mf_mac_allocated, n); | |||||
mf_mac_p mfp_new = realloc(mfp->mf_macs, sizeof(mfp->mf_macs[0]) * n, M_NETGRAPH, M_NOWAIT | M_ZERO); | |||||
if (mfp_new == NULL) { | |||||
error = -1; | |||||
} else { | |||||
mfp->mf_macs = mfp_new; | |||||
mfp->mf_mac_allocated = n; | |||||
} | |||||
} | |||||
return error; | |||||
} | |||||
/* | |||||
* Resets the macfilter to pass all received packets | |||||
* to the default hook. | |||||
* | |||||
* Note: mtx already held | |||||
*/ | |||||
static void | |||||
macfilter_reset(macfilter_p mfp) | |||||
{ | |||||
mfp->mf_mac_used = 0; | |||||
macfilter_mactable_resize(mfp); | |||||
} | |||||
/* | |||||
* Resets the counts for each MAC address. | |||||
* | |||||
* Note: mtx already held | |||||
*/ | |||||
static void | |||||
macfilter_reset_stats(macfilter_p mfp) | |||||
{ | |||||
int i; | |||||
for (i = 0; i < mfp->mf_mac_used; i++) { | |||||
mf_mac_p p = &mfp->mf_macs[i]; | |||||
p->packets_in = p->packets_out = 0; | |||||
p->bytes_in = p->bytes_out = 0; | |||||
} | |||||
} | |||||
/* | |||||
* Count the number of matching macs routed to this hook. | |||||
* | |||||
* Note: mtx already held | |||||
*/ | |||||
static int | |||||
macfilter_mac_count(macfilter_p mfp, int hookid) | |||||
{ | |||||
int i; | |||||
int cnt = 0; | |||||
for (i = 0; i < mfp->mf_mac_used; i++) | |||||
if (mfp->mf_macs[i].hookid == hookid) | |||||
cnt++; | |||||
return cnt; | |||||
} | |||||
/* | |||||
* Find a MAC address in the mac table. | |||||
* | |||||
* Returns 0 on failure with *ri set to index before which to insert a new | |||||
* element. Or returns 1 on success with *ri set to the index of the element | |||||
* that matches. | |||||
* | |||||
* Note: mtx already held. | |||||
*/ | |||||
static u_int | |||||
macfilter_find_mac(macfilter_p mfp, const u_char *ether, u_int *ri) | |||||
{ | |||||
mf_mac_p mf_macs = mfp->mf_macs; | |||||
u_int base = 0; | |||||
u_int range = mfp->mf_mac_used; | |||||
while (range > 0) { | |||||
u_int middle = base + (range >> 1); /* middle */ | |||||
int d = bcmp(ether, mf_macs[middle].ether, ETHER_ADDR_LEN); | |||||
if (d == 0) { /* match */ | |||||
*ri = middle; | |||||
return 1; | |||||
} else if (d > 0) { /* move right */ | |||||
range -= middle - base + 1; | |||||
base = middle + 1; | |||||
} else { /* move left */ | |||||
range = middle - base; | |||||
} | |||||
} | |||||
*ri = base; | |||||
return 0; | |||||
} | |||||
/* | |||||
* Change the upper hook for the given MAC address. If the hook id is zero (the | |||||
* default hook), the MAC address is removed from the table. Otherwise it is | |||||
* inserted to the table at a proper location, and the id of the hook is | |||||
* marked. | |||||
* | |||||
* Note: mtx already held. | |||||
*/ | |||||
static int | |||||
macfilter_mactable_change(macfilter_p mfp, u_char *ether, int hookid) | |||||
{ | |||||
u_int i; | |||||
int found = macfilter_find_mac(mfp, ether, &i); | |||||
mf_mac_p mf_macs = mfp->mf_macs; | |||||
DEBUG("ether=" MAC_FMT " found=%d i=%d ether=" MAC_FMT " hookid=%d->%d used=%d allocated=%d", | |||||
MAC_S_ARGS(ether), found, i, MAC_S_ARGS(mf_macs[i].ether), | |||||
(found? mf_macs[i].hookid:NG_MACFILTER_HOOK_DEFAULT_ID), hookid, | |||||
mfp->mf_mac_used, mfp->mf_mac_allocated); | |||||
if (found) { | |||||
if (hookid == NG_MACFILTER_HOOK_DEFAULT_ID) { /* drop */ | |||||
/* Compress table */ | |||||
mfp->mf_mac_used--; | |||||
size_t len = (mfp->mf_mac_used - i) * sizeof(mf_macs[0]); | |||||
if (len > 0) | |||||
bcopy(&mf_macs[i+1], &mf_macs[i], len); | |||||
macfilter_mactable_resize(mfp); | |||||
} else { /* modify */ | |||||
mf_macs[i].hookid = hookid; | |||||
} | |||||
} else { | |||||
if (hookid == NG_MACFILTER_HOOK_DEFAULT_ID) { /* not found */ | |||||
/* not present and not inserted */ | |||||
return 0; | |||||
} else { /* add */ | |||||
if (macfilter_mactable_resize(mfp) == -1) { | |||||
return ENOMEM; | |||||
} else { | |||||
mf_macs = mfp->mf_macs; /* reassign; might have moved during resize */ | |||||
/* make room for new entry, unless appending */ | |||||
size_t len = (mfp->mf_mac_used - i) * sizeof(mf_macs[0]); | |||||
if (len > 0) | |||||
bcopy(&mf_macs[i], &mf_macs[i+1], len); | |||||
mf_macs[i].hookid = hookid; | |||||
bcopy(ether, mf_macs[i].ether, ETHER_ADDR_LEN); | |||||
mfp->mf_mac_used++; | |||||
} | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
static int | |||||
macfilter_mactable_remove_by_hookid(macfilter_p mfp, int hookid) | |||||
{ | |||||
int i, j; | |||||
for (i = 0, j = 0; i < mfp->mf_mac_used; i++) { | |||||
if (mfp->mf_macs[i].hookid != hookid) { | |||||
if (i != j) | |||||
bcopy(&mfp->mf_macs[i], &mfp->mf_macs[j], sizeof(mfp->mf_macs[0])); | |||||
j++; | |||||
} | |||||
} | |||||
int removed = i - j; | |||||
mfp->mf_mac_used = j; | |||||
macfilter_mactable_resize(mfp); | |||||
return removed; | |||||
} | |||||
static int | |||||
macfilter_find_hook(macfilter_p mfp, const char *hookname) | |||||
{ | |||||
int hookid; | |||||
for (hookid = 0; hookid < NG_MACFILTER_UPPER_NUM; hookid++) { | |||||
if (mfp->mf_upper[hookid].mfu_hook) { | |||||
if (strncmp(NG_HOOK_NAME(mfp->mf_upper[hookid].mfu_hook), | |||||
hookname, NG_HOOKSIZ) == 0) { | |||||
return hookid; | |||||
} | |||||
} | |||||
} | |||||
return 0; | |||||
} | |||||
static int | |||||
macfilter_direct(macfilter_p mfp, struct ngm_macfilter_direct *md) | |||||
{ | |||||
int hookid = macfilter_find_hook(mfp, md->hookname); | |||||
if (hookid < 0) | |||||
return ENOENT; | |||||
return macfilter_mactable_change(mfp, md->ether, hookid); | |||||
} | |||||
static int | |||||
macfilter_direct_hookid(macfilter_p mfp, struct ngm_macfilter_direct_hookid *mdi) | |||||
{ | |||||
if (mdi->hookid >= NG_MACFILTER_UPPER_NUM) | |||||
return EINVAL; | |||||
else if (mfp->mf_upper[mdi->hookid].mfu_hook == NULL) | |||||
return EINVAL; | |||||
return macfilter_mactable_change(mfp, mdi->ether, mdi->hookid); | |||||
} | |||||
/* | |||||
* Packet handling | |||||
*/ | |||||
/* | |||||
* Pass packets received from any upper hook to | |||||
* a lower hook | |||||
*/ | |||||
static int | |||||
macfilter_ether_output(macfilter_p mfp, struct mbuf **mp, hook_p *next_hook) | |||||
{ | |||||
struct ether_header *ether_header = mtod((*mp), struct ether_header *); | |||||
u_char *ether = ether_header->ether_dhost; | |||||
*next_hook = mfp->mf_ether_hook; | |||||
mtx_lock(&mfp->mtx); | |||||
u_int i; | |||||
int found = macfilter_find_mac(mfp, ether, &i); | |||||
if (found) { | |||||
mf_mac_p mf_macs = mfp->mf_macs; | |||||
mf_macs[i].packets_out++; | |||||
if ((*mp)->m_len > ETHER_HDR_LEN) | |||||
mf_macs[i].bytes_out += (*mp)->m_len - ETHER_HDR_LEN; | |||||
} | |||||
mtx_unlock(&mfp->mtx); | |||||
return 0; | |||||
} | |||||
/* | |||||
* Search for the right upper hook, based on the source ethernet | |||||
* address. If not found, pass to the default upper hook. | |||||
*/ | |||||
static int | |||||
macfilter_ether_input(macfilter_p mfp, struct mbuf **mp, hook_p *next_hook) | |||||
{ | |||||
struct ether_header *ether_header = mtod((*mp), struct ether_header *); | |||||
u_char *ether = ether_header->ether_shost; | |||||
int hookid = NG_MACFILTER_HOOK_DEFAULT_ID; | |||||
mtx_lock(&mfp->mtx); | |||||
u_int i; | |||||
int found = macfilter_find_mac(mfp, ether, &i); | |||||
if (found) { | |||||
mf_mac_p mf_macs = mfp->mf_macs; | |||||
mf_macs[i].packets_in++; | |||||
if ((*mp)->m_len > ETHER_HDR_LEN) | |||||
mf_macs[i].bytes_in += (*mp)->m_len - ETHER_HDR_LEN; | |||||
hookid = mf_macs[i].hookid; | |||||
} | |||||
*next_hook = mfp->mf_upper[hookid].mfu_hook; | |||||
mtx_unlock(&mfp->mtx); | |||||
return 0; | |||||
} | |||||
/* | |||||
* ====================================================================== | |||||
* Netgraph hooks | |||||
* ====================================================================== | |||||
*/ | |||||
/* | |||||
* See basic netgraph code for comments on the individual functions. | |||||
*/ | |||||
static int | |||||
ng_macfilter_constructor(node_p node) | |||||
{ | |||||
macfilter_p mfp = malloc(sizeof(*mfp), M_NETGRAPH, M_NOWAIT | M_ZERO); | |||||
if (mfp == NULL) | |||||
return ENOMEM; | |||||
int error = macfilter_mactable_resize(mfp); | |||||
if (error) | |||||
return error; | |||||
NG_NODE_SET_PRIVATE(node, mfp); | |||||
mtx_init(&mfp->mtx, "Macfilter table", NULL, MTX_DEF); | |||||
return (0); | |||||
} | |||||
/* | |||||
* Allocate a new upper hook. | |||||
*/ | |||||
static int | |||||
ng_macfilter_new_upper(const macfilter_p mfp, hook_p hook) | |||||
{ | |||||
int hookid; | |||||
for (hookid = 0; hookid < NG_MACFILTER_UPPER_NUM; hookid++) { | |||||
if (mfp->mf_upper[hookid].mfu_hook == NULL) { | |||||
mfp->mf_upper[hookid].mfu_hook = hook; | |||||
return 0; | |||||
} | |||||
} | |||||
return ENOBUFS; | |||||
} | |||||
static int | |||||
ng_macfilter_newhook(node_p node, hook_p hook, const char *hookname) | |||||
{ | |||||
const macfilter_p mfp = NG_NODE_PRIVATE(node); | |||||
DEBUG("%s", hookname); | |||||
if (strcmp(hookname, NG_MACFILTER_HOOK_ETHER) == 0) { | |||||
if (mfp->mf_ether_hook != NULL) | |||||
return EADDRINUSE; | |||||
mfp->mf_ether_hook = hook; | |||||
return(0); | |||||
} else if (strcmp(hookname, NG_MACFILTER_HOOK_DEFAULT) == 0) { | |||||
if (mfp->mf_upper[NG_MACFILTER_HOOK_DEFAULT_ID].mfu_hook != NULL) | |||||
return EADDRINUSE; | |||||
mfp->mf_upper[NG_MACFILTER_HOOK_DEFAULT_ID].mfu_hook = hook; | |||||
return(0); | |||||
} else { | |||||
if (macfilter_find_hook(mfp, hookname) > 0) | |||||
return EADDRINUSE; | |||||
Done Inline ActionsIt seems to me that check is not needed here. Because the netgraph(4) has already checked that there is no hook with the same name. afedorov: It seems to me that check is not needed here. Because the netgraph(4) has already checked that… | |||||
return ng_macfilter_new_upper(mfp, hook); | |||||
} | |||||
} | |||||
static int | |||||
ng_macfilter_rcvmsg(node_p node, item_p item, hook_p lasthook) | |||||
{ | |||||
const macfilter_p mfp = NG_NODE_PRIVATE(node); | |||||
struct ng_mesg *resp = NULL; | |||||
struct ng_mesg *msg; | |||||
int error = 0; | |||||
struct ngm_macfilter_macs *ngm_macs; | |||||
struct ngm_macfilter_hook *ngm_hooks; | |||||
struct ngm_macfilter_direct *md; | |||||
struct ngm_macfilter_direct_hookid *mdi; | |||||
int n, resplen; | |||||
NGI_GET_MSG(item, msg); | |||||
#ifdef NG_MACFILTER_DEBUG | |||||
static int getclr_macs_count = 0; /* avoid spamming kernel logs */ | |||||
if (msg->header.cmd != NGM_MACFILTER_GETCLR_MACS | |||||
|| ++getclr_macs_count <= 10) { | |||||
DEBUG("typecookie=%d cmd=%d", | |||||
msg->header.typecookie, msg->header.cmd); | |||||
} | |||||
#endif | |||||
mtx_lock(&mfp->mtx); | |||||
switch (msg->header.typecookie) { | |||||
case NGM_MACFILTER_COOKIE: | |||||
switch (msg->header.cmd) { | |||||
case NGM_MACFILTER_RESET: | |||||
macfilter_reset(mfp); | |||||
#ifdef NG_MACFILTER_DEBUG | |||||
getclr_macs_count = 0; | |||||
#endif | |||||
break; | |||||
case NGM_MACFILTER_DIRECT: | |||||
if (msg->header.arglen != sizeof(struct ngm_macfilter_direct)) { | |||||
DEBUG("direct: wrong type length (%d, expected %d)", | |||||
msg->header.arglen, sizeof(struct ngm_macfilter_direct)); | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
md = (struct ngm_macfilter_direct *)msg->data; | |||||
error = macfilter_direct(mfp, md); | |||||
break; | |||||
case NGM_MACFILTER_DIRECT_HOOKID: | |||||
if (msg->header.arglen != sizeof(struct ngm_macfilter_direct_hookid)) { | |||||
DEBUG("direct hookid: wrong type length (%d, expected %d)", | |||||
msg->header.arglen, sizeof(struct ngm_macfilter_direct)); | |||||
error = EINVAL; | |||||
break; | |||||
} | |||||
mdi = (struct ngm_macfilter_direct_hookid *)msg->data; | |||||
error = macfilter_direct_hookid(mfp, mdi); | |||||
break; | |||||
case NGM_MACFILTER_GET_MACS: | |||||
case NGM_MACFILTER_GETCLR_MACS: | |||||
n = mfp->mf_mac_used; | |||||
resplen = sizeof(struct ngm_macfilter_macs) + n * sizeof(struct ngm_macfilter_mac); | |||||
NG_MKRESPONSE(resp, msg, resplen, M_NOWAIT); | |||||
if (resp == NULL) { | |||||
error = ENOMEM; | |||||
break; | |||||
} | |||||
ngm_macs = (struct ngm_macfilter_macs *)resp->data; | |||||
ngm_macs->n = n; | |||||
bcopy(mfp->mf_macs, &ngm_macs->macs[0], n * sizeof(struct ngm_macfilter_mac)); | |||||
if (msg->header.cmd == NGM_MACFILTER_GETCLR_MACS) | |||||
macfilter_reset_stats(mfp); | |||||
break; | |||||
case NGM_MACFILTER_CLR_MACS: | |||||
macfilter_reset_stats(mfp); | |||||
#ifdef NG_MACFILTER_DEBUG | |||||
getclr_macs_count = 0; | |||||
#endif | |||||
break; | |||||
case NGM_MACFILTER_GET_HOOKS: | |||||
resplen = NG_MACFILTER_UPPER_NUM * sizeof(struct ngm_macfilter_hook); | |||||
NG_MKRESPONSE(resp, msg, resplen, M_NOWAIT | M_ZERO); | |||||
if (resp == NULL) { | |||||
error = ENOMEM; | |||||
break; | |||||
} | |||||
ngm_hooks = (struct ngm_macfilter_hook *)resp->data; | |||||
for (int i = 0, hookid = 1; hookid < NG_MACFILTER_UPPER_NUM; hookid++) { | |||||
if (mfp->mf_upper[hookid].mfu_hook != NULL) { | |||||
struct ngm_macfilter_hook *ngm_hook = &ngm_hooks[i++]; | |||||
strlcpy(ngm_hook->hookname, | |||||
NG_HOOK_NAME(mfp->mf_upper[hookid].mfu_hook), | |||||
NG_HOOKSIZ); | |||||
ngm_hook->hookid = hookid; | |||||
ngm_hook->maccnt = macfilter_mac_count(mfp, hookid); | |||||
} | |||||
} | |||||
break; | |||||
default: | |||||
error = EINVAL; /* unknown command */ | |||||
break; | |||||
} | |||||
break; | |||||
default: | |||||
error = EINVAL; /* unknown cookie type */ | |||||
break; | |||||
} | |||||
mtx_unlock(&mfp->mtx); | |||||
NG_RESPOND_MSG(error, node, item, resp); | |||||
NG_FREE_MSG(msg); | |||||
return error; | |||||
} | |||||
static int | |||||
ng_macfilter_rcvdata(hook_p hook, item_p item) | |||||
{ | |||||
const macfilter_p mfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); | |||||
int error; | |||||
hook_p next_hook = NULL; | |||||
struct mbuf *m; | |||||
m = NGI_M(item); /* 'item' still owns it. We are peeking */ | |||||
if (hook == mfp->mf_ether_hook) | |||||
error = macfilter_ether_input(mfp, &m, &next_hook); | |||||
else if (mfp->mf_ether_hook != NULL) | |||||
error = macfilter_ether_output(mfp, &m, &next_hook); | |||||
if (next_hook == NULL) { | |||||
NG_FREE_ITEM(item); | |||||
return (0); | |||||
} | |||||
#ifdef NG_MACFILTER_DEBUG_RECVDATA | |||||
struct ether_header *ether_header = mtod(m, struct ether_header *); | |||||
u_char *ether = (hook == mfp->mf_ether_hook? ether_header->ether_shost : ether_header->ether_dhost); | |||||
mtx_lock(&mfp->mtx); | |||||
u_int i; | |||||
int found = macfilter_find_mac(mfp, ether, &i); | |||||
if (found) { | |||||
DEBUG("ether=" MAC_FMT " %d: bytes: %s->%s", | |||||
MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN, | |||||
NG_HOOK_NAME(hook), NG_HOOK_NAME(next_hook)); | |||||
} | |||||
mtx_unlock(&mfp->mtx); | |||||
#endif | |||||
NG_FWD_ITEM_HOOK(error, item, next_hook); | |||||
return error; | |||||
} | |||||
static int | |||||
ng_macfilter_shutdown(node_p node) | |||||
{ | |||||
const macfilter_p mfp = NG_NODE_PRIVATE(node); | |||||
mtx_destroy(&mfp->mtx); | |||||
free(mfp->mf_macs, M_NETGRAPH); | |||||
free(mfp, M_NETGRAPH); | |||||
NG_NODE_UNREF(node); | |||||
return (0); | |||||
} | |||||
static int | |||||
ng_macfilter_disconnect(hook_p hook) | |||||
{ | |||||
const macfilter_p mfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); | |||||
if (mfp) { | |||||
if (mfp->mf_ether_hook == hook) { | |||||
mfp->mf_ether_hook = NULL; | |||||
DEBUG("%s", NG_HOOK_NAME(hook)); | |||||
} else { | |||||
int hookid; | |||||
mtx_lock(&mfp->mtx); | |||||
for (hookid = 0; hookid < NG_MACFILTER_UPPER_NUM; hookid++) { | |||||
if (mfp->mf_upper[hookid].mfu_hook == hook) { | |||||
mfp->mf_upper[hookid].mfu_hook = NULL; | |||||
#ifndef NG_MACFILTER_DEBUG | |||||
macfilter_mactable_remove_by_hookid(mfp, hookid); | |||||
#else | |||||
int cnt = macfilter_mactable_remove_by_hookid(mfp, hookid); | |||||
DEBUG("%s: removed %d MACs", NG_HOOK_NAME(hook), cnt); | |||||
#endif | |||||
break; | |||||
} | |||||
} | |||||
mtx_unlock(&mfp->mtx); | |||||
} | |||||
} | |||||
if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) | |||||
&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) { | |||||
ng_rmnode_self(NG_HOOK_NODE(hook)); | |||||
} | |||||
return (0); | |||||
} |
These 'include's are unnecessary:
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_dl.h>
#include <net/if_types.h>