[libnetfilter_conntrack] bsf: add support for IPv6 address filtering

Pablo Neira netfilter-cvslog-bounces at lists.netfilter.org
Tue Jul 14 16:45:19 CEST 2009


Gitweb:		http://git.netfilter.org/cgi-bin/gitweb.cgi?p=libnetfilter_conntrack.git;a=commit;h=dd73e5708cc2cd127ba03fd5a82fb96b3928e7fb
commit dd73e5708cc2cd127ba03fd5a82fb96b3928e7fb
Author:     Pablo Neira Ayuso <pablo at netfilter.org>
AuthorDate: Tue Jul 14 16:43:55 2009 +0200
Commit:     Pablo Neira Ayuso <pablo at netfilter.org>
CommitDate: Tue Jul 14 16:43:55 2009 +0200

    bsf: add support for IPv6 address filtering
    
    This patch adds support to auto-generate BSF code for IPv6. It
    requires a Linux kernel >= 2.6.29. The maximum number of addresses
    is limited to 20 (12 BSF lines per IPv6 address comparison). I am
    not sure that to remove this limit is useful given that oprofile
    does not show very good numbers for very large (in terms of lines)
    filters. This completes one feature that is available in IPv4 but
    that was missing in IPv6.
    
    Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>
       via  dd73e5708cc2cd127ba03fd5a82fb96b3928e7fb (commit)
      from  1c450e1595afdc8d1bfabb4f640c9251808426eb (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit dd73e5708cc2cd127ba03fd5a82fb96b3928e7fb
Author: Pablo Neira Ayuso <pablo at netfilter.org>
Date:   Tue Jul 14 16:43:55 2009 +0200

    bsf: add support for IPv6 address filtering
    
    This patch adds support to auto-generate BSF code for IPv6. It
    requires a Linux kernel >= 2.6.29. The maximum number of addresses
    is limited to 20 (12 BSF lines per IPv6 address comparison). I am
    not sure that to remove this limit is useful given that oprofile
    does not show very good numbers for very large (in terms of lines)
    filters. This completes one feature that is available in IPv4 but
    that was missing in IPv6.
    
    Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>

-----------------------------------------------------------------------

 include/internal/object.h                          |   13 ++
 .../libnetfilter_conntrack.h                       |    6 +
 src/conntrack/bsf.c                                |  164 ++++++++++++++++++++
 src/conntrack/filter.c                             |   30 ++++
 utils/conntrack_filter.c                           |   13 ++
 5 files changed, 226 insertions(+), 0 deletions(-)
This patch adds support to auto-generate BSF code for IPv6. It
requires a Linux kernel >= 2.6.29. The maximum number of addresses
is limited to 20 (12 BSF lines per IPv6 address comparison). I am
not sure that to remove this limit is useful given that oprofile
does not show very good numbers for very large (in terms of lines)
filters. This completes one feature that is available in IPv4 but
that was missing in IPv6.

Signed-off-by: Pablo Neira Ayuso <pablo at netfilter.org>

diff --git a/include/internal/object.h b/include/internal/object.h
index ef49590..df002fd 100644
--- a/include/internal/object.h
+++ b/include/internal/object.h
@@ -222,6 +222,19 @@ struct nfct_filter {
 		u_int32_t 	mask;
 	} l3proto[2][__FILTER_ADDR_MAX];
 
+	/*
+	 * FIXME: For IPv6 filtering, up to 20 IPs/masks (12 BSF lines
+	 * per comparison). I think that it is not worthy to try to support
+	 * more than that for performance reasons. It seems that oprofile
+	 * shows bad numbers for very large BSF code.
+	 */
+	u_int32_t 		l3proto_elems_ipv6[2];
+	struct {
+#define __FILTER_IPV6_MAX	20
+		u_int32_t 	addr[4];
+		u_int32_t 	mask[4];
+	} l3proto_ipv6[2][__FILTER_IPV6_MAX];
+
 	u_int32_t 		set[1];
 };
 
