]> git.ipfire.org Git - thirdparty/nftables.git/commitdiff
src: add set netlink message to the batch
authorPablo Neira Ayuso <pablo@netfilter.org>
Mon, 19 May 2014 10:18:04 +0000 (12:18 +0200)
committerPablo Neira Ayuso <pablo@netfilter.org>
Mon, 19 May 2014 10:18:06 +0000 (12:18 +0200)
This patch moves the netlink set messages to the batch that contains
the rules. This helps to speed up rule-set restoration time by
changing the operational. To achieve this, an internal set ID which
is unique to the batch is allocated as suggested by Patrick.

To retain backward compatibility, nft initially guesses if the
kernel supports set in batches. Otherwise, it falls back to the
previous (slowier) operational.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
include/mnl.h
include/netlink.h
include/rule.h
src/main.c
src/mnl.c
src/netlink.c
src/netlink_linearize.c

index ece7ee7f14f76535025a6f69395b1a9c1a26a053..f25dd533c237e5ff87e8fbbfee175008f4bd7bf7 100644 (file)
@@ -53,16 +53,24 @@ int mnl_nft_table_get(struct mnl_socket *nf_sock, struct nft_table *nlt,
 
 int mnl_nft_set_add(struct mnl_socket *nf_sock, struct nft_set *nls,
                    unsigned int flags);
+int mnl_nft_set_batch_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+                         unsigned int flags, uint32_t seq);
 int mnl_nft_set_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
                       unsigned int flags);
+int mnl_nft_set_batch_del(struct mnl_socket *nf_sock, struct nft_set *nls,
+                         unsigned int flags, uint32_t seq);
 struct nft_set_list *mnl_nft_set_dump(struct mnl_socket *nf_sock, int family,
                                      const char *table);
 int mnl_nft_set_get(struct mnl_socket *nf_sock, struct nft_set *nls);
 
 int mnl_nft_setelem_add(struct mnl_socket *nf_sock, struct nft_set *nls,
                        unsigned int flags);
+int mnl_nft_setelem_batch_add(struct mnl_socket *nf_sock, struct nft_set *nls,
+                             unsigned int flags, uint32_t seq);
 int mnl_nft_setelem_delete(struct mnl_socket *nf_sock, struct nft_set *nls,
                           unsigned int flags);
+int mnl_nft_setelem_batch_del(struct mnl_socket *nf_sock, struct nft_set *nls,
+                             unsigned int flags, uint32_t seq);
 int mnl_nft_setelem_get(struct mnl_socket *nf_sock, struct nft_set *nls);
 
 struct nft_ruleset *mnl_nft_ruleset_dump(struct mnl_socket *nf_sock,
@@ -70,4 +78,7 @@ struct nft_ruleset *mnl_nft_ruleset_dump(struct mnl_socket *nf_sock,
 int mnl_nft_event_listener(struct mnl_socket *nf_sock,
                           int (*cb)(const struct nlmsghdr *nlh, void *data),
                           void *cb_data);
+
+bool mnl_batch_supported(struct mnl_socket *nf_sock);
+
 #endif /* _NFTABLES_MNL_H_ */
index eca4a483d8b49caa5d856d0e8403e51c69cc5cf7..4ef7365f6fdade4e5bdab9ae3c7a17ccd8d7386e 100644 (file)
@@ -30,6 +30,7 @@ struct netlink_ctx {
        struct set              *set;
        const void              *data;
        uint32_t                seqnum;
+       bool                    batch_supported;
 };
 
 extern struct nft_table *alloc_nft_table(const struct handle *h);
@@ -153,4 +154,6 @@ struct netlink_mon_handler {
 };
 
 extern int netlink_monitor(struct netlink_mon_handler *monhandler);
+bool netlink_batch_supported(void);
+
 #endif /* NFTABLES_NETLINK_H */
index da604a5420c7c28ce1be64cb798eef7f6f3f5c79..ebdafe8a8c86fe5981b0727b68b4cf5678b48088 100644 (file)
@@ -14,6 +14,7 @@
  * @set:       set name (sets only)
  * @handle:    rule handle (rules only)
  * @position:  rule position (rules only)
+ * @set_id:    set ID (sets only)
  * @comment:   human-readable comment (rules only)
  */
 struct handle {
@@ -23,6 +24,7 @@ struct handle {
        const char              *set;
        uint64_t                handle;
        uint64_t                position;
+       uint32_t                set_id;
        const char              *comment;
 };
 
index 9d505776d85fe043ddfbf0567d0a15ff726d02cc..a446bc68c0cd661ca9913ff4a7ee916c14fdb836 100644 (file)
@@ -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)
index 4d60d64bb7b0dfbda11b1989dd6df1a4a935ec84..873d442c3d1ecdfebd004fd0e8828b7a90ae7df8 100644 (file)
--- a/src/mnl.c
+++ b/src/mnl.c
@@ -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;
+}
index 22150f20f53941ff4c2fa6fc758581105a556fc7..f4c512cca54bf778df5d61952ea8007872c9826b 100644 (file)
@@ -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);
+}
index e80646b65172a596a270e5a8185f708dcdbda281..e3f06afe59dbf9e71127cb7c53d5e2d10863954a 100644 (file)
@@ -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);