From 9bf3b1cc7a94357c250f77f16829c96cbae801fe Mon Sep 17 00:00:00 2001 From: Stefano Brivio Date: Tue, 13 Aug 2024 18:50:33 +0200 Subject: call, emit, match: Add support for vectorised operations, nfnetlink We want to add and delete rules with iptables(8), and manipulate set elements with nft(8). These are the first users we encounter sending multiple netlink messages in one sendmsg(). To support matching on those, we need to iterate over several messages, looking for a matching one, or a mismatching one (depending on quantifiers and match type), but we don't want to implement program loops because of security design reasons. We can't implement a generalised instruction that vectorises existing ones, either, because we need to support universal and existential quantifiers in fields that are repeated multiple times, once per each netlink message, with bitwise operations and non-exact matching types. Add vectorisation support to OP_CMP and OP_BITWISE instead, with a generic description for a vector (only sequences of netlink messages with length in nlmsghdr are supported at the moment) so that, depending on the quantifiers, we'll repeat those operations as many times as needed. This way, we don't risk any O(n^2) explosion, and we are bound by O(m * n) instead, with m compare/bitwise operations for a given expression, and n number of netlink messages. Add demos for nft and iptables using the new concepts. Signed-off-by: Stefano Brivio --- cooker/call.c | 10 +++- cooker/calls/net.c | 92 ++++++++++++++++++++++------- cooker/emit.c | 52 ++++++++++++++--- cooker/emit.h | 7 ++- cooker/match.c | 169 +++++++++++++++++++++++++++++++++++++++++++++-------- 5 files changed, 270 insertions(+), 60 deletions(-) (limited to 'cooker') diff --git a/cooker/call.c b/cooker/call.c index dbdc52b..19a7b7e 100644 --- a/cooker/call.c +++ b/cooker/call.c @@ -77,8 +77,8 @@ static union value parse_metadata(struct gluten_ctx *g, struct field *f, } else if ((*base_offset)->type == OFFSET_NULL || (f->flags & WBUF)) { **base_offset = tag_offset; } else if (f->flags & MASK || add) { - emit_bitwise(g, f->type, BITWISE_OR, offset, offset, - tag_offset); + emit_bitwise(g, f->type, NULL, BITWISE_OR, + offset, offset, tag_offset); } else { emit_copy_field(g, f, offset, tag_offset); } @@ -337,6 +337,9 @@ bool arg_needs_temp(struct field *f, int pos, JSON_Value *jvalue, if (json_object_get_string(tmp, "set")) return true; + if (json_object_get_string(tmp, "get") && f->flags & FD) + return true; + if (level) return true; @@ -603,6 +606,9 @@ void handle_calls(struct gluten_ctx *g, JSON_Value *value) } name = json_object_get_name(obj, n); + if (!name) + continue; + value = json_object_get_value_at(obj, n); args = json_object_get_object(obj, name); diff --git a/cooker/calls/net.c b/cooker/calls/net.c index 94b13cd..0688467 100644 --- a/cooker/calls/net.c +++ b/cooker/calls/net.c @@ -65,22 +65,23 @@ static struct num socket_flags[] = { }; static struct num protocols[] = { - { "ip", IPPROTO_IP }, - { "icmp", IPPROTO_ICMP }, - { "igmp", IPPROTO_IGMP }, - { "tcp", IPPROTO_TCP }, - { "udp", IPPROTO_UDP }, - { "ipv6", IPPROTO_IPV6 }, - { "gre", IPPROTO_GRE }, - { "esp", IPPROTO_ESP }, - { "ah", IPPROTO_AH }, - { "sctp", IPPROTO_SCTP }, - { "udplite", IPPROTO_UDPLITE }, - { "mpls", IPPROTO_MPLS }, - { "raw", IPPROTO_RAW }, - { "mptcp", IPPROTO_MPTCP }, + { "ip", IPPROTO_IP }, + { "icmp", IPPROTO_ICMP }, + { "igmp", IPPROTO_IGMP }, + { "tcp", IPPROTO_TCP }, + { "udp", IPPROTO_UDP }, + { "ipv6", IPPROTO_IPV6 }, + { "gre", IPPROTO_GRE }, + { "esp", IPPROTO_ESP }, + { "ah", IPPROTO_AH }, + { "sctp", IPPROTO_SCTP }, + { "udplite", IPPROTO_UDPLITE }, + { "mpls", IPPROTO_MPLS }, + { "raw", IPPROTO_RAW }, + { "mptcp", IPPROTO_MPTCP }, - { "nl_route", NETLINK_ROUTE }, + { "nl_route", NETLINK_ROUTE }, + { "nl_netfilter", NETLINK_NETFILTER }, { 0 }, }; @@ -261,7 +262,7 @@ static struct num send_flags[] = { static struct arg send_args[] = { { 0, { - "fd", INT, 0, + "fd", INT, FD, 0, 0, { 0 }, @@ -297,7 +298,7 @@ static struct arg send_args[] = { static struct arg sendto_args[] = { { 0, { - "fd", INT, 0, + "fd", INT, FD, 0, 0, { 0 }, @@ -346,7 +347,7 @@ static struct arg sendto_args[] = { { 0 } }; -static struct select sendmsg_name_select = { +static struct select msg_name_select = { &connect_family, { .d_num = connect_addr_select_family } }; @@ -355,13 +356,13 @@ static struct field sendmsg_msghdr[] = { "name", SELECT, 0, offsetof(struct msghdr, msg_name), sizeof(struct sockaddr_storage), - { .d_select = &sendmsg_name_select }, + { .d_select = &msg_name_select }, }, { "namelen", LONG, SIZE, offsetof(struct msghdr, msg_namelen), 0, - { .d_size = (intptr_t)&sendmsg_name_select }, + { .d_size = (intptr_t)&msg_name_select }, }, { "iov", STRING, WBUF | IOV, @@ -400,7 +401,7 @@ static struct field sendmsg_msghdr[] = { static struct arg sendmsg_args[] = { { 0, { - "fd", INT, 0, + "fd", INT, FD, 0, 0, { 0 }, @@ -425,6 +426,53 @@ static struct arg sendmsg_args[] = { { 0 } }; +static struct field recvmsg_msghdr[] = { + { + "name", SELECT, 0, + offsetof(struct msghdr, msg_name), + sizeof(struct sockaddr_storage), + { .d_select = &msg_name_select }, + }, + { + "namelen", LONG, SIZE, + offsetof(struct msghdr, msg_namelen), + 0, + { .d_size = (intptr_t)&msg_name_select }, + }, + { + "iov", STRING, RBUF | IOV, + offsetof(struct msghdr, msg_iov), + BUFSIZ, + { .d_iovlen = offsetof(struct msghdr, msg_iovlen) - + offsetof(struct msghdr, msg_iov) }, + }, + { + "iovlen", LONG, 0, + offsetof(struct msghdr, msg_iovlen), + 0, + { 0 }, + }, + { + "control", STRING, 0, + offsetof(struct msghdr, msg_control), + BUFSIZ, + { 0 }, + }, + { + "controllen", LONG, SIZE, + offsetof(struct msghdr, msg_controllen), + 0, + { 0 }, + }, + { + "flags", INT, 0, + offsetof(struct msghdr, msg_flags), + 0, + { 0 }, + }, + { 0 } +}; + static struct arg recvmsg_args[] = { { 0, { @@ -439,7 +487,7 @@ static struct arg recvmsg_args[] = { "msg", STRUCT, 0, 0, sizeof(struct msghdr), - { .d_struct = sendmsg_msghdr }, + { .d_struct = recvmsg_msghdr }, }, }, { 2, diff --git a/cooker/emit.c b/cooker/emit.c index 33355b6..7d13a02 100644 --- a/cooker/emit.c +++ b/cooker/emit.c @@ -217,18 +217,19 @@ void emit_store(struct gluten_ctx *g, struct gluten_offset dst, * @iov: Pointer to msg_iov, already stored in gluten * @iovlen: Pointer to msg_iovlen, already stored in gluten * @dst: gluten destination to copy dereferenced data + * @alloc: Allocation size (alloc - len bytes filled with zeroes) * @len: Maximum length of data to copy altogether */ struct gluten_offset emit_iovload(struct gluten_ctx *g, struct gluten_offset iov, struct gluten_offset iovlen, - size_t len) + size_t alloc, size_t len) { struct op *op = (struct op *)gluten_ptr(&g->g, g->ip); struct op_iovload *load = &op->op.iovload; struct gluten_offset dst; - dst = gluten_rw_alloc(g, len); + dst = gluten_rw_alloc(g, alloc); op->type = OP_IOVLOAD; @@ -237,6 +238,7 @@ struct gluten_offset emit_iovload(struct gluten_ctx *g, load->dst = dst; load->size = len; + load->zero_fill = alloc - len; debug(" %i: OP_IOVLOAD: #%i < (#%i) as iovec (size: %lu)", g->ip.offset, dst.offset, iov.offset, len); @@ -294,6 +296,7 @@ void emit_resolvefd(struct gluten_ctx *g, enum type type, * emit_bitwise(): Emit OP_BITWISE instruction: bitwise operation and store * @g: gluten context * @type: Type of operands + * @vec: Description of vector structure, NULL for scalar operation * @op_type: Type of bitwise operation * @dst: gluten pointer to destination operand, can be OFFSET_NULL * @x: gluten pointer to first source operand @@ -302,6 +305,7 @@ void emit_resolvefd(struct gluten_ctx *g, enum type type, * Return: offset to destination operand, allocated here if not given */ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, + struct vec_desc *vec, enum bitwise_type op_type, struct gluten_offset dst, struct gluten_offset x, @@ -311,6 +315,7 @@ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, struct op_bitwise *op_bitwise = &op->op.bitwise; struct gluten_offset o; struct bitwise_desc *desc; + char ip_str[BUFSIZ]; op->type = OP_BITWISE; @@ -319,21 +324,42 @@ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, desc->size = gluten_size[type]; desc->type = op_type; - if (dst.type == OFFSET_NULL) - desc->dst = gluten_rw_alloc(g, desc->size); - else + + if (vec) + desc->vec = *vec; + + if (dst.type == OFFSET_NULL) { + size_t dst_size; + if (vec) /* FIXME: UIO_MAXIOV (1024) is a dubious choice */ + dst_size = (desc->size + sizeof(uint32_t)) * 1024; + else + dst_size = desc->size; + + desc->dst = gluten_rw_alloc(g, dst_size); + } else { desc->dst = dst; + } + desc->x = x; desc->y = y; op_bitwise->desc = o; - debug(" %i: OP_BITWISE: %s: #%lu (size: %lu) := %s: #%lu %s %s: #%lu", - g->ip.offset, + snprintf(ip_str, BUFSIZ, "%i", g->ip.offset); + + debug(" %s: OP_BITWISE: %s: #%lu (size: %lu) := %s: #%lu %s %s: #%lu", + ip_str, gluten_offset_name[desc->dst.type], desc->dst.offset, desc->size, gluten_offset_name[desc->x.type], desc->x.offset, bitwise_type_str[op_type], gluten_offset_name[desc->y.type], desc->y.offset); + if (vec) { + memset(ip_str, ' ', strlen(ip_str)); + debug(" %s vector start: %s: #%lu, length descriptor at %lu", + ip_str, + gluten_offset_name[vec->start.type], vec->start.offset, + vec->len_offset); + } if (++g->ip.offset > INST_MAX) die("Too many instructions"); @@ -345,12 +371,14 @@ struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, * emit_cmp(): Emit OP_CMP instruction: compare data from two offsets * @g: gluten context * @cmp_type: Type of comparison + * @vec: Description of vector structure, NULL for scalar comparison * @x: gluten pointer to first operand of comparison * @y: gluten pointer to second operand of comparison * @size: Size of comparison * @jmp: Jump direction if comparison is true */ void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type, + struct vec_desc *vec, struct gluten_offset x, struct gluten_offset y, size_t size, enum jump_type jmp) { @@ -367,6 +395,11 @@ void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type, desc->x = x; desc->y = y; desc->size = size; + if (vec) + desc->vec = *vec; + else + desc->vec.start.type = OFFSET_NULL; + desc->cmp = cmp_type; desc->jmp.type = OFFSET_INSTRUCTION; desc->jmp.offset = jmp; @@ -388,17 +421,18 @@ void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp_type, * emit_cmp_field() - Emit OP_CMP for a given field type * @g: gluten context * @cmp: Type of comparison + * @vec: Description of vector structure, NULL for scalar comparison * @field: Description of field from system call model * @x: gluten pointer to first operand of comparison * @y: gluten pointer to second operand of comparison * @jmp: Jump direction if comparison is true */ void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp, - struct field *field, + struct vec_desc *vec, struct field *field, struct gluten_offset x, struct gluten_offset y, enum jump_type jmp) { - emit_cmp(g, cmp, x, y, + emit_cmp(g, cmp, vec, x, y, field->size ? field->size : gluten_size[field->type], jmp); } diff --git a/cooker/emit.h b/cooker/emit.h index abdeda9..7948071 100644 --- a/cooker/emit.h +++ b/cooker/emit.h @@ -19,20 +19,21 @@ void emit_load(struct gluten_ctx *g, struct gluten_offset dst, struct gluten_offset emit_iovload(struct gluten_ctx *g, struct gluten_offset iov, struct gluten_offset iovlen, - size_t len); + size_t alloc, size_t len); void emit_store(struct gluten_ctx *g, struct gluten_offset dst, struct gluten_offset src, struct gluten_offset count); struct gluten_offset emit_seccomp_data(int index); struct gluten_offset emit_bitwise(struct gluten_ctx *g, enum type type, + struct vec_desc *desc, enum bitwise_type op_type, struct gluten_offset dst, struct gluten_offset x, struct gluten_offset y); -void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp, +void emit_cmp(struct gluten_ctx *g, enum op_cmp_type cmp, struct vec_desc *vec, struct gluten_offset x, struct gluten_offset y, size_t size, enum jump_type jmp); void emit_cmp_field(struct gluten_ctx *g, enum op_cmp_type cmp, - struct field *field, + struct vec_desc *vec, struct field *field, struct gluten_offset base, struct gluten_offset match, enum jump_type jmp); void emit_return(struct gluten_ctx *g, struct gluten_offset v, 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 #include +#include +#include +#include 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); -- cgit v1.2.3