summaryrefslogtreecommitdiffstats
path: root/src/conntrack
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2009-07-14 16:43:55 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2009-07-14 16:43:55 +0200
commitdd73e5708cc2cd127ba03fd5a82fb96b3928e7fb (patch)
tree51c6fcb589cdb399bda4f5b134dedf2d1ca5b328 /src/conntrack
parent1c450e1595afdc8d1bfabb4f640c9251808426eb (diff)
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@netfilter.org>
Diffstat (limited to 'src/conntrack')
-rw-r--r--src/conntrack/bsf.c164
-rw-r--r--src/conntrack/filter.c30
2 files changed, 194 insertions, 0 deletions
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,
};