summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/internal/object.h13
-rw-r--r--include/libnetfilter_conntrack/libnetfilter_conntrack.h6
-rw-r--r--src/conntrack/bsf.c164
-rw-r--r--src/conntrack/filter.c30
-rw-r--r--utils/conntrack_filter.c13
5 files changed, 226 insertions, 0 deletions
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;