diff options
Diffstat (limited to 'cooker/match.c')
-rw-r--r-- | cooker/match.c | 169 |
1 files changed, 145 insertions, 24 deletions
diff --git a/cooker/match.c b/cooker/match.c index 1fd726f..f65ac5c 100644 --- a/cooker/match.c +++ b/cooker/match.c @@ -22,9 +22,70 @@ #include <linux/netlink.h> #include <linux/rtnetlink.h> +#include <linux/netfilter/nfnetlink.h> +#include <linux/netfilter/nf_tables.h> +#include <linux/netfilter/nf_tables_compat.h> static struct num netlink_types[] = { - { "newroute", RTM_NEWROUTE }, + { "newroute", RTM_NEWROUTE }, + +#define SUBSYS_NFCOMPAT(x) (NFNL_SUBSYS_NFT_COMPAT << 8 | (x)) + { "nf_compat_get", (SUBSYS_NFCOMPAT(NFNL_MSG_COMPAT_GET))}, + +#define SUBSYS_NFT(x) (NFNL_SUBSYS_NFTABLES << 8 | (x)) + { "nf_get_any", + SUBSYS_NFT(NFT_MSG_GETTABLE | NFT_MSG_GETCHAIN | NFT_MSG_GETRULE | + NFT_MSG_GETSET | NFT_MSG_GETSETELEM | NFT_MSG_GETGEN | + NFT_MSG_GETFLOWTABLE | NFT_MSG_GETOBJ) | + SUBSYS_NFCOMPAT(NFNL_MSG_COMPAT_GET) }, + +#undef SUBSYS_NFCOMPAT + + { "nf_newtable", SUBSYS_NFT(NFT_MSG_NEWTABLE) }, + { "nf_gettable", SUBSYS_NFT(NFT_MSG_GETTABLE) }, + { "nf_deltable", SUBSYS_NFT(NFT_MSG_DELTABLE) }, + { "nf_destroytable", SUBSYS_NFT(NFT_MSG_DESTROYTABLE) }, + /* ignores ENOENT */ + + { "nf_newchain", SUBSYS_NFT(NFT_MSG_NEWCHAIN) }, + { "nf_getchain", SUBSYS_NFT(NFT_MSG_GETCHAIN) }, + { "nf_delchain", SUBSYS_NFT(NFT_MSG_DELCHAIN) }, + { "nf_destroychain", SUBSYS_NFT(NFT_MSG_DESTROYCHAIN) }, + + { "nf_newrule", SUBSYS_NFT(NFT_MSG_NEWRULE) }, + { "nf_getrule", SUBSYS_NFT(NFT_MSG_GETRULE) }, + { "nf_getrule_reset", SUBSYS_NFT(NFT_MSG_GETRULE_RESET) }, + { "nf_delrule", SUBSYS_NFT(NFT_MSG_DELRULE) }, + { "nf_destroyrule", SUBSYS_NFT(NFT_MSG_DESTROYRULE) }, + + { "nf_newset", SUBSYS_NFT(NFT_MSG_NEWSET) }, + { "nf_getset", SUBSYS_NFT(NFT_MSG_GETSET) }, + { "nf_delset", SUBSYS_NFT(NFT_MSG_DELSET) }, + { "nf_destroyset", SUBSYS_NFT(NFT_MSG_DESTROYSET) }, + + { "nf_newsetelem", SUBSYS_NFT(NFT_MSG_NEWSETELEM) }, + { "nf_getsetelem", SUBSYS_NFT(NFT_MSG_GETSETELEM) }, + { "nf_getsetelem_reset", SUBSYS_NFT(NFT_MSG_GETSETELEM_RESET) }, + { "nf_delsetelem", SUBSYS_NFT(NFT_MSG_DELSETELEM) }, + { "nf_destroysetelem", SUBSYS_NFT(NFT_MSG_DESTROYSETELEM) }, + + { "nf_newgen", SUBSYS_NFT(NFT_MSG_NEWGEN) }, + { "nf_getgen", SUBSYS_NFT(NFT_MSG_GETGEN) }, + + { "nf_trace", SUBSYS_NFT(NFT_MSG_TRACE) }, + + { "nf_newobj", SUBSYS_NFT(NFT_MSG_NEWOBJ) }, + { "nf_getobj", SUBSYS_NFT(NFT_MSG_GETOBJ) }, + { "nf_getobj_reset", SUBSYS_NFT(NFT_MSG_GETOBJ_RESET) }, + { "nf_delobj", SUBSYS_NFT(NFT_MSG_DELOBJ) }, + { "nf_destroyobj", SUBSYS_NFT(NFT_MSG_DESTROYOBJ) }, + + { "nf_newflowtable", SUBSYS_NFT(NFT_MSG_NEWFLOWTABLE) }, + { "nf_getflowtable", SUBSYS_NFT(NFT_MSG_GETFLOWTABLE) }, + { "nf_delflowtable", SUBSYS_NFT(NFT_MSG_DELFLOWTABLE) }, + { "nf_destroyflowtable", SUBSYS_NFT(NFT_MSG_DESTROYFLOWTABLE) }, +#undef SUBSYS_NFT + { 0 }, }; @@ -109,6 +170,7 @@ static struct gluten_offset arg_load(struct gluten_ctx *g, struct arg *a) */ static union value parse_field(struct gluten_ctx *g, struct gluten_offset offset, + struct vec_desc *vec, enum op_cmp_type cmp, enum jump_type jump, int index, struct field *f, JSON_Value *jvalue) { @@ -134,7 +196,7 @@ ______________________ _____________ */ v.v_num = ((long long)0xfff << 44) | (0xfff << 8); mask_offset = emit_data(g, U64, 0, &v); - offset = emit_bitwise(g, U64, BITWISE_AND, NULL_OFFSET, + offset = emit_bitwise(g, U64, NULL, BITWISE_AND, NULL_OFFSET, offset, mask_offset); break; case GNU_DEV_MINOR: @@ -144,7 +206,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx */ v.v_num = 0xff | ((long long)0xfff << 20); mask_offset = emit_data(g, U64, 0, &v); - offset = emit_bitwise(g, U64, BITWISE_AND, NULL_OFFSET, + offset = emit_bitwise(g, U64, NULL, BITWISE_AND, NULL_OFFSET, offset, mask_offset); break; default: @@ -155,9 +217,19 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx if (f->flags & IOV) { struct gluten_offset iovlen = offset; + size_t alloc; + + /* vectorised ops need a zero length descriptor at the end */ + if (json_value_get_type(jvalue) == JSONObject && + (tmp = json_value_get_object(jvalue)) && + json_object_get_value(tmp, "netlink")) + alloc = f->size + + sizeof(((struct nlmsghdr *)NULL)->nlmsg_len); + else + alloc = f->size; iovlen.offset += f->desc.d_iovlen; - offset = emit_iovload(g, offset, iovlen, f->size); + offset = emit_iovload(g, offset, iovlen, alloc, f->size); } if (json_value_get_type(jvalue) == JSONObject && @@ -188,7 +260,16 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx } jvalue = json_array_get_value(set, i); - parse_field(g, offset, cmp, jump, index, f, jvalue); + + /* FIXME: ugly. Otherwise nested parse_field() will + * increment twice. + */ + offset.offset -= f->offset; + + parse_field(g, offset, vec, cmp, jump, index, f, + jvalue); + + offset.offset += f->offset; } return v; /* No SELECT based on sets... of course */ @@ -215,12 +296,12 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx &set, &cmp, &cmpterm); set_offset = emit_data(g, f->type, 0, &set); - masked = emit_bitwise(g, f->type, BITWISE_AND, + masked = emit_bitwise(g, f->type, vec, BITWISE_AND, NULL_OFFSET, offset, set_offset); cmp_offset = emit_data(g, f->type, 0, &cmpterm); - emit_cmp(g, cmp, masked, cmp_offset, + emit_cmp(g, cmp, vec, masked, cmp_offset, gluten_size[f->type], jump); emit_bpf_arg(index, f->type, cmpterm, set, cmp, g->mode); @@ -241,9 +322,13 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx } else if (f->flags & MASK) { mask.v_num = value_get_mask(f->desc.d_num); mask_offset = emit_data(g, f->type, 0, &mask); - data_offset = emit_bitwise(g, f->type, BITWISE_AND, + + data_offset = emit_bitwise(g, f->type, vec, BITWISE_AND, NULL_OFFSET, offset, mask_offset); + if (vec) + vec->len_offset = 0; + v.v_num = value_get_num(f->desc.d_num, jvalue); } else { v.v_num = value_get_num(f->desc.d_num, jvalue); @@ -251,7 +336,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx const_offset = emit_data(g, f->type, 0, &v); - emit_cmp(g, cmp, data_offset, const_offset, + emit_cmp(g, cmp, vec, data_offset, const_offset, gluten_size[f->type], jump); emit_bpf_arg(index, f->type, v, mask, cmp, g->mode); @@ -266,7 +351,7 @@ ______________________ _____________ v.v_num = (v.v_num & 0xfff) << 8 | (v.v_num & ~0xfff) << 32; const_offset = emit_data(g, U64, 0, &v); - emit_cmp_field(g, cmp, f, offset, const_offset, jump); + emit_cmp_field(g, cmp, vec, f, offset, const_offset, jump); filter_needs_deref(); /* No shifts in BPF */ break; @@ -278,7 +363,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx v.v_num = value_get_num(f->desc.d_num, jvalue); v.v_num = (v.v_num & 0xff) | (v.v_num & ~0xfff) << 12; const_offset = emit_data(g, U64, 0, &v); - emit_cmp_field(g, cmp, f, offset, const_offset, jump); + emit_cmp_field(g, cmp, vec, f, offset, const_offset, jump); filter_needs_deref(); /* No shifts in BPF */ break; @@ -292,17 +377,43 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx sel = jvalue; } - v = parse_field(g, offset, cmp, jump, index, f_inner, sel); + v = parse_field(g, offset, vec, cmp, jump, index, f_inner, sel); f = select_field(g, index, f->desc.d_select, v); - if (f) - parse_field(g, offset, cmp, jump, index, f, jvalue); + if (f) { + parse_field(g, offset, vec, cmp, jump, index, f, + jvalue); + } break; case STRING: if (json_value_get_type(jvalue) == JSONObject && (tmp = json_value_get_object(jvalue))) { if ((jvalue = json_object_get_value(tmp, "netlink"))) { - parse_field(g, offset, cmp, jump, index, + struct vec_desc v_nl, *vecptr; + + /* FIXME: even send()/sendto() might need + * vectorised operations + */ + if (f->flags & IOV) { + v_nl.start = offset; + v_nl.len_offset = offsetof(struct nlmsghdr, + nlmsg_len); + vecptr = &v_nl; + } else { + vecptr = NULL; + } + + /* TODO: mark as CMPVEC, with: + * - offset of length (offsetof nlmsghdr) + * - vector pointer: that's already offset? + * - offset of comparison in vector (0 atm) + * - second term of comparison (from jvalue) + * - length (still needed?) + * + * plus BITWISEVEC? is it even needed? + */ + + parse_field(g, offset, vecptr, cmp, jump, index, &netlink_header, jvalue); } else { die(" unrecognised blob type"); @@ -317,8 +428,8 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx die(" string %s too long for field", v.v_str); const_offset = emit_data(g, STRING, strlen(v.v_str) + 1, &v); - emit_cmp(g, CMP_NE, offset, const_offset, strlen(v.v_str) + 1, - JUMP_NEXT_BLOCK); + emit_cmp(g, CMP_NE, vec, offset, const_offset, + strlen(v.v_str) + 1, JUMP_NEXT_BLOCK); break; case FDPATH: case FDMOUNT: @@ -328,8 +439,8 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx const_offset = emit_data(g, STRING, size, &v); seccomp_offset = emit_seccomp_data(index); emit_resolvefd(g, f->type, seccomp_offset, offset, size); - emit_cmp(g, CMP_NE, offset, const_offset, size, - JUMP_NEXT_BLOCK); + emit_cmp(g, CMP_NE, vec, offset, const_offset, + size, JUMP_NEXT_BLOCK); break; case STRUCT: for (f_inner = f->desc.d_struct; f_inner->name; f_inner++) { @@ -340,7 +451,7 @@ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx if (!field_value) continue; - parse_field(g, offset, cmp, jump, index, f_inner, + parse_field(g, offset, vec, cmp, jump, index, f_inner, field_value); } break; @@ -365,7 +476,8 @@ static void parse_arg(struct gluten_ctx *g, JSON_Value *jvalue, struct arg *a) offset = arg_load(g, a); - parse_field(g, offset, CMP_NE, JUMP_NEXT_BLOCK, a->pos, &a->f, jvalue); + parse_field(g, offset, NULL, CMP_NE, JUMP_NEXT_BLOCK, a->pos, &a->f, + jvalue); } /** @@ -407,16 +519,25 @@ static void parse_match(struct gluten_ctx *g, JSON_Object *obj, void handle_matches(struct gluten_ctx *g, JSON_Value *value) { JSON_Array *matches = json_value_get_array(value); - unsigned i; + unsigned i, count; - for (i = 0; i < json_array_get_count(matches); i++) { + if (matches) + count = json_array_get_count(matches); + else + count = 1; + + for (i = 0; i < count; i++) { JSON_Object *match, *args; struct call **set, *call; const char *name; + if (matches) + match = json_array_get_object(matches, i); + else + match = json_value_get_object(value); + g->mr = g->ip; - match = json_array_get_object(matches, i); name = json_object_get_name(match, 0); args = json_object_get_object(match, name); debug(" Parsing match %i: %s", i, name); |