This patch implements two filtering options in NFCT input plugin. If 'src_filter' is set to a network it will only catch the event where the source is that specific network. 'dst_filter' does the same for the destination. The filters are simple and does only accept one network at a time. Multiple networks can be handled by using multiple stacks. To handle connection from and to a network, two stacks are needed too. --- input/flow/ulogd_inpflow_NFCT.c | 239 ++++++++++++++++++++++++++++++++++++++- ulogd.conf.in | 2 + 2 files changed, 239 insertions(+), 2 deletions(-) diff --git a/input/flow/ulogd_inpflow_NFCT.c b/input/flow/ulogd_inpflow_NFCT.c index dcba58f..49ff66e 100644 --- a/input/flow/ulogd_inpflow_NFCT.c +++ b/input/flow/ulogd_inpflow_NFCT.c @@ -33,6 +33,10 @@ #include <string.h> #include <errno.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + #include <sys/time.h> #include <time.h> #include <netinet/in.h> @@ -72,7 +76,7 @@ struct nfct_pluginstance { #define EVENT_MASK NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY static struct config_keyset nfct_kset = { - .num_ces = 9, + .num_ces = 11, .ces = { { .key = "pollinterval", @@ -128,6 +132,16 @@ static struct config_keyset nfct_kset = { .options = CONFIG_OPT_NONE, .u.value = 0, }, + { + .key = "src_filter", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_NONE, + }, + { + .key = "dst_filter", + .type = CONFIG_TYPE_STRING, + .options = CONFIG_OPT_NONE, + }, }, }; #define pollint_ce(x) (x->ces[0]) @@ -139,6 +153,8 @@ static struct config_keyset nfct_kset = { #define nlsockbufmaxsize_ce(x) (x->ces[6]) #define nlresynctimeout_ce(x) (x->ces[7]) #define reliable_ce(x) (x->ces[8]) +#define src_filter_ce(x) ((x)->ces[9]) +#define dst_filter_ce(x) ((x)->ces[10]) enum nfct_keys { NFCT_ORIG_IP_SADDR = 0, @@ -995,10 +1011,54 @@ static void overrun_timeout(struct ulogd_timer *a, void *data) nfct_send(cpi->ovh, NFCT_Q_DUMP, &family); } +static u_int32_t bits2netmask(int bits) +{ + u_int32_t netmask, bm; + + if (bits >= 32 || bits < 0) + return(~0); + for (netmask = 0, bm = 0x80000000; bits; bits--, bm >>= 1) + netmask |= bm; + return netmask; +} + + +static void ipv6_cidr2mask_host(uint8_t cidr, uint32_t *res) +{ + int i, j; + + memset(res, 0, sizeof(uint32_t)*4); + for (i = 0; i < 4 && cidr > 32; i++) { + res[i] = 0xFFFFFFFF; + cidr -= 32; + } + res[i] = 0xFFFFFFFF << (32 - cidr); + for (j = i+1; j < 4; j++) { + res[j] = 0; + } +} + +/* I need this function because I initially defined an IPv6 address as + * uint32 u[4]. Using char u[16] instead would allow to remove this. */ +static void ipv6_addr2addr_host(uint32_t *addr, uint32_t *res) +{ + int i; + + memset(res, 0, sizeof(uint32_t)*4); + for (i = 0; i < 4; i++) { + res[i] = ntohl(addr[i]); + } +} + static int constructor_nfct_events(struct ulogd_pluginstance *upi) { struct nfct_pluginstance *cpi = (struct nfct_pluginstance *)upi->private; + char filter_addr[128]; + uint32_t faddr[4]; + int netmask; + char *slash; + struct nfct_filter *filter = NULL; cpi->cth = nfct_open(NFNL_SUBSYS_CTNETLINK, eventmask_ce(upi->config_kset).u.value); @@ -1007,9 +1067,184 @@ static int constructor_nfct_events(struct ulogd_pluginstance *upi) goto err_cth; } + if ((strlen(src_filter_ce(upi->config_kset).u.string) != 0) || + (strlen(dst_filter_ce(upi->config_kset).u.string) != 0) + ) { + ulogd_log(ULOGD_NOTICE, "adding filter: \"%s\"\n", + src_filter_ce(upi->config_kset).u.string + ); + filter = nfct_filter_create(); + if (!filter) { + ulogd_log(ULOGD_FATAL, "error creating NFCT filter\n"); + goto err_cth; + } + + } + + if (strlen(src_filter_ce(upi->config_kset).u.string) != 0) { + char *filter_string = src_filter_ce(upi->config_kset).u.string; + if (strchr(filter_string, ':')) { + struct nfct_filter_ipv6 src_filter_ipv6; + struct nfct_filter_ipv4 src_filter_ipv4; + + slash = strchr(filter_string, '/'); + if (slash == NULL) { + ulogd_log(ULOGD_FATAL, + "No network specified\n"); + goto err_cth; + } + + strncpy(filter_addr, filter_string, + slash - filter_string); + filter_addr[slash - filter_string] = 0; + if (inet_pton(AF_INET6, filter_addr, (void *)faddr) + != 1) { + ulogd_log(ULOGD_FATAL, + "error reading address\n"); + goto err_cth; + } + netmask = atoi(slash + 1); + /* BSF always wants data in host-byte order */ + ipv6_addr2addr_host(faddr, src_filter_ipv6.addr); + ipv6_cidr2mask_host(netmask, src_filter_ipv6.mask); + + nfct_filter_set_logic(filter, + NFCT_FILTER_SRC_IPV6, + NFCT_FILTER_LOGIC_POSITIVE); + nfct_filter_add_attr(filter, + NFCT_FILTER_SRC_IPV6, + &src_filter_ipv6); + + nfct_filter_set_logic(filter, + NFCT_FILTER_SRC_IPV4, + NFCT_FILTER_LOGIC_NEGATIVE); + nfct_filter_add_attr(filter, + NFCT_FILTER_SRC_IPV4, + &src_filter_ipv4); + } else if (strchr(filter_string, '.')) { + struct nfct_filter_ipv6 src_filter_ipv6; + + slash = strchr(filter_string, '/'); + if (slash == NULL) { + ulogd_log(ULOGD_FATAL, + "No network specified\n"); + goto err_cth; + } + strncpy(filter_addr, filter_string, + slash - filter_string); + filter_addr[slash - filter_string] = 0; + netmask = atoi(slash + 1); + /* BSF always wants data in host-byte order */ + struct nfct_filter_ipv4 filter_ipv4 = { + .addr = ntohl(inet_addr(filter_addr)), + .mask = bits2netmask(netmask), + }; + + nfct_filter_set_logic(filter, + NFCT_FILTER_SRC_IPV4, + NFCT_FILTER_LOGIC_POSITIVE); + nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV4, + &filter_ipv4); + + nfct_filter_set_logic(filter, + NFCT_FILTER_SRC_IPV6, + NFCT_FILTER_LOGIC_NEGATIVE); + nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV6, + &src_filter_ipv6); + } else { + ulogd_log(ULOGD_FATAL, + "filter does not look like an IP\n"); + goto err_cth; + } + } + + if (strlen(dst_filter_ce(upi->config_kset).u.string) != 0) { + char *filter_string = dst_filter_ce(upi->config_kset).u.string; + if (strchr(filter_string, ':')) { + struct nfct_filter_ipv6 dst_filter_ipv6; + struct nfct_filter_ipv4 dst_filter_ipv4; + + /* handle dest filter */ + slash = strchr(filter_string, '/'); + if (slash == NULL) { + ulogd_log(ULOGD_FATAL, + "No network specified\n"); + goto err_cth; + } + strncpy(filter_addr, filter_string, + slash - filter_string); + filter_addr[slash - filter_string] = 0; + if (inet_pton(AF_INET6, filter_addr, + (void *)&faddr + ) != 1) { + ulogd_log(ULOGD_FATAL, + "error reading address\n"); + goto err_cth; + } + netmask = atoi(slash + 1); + /* BSF always wants data in host-byte order */ + ipv6_addr2addr_host(faddr, dst_filter_ipv6.addr); + ipv6_cidr2mask_host(netmask, dst_filter_ipv6.mask); + + nfct_filter_set_logic(filter, + NFCT_FILTER_DST_IPV6, + NFCT_FILTER_LOGIC_POSITIVE); + nfct_filter_add_attr(filter, NFCT_FILTER_DST_IPV6, + &dst_filter_ipv6); + + nfct_filter_set_logic(filter, + NFCT_FILTER_DST_IPV4, + NFCT_FILTER_LOGIC_NEGATIVE); + nfct_filter_add_attr(filter, NFCT_FILTER_DST_IPV4, + &dst_filter_ipv4); + } else if (strchr(filter_string, '.')) { + struct nfct_filter_ipv6 dst_filter_ipv6; + + slash = strchr(filter_string, '/'); + if (slash == NULL) { + ulogd_log(ULOGD_FATAL, "No network specified\n"); + goto err_cth; + } + strncpy(filter_addr, filter_string, + slash - filter_string); + filter_addr[slash - filter_string] = 0; + netmask = atoi(slash + 1); + /* BSF always wants data in host-byte order */ + struct nfct_filter_ipv4 filter_ipv4 = { + .addr = ntohl(inet_addr(filter_addr)), + .mask = bits2netmask(netmask), + }; + + nfct_filter_set_logic(filter, + NFCT_FILTER_DST_IPV4, + NFCT_FILTER_LOGIC_POSITIVE); + nfct_filter_add_attr(filter, NFCT_FILTER_DST_IPV4, + &filter_ipv4); + + nfct_filter_set_logic(filter, + NFCT_FILTER_DST_IPV6, + NFCT_FILTER_LOGIC_NEGATIVE); + nfct_filter_add_attr(filter, NFCT_FILTER_DST_IPV6, + &dst_filter_ipv6); + } else { + ulogd_log(ULOGD_FATAL, + "filter does not look like an IP\n"); + goto err_cth; + } + } + + if (filter) { + if (nfct_filter_attach(nfct_fd(cpi->cth), filter) == -1) { + ulogd_log(ULOGD_FATAL, "nfct_filter_attach"); + } + + /* release the filter object, this does not detach the filter */ + nfct_filter_destroy(filter); + } + if (usehash_ce(upi->config_kset).u.value != 0) { nfct_callback_register(cpi->cth, NFCT_T_ALL, - &event_handler_hashtable, upi); + &event_handler_hashtable, upi); } else { nfct_callback_register(cpi->cth, NFCT_T_ALL, &event_handler_no_hashtable, upi); diff --git a/ulogd.conf.in b/ulogd.conf.in index e99212f..7167732 100644 --- a/ulogd.conf.in +++ b/ulogd.conf.in @@ -121,6 +121,8 @@ plugin="@pkglibdir@/ulogd_inpflow_NFACCT.so" #netlink_socket_buffer_maxsize=1085440 #netlink_resync_timeout=60 # seconds to wait to perform resynchronization #pollinterval=10 # use poll-based logging instead of event-driven +#src_filter=192.168.1.0/24 # source ip of connection must belong to this network +#dst_filter=192.168.1.0/24 # destination ip of connection must belong to this network [ct2] #netlink_socket_buffer_size=217088 -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html