New Netgraph module ng_macfilter:
Macfilter to route packets through different hooks based on sender MAC address. Based on ng_macfilter written by Pekka Nikander Sponsered by Retina b.v. Reviewed by: afedorov MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D27268
This commit is contained in:
parent
b7b5d7d7f5
commit
e8db04c389
@ -356,6 +356,7 @@ MAN= aac.4 \
|
||||
ng_l2cap.4 \
|
||||
ng_l2tp.4 \
|
||||
ng_lmi.4 \
|
||||
ng_macfilter.4 \
|
||||
ng_mppc.4 \
|
||||
ng_nat.4 \
|
||||
ng_netflow.4 \
|
||||
|
222
share/man/man4/ng_macfilter.4
Normal file
222
share/man/man4/ng_macfilter.4
Normal file
@ -0,0 +1,222 @@
|
||||
.\" Copyright (c) 2012-2017 Pekka Nikander
|
||||
.\" Copyright (c) 2018 Retina b.v.
|
||||
.\" 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, 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.
|
||||
.\" 3. Neither the name of the University nor the names of its contributors
|
||||
.\" may be used to endorse or promote products derived from this software
|
||||
.\" without specific prior written permission.
|
||||
.\"
|
||||
.\" 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$
|
||||
.\"
|
||||
.Dd December 10, 2018
|
||||
.Dt NG_MACFILTER 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm ng_macfilter
|
||||
.Nd packet filtering netgraph node using ethernet MAC addresses
|
||||
.Sh SYNOPSIS
|
||||
.In sys/types.h
|
||||
.In netgraph/ng_macfilter.h
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm macfilter
|
||||
allows routing ethernet packets over different hooks based on the sender MAC
|
||||
address.
|
||||
.Pp
|
||||
This processing is done when traffic flows from the
|
||||
.Dq ether
|
||||
hook trough
|
||||
.Nm macfilter
|
||||
to one of the outgoing hooks.
|
||||
Outbound hooks can be added to and remove from
|
||||
.Nm macfilter
|
||||
and arbitrarily named.
|
||||
By default one hook called
|
||||
.Dq default
|
||||
is present and used for all packets which have no MAC address in the MAC table.
|
||||
By adding MAC addresses to the MAC table traffic coming from this host can be
|
||||
directed out other hooks.
|
||||
.Nm macfilter
|
||||
keeps track of packets and bytes from and to this MAC address in the MAC table.
|
||||
.Pp
|
||||
Packets are not altered in any way.
|
||||
If hooks are not connected, packets are
|
||||
dropped.
|
||||
.Sh HOOKS
|
||||
This node type by default has an
|
||||
.Dv ether
|
||||
hook, to be connected to the
|
||||
.Dv lower
|
||||
hook of the NIC, and a
|
||||
.Dv default
|
||||
hook where packets are sent if the MAC adddress is not found in the table.
|
||||
.Nm macfilter
|
||||
supports up to
|
||||
.Dv NG_MACFILTER_UPPER_NUM
|
||||
hooks to be connected to the NIC's upper hook.
|
||||
Other nodes can be inserted to provide additional processing.
|
||||
All outbound can be combined back into one by using
|
||||
.Dv ng_one2many .
|
||||
.Sh CONTROL MESSAGES
|
||||
This node type supports the generic control messages, plus the
|
||||
following:
|
||||
.Bl -tag -width foo
|
||||
.It Dv NGM_MACFILTER_RESET Pq Ic reset
|
||||
Resets the MAC table in the node.
|
||||
.It Dv NGM_MACFILTER_DIRECT Pq Ic direct
|
||||
Takes the following argument struct:
|
||||
.Bd -literal -offset indent
|
||||
struct ngm_macfilter_direct {
|
||||
u_char ether[ETHER_ADDR_LEN]; /* MAC address */
|
||||
u_char hookname[NG_HOOKSIZ]; /* Upper hook name*/
|
||||
};
|
||||
.Ed
|
||||
The given ethernet MAC address will be forwarded out the named hook.
|
||||
.It Dv NGM_MACFILTER_DIRECT_HOOKID Pq Ic directi
|
||||
Takes the following argument struct:
|
||||
.Bd -literal -offset indent
|
||||
struct ngm_macfilter_direct_hookid {
|
||||
u_char ether[ETHER_ADDR_LEN]; /* MAC address */
|
||||
u_int16_t hookid; /* Upper hook hookid */
|
||||
};
|
||||
.Ed
|
||||
The given ethernet MAC address will be forwarded out the hook at id
|
||||
.Dv hookid .
|
||||
.It Dv NGM_MACFILTER_GET_MACS Pq Ic getmacs
|
||||
Returns the list of MAC addresses in the node in the following structure:
|
||||
.Bd -literal -offset indent
|
||||
struct ngm_macfilter_mac {
|
||||
u_char ether[ETHER_ADDR_LEN]; /* MAC address */
|
||||
u_int16_t hookid; /* Upper hook hookid */
|
||||
u_int64_t packets_in; /* packets in from downstream */
|
||||
u_int64_t bytes_in; /* bytes in from upstream */
|
||||
u_int64_t packets_out; /* packets out towards downstream */
|
||||
u_int64_t bytes_out; /* bytes out towards downstream */
|
||||
};
|
||||
struct ngm_macfilter_macs {
|
||||
u_int32_t n; /* Number of entries in macs */
|
||||
struct ngm_macfilter_mac macs[]; /* Macs table */
|
||||
};
|
||||
.Ed
|
||||
.It Dv NGM_MACFILTER_GETCLR_MACS Pq Ic getclrmacs
|
||||
Same as above, but will also atomically clear the
|
||||
.Dv packets_in ,
|
||||
.Dv bytes_in ,
|
||||
.Dv packets_out , and
|
||||
.Dv bytes_out
|
||||
fields in the table.
|
||||
.It Dv NGM_MACFILTER_CLR_STATS Pq Ic clrmacs
|
||||
Will clear the per MAC address packet and byte counters.
|
||||
.It Dv NGM_MACFILTER_GET_HOOKS Pq Ic gethooks
|
||||
Will return a list of hooks and their hookids in an array of the following struct's:
|
||||
.Bd -literal -offset indent
|
||||
struct ngm_macfilter_hook {
|
||||
u_char hookname[NG_HOOKSIZ]; /* Upper hook name*/
|
||||
u_int16_t hookid; /* Upper hook hookid */
|
||||
u_int32_t maccnt; /* Number of mac addresses associated with hook */
|
||||
};
|
||||
.Ed
|
||||
.El
|
||||
.Sh SHUTDOWN
|
||||
This node shuts down upon receipt of a
|
||||
.Dv NGM_SHUTDOWN
|
||||
control message or when all have been disconnected.
|
||||
.Sh EXAMPLES
|
||||
The following netgraph configuration will apply
|
||||
.Xr ipfw 8
|
||||
tag 42 to each packet that is routed over the
|
||||
.Dq accepted
|
||||
hook.
|
||||
The graph looks like the following:
|
||||
.Bd -literal -offset indent
|
||||
/------<one>-[combiner]-<many1>--------\\
|
||||
<upper> | <out>
|
||||
/ <many0> \\
|
||||
[em0] | [tagger]
|
||||
\\ <default> /
|
||||
<lower> | <in>
|
||||
\\----<ether>-[macfilter]-<accepted>-----/
|
||||
.Ed
|
||||
.Pp
|
||||
Commands:
|
||||
.Bd -literal -offset indent
|
||||
ngctl mkpeer em0: macfilter lower ether
|
||||
ngctl name em0:lower macfilter
|
||||
|
||||
# Funnel both streams back into ether:upper
|
||||
ngctl mkpeer em0: one2many upper one
|
||||
ngctl name em0:upper recombiner
|
||||
# Connect macfilter:default to recombiner:many0
|
||||
ngctl connect macfilter: recombiner: default many0
|
||||
# Connect macfilter:accepted to tagger:in
|
||||
ngctl mkpeer macfilter: tag accepted in
|
||||
ngctl name macfilter:accepted tagger
|
||||
# Connect tagger:out to recombiner:many1
|
||||
ngctl connect tagger: recombiner: out many1
|
||||
|
||||
# Mark tag all traffic through tagger in -> out with an ipfw tag 42
|
||||
ngctl msg tagger: sethookin '{ thisHook="in" ifNotMatch="out" }'
|
||||
ngctl msg tagger: sethookout '{ thisHook="out" tag_cookie=1148380143 tag_id=42 }'
|
||||
|
||||
# Pass traffic from ether:upper / combiner:one via combiner:many0 on to
|
||||
# macfilter:default and on to ether:lower.
|
||||
ngctl msg recombiner: setconfig '{ xmitAlg=3 failAlg=1 enabledLinks=[ 1 1 ] }'
|
||||
.Ed
|
||||
.Pp
|
||||
.Em Note :
|
||||
The tag_cookie 1148380143 was retrieved from
|
||||
.Dv MTAG_IPFW
|
||||
in
|
||||
.Pa /usr/include/netinet/ip_var.h .
|
||||
.Pp
|
||||
The following command can be used to add a MAC address to be output via
|
||||
.Dv macfilter:accepted :
|
||||
.Bd -literal -offset indent
|
||||
ngctl msg macfilter: direct '{ hookname="known" ether=08:00:27:92:eb:aa }'
|
||||
.Ed
|
||||
.Pp
|
||||
The following command can be used to retrieve the packet and byte counters :
|
||||
.Bd -literal -offset indent
|
||||
ngctl msg macfilter: getmacs
|
||||
.Ed
|
||||
.Pp
|
||||
It will return the contents of the MAC table:
|
||||
.Bd -literal -offset indent
|
||||
Rec'd response "getmacs" (4) from "[54]:":
|
||||
Args: { n=1 macs=[ { ether=08:00:27:92:eb:aa hookid=1 packets_in=3571 bytes_in=592631 packets_out=3437 bytes_out=777142 } ] }
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr divert 4 ,
|
||||
.Xr ipfw 4 ,
|
||||
.Xr netgraph 4 ,
|
||||
.Xr ng_ether 4 ,
|
||||
.Xr ng_one2many 4 ,
|
||||
.Xr ng_tag 4 ,
|
||||
.Xr ngctl 8
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The original version of this code was written by Pekka Nikander, and
|
||||
subsequently modified heavily by
|
||||
.An Nick Hibma Aq Mt n_hibma@FreeBSD.org .
|
||||
.Sh BUGS
|
||||
None known.
|
@ -4299,6 +4299,7 @@ netgraph/ng_ipfw.c optional netgraph_ipfw inet ipfirewall
|
||||
netgraph/ng_ksocket.c optional netgraph_ksocket
|
||||
netgraph/ng_l2tp.c optional netgraph_l2tp
|
||||
netgraph/ng_lmi.c optional netgraph_lmi
|
||||
netgraph/ng_macfilter.c optional netgraph_macfilter
|
||||
netgraph/ng_mppc.c optional netgraph_mppc_compression | \
|
||||
netgraph_mppc_encryption
|
||||
netgraph/ng_nat.c optional netgraph_nat inet libalias
|
||||
|
@ -31,6 +31,7 @@ SUBDIR= async \
|
||||
ksocket \
|
||||
l2tp \
|
||||
lmi \
|
||||
macfilter \
|
||||
${_mppc} \
|
||||
nat \
|
||||
netflow \
|
||||
|
9
sys/modules/netgraph/macfilter/Makefile
Normal file
9
sys/modules/netgraph/macfilter/Makefile
Normal file
@ -0,0 +1,9 @@
|
||||
# $FreeBSD$
|
||||
|
||||
KMOD= ng_macfilter
|
||||
SRCS= ng_macfilter.c
|
||||
|
||||
.include <bsd.kmod.mk>
|
||||
|
||||
#CFLAGS+= -DNG_MACFILTER_DEBUG
|
||||
#CFLAGS+= -DNG_MACFILTER_DEBUG_RECVDATA
|
878
sys/netgraph/ng_macfilter.c
Normal file
878
sys/netgraph/ng_macfilter.c
Normal file
@ -0,0 +1,878 @@
|
||||
/*
|
||||
* 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 <netgraph/ng_message.h>
|
||||
#include <netgraph/netgraph.h>
|
||||
#include <netgraph/ng_parse.h>
|
||||
|
||||
#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__)
|
||||
#else
|
||||
#define DEBUG(fmt, ...)
|
||||
#endif
|
||||
#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]
|
||||
|
||||
/*
|
||||
* 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 ngm_macs =
|
||||
(const struct ngm_macfilter_macs *)(buf - OFFSETOF(struct ngm_macfilter_macs, macs));
|
||||
|
||||
return ngm_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 int
|
||||
macfilter_get_upper_hook_count(const struct ng_parse_type *type,
|
||||
const u_char *start, const u_char *buf)
|
||||
{
|
||||
const struct ngm_macfilter_hooks *const ngm_hooks =
|
||||
(const struct ngm_macfilter_hooks *)(buf - OFFSETOF(struct ngm_macfilter_hooks, hooks));
|
||||
|
||||
DEBUG("buf %p, ngm_hooks %p, n %d", buf, ngm_hooks, ngm_hooks->n);
|
||||
|
||||
return ngm_hooks->n;
|
||||
}
|
||||
|
||||
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_array_info ng_macfilter_hooks_array_info = {
|
||||
&ng_macfilter_hook_type,
|
||||
macfilter_get_upper_hook_count
|
||||
};
|
||||
static const struct ng_parse_type ng_macfilter_hooks_array_type = {
|
||||
&ng_parse_array_type,
|
||||
&ng_macfilter_hooks_array_info
|
||||
};
|
||||
static const struct ng_parse_struct_field ng_macfilter_hooks_fields[]
|
||||
= NGM_MACFILTER_HOOKS_FIELDS;
|
||||
static const struct ng_parse_type ng_macfilter_hooks_type = {
|
||||
&ng_parse_struct_type,
|
||||
&ng_macfilter_hooks_fields
|
||||
};
|
||||
|
||||
/*
|
||||
* 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_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_p;
|
||||
|
||||
/*
|
||||
* Node info
|
||||
*/
|
||||
typedef struct {
|
||||
hook_p mf_ether_hook; /* Ethernet hook */
|
||||
|
||||
hook_p *mf_upper; /* Upper hooks */
|
||||
u_int mf_upper_cnt; /* Allocated # of upper slots */
|
||||
|
||||
struct mtx mtx; /* Mutex for MACs table */
|
||||
mf_mac_p 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 < mfp->mf_upper_cnt; hookid++) {
|
||||
if (mfp->mf_upper[hookid]) {
|
||||
if (strncmp(NG_HOOK_NAME(mfp->mf_upper[hookid]),
|
||||
hookname, NG_HOOKSIZ) == 0) {
|
||||
return hookid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
macfilter_direct(macfilter_p mfp, struct ngm_macfilter_direct *md)
|
||||
{
|
||||
DEBUG("ether=" MAC_FMT " hook=%s",
|
||||
MAC_S_ARGS(md->ether), md->hookname);
|
||||
|
||||
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)
|
||||
{
|
||||
DEBUG("ether=" MAC_FMT " hookid=%d",
|
||||
MAC_S_ARGS(mdi->ether), mdi->hookid);
|
||||
|
||||
if (mdi->hookid >= mfp->mf_upper_cnt)
|
||||
return EINVAL;
|
||||
else if (mfp->mf_upper[mdi->hookid] == 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(hook_p hook, macfilter_p mfp, struct mbuf *m, hook_p *next_hook)
|
||||
{
|
||||
struct ether_header *ether_header = mtod(m, 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 (m->m_len > ETHER_HDR_LEN)
|
||||
mf_macs[i].bytes_out += m->m_len - ETHER_HDR_LEN;
|
||||
|
||||
#ifdef NG_MACFILTER_DEBUG_RECVDATA
|
||||
DEBUG("ether=" MAC_FMT " len=%db->%lldb: bytes: %s -> %s",
|
||||
MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN, mf_macs[i].bytes_out,
|
||||
NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook));
|
||||
#endif
|
||||
} else {
|
||||
#ifdef NG_MACFILTER_DEBUG_RECVDATA
|
||||
DEBUG("ether=" MAC_FMT " len=%db->?b: bytes: %s->%s",
|
||||
MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN,
|
||||
NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook));
|
||||
#endif
|
||||
}
|
||||
|
||||
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(hook_p hook, macfilter_p mfp, struct mbuf *m, hook_p *next_hook)
|
||||
{
|
||||
struct ether_header *ether_header = mtod(m, 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 (m->m_len > ETHER_HDR_LEN)
|
||||
mf_macs[i].bytes_in += m->m_len - ETHER_HDR_LEN;
|
||||
|
||||
hookid = mf_macs[i].hookid;
|
||||
|
||||
#ifdef NG_MACFILTER_DEBUG_RECVDATA
|
||||
DEBUG("ether=" MAC_FMT " len=%db->%lldb: bytes: %s->%s",
|
||||
MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN, mf_macs[i].bytes_in,
|
||||
NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook));
|
||||
#endif
|
||||
} else {
|
||||
#ifdef NG_MACFILTER_DEBUG_RECVDATA
|
||||
DEBUG("ether=" MAC_FMT " len=%db->?b: bytes: %s->%s",
|
||||
MAC_S_ARGS(ether), m->m_len - ETHER_HDR_LEN,
|
||||
NG_HOOK_NAME(hook), NG_HOOK_NAME(*next_hook));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (hookid >= mfp->mf_upper_cnt)
|
||||
*next_hook = NULL;
|
||||
else
|
||||
*next_hook = mfp->mf_upper[hookid];
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
mfp->mf_ether_hook = hook;
|
||||
} else {
|
||||
int hookid;
|
||||
if (strcmp(hookname, NG_MACFILTER_HOOK_DEFAULT) == 0) {
|
||||
hookid = NG_MACFILTER_HOOK_DEFAULT_ID;
|
||||
} else {
|
||||
for (hookid = 1; hookid < mfp->mf_upper_cnt; hookid++)
|
||||
if (mfp->mf_upper[hookid] == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
if (hookid >= mfp->mf_upper_cnt) {
|
||||
DEBUG("upper cnt %d -> %d", mfp->mf_upper_cnt, hookid + 1);
|
||||
|
||||
mfp->mf_upper_cnt = hookid + 1;
|
||||
mfp->mf_upper = realloc(mfp->mf_upper,
|
||||
sizeof(mfp->mf_upper[0])*mfp->mf_upper_cnt,
|
||||
M_NETGRAPH, M_NOWAIT | M_ZERO);
|
||||
}
|
||||
|
||||
mfp->mf_upper[hookid] = hook;
|
||||
}
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
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_hooks *ngm_hooks;
|
||||
struct ngm_macfilter_direct *md;
|
||||
struct ngm_macfilter_direct_hookid *mdi;
|
||||
int n = 0, i = 0;
|
||||
int hookid = 0;
|
||||
int resplen;
|
||||
|
||||
NGI_GET_MSG(item, msg);
|
||||
|
||||
mtx_lock(&mfp->mtx);
|
||||
|
||||
switch (msg->header.typecookie) {
|
||||
case NGM_MACFILTER_COOKIE:
|
||||
switch (msg->header.cmd) {
|
||||
|
||||
case NGM_MACFILTER_RESET:
|
||||
macfilter_reset(mfp);
|
||||
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);
|
||||
break;
|
||||
|
||||
case NGM_MACFILTER_GET_HOOKS:
|
||||
for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++)
|
||||
if (mfp->mf_upper[hookid] != NULL)
|
||||
n++;
|
||||
resplen = sizeof(struct ngm_macfilter_hooks) + n * 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_hooks *)resp->data;
|
||||
ngm_hooks->n = n;
|
||||
for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++) {
|
||||
if (mfp->mf_upper[hookid] != NULL) {
|
||||
struct ngm_macfilter_hook *ngm_hook = &ngm_hooks->hooks[i++];
|
||||
strlcpy(ngm_hook->hookname,
|
||||
NG_HOOK_NAME(mfp->mf_upper[hookid]),
|
||||
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 */
|
||||
DEBUG("%s", NG_HOOK_NAME(hook));
|
||||
|
||||
if (hook == mfp->mf_ether_hook)
|
||||
error = macfilter_ether_input(hook, mfp, m, &next_hook);
|
||||
else if (mfp->mf_ether_hook != NULL)
|
||||
error = macfilter_ether_output(hook, mfp, m, &next_hook);
|
||||
|
||||
if (next_hook == NULL) {
|
||||
NG_FREE_ITEM(item);
|
||||
return (0);
|
||||
}
|
||||
|
||||
NG_FWD_ITEM_HOOK(error, item, next_hook);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int
|
||||
ng_macfilter_disconnect(hook_p hook)
|
||||
{
|
||||
const macfilter_p mfp = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
|
||||
|
||||
mtx_lock(&mfp->mtx);
|
||||
|
||||
if (mfp->mf_ether_hook == hook) {
|
||||
mfp->mf_ether_hook = NULL;
|
||||
|
||||
DEBUG("%s", NG_HOOK_NAME(hook));
|
||||
} else {
|
||||
int hookid;
|
||||
|
||||
for (hookid = 0; hookid < mfp->mf_upper_cnt; hookid++) {
|
||||
if (mfp->mf_upper[hookid] == hook) {
|
||||
mfp->mf_upper[hookid] = 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (hookid == mfp->mf_upper_cnt - 1) {
|
||||
/* Reduce the size of the array when the last element was removed */
|
||||
for (--hookid; hookid >= 0 && mfp->mf_upper[hookid] == NULL; hookid--)
|
||||
;
|
||||
|
||||
DEBUG("upper cnt %d -> %d", mfp->mf_upper_cnt, hookid + 1);
|
||||
mfp->mf_upper_cnt = hookid + 1;
|
||||
mfp->mf_upper = realloc(mfp->mf_upper,
|
||||
sizeof(mfp->mf_upper[0])*mfp->mf_upper_cnt,
|
||||
M_NETGRAPH, M_NOWAIT | M_ZERO);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static int
|
||||
ng_macfilter_shutdown(node_p node)
|
||||
{
|
||||
const macfilter_p mfp = NG_NODE_PRIVATE(node);
|
||||
|
||||
mtx_destroy(&mfp->mtx);
|
||||
free(mfp->mf_upper, M_NETGRAPH);
|
||||
free(mfp->mf_macs, M_NETGRAPH);
|
||||
free(mfp, M_NETGRAPH);
|
||||
|
||||
NG_NODE_UNREF(node);
|
||||
|
||||
return (0);
|
||||
}
|
132
sys/netgraph/ng_macfilter.h
Normal file
132
sys/netgraph/ng_macfilter.h
Normal file
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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$
|
||||
*/
|
||||
|
||||
#ifndef _NETGRAPH_MACFILTER_H_
|
||||
#define _NETGRAPH_MACFILTER_H_
|
||||
|
||||
#define NG_MACFILTER_NODE_TYPE "macfilter"
|
||||
#define NGM_MACFILTER_COOKIE 1042445461
|
||||
|
||||
/* Hook names */
|
||||
#define NG_MACFILTER_HOOK_ETHER "ether" /* connected to ether:lower */
|
||||
#define NG_MACFILTER_HOOK_DEFAULT "default" /* connected to ether:upper; upper[0] */
|
||||
/* Other hooks may be named freely connected to ether:upper; upper[1..n]*/
|
||||
#define NG_MACFILTER_HOOK_DEFAULT_ID 0
|
||||
|
||||
#define OFFSETOF(s, e) ((char *)&((s *)0)->e - (char *)((s *)0))
|
||||
|
||||
/* Netgraph commands understood/sent by this node type */
|
||||
enum {
|
||||
NGM_MACFILTER_RESET = 1,
|
||||
NGM_MACFILTER_DIRECT = 2,
|
||||
NGM_MACFILTER_DIRECT_HOOKID = 3,
|
||||
NGM_MACFILTER_GET_MACS = 4,
|
||||
NGM_MACFILTER_GETCLR_MACS = 5,
|
||||
NGM_MACFILTER_CLR_MACS = 6,
|
||||
NGM_MACFILTER_GET_HOOKS = 7
|
||||
};
|
||||
|
||||
/* This structure is supplied with the NGM_MACFILTER_DIRECT command */
|
||||
struct ngm_macfilter_direct {
|
||||
u_char ether[ETHER_ADDR_LEN]; /* MAC address */
|
||||
u_char hookname[NG_HOOKSIZ]; /* Upper hook name*/
|
||||
};
|
||||
#define NGM_MACFILTER_DIRECT_FIELDS { \
|
||||
{ "ether", &ng_parse_enaddr_type }, \
|
||||
{ "hookname", &ng_parse_hookbuf_type }, \
|
||||
{ NULL } \
|
||||
}
|
||||
|
||||
/* This structure is supplied with the NGM_MACFILTER_DIRECT_HOOKID command */
|
||||
struct ngm_macfilter_direct_hookid {
|
||||
u_char ether[ETHER_ADDR_LEN]; /* MAC address */
|
||||
u_int16_t hookid; /* Upper hook hookid */
|
||||
};
|
||||
#define NGM_MACFILTER_DIRECT_NDX_FIELDS { \
|
||||
{ "ether", &ng_parse_enaddr_type }, \
|
||||
{ "hookid", &ng_parse_uint16_type }, \
|
||||
{ NULL } \
|
||||
}
|
||||
|
||||
/* This structure is returned in the array by the NGM_MACFILTER_GET(CLR)_MACS commands */
|
||||
struct ngm_macfilter_mac {
|
||||
u_char ether[ETHER_ADDR_LEN]; /* MAC address */
|
||||
u_int16_t hookid; /* Upper hook hookid */
|
||||
u_int64_t packets_in; /* packets in from downstream */
|
||||
u_int64_t bytes_in; /* bytes in from upstream */
|
||||
u_int64_t packets_out; /* packets out towards downstream */
|
||||
u_int64_t bytes_out; /* bytes out towards downstream */
|
||||
};
|
||||
#define NGM_MACFILTER_MAC_FIELDS { \
|
||||
{ "ether", &ng_parse_enaddr_type }, \
|
||||
{ "hookid", &ng_parse_uint16_type }, \
|
||||
{ "packets_in", &ng_parse_uint64_type }, \
|
||||
{ "bytes_in", &ng_parse_uint64_type }, \
|
||||
{ "packets_out", &ng_parse_uint64_type }, \
|
||||
{ "bytes_out", &ng_parse_uint64_type }, \
|
||||
{ NULL } \
|
||||
}
|
||||
/* This structure is returned by the NGM_MACFILTER_GET(CLR)_MACS commands */
|
||||
struct ngm_macfilter_macs {
|
||||
u_int32_t n; /* Number of entries in macs */
|
||||
struct ngm_macfilter_mac macs[]; /* Macs table */
|
||||
};
|
||||
#define NGM_MACFILTER_MACS_FIELDS { \
|
||||
{ "n", &ng_parse_uint32_type }, \
|
||||
{ "macs", &ng_macfilter_macs_array_type },\
|
||||
{ NULL } \
|
||||
}
|
||||
|
||||
/* This structure is returned in an array by the NGM_MACFILTER_GET_HOOKS command */
|
||||
struct ngm_macfilter_hook {
|
||||
u_char hookname[NG_HOOKSIZ]; /* Upper hook name*/
|
||||
u_int16_t hookid; /* Upper hook hookid */
|
||||
u_int32_t maccnt; /* Number of mac addresses associated with hook */
|
||||
};
|
||||
#define NGM_MACFILTER_HOOK_FIELDS { \
|
||||
{ "hookname", &ng_parse_hookbuf_type }, \
|
||||
{ "hookid", &ng_parse_uint16_type }, \
|
||||
{ "maccnt", &ng_parse_uint32_type }, \
|
||||
{ NULL } \
|
||||
}
|
||||
/* This structure is returned by the NGM_MACFILTER_GET_HOOKS command */
|
||||
struct ngm_macfilter_hooks {
|
||||
u_int32_t n; /* Number of entries in hooks */
|
||||
struct ngm_macfilter_hook hooks[]; /* Hooks table */
|
||||
};
|
||||
#define NGM_MACFILTER_HOOKS_FIELDS { \
|
||||
{ "n", &ng_parse_uint32_type }, \
|
||||
{ "hooks", &ng_macfilter_hooks_array_type },\
|
||||
{ NULL } \
|
||||
}
|
||||
|
||||
#endif /* _NETGRAPH_MACFILTER_H_ */
|
@ -20,6 +20,7 @@ TESTS_SUBDIRS+= kqueue
|
||||
TESTS_SUBDIRS+= mac
|
||||
TESTS_SUBDIRS+= mqueue
|
||||
TESTS_SUBDIRS+= net
|
||||
TESTS_SUBDIRS+= netgraph
|
||||
TESTS_SUBDIRS+= netinet
|
||||
TESTS_SUBDIRS+= netinet6
|
||||
TESTS_SUBDIRS+= netipsec
|
||||
|
14
tests/sys/netgraph/Makefile
Normal file
14
tests/sys/netgraph/Makefile
Normal file
@ -0,0 +1,14 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PACKAGE= tests
|
||||
|
||||
TESTSDIR= ${TESTSBASE}/sys/netgraph
|
||||
BINDIR= ${TESTSDIR}
|
||||
|
||||
TAP_TESTS_SH+= ng_macfilter_test
|
||||
|
||||
TEST_METADATA.runtests+= required_user="root"
|
||||
|
||||
MAN=
|
||||
|
||||
.include <bsd.test.mk>
|
430
tests/sys/netgraph/ng_macfilter_test.sh
Executable file
430
tests/sys/netgraph/ng_macfilter_test.sh
Executable file
@ -0,0 +1,430 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2018-2020 Retina b.v.
|
||||
# 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, 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.
|
||||
# 3. Neither the name of the University nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# 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$
|
||||
|
||||
progname="$(basename $0 .sh)"
|
||||
entries_lst="/tmp/$progname.entries.lst"
|
||||
entries2_lst="/tmp/$progname.entries2.lst"
|
||||
|
||||
HOOKS=3
|
||||
HOOKSADD=42
|
||||
ITERATIONS=7
|
||||
SUBITERATIONS=71
|
||||
|
||||
find_iface () {
|
||||
# Figure out the first ethernet interface
|
||||
ifconfig -u -l ether | awk '{print $1}'
|
||||
}
|
||||
|
||||
loaded_modules=''
|
||||
load_modules () {
|
||||
for kmod in $*; do
|
||||
if ! kldstat -q -m $kmod; then
|
||||
test_comment "Loading $kmod..."
|
||||
kldload $kmod
|
||||
loaded_modules="$loaded_modules $kmod"
|
||||
fi
|
||||
done
|
||||
}
|
||||
unload_modules () {
|
||||
for kmod in $loaded_modules; do
|
||||
# These cannot be unloaded
|
||||
test $kmod = 'ng_ether' -o $kmod = 'ng_socket' \
|
||||
&& continue
|
||||
|
||||
test_comment "Unloading $kmod..."
|
||||
kldunload $kmod
|
||||
done
|
||||
loaded_modules=''
|
||||
}
|
||||
|
||||
configure_nodes () {
|
||||
ngctl mkpeer $eth: macfilter lower ether # Connect the lower hook of the ether instance $eth to the ether hook of a new macfilter instance
|
||||
ngctl name $eth:lower MF # Give the macfilter instance a name
|
||||
ngctl mkpeer $eth: one2many upper one # Connect the upper hook of the ether instance $eth to the one hook of a new one2many instance
|
||||
ngctl name $eth:upper O2M # Give the one2many instance a name
|
||||
ngctl msg O2M: setconfig "{ xmitAlg=3 failAlg=1 enabledLinks=[ 1 1 ] }" # XMIT_FAILOVER -> send replies always out many0
|
||||
|
||||
ngctl connect MF: O2M: default many0 # Connect macfilter:default to the many0 hook of a one2many instance
|
||||
for i in $(seq 1 1 $HOOKS); do
|
||||
ngctl connect MF: O2M: out$i many$i
|
||||
done
|
||||
}
|
||||
|
||||
deconfigure_nodes () {
|
||||
ngctl shutdown MF:
|
||||
ngctl shutdown O2M:
|
||||
}
|
||||
|
||||
cleanup () {
|
||||
test_title "Cleaning up"
|
||||
|
||||
deconfigure_nodes
|
||||
unload_modules
|
||||
|
||||
rm -f $entries_lst $entries2_lst
|
||||
}
|
||||
|
||||
TSTNR=0
|
||||
TSTFAILS=0
|
||||
TSTSUCCS=0
|
||||
|
||||
_test_next () { TSTNR=$(($TSTNR + 1)); }
|
||||
_test_succ () { TSTSUCCS=$(($TSTSUCCS + 1)); }
|
||||
_test_fail () { TSTFAILS=$(($TSTFAILS + 1)); }
|
||||
|
||||
test_cnt () { echo "1..${1:-$TSTNR}"; }
|
||||
test_title () {
|
||||
local msg="$1"
|
||||
|
||||
printf '### %s ' "$msg"
|
||||
printf '#%.0s' `seq $((80 - ${#msg} - 5))`
|
||||
printf "\n"
|
||||
}
|
||||
test_comment () { echo "# $1"; }
|
||||
test_bailout () { echo "Bail out!${1+:- $1}"; exit 1; }
|
||||
test_bail_on_fail () { test $TSTFAILS -eq 0 || test_bailout $1; }
|
||||
test_ok () {
|
||||
local msg="$1"
|
||||
|
||||
_test_next
|
||||
_test_succ
|
||||
echo "ok $TSTNR - $msg"
|
||||
|
||||
return 0
|
||||
}
|
||||
test_not_ok () {
|
||||
local msg="$1"
|
||||
|
||||
_test_next
|
||||
_test_fails
|
||||
echo "not ok $TSTNR - $msg"
|
||||
|
||||
return 1
|
||||
}
|
||||
test_eq () {
|
||||
local v1="$1" v2="$2" msg="$3"
|
||||
|
||||
if [ "$v1" = "$v2" ]; then
|
||||
test_ok "$v1 $msg"
|
||||
else
|
||||
test_not_ok "$v1 vs $v2 $msg"
|
||||
fi
|
||||
}
|
||||
test_ne () {
|
||||
local v1="$1" v2="$2" msg="$3"
|
||||
|
||||
if [ "$v1" != "$v2" ]; then
|
||||
test_ok "$v1 $msg"
|
||||
else
|
||||
test_not_ok "$v1 vs $v2 $msg"
|
||||
fi
|
||||
}
|
||||
test_lt () {
|
||||
local v1=$1 v2=$2 msg="$3"
|
||||
|
||||
if [ "$v1" -lt "$v2" ]; then
|
||||
test_ok "$v1 $msg"
|
||||
else
|
||||
test_not_ok "$v1 >= $v2 $msg"
|
||||
fi
|
||||
}
|
||||
test_le () {
|
||||
local v1=$1 v2=$2 msg="$3"
|
||||
|
||||
if [ "$v1" -le "$v2" ]; then
|
||||
test_ok "$v1 $msg"
|
||||
else
|
||||
test_not_ok "$v1 >= $v2 $msg"
|
||||
fi
|
||||
}
|
||||
test_gt () {
|
||||
local v1=$1 v2=$2 msg="$3"
|
||||
|
||||
if [ "$v1" -gt "$v2" ]; then
|
||||
test_ok "$v1 $msg"
|
||||
else
|
||||
test_not_ok "$v1 <= $v2 $msg"
|
||||
fi
|
||||
}
|
||||
test_ge () {
|
||||
local v1=$1 v2=$2 msg="$3"
|
||||
|
||||
if [ "$v1" -ge "$v2" ]; then
|
||||
test_ok "$v1 $msg"
|
||||
else
|
||||
test_not_ok "$v1 <= $v2 $msg"
|
||||
fi
|
||||
}
|
||||
test_rc () { test_eq $? $1 "$2"; }
|
||||
test_failure () { test_ne $? 0 "$1"; }
|
||||
test_success () { test_eq $? 0 "$1"; }
|
||||
|
||||
gethooks () {
|
||||
ngctl msg MF: 'gethooks' \
|
||||
| perl -ne '$h{$1}=1 while s/hookname="(.*?)"//; sub END {print join(":", sort keys %h)."\n"}'
|
||||
}
|
||||
|
||||
countmacs () {
|
||||
local hookname=${1:-'[^"]*'}
|
||||
|
||||
ngctl msg MF: 'gethooks' \
|
||||
| perl -ne 'sub BEGIN {$c=0} $c += $1 while s/hookname="'$hookname'" hookid=\d+ maccnt=(\d+)//; sub END {print "$c\n"}'
|
||||
}
|
||||
randomedge () {
|
||||
local edge="out$(seq 0 1 $HOOKS | sort -R | head -1)"
|
||||
test $edge = 'out0' \
|
||||
&& echo default \
|
||||
|| echo $edge
|
||||
}
|
||||
genmac () {
|
||||
echo "00:00:00:00:$(printf "%02x" $1):$(printf "%02x" $2)"
|
||||
}
|
||||
|
||||
|
||||
|
||||
################################################################################
|
||||
### Start ######################################################################
|
||||
################################################################################
|
||||
|
||||
test_title "Setting up system..."
|
||||
load_modules netgraph ng_socket ng_ether ng_macfilter ng_one2many
|
||||
eth=$(find_iface)
|
||||
test_comment "Using $eth..."
|
||||
|
||||
|
||||
test_title "Configuring netgraph nodes..."
|
||||
configure_nodes
|
||||
|
||||
trap 'exit 99' 1 2 3 13 14 15
|
||||
trap 'cleanup' EXIT
|
||||
|
||||
created_hooks=$(gethooks)
|
||||
rc=0
|
||||
|
||||
test_cnt
|
||||
|
||||
|
||||
################################################################################
|
||||
### Tests ######################################################################
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
test_title "Test: Duplicate default hook"
|
||||
ngctl connect MF: O2M: default many99 2>/dev/null
|
||||
test_failure "duplicate connect of default hook"
|
||||
|
||||
|
||||
################################################################################
|
||||
test_title "Test: Add and remove hooks"
|
||||
ngctl connect MF: O2M: xxx1 many$(($HOOKS + 1))
|
||||
test_success "connect MF:xxx1 to O2M:many$(($HOOKS + 1))"
|
||||
ngctl connect MF: O2M: xxx2 many$(($HOOKS + 2))
|
||||
test_success "connect MF:xxx2 to O2M:many$(($HOOKS + 2))"
|
||||
ngctl connect MF: O2M: xxx3 many$(($HOOKS + 3))
|
||||
test_success "connect MF:xxx3 to O2M:many$(($HOOKS + 3))"
|
||||
hooks=$(gethooks)
|
||||
test_eq $created_hooks:xxx1:xxx2:xxx3 $hooks 'hooks after adding xxx1-3'
|
||||
|
||||
ngctl rmhook MF: xxx1
|
||||
test_success "rmhook MF:xxx$i"
|
||||
hooks=$(gethooks)
|
||||
test_eq $created_hooks:xxx2:xxx3 $hooks 'hooks after removing xxx1'
|
||||
ngctl rmhook MF: xxx2
|
||||
test_success "rmhook MF:xxx$i"
|
||||
hooks=$(gethooks)
|
||||
test_eq $created_hooks:xxx3 $hooks 'hooks after removing xxx2'
|
||||
ngctl rmhook MF: xxx3
|
||||
test_success "rmhook MF:xxx$i"
|
||||
hooks=$(gethooks)
|
||||
test_eq $created_hooks $hooks 'hooks after removing xxx3'
|
||||
|
||||
test_bail_on_fail
|
||||
|
||||
################################################################################
|
||||
test_title "Test: Add many hooks"
|
||||
added_hooks=""
|
||||
for i in $(seq 10 1 $HOOKSADD); do
|
||||
added_hooks="$added_hooks:xxx$i"
|
||||
ngctl connect MF: O2M: xxx$i many$(($HOOKS + $i))
|
||||
done
|
||||
hooks=$(gethooks)
|
||||
test_eq $created_hooks$added_hooks $hooks 'hooks after adding many hooks'
|
||||
|
||||
for h in $(echo $added_hooks | perl -ne 'chomp; %h=map { $_=>1 } split /:/; print "$_\n" for grep {$_} keys %h'); do
|
||||
ngctl rmhook MF: $h
|
||||
done
|
||||
hooks=$(gethooks)
|
||||
test_eq $created_hooks $hooks 'hooks after adding many hooks'
|
||||
|
||||
test_bail_on_fail
|
||||
|
||||
|
||||
################################################################################
|
||||
test_title "Test: Adding many MACs..."
|
||||
I=1
|
||||
for i in $(seq $ITERATIONS | sort -R); do
|
||||
test_comment "Iteration $I/$iterations..."
|
||||
for j in $(seq 0 1 $SUBITERATIONS); do
|
||||
test $i = 2 && edge='out2' || edge='out1'
|
||||
ether=$(genmac $j $i)
|
||||
|
||||
ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }"
|
||||
done
|
||||
I=$(($I + 1))
|
||||
done
|
||||
|
||||
n=$(countmacs out1)
|
||||
n2=$(( ( $ITERATIONS - 1 ) * ( $SUBITERATIONS + 1 ) ))
|
||||
test_eq $n $n2 'MACs in table for out1'
|
||||
n=$(countmacs out2)
|
||||
n2=$(( 1 * ( $SUBITERATIONS + 1 ) ))
|
||||
test_eq $n $n2 'MACs in table for out2'
|
||||
n=$(countmacs out3)
|
||||
n2=0
|
||||
test_eq $n $n2 'MACs in table for out3'
|
||||
|
||||
test_bail_on_fail
|
||||
|
||||
|
||||
################################################################################
|
||||
test_title "Test: Changing hooks for MACs..."
|
||||
for i in $(seq $ITERATIONS); do
|
||||
edge='out3'
|
||||
ether=$(genmac 0 $i)
|
||||
|
||||
ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }"
|
||||
done
|
||||
|
||||
n=$(countmacs out1)
|
||||
n2=$(( ( $ITERATIONS - 1 ) * ( $SUBITERATIONS + 1 - 1 ) ))
|
||||
test_eq $n $n2 'MACs in table for out1'
|
||||
n=$(countmacs out2)
|
||||
n2=$(( 1 * ( $SUBITERATIONS + 1 - 1 ) ))
|
||||
test_eq $n $n2 'MACs in table for out2'
|
||||
n=$(countmacs out3)
|
||||
n2=$ITERATIONS
|
||||
test_eq $n $n2 'MACs in table for out3'
|
||||
|
||||
test_bail_on_fail
|
||||
|
||||
|
||||
################################################################################
|
||||
test_title "Test: Removing all MACs one by one..."
|
||||
I=1
|
||||
for i in $(seq $ITERATIONS | sort -R); do
|
||||
test_comment "Iteration $I/$iterations..."
|
||||
for j in $(seq 0 1 $SUBITERATIONS | sort -R); do
|
||||
edge="default"
|
||||
ether=$(genmac $j $i)
|
||||
|
||||
ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }"
|
||||
done
|
||||
I=$(($I + 1))
|
||||
done
|
||||
n=$(countmacs)
|
||||
n2=0
|
||||
test_eq $n $n2 'MACs in table'
|
||||
|
||||
test_bail_on_fail
|
||||
|
||||
|
||||
################################################################################
|
||||
test_title "Test: Randomly adding MACs on random hooks..."
|
||||
rm -f $entries_lst
|
||||
for i in $(seq $ITERATIONS); do
|
||||
test_comment "Iteration $i/$iterations..."
|
||||
for j in $(seq 0 1 $SUBITERATIONS | sort -R); do
|
||||
edge=$(randomedge)
|
||||
ether=$(genmac $j $i)
|
||||
|
||||
ngctl msg MF: 'direct' "{ hookname=\"$edge\" ether=$ether }"
|
||||
|
||||
echo $ether $edge >> $entries_lst
|
||||
done
|
||||
|
||||
n=$(countmacs)
|
||||
done
|
||||
|
||||
n=$(countmacs out1)
|
||||
n2=$(grep -c ' out1' $entries_lst)
|
||||
test_eq $n $n2 'MACs in table for out1'
|
||||
n=$(countmacs out2)
|
||||
n2=$(grep -c ' out2' $entries_lst)
|
||||
test_eq $n $n2 'MACs in table for out2'
|
||||
n=$(countmacs out3)
|
||||
n2=$(grep -c ' out3' $entries_lst)
|
||||
test_eq $n $n2 'MACs in table for out3'
|
||||
|
||||
test_bail_on_fail
|
||||
|
||||
|
||||
################################################################################
|
||||
test_title "Test: Randomly changing MAC assignments..."
|
||||
rm -f $entries2_lst
|
||||
for i in $(seq $ITERATIONS); do
|
||||
test_comment "Iteration $i/$iterations..."
|
||||
cat $entries_lst | while read ether edge; do
|
||||
edge2=$(randomedge)
|
||||
|
||||
ngctl msg MF: 'direct' "{ hookname=\"$edge2\" ether=$ether }"
|
||||
|
||||
echo $ether $edge2 >> $entries2_lst
|
||||
done
|
||||
|
||||
n=$(countmacs out1)
|
||||
n2=$(grep -c ' out1' $entries2_lst)
|
||||
test_eq $n $n2 'MACs in table for out1'
|
||||
n=$(countmacs out2)
|
||||
n2=$(grep -c ' out2' $entries2_lst)
|
||||
test_eq $n $n2 'MACs in table for out2'
|
||||
n=$(countmacs out3)
|
||||
n2=$(grep -c ' out3' $entries2_lst)
|
||||
test_eq $n $n2 'MACs in table for out3'
|
||||
|
||||
test_bail_on_fail
|
||||
|
||||
mv $entries2_lst $entries_lst
|
||||
done
|
||||
|
||||
|
||||
################################################################################
|
||||
test_title "Test: Resetting macfilter..."
|
||||
ngctl msg MF: reset
|
||||
test_success "**** reset failed"
|
||||
test_eq $(countmacs) 0 'MACs in table'
|
||||
|
||||
test_bail_on_fail
|
||||
|
||||
|
||||
################################################################################
|
||||
test_cnt
|
||||
|
||||
exit 0
|
Loading…
Reference in New Issue
Block a user