diff --git a/include/libnetfilter_conntrack/libnetfilter_conntrack.h b/include/libnetfilter_conntrack/libnetfilter_conntrack.h
index f77d273..766fb47 100644
--- a/include/libnetfilter_conntrack/libnetfilter_conntrack.h
+++ b/include/libnetfilter_conntrack/libnetfilter_conntrack.h
@@ -416,12 +416,18 @@ struct nfct_filter_ipv4 {
 	u_int32_t addr;
 	u_int32_t mask;
 };
+struct nfct_filter_ipv6 {
+	u_int32_t addr[4];
+	u_int32_t mask[4];
+};
 
 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_SRC_IPV6,		/* struct nfct_filter_ipv6 */
+	NFCT_FILTER_DST_IPV6,		/* struct nfct_filter_ipv6 */
 	NFCT_FILTER_MAX
 };
 
diff --git a/src/conntrack/bsf.c b/src/conntrack/bsf.c
index 9a0dfff..0b71e8e 100644
--- a/src/conntrack/bsf.c
+++ b/src/conntrack/bsf.c
@@ -13,6 +13,11 @@
 #define SKF_AD_NLATTR		12
 #endif
 
+/* this requires a Linux kernel >= 2.6.29 */
+#ifndef SKF_AD_NLATTR_NEST
+#define SKF_AD_NLATTR_NEST	16
+#endif
+
 #define NFCT_FILTER_REJECT	0U
 #define NFCT_FILTER_ACCEPT	~0U
 
@@ -65,6 +70,26 @@ nfct_bsf_find_attr(struct sock_filter *this, int attr, int pos)
 	return NEW_POS(__code);
 }
 
