summaryrefslogtreecommitdiffstats
path: root/src/conntrack
diff options
context:
space:
mode:
Diffstat (limited to 'src/conntrack')
-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
4 files changed, 550 insertions, 1 deletions
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,
+};