diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/mnl.c | 129 | ||||
-rw-r--r-- | src/netlink.c | 170 | ||||
-rw-r--r-- | src/netlink_linearize.c | 4 |
4 files changed, 297 insertions, 8 deletions
@@ -170,6 +170,7 @@ static int nft_netlink(struct parser_state *state, struct list_head *msgs) struct mnl_err *err, *tmp; LIST_HEAD(err_list); uint32_t batch_seqnum; + bool batch_supported = netlink_batch_supported(); int ret = 0; batch_seqnum = mnl_batch_begin(); @@ -177,6 +178,7 @@ static int nft_netlink(struct parser_state *state, struct list_head *msgs) memset(&ctx, 0, sizeof(ctx)); ctx.msgs = msgs; ctx.seqnum = cmd->seqnum = mnl_seqnum_alloc(); + ctx.batch_supported = batch_supported; init_list_head(&ctx.list); ret = do_command(&ctx, cmd); if (ret < 0) @@ -630,6 +630,38 @@ int mnl_nft_set_delete(struct mnl_socket *nf_sock, struct nft_set *nls, return nft_mnl_talk(nf_sock, nlh, nlh->nlmsg_len, NULL, NULL); } +int mnl_nft_set_batch_add(struct mnl_socket *nf_sock, struct nft_set *nls, + unsigned int flags, uint32_t seqnum) +{ + struct nlmsghdr *nlh; + + nlh = nft_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWSET, + nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY), + NLM_F_CREATE | flags, seqnum); + nft_set_nlmsg_build_payload(nlh, nls); + if (!mnl_nlmsg_batch_next(batch)) + mnl_batch_page_add(); + + return 0; +} + +int mnl_nft_set_batch_del(struct mnl_socket *nf_sock, struct nft_set *nls, + unsigned int flags, uint32_t seqnum) +{ + struct nlmsghdr *nlh; + + nlh = nft_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_DELSET, + nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY), + flags, seqnum); + nft_set_nlmsg_build_payload(nlh, nls); + if (!mnl_nlmsg_batch_next(batch)) + mnl_batch_page_add(); + + return 0; +} + static int set_cb(const struct nlmsghdr *nlh, void *data) { struct nft_set_list *nls_list = data; @@ -742,6 +774,38 @@ static int set_elem_cb(const struct nlmsghdr *nlh, void *data) return MNL_CB_OK; } +int mnl_nft_setelem_batch_add(struct mnl_socket *nf_sock, struct nft_set *nls, + unsigned int flags, uint32_t seqnum) +{ + struct nlmsghdr *nlh; + + nlh = nft_set_elem_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_NEWSETELEM, + nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY), + NLM_F_CREATE | flags, seqnum); + nft_set_elems_nlmsg_build_payload(nlh, nls); + if (!mnl_nlmsg_batch_next(batch)) + mnl_batch_page_add(); + + return 0; +} + +int mnl_nft_setelem_batch_del(struct mnl_socket *nf_sock, struct nft_set *nls, + unsigned int flags, uint32_t seqnum) +{ + struct nlmsghdr *nlh; + + nlh = nft_set_elem_nlmsg_build_hdr(mnl_nlmsg_batch_current(batch), + NFT_MSG_DELSETELEM, + nft_set_attr_get_u32(nls, NFT_SET_ATTR_FAMILY), + 0, seqnum); + nft_set_elems_nlmsg_build_payload(nlh, nls); + if (!mnl_nlmsg_batch_next(batch)) + mnl_batch_page_add(); + + return 0; +} + int mnl_nft_setelem_get(struct mnl_socket *nf_sock, struct nft_set *nls) { char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -823,3 +887,68 @@ int mnl_nft_event_listener(struct mnl_socket *nf_sock, { return nft_mnl_recv(nf_sock, 0, 0, cb, cb_data); } + +static void nft_mnl_batch_put(char *buf, uint16_t type, uint32_t seq) +{ + struct nlmsghdr *nlh; + struct nfgenmsg *nfg; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = type; + nlh->nlmsg_flags = NLM_F_REQUEST; + nlh->nlmsg_seq = seq; + + nfg = mnl_nlmsg_put_extra_header(nlh, sizeof(*nfg)); + nfg->nfgen_family = AF_INET; + nfg->version = NFNETLINK_V0; + nfg->res_id = NFNL_SUBSYS_NFTABLES; +} + +bool mnl_batch_supported(struct mnl_socket *nf_sock) +{ + struct mnl_nlmsg_batch *b; + char buf[MNL_SOCKET_BUFFER_SIZE]; + int ret; + + b = mnl_nlmsg_batch_start(buf, sizeof(buf)); + + nft_mnl_batch_put(mnl_nlmsg_batch_current(b), NFNL_MSG_BATCH_BEGIN, + seq++); + mnl_nlmsg_batch_next(b); + + nft_set_nlmsg_build_hdr(mnl_nlmsg_batch_current(b), + NFT_MSG_NEWSET, AF_INET, + NLM_F_ACK, seq++); + mnl_nlmsg_batch_next(b); + + nft_mnl_batch_put(mnl_nlmsg_batch_current(b), NFNL_MSG_BATCH_END, + seq++); + mnl_nlmsg_batch_next(b); + + ret = mnl_socket_sendto(nf_sock, mnl_nlmsg_batch_head(b), + mnl_nlmsg_batch_size(b)); + if (ret < 0) + goto err; + + mnl_nlmsg_batch_stop(b); + + ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf)); + while (ret > 0) { + ret = mnl_cb_run(buf, ret, 0, mnl_socket_get_portid(nf_sock), + NULL, NULL); + if (ret <= 0) + break; + + ret = mnl_socket_recvfrom(nf_sock, buf, sizeof(buf)); + } + + /* We're sending an incomplete message to see if the kernel supports + * set messages in batches. EINVAL means that we sent an incomplete + * message with missing attributes. The kernel just ignores messages + * that we cannot include in the batch. + */ + return (ret == -1 && errno == EINVAL) ? true : false; +err: + mnl_nlmsg_batch_stop(b); + return ret; +} diff --git a/src/netlink.c b/src/netlink.c index 22150f20..f4c512cc 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -180,6 +180,8 @@ struct nft_set *alloc_nft_set(const struct handle *h) nft_set_attr_set_str(nls, NFT_SET_ATTR_TABLE, h->table); if (h->set != NULL) nft_set_attr_set_str(nls, NFT_SET_ATTR_NAME, h->set); + if (h->set_id) + nft_set_attr_set_u32(nls, NFT_SET_ATTR_ID, h->set_id); return nls; } @@ -842,8 +844,8 @@ static struct set *netlink_delinearize_set(struct netlink_ctx *ctx, return set; } -int netlink_add_set(struct netlink_ctx *ctx, const struct handle *h, - struct set *set) +static int netlink_add_set_compat(struct netlink_ctx *ctx, + const struct handle *h, struct set *set) { struct nft_set *nls; int err; @@ -874,8 +876,57 @@ int netlink_add_set(struct netlink_ctx *ctx, const struct handle *h, return err; } -int netlink_delete_set(struct netlink_ctx *ctx, const struct handle *h, - const struct location *loc) +/* internal ID to uniquely identify a set in the batch */ +static uint32_t set_id; + +static int netlink_add_set_batch(struct netlink_ctx *ctx, + const struct handle *h, struct set *set) +{ + struct nft_set *nls; + int err; + + nls = alloc_nft_set(h); + nft_set_attr_set_u32(nls, NFT_SET_ATTR_FLAGS, set->flags); + nft_set_attr_set_u32(nls, NFT_SET_ATTR_KEY_TYPE, + dtype_map_to_kernel(set->keytype)); + nft_set_attr_set_u32(nls, NFT_SET_ATTR_KEY_LEN, + set->keylen / BITS_PER_BYTE); + if (set->flags & NFT_SET_MAP) { + nft_set_attr_set_u32(nls, NFT_SET_ATTR_DATA_TYPE, + dtype_map_to_kernel(set->datatype)); + nft_set_attr_set_u32(nls, NFT_SET_ATTR_DATA_LEN, + set->datalen / BITS_PER_BYTE); + } + set->handle.set_id = ++set_id; + nft_set_attr_set_u32(nls, NFT_SET_ATTR_ID, set->handle.set_id); + netlink_dump_set(nls); + + err = mnl_nft_set_batch_add(nf_sock, nls, NLM_F_EXCL, ctx->seqnum); + if (err < 0) { + netlink_io_error(ctx, &set->location, "Could not add set: %s", + strerror(errno)); + } + nft_set_free(nls); + + return err; +} + +int netlink_add_set(struct netlink_ctx *ctx, const struct handle *h, + struct set *set) +{ + int ret; + + if (ctx->batch_supported) + ret = netlink_add_set_batch(ctx, h, set); + else + ret = netlink_add_set_compat(ctx, h, set); + + return ret; +} + +static int netlink_del_set_compat(struct netlink_ctx *ctx, + const struct handle *h, + const struct location *loc) { struct nft_set *nls; int err; @@ -890,6 +941,36 @@ int netlink_delete_set(struct netlink_ctx *ctx, const struct handle *h, return err; } +static int netlink_del_set_batch(struct netlink_ctx *ctx, + const struct handle *h, + const struct location *loc) +{ + struct nft_set *nls; + int err; + + nls = alloc_nft_set(h); + err = mnl_nft_set_batch_del(nf_sock, nls, 0, ctx->seqnum); + nft_set_free(nls); + + if (err < 0) + netlink_io_error(ctx, loc, "Could not delete set: %s", + strerror(errno)); + return err; +} + +int netlink_delete_set(struct netlink_ctx *ctx, const struct handle *h, + const struct location *loc) +{ + int ret; + + if (ctx->batch_supported) + ret = netlink_del_set_batch(ctx, h, loc); + else + ret = netlink_del_set_compat(ctx, h, loc); + + return ret; +} + static int list_set_cb(struct nft_set *nls, void *arg) { struct netlink_ctx *ctx = arg; @@ -946,8 +1027,29 @@ static void alloc_setelem_cache(const struct expr *set, struct nft_set *nls) } } -int netlink_add_setelems(struct netlink_ctx *ctx, const struct handle *h, - const struct expr *expr) +static int netlink_add_setelems_batch(struct netlink_ctx *ctx, + const struct handle *h, + const struct expr *expr) +{ + struct nft_set *nls; + int err; + + nls = alloc_nft_set(h); + alloc_setelem_cache(expr, nls); + netlink_dump_set(nls); + + err = mnl_nft_setelem_batch_add(nf_sock, nls, 0, ctx->seqnum); + nft_set_free(nls); + if (err < 0) + netlink_io_error(ctx, &expr->location, + "Could not add set elements: %s", + strerror(errno)); + return err; +} + +static int netlink_add_setelems_compat(struct netlink_ctx *ctx, + const struct handle *h, + const struct expr *expr) { struct nft_set *nls; int err; @@ -965,8 +1067,42 @@ int netlink_add_setelems(struct netlink_ctx *ctx, const struct handle *h, return err; } -int netlink_delete_setelems(struct netlink_ctx *ctx, const struct handle *h, - const struct expr *expr) +int netlink_add_setelems(struct netlink_ctx *ctx, const struct handle *h, + const struct expr *expr) +{ + int ret; + + if (ctx->batch_supported) + ret = netlink_add_setelems_batch(ctx, h, expr); + else + ret = netlink_add_setelems_compat(ctx, h, expr); + + return ret; +} + +static int netlink_del_setelems_batch(struct netlink_ctx *ctx, + const struct handle *h, + const struct expr *expr) +{ + struct nft_set *nls; + int err; + + nls = alloc_nft_set(h); + alloc_setelem_cache(expr, nls); + netlink_dump_set(nls); + + err = mnl_nft_setelem_batch_del(nf_sock, nls, 0, ctx->seqnum); + nft_set_free(nls); + if (err < 0) + netlink_io_error(ctx, &expr->location, + "Could not delete set elements: %s", + strerror(errno)); + return err; +} + +static int netlink_del_setelems_compat(struct netlink_ctx *ctx, + const struct handle *h, + const struct expr *expr) { struct nft_set *nls; int err; @@ -1031,6 +1167,19 @@ out: return 0; } +int netlink_delete_setelems(struct netlink_ctx *ctx, const struct handle *h, + const struct expr *expr) +{ + int ret; + + if (ctx->batch_supported) + ret = netlink_del_setelems_batch(ctx, h, expr); + else + ret = netlink_del_setelems_compat(ctx, h, expr); + + return ret; +} + static int list_setelem_cb(struct nft_set_elem *nlse, void *arg) { struct netlink_ctx *ctx = arg; @@ -1603,3 +1752,8 @@ int netlink_monitor(struct netlink_mon_handler *monhandler) return mnl_nft_event_listener(nf_mon_sock, netlink_events_cb, monhandler); } + +bool netlink_batch_supported(void) +{ + return mnl_batch_supported(nf_sock); +} diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index e80646b6..e3f06afe 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -129,6 +129,8 @@ static void netlink_gen_map(struct netlink_linearize_ctx *ctx, nft_rule_expr_set_u32(nle, NFT_EXPR_LOOKUP_DREG, dreg); nft_rule_expr_set_str(nle, NFT_EXPR_LOOKUP_SET, expr->mappings->set->handle.set); + nft_rule_expr_set_u32(nle, NFT_EXPR_LOOKUP_SET_ID, + expr->mappings->set->handle.set_id); if (dreg == NFT_REG_VERDICT) release_register(ctx); @@ -153,6 +155,8 @@ static void netlink_gen_lookup(struct netlink_linearize_ctx *ctx, nft_rule_expr_set_u32(nle, NFT_EXPR_LOOKUP_SREG, sreg); nft_rule_expr_set_str(nle, NFT_EXPR_LOOKUP_SET, expr->right->set->handle.set); + nft_rule_expr_set_u32(nle, NFT_EXPR_LOOKUP_SET_ID, + expr->right->set->handle.set_id); release_register(ctx); nft_rule_add_expr(ctx->nlr, nle); |