summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2008-11-25 01:03:19 +0100
committerPablo Neira Ayuso <pablo@netfilter.org>2008-11-25 01:03:19 +0100
commit20506e55b12ba22b761a1ad84dc8a47ce8c82f2e (patch)
treea23824017b20e4161e6310fefdfd0a20503fca99
parent972e6b3c19f3c79b59804308efac447bd2d016ec (diff)
bsf: major rework of the BSF generation code
This patch reworks the BSF automatic generation code. This feature needs more love and it has several limitations like that the maximum number of IPs are 127 due to BSF code restrictions. See this patch as a first step forward. This patch also adds the stack data type, which is used to resolve jump dynamically instead of the previous static approach. This patch also includes fixes in the limitations, previous calculations were wrong. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r--include/internal/object.h14
-rw-r--r--include/internal/stack.h11
-rw-r--r--qa/Makefile.am6
-rw-r--r--qa/test_filter.c75
-rw-r--r--src/conntrack/Makefile.am3
-rw-r--r--src/conntrack/api.c6
-rw-r--r--src/conntrack/bsf.c495
-rw-r--r--src/conntrack/filter.c11
-rw-r--r--src/conntrack/stack.c67
9 files changed, 420 insertions, 268 deletions
diff --git a/include/internal/object.h b/include/internal/object.h
index 8213f4a..53f942d 100644
--- a/include/internal/object.h
+++ b/include/internal/object.h
@@ -171,9 +171,13 @@ struct nfct_filter {
enum nfct_filter_logic logic[NFCT_FILTER_MAX];
/*
- * This the layer 4 protocol map for filtering.
+ * This the layer 4 protocol map for filtering. Not more than
+ * 255 protocols (maximum is IPPROTO_MAX which is 256). Actually,
+ * I doubt that anyone can reach such a limit.
*/
+#define __FILTER_L4PROTO_MAX 255
u_int32_t l4proto_map[IPPROTO_MAX/32];
+ u_int32_t l4proto_len;
struct {
/*
@@ -183,19 +187,21 @@ struct nfct_filter {
*/
#define __FILTER_PROTO_MAX 16
u_int16_t map;
+ u_int16_t len;
} 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.
+ * FIXME: For IPv4 filtering, up to 127 IPs by now.
* This limitation is related to the existing autogenerated BSF code
- * and the fact that the maximum jump offset if 2^8 = 256.
+ * (two BSF lines per comparison) and the fact that the maximum
+ * jump offset is 0xff which is 255.
*/
u_int32_t l3proto_elems[2];
struct {
-#define __FILTER_ADDR_MAX 256
+#define __FILTER_ADDR_MAX 127
u_int32_t addr;
u_int32_t mask;
} l3proto[2][__FILTER_ADDR_MAX];
diff --git a/include/internal/stack.h b/include/internal/stack.h
new file mode 100644
index 0000000..f57bd15
--- /dev/null
+++ b/include/internal/stack.h
@@ -0,0 +1,11 @@
+#ifndef _STACK_H_
+#define _STACK_H_
+
+struct stack;
+
+struct stack *stack_create(size_t elem_size, int max_elems);
+void stack_destroy(struct stack *s);
+int stack_push(struct stack *s, void *data);
+int stack_pop(struct stack *s, void *data);
+
+#endif
diff --git a/qa/Makefile.am b/qa/Makefile.am
index 6a9471b..2bf568a 100644
--- a/qa/Makefile.am
+++ b/qa/Makefile.am
@@ -1,7 +1,11 @@
include $(top_srcdir)/Make_global.am
-check_PROGRAMS = test_api
+check_PROGRAMS = test_api test_filter
test_api_SOURCES = test_api.c
test_api_LDADD = ../src/libnetfilter_conntrack.la
test_api_LDFLAGS = -dynamic -ldl
+
+test_filter_SOURCES = test_filter.c
+test_filter_LDADD = ../src/libnetfilter_conntrack.la
+test_filter_LDFLAGS = -dynamic -ldl
diff --git a/qa/test_filter.c b/qa/test_filter.c
new file mode 100644
index 0000000..42d067f
--- /dev/null
+++ b/qa/test_filter.c
@@ -0,0 +1,75 @@
+/*
+ * Test for the filter API
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <libnetfilter_conntrack/libnetfilter_conntrack.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 i, ret;
+ struct nfct_handle *h;
+ struct nfct_filter *filter;
+
+ 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;
+ }
+
+ if (nfct_filter_attach(nfct_fd(h), filter) == -1) {
+ perror("nfct_filter_attach");
+ return 0;
+ }
+
+ /* protocol 255 is skipped since we support up to 255 protocols max */
+ for (i=0; i<IPPROTO_MAX; i++)
+ nfct_filter_add_attr_u32(filter,NFCT_FILTER_L4PROTO,i);
+
+ /* up to 127 IP addresses, above that adding is noop */
+ for (i=0; i<128; i++) {
+ /* BSF always wants data in host-byte order */
+ struct nfct_filter_ipv4 fltr_ipv4 = {
+ .addr = ntohl(inet_addr("127.0.0.1")) + i,
+ .mask = 0xffffffff,
+ };
+ nfct_filter_add_attr(filter, NFCT_FILTER_SRC_IPV4, &fltr_ipv4);
+ };
+
+ if (nfct_filter_attach(nfct_fd(h), filter) == -1) {
+ perror("nfct_filter_attach");
+ return 0;
+ }
+
+ nfct_filter_destroy(filter);
+
+ nfct_callback_register(h, NFCT_T_ALL, event_cb, NULL);
+
+ ret = nfct_catch(h);
+ printf("test ret=%d (%s)\n", ret, strerror(errno));
+}
diff --git a/src/conntrack/Makefile.am b/src/conntrack/Makefile.am
index 2d62177..d989d10 100644
--- a/src/conntrack/Makefile.am
+++ b/src/conntrack/Makefile.am
@@ -14,4 +14,5 @@ libnfconntrack_la_SOURCES = api.c callback.c \
compare.c \
copy.c \
filter.c bsf.c \
- grp.c grp_getter.c grp_setter.c
+ grp.c grp_getter.c grp_setter.c \
+ stack.c
diff --git a/src/conntrack/api.c b/src/conntrack/api.c
index 6dae83f..141aa9d 100644
--- a/src/conntrack/api.c
+++ b/src/conntrack/api.c
@@ -1009,8 +1009,8 @@ void nfct_filter_destroy(struct nfct_filter *filter)
* @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.
+ * Limitations: You can add up to 127 IPv4 addresses and masks for
+ * NFCT_FILTER_SRC_IPV4 and, similarly, 127 for NFCT_FILTER_DST_IPV4.
*/
void nfct_filter_add_attr(struct nfct_filter *filter,
const enum nfct_filter_attr type,
@@ -1033,6 +1033,8 @@ void nfct_filter_add_attr(struct nfct_filter *filter,
* @filter: filter object that we want to modify
* @type: filter attribute type
* @value: value of the filter attribute using unsigned int (32 bits).
+ *
+ * Limitations: You can add up to 255 protocols which is a reasonable limit.
*/
void nfct_filter_add_attr_u32(struct nfct_filter *filter,
const enum nfct_filter_attr type,
diff --git a/src/conntrack/bsf.c b/src/conntrack/bsf.c
index 0d20949..f2d14f8 100644
--- a/src/conntrack/bsf.c
+++ b/src/conntrack/bsf.c
@@ -6,6 +6,7 @@
*/
#include "internal/internal.h"
+#include "internal/stack.h"
#include <linux/filter.h>
#ifndef SKF_AD_NLATTR
@@ -32,175 +33,192 @@ static void show_filter(struct sock_filter *this, int size)
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)
+#define NEW_POS(x) (sizeof(x)/sizeof(struct sock_filter))
+
+static inline int
+nfct_bsf_load_payload_offset(struct sock_filter *this, int pos)
{
- 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,
+ struct sock_filter __code = {
+ .code = BPF_LD|BPF_IMM,
+ .k = sizeof(struct nlmsghdr) + sizeof(struct nfgenmsg),
+ };
+ memcpy(&this[pos], &__code, sizeof(__code));
+ return NEW_POS(__code);
+}
+
+static inline int
+nfct_bsf_find_attr(struct sock_filter *this, int attr, int pos)
+{
+ struct sock_filter __code[] = {
+ [0] = {
+ /* X = attribute type */
+ .code = BPF_LDX|BPF_IMM,
+ .k = attr,
},
- [7] = {
- /* Reject if not found (A == 0) */
+ [1] = {
+ /* A = netlink attribute offset */
+ .code = BPF_LD|BPF_B|BPF_ABS,
+ .k = SKF_AD_OFF + SKF_AD_NLATTR,
+ }
+ };
+ memcpy(&this[pos], __code, sizeof(__code));
+ return NEW_POS(__code);
+}
+
+struct jump {
+ int line;
+ u_int8_t jt;
+ u_int8_t jf;
+};
+
+static inline int
+nfct_bsf_cmp_k_stack(struct sock_filter *this, int k,
+ int jump_true, int pos, struct stack *s)
+{
+ struct sock_filter __code = {
.code = BPF_JMP|BPF_JEQ|BPF_K,
- .k = 0,
- .jt = label - 7 - 1,
- },
- [8] = {
+ .k = k,
+ };
+ struct jump jmp = {
+ .line = pos,
+ .jt = jump_true - 1,
+ .jf = 0,
+ };
+ stack_push(s, &jmp);
+ memcpy(&this[pos], &__code, sizeof(__code));
+ return NEW_POS(__code);
+}
+
+static inline int
+nfct_bsf_alu_and(struct sock_filter *this, int k, int pos)
+{
+ struct sock_filter __code = {
+ .code = BPF_ALU|BPF_AND|BPF_K,
+ .k = k,
+ };
+ memcpy(&this[pos], &__code, sizeof(__code));
+ return NEW_POS(__code);
+}
+
+static inline int
+nfct_bsf_add_attr_data_offset(struct sock_filter *this, int pos)
+{
+ struct sock_filter __code = {
/* 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 */
+ };
+ memcpy(&this[pos], &__code, sizeof(__code));
+ return NEW_POS(__code);
+}
+
+static inline int
+nfct_bsf_x_equal_a(struct sock_filter *this, int pos)
+{
+ struct sock_filter __code = {
.code = BPF_MISC|BPF_TAX,
- },
- [13] = {
+ };
+ memcpy(&this[pos], &__code, sizeof(__code));
+ return NEW_POS(__code);
+}
+
+static inline int
+nfct_bsf_load_attr(struct sock_filter *this, int word_size, int pos)
+{
+ struct sock_filter __code = {
/* A = skb->data[X + k:word_size] */
.code = BPF_LD|word_size|BPF_IND,
.k = sizeof(struct nfattr),
- },
+
};
+ memcpy(&this[pos], &__code, sizeof(__code));
+ return NEW_POS(__code);
+}
- memcpy(this, filter, sizeof(filter));
+static inline int
+nfct_bsf_ret_verdict(struct sock_filter *this, int verdict, int pos)
+{
+ struct sock_filter __code = {
+ .code = BPF_RET|BPF_K,
+ .k = verdict,
+ };
+ memcpy(&this[pos], &__code, sizeof(__code));
+ return NEW_POS(__code);
}
+static inline int
+nfct_bsf_jump_to(struct sock_filter *this, int line, int pos)
+{
+ struct sock_filter __code = {
+ .code = BPF_JMP|BPF_JA,
+ .k = line,
+ };
+ memcpy(&this[pos], &__code, sizeof(__code));
+ return NEW_POS(__code);
+};
+
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,
- unsigned int logic,
- size_t remain)
+ unsigned int logic)
{
- 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, jt;
+ struct stack *s;
+ struct jump jmp;
- /* 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;
+ /* XXX: 32 maximum states + 3 jumps in the three-level iteration */
+ s = stack_create(sizeof(struct jump), 3 + 32);
+ if (s == NULL) {
+ errno = ENOMEM;
return -1;
}
- memset(filter, 0, sizeof(filter));
-
- jt = j + 1;
+ jt = 1;
if (logic == NFCT_FILTER_LOGIC_POSITIVE)
- label_continue = j + 1;
+ label_continue = 1;
else
- label_continue = j + 2;
-
- 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 = jt - j - 1,
- };
-
+ label_continue = 2;
+
+ j = 0;
+ j += nfct_bsf_load_payload_offset(this, j);
+ j += nfct_bsf_find_attr(this, CTA_PROTOINFO, j);
+ j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+ j += nfct_bsf_add_attr_data_offset(this, j);
+ j += nfct_bsf_find_attr(this, cta_protoinfo_proto, j);
+ j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+ j += nfct_bsf_add_attr_data_offset(this, j);
+ j += nfct_bsf_find_attr(this, cta_protoinfo_state, j);
+ j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+ j += nfct_bsf_x_equal_a(this, j);
+ j += nfct_bsf_load_attr(this, BPF_B, j);
+
+ for (i = 0; i < sizeof(state_flags) * 8; i++) {
if (state_flags & (1 << i)) {
- memcpy(&filter[j + 14], &cmp, sizeof(cmp));
- j++;
+ j += nfct_bsf_cmp_k_stack(this, i, jt - j, j, s);
}
}
- memcpy(this, filter, sizeof(struct sock_filter) * (j + 14));
+ while (stack_pop(s, &jmp) != -1)
+ this[jmp.line].jt += jmp.jt + j;
- if (logic == NFCT_FILTER_LOGIC_NEGATIVE) {
- struct sock_filter jump = {
- .code = BPF_JMP|BPF_JA,
- .k = 1,
- };
- memcpy(&this[j + 14], &jump, sizeof(jump));
- j++;
- }
+ if (logic == NFCT_FILTER_LOGIC_NEGATIVE)
+ j += nfct_bsf_jump_to(this, 1, j);
- memcpy(&this[j + 14], &verdict, sizeof(verdict));
- j++;
+ j += nfct_bsf_ret_verdict(this, NFCT_FILTER_REJECT, j);
- return j + 14;
+ stack_destroy(s);
+
+ return j;
}
static int
add_state_filter(struct sock_filter *this,
int proto,
u_int16_t flags,
- unsigned int logic,
- size_t remain)
+ unsigned int logic)
{
struct {
unsigned int cta_protoinfo;
@@ -229,25 +247,22 @@ add_state_filter(struct sock_filter *this,
cta[proto].cta_protoinfo,
cta[proto].cta_state,
flags,
- logic,
- remain);
+ logic);
}
static int
-bsf_add_state_filter(const struct nfct_filter *filter,
- struct sock_filter *this,
- size_t remain)
+bsf_add_state_filter(const struct nfct_filter *filter, struct sock_filter *this)
{
unsigned int i, j;
for (i = 0, j = 0; i < IPPROTO_MAX; i++) {
- if (filter->l4proto_state[i].map) {
+ if (filter->l4proto_state[i].map &&
+ filter->l4proto_state[i].len > 0) {
j += add_state_filter(
this,
i,
filter->l4proto_state[i].map,
- filter->logic[NFCT_FILTER_L4PROTO_STATE],
- remain);
+ filter->logic[NFCT_FILTER_L4PROTO_STATE]);
}
}
@@ -255,93 +270,71 @@ bsf_add_state_filter(const struct nfct_filter *filter,
}
static int
-bsf_add_proto_filter(const struct nfct_filter *f,
- struct sock_filter *this,
- size_t remain)
+bsf_add_proto_filter(const struct nfct_filter *f, struct sock_filter *this)
{
- 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, jt;
-
- for (i = 0, j = 0; i < IPPROTO_MAX; i++) {
- if (test_bit(i, f->l4proto_map)) {
- j++;
- }
- }
+ struct stack *s;
+ struct jump jmp;
/* nothing to filter, skip */
- if (j == 0)
+ if (f->l4proto_len == 0)
return 0;
- if (j + 14 >= IPPROTO_MAX + 14 || j + 14 > remain) {
- errno = ENOSPC;
+ /* XXX: 255 maximum proto + 3 jumps in the three-level iteration */
+ s = stack_create(sizeof(struct jump), 3 + 255);
+ if (s == NULL) {
+ errno = ENOMEM;
return -1;
}
- jt = j + 1;
+ jt = 1;
if (f->logic[NFCT_FILTER_L4PROTO] == NFCT_FILTER_LOGIC_POSITIVE)
- label_continue = j + 1;
+ label_continue = 1;
else
- label_continue = j + 2;
-
- 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 = jt - j - 1,
- };
-
+ 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);
+ j += nfct_bsf_add_attr_data_offset(this, j);
+ j += nfct_bsf_find_attr(this, CTA_TUPLE_PROTO, j);
+ j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+ j += nfct_bsf_add_attr_data_offset(this, j);
+ j += nfct_bsf_find_attr(this, CTA_PROTO_NUM, j);
+ j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+ j += nfct_bsf_x_equal_a(this, j);
+ j += nfct_bsf_load_attr(this, BPF_B, j);
+
+ for (i = 0; i < IPPROTO_MAX; i++) {
if (test_bit(i, f->l4proto_map)) {
- memcpy(&filter[j + 14], &cmp, sizeof(cmp));
- j++;
+ j += nfct_bsf_cmp_k_stack(this, i, jt - j, j, s);
}
}
- memcpy(this, filter, sizeof(struct sock_filter) * (j + 14));
+ while (stack_pop(s, &jmp) != -1)
+ this[jmp.line].jt += jmp.jt + j;
- if (f->logic[NFCT_FILTER_L4PROTO] == NFCT_FILTER_LOGIC_NEGATIVE) {
- struct sock_filter jump = {
- .code = BPF_JMP|BPF_JA,
- .k = 1,
- };
- memcpy(&this[j + 14], &jump, sizeof(jump));
- j++;
- }
+ if (f->logic[NFCT_FILTER_L4PROTO] == NFCT_FILTER_LOGIC_NEGATIVE)
+ j += nfct_bsf_jump_to(this, 1, j);
+
+ j += nfct_bsf_ret_verdict(this, NFCT_FILTER_REJECT, j);
- memcpy(&this[j + 14], &verdict, sizeof(verdict));
- j++;
+ stack_destroy(s);
- return j + 14;
+ return j;
}
static int
bsf_add_addr_ipv4_filter(const struct nfct_filter *f,
struct sock_filter *this,
- unsigned int type,
- size_t remain)
+ unsigned int type)
{
- 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, attr;
unsigned int label_continue, jt;
+ struct stack *s;
+ struct jump jmp;
switch(type) {
case CTA_IP_V4_SRC:
@@ -360,107 +353,89 @@ bsf_add_addr_ipv4_filter(const struct nfct_filter *f,
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;
+ /* XXX: 127 maximum IPs + 3 jumps in the three-level iteration */
+ s = stack_create(sizeof(struct jump), 3 + 127);
+ if (s == NULL) {
+ errno = ENOMEM;
return -1;
}
- jt = (f->l3proto_elems[dir] * 2) + 1;
+ jt = 1;
if (f->logic[attr] == NFCT_FILTER_LOGIC_POSITIVE)
- label_continue = (f->l3proto_elems[dir] * 2) + 1;
+ label_continue = 1;
else
- label_continue = (f->l3proto_elems[dir] * 2) + 2;
-
- 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 = jt - j - 2,
- },
- };
- memcpy(&filter[j + 14], cmp, sizeof(cmp));
- j+=2;
+ 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);
+ j += nfct_bsf_add_attr_data_offset(this, j);
+ j += nfct_bsf_find_attr(this, CTA_TUPLE_IP, j);
+ j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+ j += nfct_bsf_add_attr_data_offset(this, j);
+ j += nfct_bsf_find_attr(this, type, j);
+ j += nfct_bsf_cmp_k_stack(this, 0, label_continue - j, j, s);
+ j += nfct_bsf_x_equal_a(this, j);
+ j += nfct_bsf_load_attr(this, BPF_W, j);
+
+ for (i = 0; i < f->l3proto_elems[dir]; i++) {
+ int ip = f->l3proto[dir][i].addr & f->l3proto[dir][i].mask;
+
+ j += nfct_bsf_alu_and(this, f->l3proto[dir][i].mask, j);
+ j += nfct_bsf_cmp_k_stack(this, ip, jt - j, j, s);
}
- memcpy(this, filter, sizeof(struct sock_filter) * (j + 14));
+ while (stack_pop(s, &jmp) != -1)
+ this[jmp.line].jt += jmp.jt + j;
- if (f->logic[attr] == NFCT_FILTER_LOGIC_NEGATIVE) {
- struct sock_filter jump = {
- .code = BPF_JMP|BPF_JA,
- .k = 1,
- };
- memcpy(&this[j + 14], &jump, sizeof(jump));
- 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);
- memcpy(&this[j + 14], &verdict, sizeof(verdict));
- j++;
+ stack_destroy(s);
- return j + 14;
+ return j;
}
static int
-bsf_add_saddr_ipv4_filter(const struct nfct_filter *f,
- struct sock_filter *this,
- size_t remain)
+bsf_add_saddr_ipv4_filter(const struct nfct_filter *f, struct sock_filter *this)
{
- return bsf_add_addr_ipv4_filter(f, this, CTA_IP_V4_SRC, remain);
+ return bsf_add_addr_ipv4_filter(f, this, CTA_IP_V4_SRC);
}
static int
-bsf_add_daddr_ipv4_filter(const struct nfct_filter *f,
- struct sock_filter *this,
- size_t remain)
+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, remain);
+ return bsf_add_addr_ipv4_filter(f, this, CTA_IP_V4_DST);
}
/* this buffer must be big enough to store all the autogenerated lines */
-#define BSF_BUFFER_SIZE 1024
+#define BSF_BUFFER_SIZE 2048
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);
+ 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_state_filter(f, &bsf[j]);
/* nothing to filter, skip */
if (j == 0)
return 0;
- memcpy(&bsf[j], &bsf_accept, sizeof(struct sock_filter));
+ j += nfct_bsf_ret_verdict(bsf, NFCT_FILTER_ACCEPT, j);
- show_filter(bsf, j+1);
+ show_filter(bsf, j);
- sf.len = (sizeof(struct sock_filter) * (j + 1)) / sizeof(bsf[0]);
+ sf.len = (sizeof(struct sock_filter) * j) / 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
index 7966e54..7cee673 100644
--- a/src/conntrack/filter.c
+++ b/src/conntrack/filter.c
@@ -9,7 +9,11 @@
static void filter_attr_l4proto(struct nfct_filter *filter, const void *value)
{
+ if (filter->l4proto_len >= __FILTER_L4PROTO_MAX)
+ return;
+
set_bit(*((int *) value), filter->l4proto_map);
+ filter->l4proto_len++;
}
static void
@@ -18,12 +22,16 @@ 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);
+ filter->l4proto_state[this->proto].len++;
}
static void filter_attr_src_ipv4(struct nfct_filter *filter, const void *value)
{
const struct nfct_filter_ipv4 *this = value;
+ if (filter->l3proto_elems[0] >= __FILTER_ADDR_MAX)
+ return;
+
filter->l3proto[0][filter->l3proto_elems[0]].addr = this->addr;
filter->l3proto[0][filter->l3proto_elems[0]].mask = this->mask;
filter->l3proto_elems[0]++;
@@ -33,6 +41,9 @@ static void filter_attr_dst_ipv4(struct nfct_filter *filter, const void *value)
{
const struct nfct_filter_ipv4 *this = value;
+ if (filter->l3proto_elems[1] >= __FILTER_ADDR_MAX)
+ return;
+
filter->l3proto[1][filter->l3proto_elems[1]].addr = this->addr;
filter->l3proto[1][filter->l3proto_elems[1]].mask = this->mask;
filter->l3proto_elems[1]++;
diff --git a/src/conntrack/stack.c b/src/conntrack/stack.c
new file mode 100644
index 0000000..404e38b
--- /dev/null
+++ b/src/conntrack/stack.c
@@ -0,0 +1,67 @@
+/*
+ * (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "internal/stack.h"
+
+struct stack {
+ int num_elems;
+ int max_elems;
+ size_t elem_size;
+ char *data;
+};
+
+struct stack *stack_create(size_t elem_size, int max_elems)
+{
+ struct stack *s;
+
+ s = calloc(sizeof(struct stack), 1);
+ if (s == NULL)
+ return NULL;
+
+ s->data = calloc(elem_size * max_elems, 1);
+ if (s->data == NULL) {
+ free(s);
+ return NULL;
+ }
+ s->elem_size = elem_size;
+ s->max_elems = max_elems;
+
+ return s;
+}
+
+void stack_destroy(struct stack *s)
+{
+ free(s->data);
+ free(s);
+}
+
+int stack_push(struct stack *s, void *data)
+{
+ if (s->num_elems >= s->max_elems) {
+ errno = ENOSPC;
+ return -1;
+ }
+ memcpy(s->data + (s->elem_size * s->num_elems), data, s->elem_size);
+ s->num_elems++;
+ return 0;
+}
+
+int stack_pop(struct stack *s, void *data)
+{
+ if (s->num_elems <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ s->num_elems--;
+ memcpy(data, s->data + (s->elem_size * s->num_elems), s->elem_size);
+ return 0;
+}