From 99c41c0de4e65bd881bccda3796481d3dc2007d2 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 19 Jun 2020 14:58:41 +0200 Subject: [PATCH] sd-netlink: add sd_netlink_sendv nftables uses a transaction-based netlink model: one netlink write comes with multiple messages. A 'BEGIN' message to tell nf_tables/kernel that a new transaction starts. Then, one more messages to add/delete tables/chains/rules etc. Lastly, an END message that commits all changes. This function will be used to send all the individual messages that should make up a single transaction as a single write. --- src/libsystemd/sd-netlink/netlink-internal.h | 1 + src/libsystemd/sd-netlink/netlink-socket.c | 25 ++++++++++++++ src/libsystemd/sd-netlink/sd-netlink.c | 35 ++++++++++++++++++++ src/systemd/sd-netlink.h | 1 + 4 files changed, 62 insertions(+) diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h index 1240f0d66d..2845700ffb 100644 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ b/src/libsystemd/sd-netlink/netlink-internal.h @@ -139,6 +139,7 @@ int socket_bind(sd_netlink *nl); int socket_broadcast_group_ref(sd_netlink *nl, unsigned group); int socket_broadcast_group_unref(sd_netlink *nl, unsigned group); int socket_write_message(sd_netlink *nl, sd_netlink_message *m); +int socket_writev_message(sd_netlink *nl, sd_netlink_message *m[], size_t msgcount); int socket_read_message(sd_netlink *nl); int rtnl_rqueue_make_room(sd_netlink *rtnl); diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index 228e38df90..a1a839f57a 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -238,6 +238,31 @@ int socket_write_message(sd_netlink *nl, sd_netlink_message *m) { return k; } +int socket_writev_message(sd_netlink *nl, sd_netlink_message *m[], size_t msgcount) { + _cleanup_free_ struct iovec *iovs = NULL; + ssize_t k; + size_t i; + + assert(nl); + assert(msgcount); + + iovs = new0(struct iovec, msgcount); + if (!iovs) + return -ENOMEM; + + for (i = 0; i < msgcount; i++) { + assert(m[i]->hdr != NULL); + assert(m[i]->hdr->nlmsg_len > 0); + iovs[i] = IOVEC_MAKE(m[i]->hdr, m[i]->hdr->nlmsg_len); + } + + k = writev(nl->fd, iovs, msgcount); + if (k < 0) + return -errno; + + return k; +} + static int socket_recv_message(int fd, struct iovec *iov, uint32_t *ret_mcast_group, bool peek) { union sockaddr_union sender; CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct nl_pktinfo))) control; diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index 7801101807..5a8b4d9322 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -226,6 +226,41 @@ int sd_netlink_send(sd_netlink *nl, return 1; } +int sd_netlink_sendv(sd_netlink *nl, + sd_netlink_message *messages[], + size_t msgcount, + uint32_t **ret_serial) { + _cleanup_free_ uint32_t *serials = NULL; + unsigned i; + int r; + + assert_return(nl, -EINVAL); + assert_return(!rtnl_pid_changed(nl), -ECHILD); + assert_return(messages, -EINVAL); + + if (ret_serial) { + serials = new0(uint32_t, msgcount); + if (!serials) + return -ENOMEM; + } + + for (i = 0; i < msgcount; i++) { + assert_return(!messages[i]->sealed, -EPERM); + rtnl_seal_message(nl, messages[i]); + if (serials) + serials[i] = rtnl_message_get_serial(messages[i]); + } + + r = socket_writev_message(nl, messages, msgcount); + if (r < 0) + return r; + + if (ret_serial) + *ret_serial = TAKE_PTR(serials); + + return r; +} + int rtnl_rqueue_make_room(sd_netlink *rtnl) { assert(rtnl); diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 15fa84de28..2b52c4ca88 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -60,6 +60,7 @@ sd_netlink *sd_netlink_ref(sd_netlink *nl); sd_netlink *sd_netlink_unref(sd_netlink *nl); int sd_netlink_send(sd_netlink *nl, sd_netlink_message *message, uint32_t *serial); +int sd_netlink_sendv(sd_netlink *nl, sd_netlink_message *messages[], size_t msgcnt, uint32_t **ret_serial); int sd_netlink_call_async(sd_netlink *nl, sd_netlink_slot **ret_slot, sd_netlink_message *message, sd_netlink_message_handler_t callback, sd_netlink_destroy_t destoy_callback, void *userdata, uint64_t usec, const char *description);