diff options
Diffstat (limited to 'src/conntrack/bsf.c')
-rw-r--r-- | src/conntrack/bsf.c | 55 |
1 files changed, 55 insertions, 0 deletions
diff --git a/src/conntrack/bsf.c b/src/conntrack/bsf.c index 1e78bad..da5919c 100644 --- a/src/conntrack/bsf.c +++ b/src/conntrack/bsf.c @@ -738,6 +738,58 @@ bsf_add_mark_filter(const struct nfct_filter *f, struct sock_filter *this) return j; } +static int +bsf_add_zone_filter(const struct nfct_filter *f, struct sock_filter *this) +{ + unsigned int i, j; + unsigned int jt; + struct stack *s; + struct jump jmp; + struct sock_filter __code = { + /* if (A == 0) skip next two */ + .code = BPF_JMP|BPF_JEQ|BPF_K, + .k = 0, + .jt = 2, + .jf = 0, + }; + + /* nothing to filter, skip */ + if (f->zone_elems == 0) + return 0; + + /* 127 max filterable zones. One JMP instruction per zone. */ + s = stack_create(sizeof(struct jump), 127); + if (s == NULL) { + errno = ENOMEM; + return 0; + } + + jt = 1; + j = 0; + j += nfct_bsf_load_payload_offset(this, j); /* A = nla header offset */ + j += nfct_bsf_find_attr(this, CTA_ZONE, j); /* A = CTA_ZONE offset, started from A */ + memcpy(&this[j], &__code, sizeof(__code)); /* if A == 0 skip next two op */ + j += NEW_POS(__code); + j += nfct_bsf_x_equal_a(this, j); /* X = A <CTA_ZONE offset> */ + j += nfct_bsf_load_attr(this, BPF_H, j); /* A = skb->data[X:X + BPF_H] */ + + for (i = 0; i < f->zone_elems; i++) { + j += nfct_bsf_cmp_k_stack(this, f->zone[i], jt - j, j, s); + } + + while (stack_pop(s, &jmp) != -1) + this[jmp.line].jt += jmp.jt + j; + + if (f->logic[NFCT_FILTER_ZONE] == 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; +} + /* this buffer must be big enough to store all the autogenerated lines */ #define BSF_BUFFER_SIZE 2048 @@ -774,6 +826,9 @@ int __setup_netlink_socket_filter(int fd, struct nfct_filter *f) j += bsf_add_mark_filter(f, &bsf[j]); show_filter(bsf, from, j, "---- check mark ----"); from = j; + j += bsf_add_zone_filter(f, &bsf[j]); + show_filter(bsf, from, j, "---- check zone ----"); + from = j; /* nothing to filter, skip */ if (j == 0) |