diff options
-rw-r--r-- | include/internal/object.h | 14 | ||||
-rw-r--r-- | include/internal/stack.h | 11 | ||||
-rw-r--r-- | qa/Makefile.am | 6 | ||||
-rw-r--r-- | qa/test_filter.c | 75 | ||||
-rw-r--r-- | src/conntrack/Makefile.am | 3 | ||||
-rw-r--r-- | src/conntrack/api.c | 6 | ||||
-rw-r--r-- | src/conntrack/bsf.c | 495 | ||||
-rw-r--r-- | src/conntrack/filter.c | 11 | ||||
-rw-r--r-- | src/conntrack/stack.c | 67 |
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; +} |