+/* like the previous, but limit the search to the bound of the nest */
+static int
+nfct_bsf_find_attr_nest(struct sock_filter *this, int attr, int pos)
+{
+	struct sock_filter __code[] = {
+		[0] = {
+			/* X = attribute type */
+			.code	= BPF_LDX|BPF_IMM,
+			.k	= attr,
+		},
+		[1] = {
+			/* A = netlink attribute offset */
+			.code	= BPF_LD|BPF_B|BPF_ABS,
+			.k	= SKF_AD_OFF + SKF_AD_NLATTR_NEST,
+		}
+	};
+	memcpy(&this[pos], __code, sizeof(__code));
+	return NEW_POS(__code);
+}
+
 struct jump {
 	int line;
 	u_int8_t jt;
@@ -89,6 +114,25 @@ nfct_bsf_cmp_k_stack(struct sock_filter *this, int k,
 	return NEW_POS(__code);
 }
 
+/* like previous, but use jf instead of jt. We can merge both functions */
+static int
+nfct_bsf_cmp_k_stack_jf(struct sock_filter *this, int k,
+			int jump_false, int pos, struct stack *s)
+{
+	struct sock_filter __code = {
+		.code	= BPF_JMP|BPF_JEQ|BPF_K,
+		.k	= k,
+	};
+	struct jump jmp = {
+		.line	= pos,
+		.jt	= 0,
+		.jf	= jump_false - 1,
+	};
+	stack_push(s, &jmp);
+	memcpy(&this[pos], &__code, sizeof(__code));
+	return NEW_POS(__code);
+}
+
 static int
 nfct_bsf_alu_and(struct sock_filter *this, int k, int pos)
 {
@@ -136,6 +180,19 @@ nfct_bsf_load_attr(struct sock_filter *this, int word_size, int pos)
 }
 
 static int
+nfct_bsf_load_attr_offset(struct sock_filter *this, int word_size,
+			  int offset, int pos)
+{
+	struct sock_filter __code = {
+		/* A = skb->data[X + k:word_size] */
+		.code	= BPF_LD|word_size|BPF_IND,
+		.k	= sizeof(struct nfattr) + offset,
+	};
+	memcpy(&this[pos], &__code, sizeof(__code));
+	return NEW_POS(__code);
+}
+
+static int
 nfct_bsf_ret_verdict(struct sock_filter *this, int verdict, int pos)
 {
 	struct sock_filter __code = {
@@ -411,6 +468,111 @@ bsf_add_daddr_ipv4_filter(const struct nfct_filter *f, struct sock_filter *this)
 	return bsf_add_addr_ipv4_filter(f, this, CTA_IP_V4_DST);
 }
 
+static int
+bsf_add_addr_ipv6_filter(const struct nfct_filter *f,
+		         struct sock_filter *this,
+			 unsigned int type)
+{
+	unsigned int i, j, dir, attr;
+	unsigned int label_continue, jf;
+	struct stack *s;
+	struct jump jmp;
+
+	switch(type) {
+	case CTA_IP_V6_SRC:
+		dir = __FILTER_ADDR_SRC;
+		attr = NFCT_FILTER_SRC_IPV6;
+		break;
+	case CTA_IP_V6_DST:
+		dir = __FILTER_ADDR_DST;
+		attr = NFCT_FILTER_DST_IPV6;
+		break;
+	default:
+		return 0;
+	}
+
+	/* nothing to filter, skip */
+	if (f->l3proto_elems_ipv6[dir] == 0)
+		return 0;
+
+	/* XXX: 80 jumps (4*20) + 3 jumps in the three-level iteration */
+	s = stack_create(sizeof(struct jump), 3 + 80);
+	if (s == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	jf = 1;
+	if (f->logic[attr] == NFCT_FILTER_LOGIC_POSITIVE)
+		label_continue = 1;
+	else
+		label_continue = 2;
+
+	j = 0;
+	j += nfct_bsf_load_payload_offset(this, j);
+	j += nfct_bsf_find_attr(this, CTA_TUPLE_ORIG, j);
+	j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+	/* no need to access attribute payload, we are using nest-based finder
+	 * j += nfct_bsf_add_attr_data_offset(this, j); */
+	j += nfct_bsf_find_attr_nest(this, CTA_TUPLE_IP, j);
+	j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+	j += nfct_bsf_find_attr_nest(this, type, j);
+	j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+	j += nfct_bsf_x_equal_a(this, j);
+
+	for (i = 0; i < f->l3proto_elems_ipv6[dir]; i++) {
+		int k, offset;
+
+		for (k = 0, offset = 0; k < 4; k++, offset += 4) {
+			int ip = f->l3proto_ipv6[dir][i].addr[k] &
+				 f->l3proto_ipv6[dir][i].mask[k];
+
+			j += nfct_bsf_load_attr_offset(this, BPF_W, offset, j);
+			j += nfct_bsf_alu_and(this,
+					      f->l3proto_ipv6[dir][i].mask[k],
+					      j);
+			if (k < 3) {
+				j += nfct_bsf_cmp_k_stack_jf(this, ip,
+							     jf - j, j, s);
+			} else {
+				/* last word: jump if true */
+				j += nfct_bsf_cmp_k_stack(this, ip, jf - j,
+							  j, s);
+			}
+		}
+	}
+
+	while (stack_pop(s, &jmp) != -1) {
+		if (jmp.jt) {
+			this[jmp.line].jt += jmp.jt + j;
+		}
+		if (jmp.jf) {
+			this[jmp.line].jf += jmp.jf + j;
+		}
+	}
+
+	if (f->logic[attr] == NFCT_FILTER_LOGIC_NEGATIVE)
+		j += nfct_bsf_jump_to(this, 1, j);
+
+	j += nfct_bsf_ret_verdict(this, NFCT_FILTER_REJECT, j);
+
+	stack_destroy(s);
+
+	return j;
+}
+
+static int
+bsf_add_saddr_ipv6_filter(const struct nfct_filter *f, struct sock_filter *this)
+{
+	return bsf_add_addr_ipv6_filter(f, this, CTA_IP_V6_SRC);
+}
+
+static int 
+bsf_add_daddr_ipv6_filter(const struct nfct_filter *f, struct sock_filter *this)
+{
+	return bsf_add_addr_ipv6_filter(f, this, CTA_IP_V6_DST);
+}
+
 /* this buffer must be big enough to store all the autogenerated lines */
 #define BSF_BUFFER_SIZE 	2048
 
@@ -425,6 +587,8 @@ int __setup_netlink_socket_filter(int fd, struct nfct_filter *f)
 	j += bsf_add_proto_filter(f, &bsf[j]);
 	j += bsf_add_saddr_ipv4_filter(f, &bsf[j]);
 	j += bsf_add_daddr_ipv4_filter(f, &bsf[j]);
+	j += bsf_add_saddr_ipv6_filter(f, &bsf[j]);
+	j += bsf_add_daddr_ipv6_filter(f, &bsf[j]);
 	j += bsf_add_state_filter(f, &bsf[j]);
 
 	/* nothing to filter, skip */
diff --git a/src/conntrack/filter.c b/src/conntrack/filter.c
index 7cee673..bf29f96 100644
--- a/src/conntrack/filter.c
+++ b/src/conntrack/filter.c
@@ -49,9 +49,39 @@ static void filter_attr_dst_ipv4(struct nfct_filter *filter, const void *value)
 	filter->l3proto_elems[1]++;
 }
 
+static void filter_attr_src_ipv6(struct nfct_filter *filter, const void *value)
+{
+	const struct nfct_filter_ipv6 *this = value;
+
+	if (filter->l3proto_elems_ipv6[0] >= __FILTER_IPV6_MAX)
+		return;
+
+	memcpy(filter->l3proto_ipv6[0][filter->l3proto_elems_ipv6[0]].addr,
+	       this->addr, sizeof(u_int32_t)*4);
+	memcpy(filter->l3proto_ipv6[0][filter->l3proto_elems_ipv6[0]].mask,
+	       this->mask, sizeof(u_int32_t)*4);
+	filter->l3proto_elems_ipv6[0]++;
+}
+
+static void filter_attr_dst_ipv6(struct nfct_filter *filter, const void *value)
+{
+	const struct nfct_filter_ipv6 *this = value;
+
+	if (filter->l3proto_elems_ipv6[1] >= __FILTER_IPV6_MAX)
+		return;
+
+	memcpy(filter->l3proto_ipv6[1][filter->l3proto_elems_ipv6[1]].addr,
+	       this->addr, sizeof(u_int32_t)*4);
+	memcpy(filter->l3proto_ipv6[1][filter->l3proto_elems_ipv6[1]].mask,
+	       this->mask, sizeof(u_int32_t)*4);
+	filter->l3proto_elems_ipv6[1]++;
+}
+
 filter_attr filter_attr_array[NFCT_FILTER_MAX] = {
 	[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,
+	[NFCT_FILTER_SRC_IPV6]		= filter_attr_src_ipv6,
+	[NFCT_FILTER_DST_IPV6]		= filter_attr_dst_ipv6,
 };
diff --git a/utils/conntrack_filter.c b/utils/conntrack_filter.c
index 7c44c50..0252fbf 100644
--- a/utils/conntrack_filter.c
+++ b/utils/conntrack_filter.c
@@ -66,6 +66,19 @@ int main()
 
 	nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV4, &filter_ipv4);
 
+	/* BSF always wants data in host-byte order */
+	struct nfct_filter_ipv6 filter_ipv6 = {
+		.addr = { 0x0, 0x0, 0x0, 0x1 },
+		.mask = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff },
+	}; 
+
+	/* ignore whatever that comes from ::1 (loopback) */
+	nfct_filter_set_logic(filter,
+			      NFCT_FILTER_SRC_IPV6,
+			      NFCT_FILTER_LOGIC_NEGATIVE);
+
+	nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV6, &filter_ipv6);
+
 	if (nfct_filter_attach(nfct_fd(h), filter) == -1) {
 		perror("nfct_filter_attach");
 		return 0;



More information about the netfilter-cvslog mailing list