summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.in2
-rw-r--r--include/internal.h64
-rw-r--r--include/libnetfilter_conntrack/libnetfilter_conntrack.h35
-rw-r--r--src/conntrack/Makefile.am3
-rw-r--r--src/conntrack/api.c93
-rw-r--r--src/conntrack/bsf.c416
-rw-r--r--src/conntrack/filter.c39
-rw-r--r--utils/Makefile.am6
-rw-r--r--utils/conntrack_filter.c83
9 files changed, 738 insertions, 3 deletions
diff --git a/configure.in b/configure.in
index fef5508..6568334 100644
--- a/configure.in
+++ b/configure.in
@@ -4,7 +4,7 @@ AC_INIT
AC_CANONICAL_SYSTEM
-AM_INIT_AUTOMAKE(libnetfilter_conntrack, 0.0.96)
+AM_INIT_AUTOMAKE(libnetfilter_conntrack, 0.0.97)
AC_PROG_CC
AM_PROG_LIBTOOL
diff --git a/include/internal.h b/include/internal.h
index 6661dbe..33b0dcd 100644
--- a/include/internal.h
+++ b/include/internal.h
@@ -19,6 +19,7 @@
#include <arpa/inet.h>
#include <time.h>
#include <errno.h>
+#include <netinet/in.h>
#include <libnfnetlink/libnfnetlink.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
@@ -31,15 +32,22 @@
#define IPPROTO_UDPLITE 136
#endif
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP 33
+#endif
+
struct nfct_handle;
+struct nfct_filter;
typedef void (*set_attr)(struct nf_conntrack *ct, const void *value);
typedef const void *(*get_attr)(const struct nf_conntrack *ct);
typedef void (*copy_attr)(struct nf_conntrack *d, const struct nf_conntrack *o);
+typedef void (*filter_attr)(struct nfct_filter *filter, const void *value);
extern set_attr set_attr_array[];
extern get_attr get_attr_array[];
extern copy_attr copy_attr_array[];
+extern filter_attr filter_attr_array[];
typedef int (*nfct_handler)(struct nfct_handle *cth, struct nlmsghdr *nlh,
void *arg);
@@ -165,6 +173,50 @@ struct nf_conntrack {
u_int32_t set[2];
};
+struct nfct_filter {
+ /*
+ * As many other objects in this library, the attributes are
+ * private. This gives us the chance to modify the layout and
+ * object size.
+ *
+ * Another observation, although this object might seem too
+ * memory consuming, it is only needed to build the filter. Thus,
+ * once it is attached, you can release this object.
+ */
+
+ /*
+ * This the layer 4 protocol map for filtering.
+ */
+ u_int32_t l4proto_map[IPPROTO_MAX/32];
+
+ struct {
+ /*
+ * No limitations in the protocol filtering. We use a map of
+ * 16 bits per protocol. As for now, DCCP has 10 states, TCP has
+ * 10 states, SCTP has 8 state. Therefore, 16 bits is enough.
+ */
+#define __FILTER_PROTO_MAX 16
+ u_int16_t map;
+ } l4proto_state[IPPROTO_MAX];
+
+#define __FILTER_ADDR_SRC 0
+#define __FILTER_ADDR_DST 1
+
+ /*
+ * FIXME: For IPv4 filtering, up to 256 IPs or masks by now.
+ * This limitation is related to the existing autogenerated BSF code
+ * and the fact that the maximum jump offset if 2^8 = 256.
+ */
+ u_int32_t l3proto_elems[2];
+ struct {
+#define __FILTER_ADDR_MAX 256
+ u_int32_t addr;
+ u_int32_t mask;
+ } l3proto[2][__FILTER_ADDR_MAX];
+
+ u_int32_t set[1];
+};
+
struct nf_expect {
struct nf_conntrack master;
struct nf_conntrack expected;
@@ -193,6 +245,16 @@ static inline void unset_bit(int nr, u_int32_t *addr)
addr[nr >> 5] &= ~(1UL << (nr & 31));
}
+static inline void set_bit_u16(int nr, u_int16_t *addr)
+{
+ addr[nr >> 4] |= (1UL << (nr & 15));
+}
+
+static inline void unset_bit_u16(int nr, u_int16_t *addr)
+{
+ addr[nr >> 4] &= ~(1UL << (nr & 15));
+}
+
static inline int test_bit(int nr, const u_int32_t *addr)
{
return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0;
@@ -224,6 +286,8 @@ int __setobjopt(struct nf_conntrack *ct, unsigned int option);
int __getobjopt(const struct nf_conntrack *ct, unsigned int option);
int __compare(const struct nf_conntrack *ct1, const struct nf_conntrack *ct2, unsigned int flags);
+int __setup_netlink_socket_filter(int fd, struct nfct_filter *filter);
+
typedef void (*set_exp_attr)(struct nf_expect *exp, const void *value);
typedef const void *(*get_exp_attr)(const struct nf_expect *exp);
diff --git a/include/libnetfilter_conntrack/libnetfilter_conntrack.h b/include/libnetfilter_conntrack/libnetfilter_conntrack.h
index a043f91..328cf8b 100644
--- a/include/libnetfilter_conntrack/libnetfilter_conntrack.h
+++ b/include/libnetfilter_conntrack/libnetfilter_conntrack.h
@@ -324,6 +324,41 @@ extern void nfct_copy_attr(struct nf_conntrack *ct1,
const struct nf_conntrack *ct2,
const enum nf_conntrack_attr type);
+/* filter */
+
+struct nfct_filter;
+
+extern struct nfct_filter *nfct_filter_create(void);
+extern void nfct_filter_destroy(struct nfct_filter *filter);
+
+struct nfct_filter_proto {
+ u_int16_t proto;
+ u_int16_t state;
+};
+struct nfct_filter_ipv4 {
+ u_int32_t addr;
+ u_int32_t mask;
+};
+
+enum nfct_filter_attr {
+ NFCT_FILTER_L4PROTO = 0, /* u_int32_t */
+ NFCT_FILTER_L4PROTO_STATE, /* struct nfct_filter_proto */
+ NFCT_FILTER_SRC_IPV4, /* struct nfct_filter_ipv4 */
+ NFCT_FILTER_DST_IPV4, /* struct nfct_filter_ipv4 */
+ NFCT_FILTER_MAX
+};
+
+extern void nfct_filter_add_attr(struct nfct_filter *filter,
+ const enum nfct_filter_attr attr,
+ const void *value);
+
+extern void nfct_filter_add_attr_u32(struct nfct_filter *filter,
+ const enum nfct_filter_attr attr,
+ const u_int32_t value);
+
+extern int nfct_filter_attach(int fd, struct nfct_filter *filter);
+extern int nfct_filter_detach(int fd);
+
/* low level API: netlink functions */
extern int nfct_build_conntrack(struct nfnl_subsys_handle *ssh,
diff --git a/src/conntrack/Makefile.am b/src/conntrack/Makefile.am
index 6440dd7..94009ea 100644
--- a/src/conntrack/Makefile.am
+++ b/src/conntrack/Makefile.am
@@ -12,4 +12,5 @@ libnfconntrack_la_SOURCES = api.c callback.c \
snprintf_default.c snprintf_xml.c \
objopt.c \
compare.c \
- copy.c
+ copy.c \
+ filter.c bsf.c
diff --git a/src/conntrack/api.c b/src/conntrack/api.c
index 58efd32..3bd96a8 100644
--- a/src/conntrack/api.c
+++ b/src/conntrack/api.c
@@ -842,3 +842,96 @@ void nfct_copy_attr(struct nf_conntrack *ct1,
set_bit(type, ct1->set);
}
}
+
+/**
+ * nfct_filter_create - create a filter
+ *
+ * This function returns a valid pointer on success, otherwise NULL is
+ * returned and errno is appropriately set.
+ */
+struct nfct_filter *nfct_filter_create(void)
+{
+ return calloc(sizeof(struct nfct_filter), 1);
+}
+
+/**
+ * nfct_filter_destroy - destroy a filter
+ * @filter: filter that we want to destroy
+ *
+ * This function releases the memory that is used by the filter object.
+ * However, please note that this function does *not* detach an already
+ * attached filter.
+ */
+void nfct_filter_destroy(struct nfct_filter *filter)
+{
+ assert(filter != NULL);
+ free(filter);
+ filter = NULL;
+}
+
+/**
+ * nfct_filter_add_attr - add a filter attribute of the filter object
+ * @filter: filter object that we want to modify
+ * @type: filter attribute type
+ * @value: pointer to the value of the filter attribute
+ *
+ * Limitations: You can add up to 256 IPv4 addresses and masks for
+ * NFCT_FILTER_SRC_IPV4 and, similarly, 256 for NFCT_FILTER_DST_IPV4.
+ */
+void nfct_filter_add_attr(struct nfct_filter *filter,
+ const enum nfct_filter_attr type,
+ const void *value)
+{
+ assert(filter != NULL);
+ assert(value != NULL);
+
+ if (type >= NFCT_FILTER_MAX)
+ return;
+
+ if (filter_attr_array[type]) {
+ filter_attr_array[type](filter, value);
+ set_bit(type, filter->set);
+ }
+}
+
+/**
+ * nfct_filter_add_attr_u32 - add an u32 filter attribute of the filter object
+ * @filter: filter object that we want to modify
+ * @type: filter attribute type
+ * @value: value of the filter attribute using unsigned int (32 bits).
+ */
+void nfct_filter_add_attr_u32(struct nfct_filter *filter,
+ const enum nfct_filter_attr type,
+ u_int32_t value)
+{
+ nfct_filter_add_attr(filter, type, &value);
+}
+
+/**
+ * nfct_filter_attach - attach a filter to a socket descriptor
+ * @fd: socket descriptor
+ * @filter: filter that we want to attach to the socket
+ *
+ * This function returns -1 on error and set errno appropriately. If the
+ * function returns EINVAL probably you have found a bug in it. Please,
+ * report this.
+ */
+int nfct_filter_attach(int fd, struct nfct_filter *filter)
+{
+ assert(filter != NULL);
+
+ return __setup_netlink_socket_filter(fd, filter);
+}
+
+/**
+ * nfct_filter_detach - detach an existing filter
+ * @fd: socket descriptor
+ *
+ * This function returns -1 on error and set errno appropriately.
+ */
+int nfct_filter_detach(int fd)
+{
+ int val = 0;
+
+ return setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val));
+}
diff --git a/src/conntrack/bsf.c b/src/conntrack/bsf.c
new file mode 100644
index 0000000..2ac5fe1
--- /dev/null
+++ b/src/conntrack/bsf.c
@@ -0,0 +1,416 @@
+/*
+ * (C) 2008 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "internal.h"
+#include <linux/filter.h>
+
+#ifndef SKF_AD_NLATTR
+#define SKF_AD_NLATTR 12
+#endif
+
+#define NFCT_FILTER_REJECT 0U
+#define NFCT_FILTER_ACCEPT ~0U
+
+#if 0
+static void show_filter(struct sock_filter *this, int size)
+{
+ int i;
+
+ for(i=0; i<size; i++)
+ printf("(%.4x) code=%.4x jt=%.2x jf=%.2x k=%.8x\n",
+ i,
+ this[i].code & 0xFFFF,
+ this[i].jt & 0xFF,
+ this[i].jf & 0xFF,
+ this[i].k & 0xFFFFFFFF);
+}
+#else
+static inline void show_filter(struct sock_filter *this, int size) {}
+#endif
+
+static void set_basic_filter(struct sock_filter *this,
+ unsigned int first_type,
+ unsigned int second_type,
+ unsigned int third_type,
+ unsigned int label,
+ unsigned int word_size)
+{
+ struct sock_filter filter[] = {
+ [0] = {
+ /* A = sizeof(struct nlmsghdr) + sizeof(struct nfgenmsg) */
+ .code = BPF_LD|BPF_IMM,
+ .k = sizeof(struct nlmsghdr) + sizeof(struct nfgenmsg),
+ },
+ [1] = {
+ /* X = first_type */
+ .code = BPF_LDX|BPF_IMM,
+ .k = first_type,
+ },
+ [2] = {
+ /* A = netlink attribute offset */
+ .code = BPF_LD|BPF_B|BPF_ABS,
+ .k = SKF_AD_OFF + SKF_AD_NLATTR,
+ },
+ [3] = {
+ /* Reject if not found (A == 0) */
+ .code = BPF_JMP|BPF_JEQ|BPF_K,
+ .k = 0,
+ .jt = label - 3 - 1,
+ },
+ [4] = {
+ /* A += sizeof(struct nfattr) */
+ .code = BPF_ALU|BPF_ADD|BPF_K,
+ .k = sizeof(struct nfattr),
+ },
+ [5] = {
+ /* X = second_type */
+ .code = BPF_LDX|BPF_IMM,
+ .k = second_type,
+ },
+ [6] = {
+ /* A = netlink attribute offset */
+ .code = BPF_LD|BPF_B|BPF_ABS,
+ .k = SKF_AD_OFF + SKF_AD_NLATTR,
+ },
+ [7] = {
+ /* Reject if not found (A == 0) */
+ .code = BPF_JMP|BPF_JEQ|BPF_K,
+ .k = 0,
+ .jt = label - 7 - 1,
+ },
+ [8] = {
+ /* A += sizeof(struct nfattr) */
+ .code = BPF_ALU|BPF_ADD|BPF_K,
+ .k = sizeof(struct nfattr),
+ },
+ [9] = {
+ /* X = third_type */
+ .code = BPF_LDX|BPF_IMM,
+ .k = third_type,
+ },
+ [10] = {
+ /* A = netlink attribute offset */
+ .code = BPF_LD|BPF_B|BPF_ABS,
+ .k = SKF_AD_OFF + SKF_AD_NLATTR,
+ },
+ [11] = {
+ /* Reject if not found (A == 0) */
+ .code = BPF_JMP|BPF_JEQ|BPF_K,
+ .k = 0,
+ .jt = label - 11 - 1,
+ },
+ [12] = {
+ /* X = A */
+ .code = BPF_MISC|BPF_TAX,
+ },
+ [13] = {
+ /* A = skb->data[X + k:word_size] */
+ .code = BPF_LD|word_size|BPF_IND,
+ .k = sizeof(struct nfattr),
+ },
+ };
+
+ memcpy(this, filter, sizeof(filter));
+}
+
+static int
+add_state_filter_cta(struct sock_filter *this,
+ unsigned int cta_protoinfo_proto,
+ unsigned int cta_protoinfo_state,
+ u_int16_t state_flags,
+ size_t remain)
+{
+ struct sock_filter filter[14 + __FILTER_PROTO_MAX];
+ struct sock_filter verdict = {
+ /* Reject */
+ .code = BPF_RET|BPF_K,
+ .k = NFCT_FILTER_REJECT,
+ };
+ unsigned int i, j;
+ unsigned int label_continue;
+
+ /* calculate the number of filter lines */
+ for (i = 0, j = 0; i < sizeof(state_flags) * 8; i++) {
+ if (state_flags & (1 << i)) {
+ j++;
+ }
+ }
+
+ /* nothing to filter, skip */
+ if (j == 0)
+ return 0;
+
+ if (j + 14 >= __FILTER_PROTO_MAX + 14 || j + 14 > remain) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ memset(filter, 0, sizeof(filter));
+
+ label_continue = j + 1;
+
+ set_basic_filter(filter,
+ CTA_PROTOINFO,
+ cta_protoinfo_proto,
+ cta_protoinfo_state,
+ 14 + label_continue,
+ BPF_B);
+
+ for (i = 0, j = 0; i < sizeof(state_flags) * 8; i++) {
+ struct sock_filter cmp = {
+ .code = BPF_JMP|BPF_JEQ|BPF_K,
+ .k = i,
+ .jt = label_continue - j - 1,
+ };
+
+ if (state_flags & (1 << i)) {
+ memcpy(&filter[j + 14], &cmp, sizeof(cmp));
+ j++;
+ }
+ }
+
+ memcpy(this, filter, sizeof(struct sock_filter) * (j + 14));
+ memcpy(&this[j + 14], &verdict, sizeof(verdict));
+
+ return j + 14 + 1;
+}
+
+static int
+add_state_filter(struct sock_filter *this,
+ int proto,
+ u_int16_t flags,
+ size_t remain)
+{
+ struct {
+ unsigned int cta_protoinfo;
+ unsigned int cta_state;
+ } cta[IPPROTO_MAX] = {
+ [IPPROTO_TCP] = {
+ .cta_protoinfo = CTA_PROTOINFO_TCP,
+ .cta_state = CTA_PROTOINFO_TCP_STATE,
+ },
+ [IPPROTO_SCTP] = {
+ .cta_protoinfo = CTA_PROTOINFO_SCTP,
+ .cta_state = CTA_PROTOINFO_SCTP_STATE,
+ },
+ [IPPROTO_DCCP] = {
+ .cta_protoinfo = CTA_PROTOINFO_DCCP,
+ .cta_state = CTA_PROTOINFO_DCCP_STATE,
+ },
+ };
+
+ if (cta[proto].cta_protoinfo == 0 && cta[proto].cta_state == 0) {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ return add_state_filter_cta(this,
+ cta[proto].cta_protoinfo,
+ cta[proto].cta_state,
+ flags,
+ remain);
+}
+
+static int
+bsf_add_state_filter(const struct nfct_filter *filter,
+ struct sock_filter *this,
+ size_t remain)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; i < IPPROTO_MAX; i++) {
+ if (test_bit(i, filter->l4proto_map) &&
+ filter->l4proto_state[i].map) {
+ j += add_state_filter(this,
+ i,
+ filter->l4proto_state[i].map,
+ remain);
+ }
+ }
+
+ return j;
+}
+
+static int
+bsf_add_proto_filter(const struct nfct_filter *f,
+ struct sock_filter *this,
+ size_t remain)
+{
+ struct sock_filter filter[14 + IPPROTO_MAX];
+ struct sock_filter verdict = {
+ /* Reject */
+ .code = BPF_RET|BPF_K,
+ .k = NFCT_FILTER_REJECT,
+ };
+ unsigned int i, j;
+ unsigned int label_continue;
+
+ for (i = 0, j = 0; i < IPPROTO_MAX; i++) {
+ if (test_bit(i, f->l4proto_map)) {
+ j++;
+ }
+ }
+
+ /* nothing to filter, skip */
+ if (j == 0)
+ return 0;
+
+ if (j + 14 >= IPPROTO_MAX + 14 || j + 14 > remain) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ label_continue = j + 1;
+
+ memset(filter, 0, sizeof(filter));
+
+ set_basic_filter(filter,
+ CTA_TUPLE_ORIG,
+ CTA_TUPLE_PROTO,
+ CTA_PROTO_NUM,
+ 14 + label_continue,
+ BPF_B);
+
+ for (i = 0, j = 0; i < IPPROTO_MAX; i++) {
+ struct sock_filter cmp = {
+ .code = BPF_JMP|BPF_JEQ|BPF_K,
+ .k = i,
+ .jt = label_continue - j - 1,
+ };
+
+ if (test_bit(i, f->l4proto_map)) {
+ memcpy(&filter[j + 14], &cmp, sizeof(cmp));
+ j++;
+ }
+ }
+
+ memcpy(this, filter, sizeof(struct sock_filter) * (j + 14));
+ memcpy(&this[j + 14], &verdict, sizeof(verdict));
+
+ return j + 14 + 1;
+}
+
+static int
+bsf_add_addr_ipv4_filter(const struct nfct_filter *f,
+ struct sock_filter *this,
+ unsigned int type,
+ size_t remain)
+{
+ struct sock_filter filter[14 + __FILTER_ADDR_MAX];
+ struct sock_filter verdict = {
+ /* Reject */
+ .code = BPF_RET|BPF_K,
+ .k = NFCT_FILTER_REJECT,
+ };
+ unsigned int i, j, dir;
+ unsigned int label_continue;
+
+ switch(type) {
+ case CTA_IP_V4_SRC:
+ dir = __FILTER_ADDR_SRC;
+ break;
+ case CTA_IP_V4_DST:
+ dir = __FILTER_ADDR_DST;
+ break;
+ default:
+ return 0;
+ }
+
+ /* nothing to filter, skip */
+ if (f->l3proto_elems[dir] == 0)
+ return 0;
+
+ if (f->l3proto_elems[dir] + 14 >= __FILTER_ADDR_MAX + 14 ||
+ f->l3proto_elems[dir] + 14 > remain) {
+ errno = ENOSPC;
+ return -1;
+ }
+
+ label_continue = (f->l3proto_elems[dir] * 2) + 1;
+
+ memset(filter, 0, sizeof(filter));
+
+ set_basic_filter(filter,
+ CTA_TUPLE_ORIG,
+ CTA_TUPLE_IP,
+ type,
+ 14 + label_continue,
+ BPF_W);
+
+ for (i = 0, j = 0; i < f->l3proto_elems[dir]; i++) {
+ struct sock_filter cmp[] = {
+ [0] = {
+ .code = BPF_ALU|BPF_AND|BPF_K,
+ .k = f->l3proto[dir][i].mask,
+ },
+ [1] = {
+ .code = BPF_JMP|BPF_JEQ|BPF_K,
+ .k = f->l3proto[dir][i].addr &
+ f->l3proto[dir][i].mask,
+ .jt = label_continue - j - 2,
+ },
+ };
+ memcpy(&filter[j + 14], cmp, sizeof(cmp));
+ j+=2;
+ }
+
+ memcpy(this, filter, sizeof(struct sock_filter) * (j + 14));
+ memcpy(&this[j + 14], &verdict, sizeof(verdict));
+
+ return j + 14 + 1;
+}
+
+static int
+bsf_add_saddr_ipv4_filter(const struct nfct_filter *f,
+ struct sock_filter *this,
+ size_t remain)
+{
+ return bsf_add_addr_ipv4_filter(f, this, CTA_IP_V4_SRC, remain);
+}
+
+static int
+bsf_add_daddr_ipv4_filter(const struct nfct_filter *f,
+ struct sock_filter *this,
+ size_t remain)
+{
+ return bsf_add_addr_ipv4_filter(f, this, CTA_IP_V4_DST, remain);
+}
+
+/* this buffer must be big enough to store all the autogenerated lines */
+#define BSF_BUFFER_SIZE 1024
+
+int __setup_netlink_socket_filter(int fd, struct nfct_filter *f)
+{
+ struct sock_filter bsf[BSF_BUFFER_SIZE];
+ struct sock_filter bsf_accept = {
+ /* Accept */
+ .code = BPF_RET|BPF_K,
+ .k = NFCT_FILTER_ACCEPT,
+ };
+ struct sock_fprog sf;
+ unsigned int j = 0;
+
+ memset(bsf, 0, sizeof(bsf));
+
+ j += bsf_add_proto_filter(f, &bsf[j], BSF_BUFFER_SIZE-j);
+ j += bsf_add_saddr_ipv4_filter(f, &bsf[j], BSF_BUFFER_SIZE-j);
+ j += bsf_add_daddr_ipv4_filter(f, &bsf[j], BSF_BUFFER_SIZE-j);
+ j += bsf_add_state_filter(f, &bsf[j], BSF_BUFFER_SIZE-j);
+
+ /* nothing to filter, skip */
+ if (j == 0)
+ return 0;
+
+ memcpy(&bsf[j], &bsf_accept, sizeof(struct sock_filter));
+
+ show_filter(bsf, j+1);
+
+ sf.len = (sizeof(struct sock_filter) * (j + 1)) / sizeof(bsf[0]);
+ sf.filter = bsf;
+
+ return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &sf, sizeof(sf));
+}
diff --git a/src/conntrack/filter.c b/src/conntrack/filter.c
new file mode 100644
index 0000000..5ea7d5e
--- /dev/null
+++ b/src/conntrack/filter.c
@@ -0,0 +1,39 @@
+#include "internal.h"
+
+static void filter_attr_l4proto(struct nfct_filter *filter, const void *value)
+{
+ set_bit(*((int *) value), filter->l4proto_map);
+}
+
+static void
+filter_attr_l4proto_state(struct nfct_filter *filter, const void *value)
+{
+ const struct nfct_filter_proto *this = value;
+
+ set_bit_u16(this->state, &filter->l4proto_state[this->proto].map);
+}
+
+static void filter_attr_src_ipv4(struct nfct_filter *filter, const void *value)
+{
+ const struct nfct_filter_ipv4 *this = value;
+
+ filter->l3proto[0][filter->l3proto_elems[0]].addr = this->addr;
+ filter->l3proto[0][filter->l3proto_elems[0]].mask = this->mask;
+ filter->l3proto_elems[0]++;
+}
+
+static void filter_attr_dst_ipv4(struct nfct_filter *filter, const void *value)
+{
+ const struct nfct_filter_ipv4 *this = value;
+
+ filter->l3proto[1][filter->l3proto_elems[1]].addr = this->addr;
+ filter->l3proto[1][filter->l3proto_elems[1]].mask = this->mask;
+ filter->l3proto_elems[1]++;
+}
+
+filter_attr filter_attr_array[] = {
+ [NFCT_FILTER_L4PROTO] = filter_attr_l4proto,
+ [NFCT_FILTER_L4PROTO_STATE] = filter_attr_l4proto_state,
+ [NFCT_FILTER_SRC_IPV4] = filter_attr_src_ipv4,
+ [NFCT_FILTER_DST_IPV4] = filter_attr_dst_ipv4,
+};
diff --git a/utils/Makefile.am b/utils/Makefile.am
index bdf3833..b0797ae 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -5,7 +5,7 @@ check_PROGRAMS = expect_dump expect_create expect_get expect_delete \
conntrack_create conntrack_dump conntrack_update \
conntrack_delete conntrack_flush conntrack_create_nat \
conntrack_get conntrack_events \
- conntrack_master
+ conntrack_master conntrack_filter
conntrack_create_SOURCES = conntrack_create.c
conntrack_create_LDADD = ../src/libnetfilter_conntrack.la
@@ -39,6 +39,10 @@ conntrack_events_SOURCES = conntrack_events.c
conntrack_events_LDADD = ../src/libnetfilter_conntrack.la
conntrack_events_LDFLAGS = -dynamic -ldl
+conntrack_filter_SOURCES = conntrack_filter.c
+conntrack_filter_LDADD = ../src/libnetfilter_conntrack.la
+conntrack_filter_LDFLAGS = -dynamic -ldl
+
conntrack_master_SOURCES = conntrack_master.c
conntrack_master_LDADD = ../src/libnetfilter_conntrack.la
conntrack_master_LDFLAGS = -dynamic -ldl
diff --git a/utils/conntrack_filter.c b/utils/conntrack_filter.c
new file mode 100644
index 0000000..7d22950
--- /dev/null
+++ b/utils/conntrack_filter.c
@@ -0,0 +1,83 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack_tcp.h>
+
+static int event_cb(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct,
+ void *data)
+{
+ static int n = 0;
+ char buf[1024];
+
+ nfct_snprintf(buf, 1024, ct, type, NFCT_O_PLAIN, NFCT_OF_TIME);
+ printf("%s\n", buf);
+
+ if (++n == 10)
+ return NFCT_CB_STOP;
+
+ return NFCT_CB_CONTINUE;
+}
+
+int main()
+{
+ int ret;
+ u_int8_t family = AF_INET;
+ struct nfct_handle *h;
+ struct nfct_filter *filter;
+ struct nf_conntrack *ct;
+ char buf[1024];
+
+ h = nfct_open(CONNTRACK, NF_NETLINK_CONNTRACK_NEW |
+ NF_NETLINK_CONNTRACK_UPDATE);
+ if (!h) {
+ perror("nfct_open");
+ return 0;
+ }
+
+ filter = nfct_filter_create();
+ if (!filter) {
+ perror("nfct_create_filter");
+ return 0;
+ }
+
+ nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO, IPPROTO_UDP);
+ nfct_filter_add_attr_u32(filter, NFCT_FILTER_L4PROTO, IPPROTO_TCP);
+
+ struct nfct_filter_proto filter_proto = {
+ .proto = IPPROTO_TCP,
+ .state = TCP_CONNTRACK_ESTABLISHED
+ };
+
+ nfct_filter_add_attr(filter, NFCT_FILTER_L4PROTO_STATE, &filter_proto);
+
+ struct nfct_filter_ipv4 filter_ipv4 = {
+ .addr = htonl(inet_addr("127.0.0.1")),
+ .mask = 0xffffffff,
+ };
+
+ nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV4, &filter_ipv4);
+
+ if (nfct_filter_attach(nfct_fd(h), filter) == -1) {
+ perror("nfct_filter_attach");
+ return 0;
+ }
+
+ /* release the filter object, this does not detach the filter */
+ nfct_filter_destroy(filter);
+
+ nfct_callback_register(h, NFCT_T_ALL, event_cb, NULL);
+
+ printf("TEST: waiting for 10 events...\n");
+
+ ret = nfct_catch(h);
+
+ printf("TEST: OK (%d)(%s)\n", ret, strerror(errno));
+
+ if (ret == -1)
+ exit(EXIT_FAILURE);
+
+ nfct_close(h);
+}