diff options
Diffstat (limited to 'src/conntrack')
-rw-r--r-- | src/conntrack/Makefile.am | 3 | ||||
-rw-r--r-- | src/conntrack/api.c | 93 | ||||
-rw-r--r-- | src/conntrack/bsf.c | 416 | ||||
-rw-r--r-- | src/conntrack/filter.c | 39 |
4 files changed, 550 insertions, 1 deletions
diff --git a/src/conntrack/Makefile.am b/src/conntrack/Makefile.am index 6440dd7..94009ea 100644 --- a/src/conntrack/Makefile.am +++ b/src/conntrack/Makefile.am @@ -12,4 +12,5 @@ libnfconntrack_la_SOURCES = api.c callback.c \ snprintf_default.c snprintf_xml.c \ objopt.c \ compare.c \ - copy.c + copy.c \ + filter.c bsf.c diff --git a/src/conntrack/api.c b/src/conntrack/api.c index 58efd32..3bd96a8 100644 --- a/src/conntrack/api.c +++ b/src/conntrack/api.c @@ -842,3 +842,96 @@ void nfct_copy_attr(struct nf_conntrack *ct1, set_bit(type, ct1->set); } } + +/** + * nfct_filter_create - create a filter + * + * This function returns a valid pointer on success, otherwise NULL is + * returned and errno is appropriately set. + */ +struct nfct_filter *nfct_filter_create(void) +{ + return calloc(sizeof(struct nfct_filter), 1); +} + +/** + * nfct_filter_destroy - destroy a filter + * @filter: filter that we want to destroy + * + * This function releases the memory that is used by the filter object. + * However, please note that this function does *not* detach an already + * attached filter. + */ +void nfct_filter_destroy(struct nfct_filter *filter) +{ + assert(filter != NULL); + free(filter); + filter = NULL; +} + +/** + * nfct_filter_add_attr - add a filter attribute of the filter object + * @filter: filter object that we want to modify + * @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. + */ +void nfct_filter_add_attr(struct nfct_filter *filter, + const enum nfct_filter_attr type, + const void *value) +{ + assert(filter != NULL); + assert(value != NULL); + + if (type >= NFCT_FILTER_MAX) + return; + + if (filter_attr_array[type]) { + filter_attr_array[type](filter, value); + set_bit(type, filter->set); + } +} + +/** + * nfct_filter_add_attr_u32 - add an u32 filter attribute of the filter object + * @filter: filter object that we want to modify + * @type: filter attribute type + * @value: value of the filter attribute using unsigned int (32 bits). + */ +void nfct_filter_add_attr_u32(struct nfct_filter *filter, + const enum nfct_filter_attr type, + u_int32_t value) +{ + nfct_filter_add_attr(filter, type, &value); +} + +/** + * nfct_filter_attach - attach a filter to a socket descriptor + * @fd: socket descriptor + * @filter: filter that we want to attach to the socket + * + * This function returns -1 on error and set errno appropriately. If the + * function returns EINVAL probably you have found a bug in it. Please, + * report this. + */ +int nfct_filter_attach(int fd, struct nfct_filter *filter) +{ + assert(filter != NULL); + + return __setup_netlink_socket_filter(fd, filter); +} + +/** + * nfct_filter_detach - detach an existing filter + * @fd: socket descriptor + * + * This function returns -1 on error and set errno appropriately. + */ +int nfct_filter_detach(int fd) +{ + int val = 0; + + return setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &val, sizeof(val)); +} diff --git a/src/conntrack/bsf.c b/src/conntrack/bsf.c new file mode 100644 index 0000000..2ac5fe1 --- /dev/null +++ b/src/conntrack/bsf.c @@ -0,0 +1,416 @@ +/* + * (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 "internal.h" +#include <linux/filter.h> + +#ifndef SKF_AD_NLATTR +#define SKF_AD_NLATTR 12 +#endif + +#define NFCT_FILTER_REJECT 0U +#define NFCT_FILTER_ACCEPT ~0U + +#if 0 +static void show_filter(struct sock_filter *this, int size) +{ + int i; + + for(i=0; i<size; i++) + printf("(%.4x) code=%.4x jt=%.2x jf=%.2x k=%.8x\n", + i, + this[i].code & 0xFFFF, + this[i].jt & 0xFF, + this[i].jf & 0xFF, + this[i].k & 0xFFFFFFFF); +} +#else +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) +{ + 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, + }, + [7] = { + /* Reject if not found (A == 0) */ + .code = BPF_JMP|BPF_JEQ|BPF_K, + .k = 0, + .jt = label - 7 - 1, + }, + [8] = { + /* 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 */ + .code = BPF_MISC|BPF_TAX, + }, + [13] = { + /* A = skb->data[X + k:word_size] */ + .code = BPF_LD|word_size|BPF_IND, + .k = sizeof(struct nfattr), + }, + }; + + memcpy(this, filter, sizeof(filter)); +} + +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, + size_t remain) +{ + 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; + + /* 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; + return -1; + } + + memset(filter, 0, sizeof(filter)); + + label_continue = j + 1; + + 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 = label_continue - j - 1, + }; + + if (state_flags & (1 << i)) { + memcpy(&filter[j + 14], &cmp, sizeof(cmp)); + j++; + } + } + + memcpy(this, filter, sizeof(struct sock_filter) * (j + 14)); + memcpy(&this[j + 14], &verdict, sizeof(verdict)); + + return j + 14 + 1; +} + +static int +add_state_filter(struct sock_filter *this, + int proto, + u_int16_t flags, + size_t remain) +{ + struct { + unsigned int cta_protoinfo; + unsigned int cta_state; + } cta[IPPROTO_MAX] = { + [IPPROTO_TCP] = { + .cta_protoinfo = CTA_PROTOINFO_TCP, + .cta_state = CTA_PROTOINFO_TCP_STATE, + }, + [IPPROTO_SCTP] = { + .cta_protoinfo = CTA_PROTOINFO_SCTP, + .cta_state = CTA_PROTOINFO_SCTP_STATE, + }, + [IPPROTO_DCCP] = { + .cta_protoinfo = CTA_PROTOINFO_DCCP, + .cta_state = CTA_PROTOINFO_DCCP_STATE, + }, + }; + + if (cta[proto].cta_protoinfo == 0 && cta[proto].cta_state == 0) { + errno = ENOTSUP; + return -1; + } + + return add_state_filter_cta(this, + cta[proto].cta_protoinfo, + cta[proto].cta_state, + flags, + remain); +} + +static int +bsf_add_state_filter(const struct nfct_filter *filter, + struct sock_filter *this, + size_t remain) +{ + unsigned int i, j; + + for (i = 0, j = 0; i < IPPROTO_MAX; i++) { + if (test_bit(i, filter->l4proto_map) && + filter->l4proto_state[i].map) { + j += add_state_filter(this, + i, + filter->l4proto_state[i].map, + remain); + } + } + + return j; +} + +static int +bsf_add_proto_filter(const struct nfct_filter *f, + struct sock_filter *this, + size_t remain) +{ + 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; + + for (i = 0, j = 0; i < IPPROTO_MAX; i++) { + if (test_bit(i, f->l4proto_map)) { + j++; + } + } + + /* nothing to filter, skip */ + if (j == 0) + return 0; + + if (j + 14 >= IPPROTO_MAX + 14 || j + 14 > remain) { + errno = ENOSPC; + return -1; + } + + label_continue = j + 1; + + 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 = label_continue - j - 1, + }; + + if (test_bit(i, f->l4proto_map)) { + memcpy(&filter[j + 14], &cmp, sizeof(cmp)); + j++; + } + } + + memcpy(this, filter, sizeof(struct sock_filter) * (j + 14)); + memcpy(&this[j + 14], &verdict, sizeof(verdict)); + + return j + 14 + 1; +} + +static int +bsf_add_addr_ipv4_filter(const struct nfct_filter *f, + struct sock_filter *this, + unsigned int type, + size_t remain) +{ + 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; + unsigned int label_continue; + + switch(type) { + case CTA_IP_V4_SRC: + dir = __FILTER_ADDR_SRC; + break; + case CTA_IP_V4_DST: + dir = __FILTER_ADDR_DST; + break; + default: + return 0; + } + + /* nothing to filter, skip */ + 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; + return -1; + } + + label_continue = (f->l3proto_elems[dir] * 2) + 1; + + 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 = label_continue - j - 2, + }, + }; + memcpy(&filter[j + 14], cmp, sizeof(cmp)); + j+=2; + } + + memcpy(this, filter, sizeof(struct sock_filter) * (j + 14)); + memcpy(&this[j + 14], &verdict, sizeof(verdict)); + + return j + 14 + 1; +} + +static int +bsf_add_saddr_ipv4_filter(const struct nfct_filter *f, + struct sock_filter *this, + size_t remain) +{ + return bsf_add_addr_ipv4_filter(f, this, CTA_IP_V4_SRC, remain); +} + +static int +bsf_add_daddr_ipv4_filter(const struct nfct_filter *f, + struct sock_filter *this, + size_t remain) +{ + return bsf_add_addr_ipv4_filter(f, this, CTA_IP_V4_DST, remain); +} + +/* this buffer must be big enough to store all the autogenerated lines */ +#define BSF_BUFFER_SIZE 1024 + +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); + + /* nothing to filter, skip */ + if (j == 0) + return 0; + + memcpy(&bsf[j], &bsf_accept, sizeof(struct sock_filter)); + + show_filter(bsf, j+1); + + sf.len = (sizeof(struct sock_filter) * (j + 1)) / 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 new file mode 100644 index 0000000..5ea7d5e --- /dev/null +++ b/src/conntrack/filter.c @@ -0,0 +1,39 @@ +#include "internal.h" + +static void filter_attr_l4proto(struct nfct_filter *filter, const void *value) +{ + set_bit(*((int *) value), filter->l4proto_map); +} + +static void +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); +} + +static void filter_attr_src_ipv4(struct nfct_filter *filter, const void *value) +{ + const struct nfct_filter_ipv4 *this = value; + + filter->l3proto[0][filter->l3proto_elems[0]].addr = this->addr; + filter->l3proto[0][filter->l3proto_elems[0]].mask = this->mask; + filter->l3proto_elems[0]++; +} + +static void filter_attr_dst_ipv4(struct nfct_filter *filter, const void *value) +{ + const struct nfct_filter_ipv4 *this = value; + + filter->l3proto[1][filter->l3proto_elems[1]].addr = this->addr; + filter->l3proto[1][filter->l3proto_elems[1]].mask = this->mask; + filter->l3proto_elems[1]++; +} + +filter_attr filter_attr_array[] = { + [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, +}; |