diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 247868221d..39e3c36ad2 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -142,11 +142,9 @@ int sd_netlink_message_is_broadcast(const sd_netlink_message *m) { /* If successful the updated message will be correctly aligned, if unsuccessful the old message is untouched. */ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *data, size_t data_length) { - uint32_t rta_length; - size_t message_length, padding_length; + size_t message_length; struct nlmsghdr *new_hdr; struct rtattr *rta; - char *padding; unsigned i; int offset; @@ -154,16 +152,10 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da assert(m->hdr); assert(!m->sealed); assert(NLMSG_ALIGN(m->hdr->nlmsg_len) == m->hdr->nlmsg_len); - assert(!data || data_length); - - /* get offset of the new attribute */ - offset = m->hdr->nlmsg_len; - - /* get the size of the new rta attribute (with padding at the end) */ - rta_length = RTA_LENGTH(data_length); + assert(!data || data_length > 0); /* get the new message size (with padding at the end) */ - message_length = offset + RTA_ALIGN(rta_length); + message_length = m->hdr->nlmsg_len + RTA_SPACE(data_length); /* buffer should be smaller than both one page or 8K to be accepted by the kernel */ if (message_length > MIN(page_size(), 8192UL)) @@ -176,33 +168,19 @@ static int add_rtattr(sd_netlink_message *m, unsigned short type, const void *da m->hdr = new_hdr; /* get pointer to the attribute we are about to add */ - rta = (struct rtattr *) ((uint8_t *) m->hdr + offset); + rta = (struct rtattr *) ((uint8_t *) m->hdr + m->hdr->nlmsg_len); + + rtattr_append_attribute_internal(rta, type, data, data_length); /* if we are inside containers, extend them */ for (i = 0; i < m->n_containers; i++) - GET_CONTAINER(m, i)->rta_len += message_length - offset; - - /* fill in the attribute */ - rta->rta_type = type; - rta->rta_len = rta_length; - if (data) - /* we don't deal with the case where the user lies about the type - * and gives us too little data (so don't do that) - */ - padding = mempcpy(RTA_DATA(rta), data, data_length); - - else - /* if no data was passed, make sure we still initialize the padding - note that we can have data_length > 0 (used by some containers) */ - padding = RTA_DATA(rta); - - /* make sure also the padding at the end of the message is initialized */ - padding_length = (uint8_t*)m->hdr + message_length - (uint8_t*)padding; - memzero(padding, padding_length); + GET_CONTAINER(m, i)->rta_len += RTA_SPACE(data_length); /* update message size */ + offset = m->hdr->nlmsg_len; m->hdr->nlmsg_len = message_length; + /* return old message size */ return offset; } diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index 80725f6cb4..c35d6ba4a5 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -2,6 +2,7 @@ #include "sd-netlink.h" +#include "memory-util.h" #include "netlink-internal.h" #include "netlink-util.h" #include "strv.h" @@ -178,3 +179,60 @@ int rtnl_log_parse_error(int r) { int rtnl_log_create_error(int r) { return log_error_errno(r, "Failed to create netlink message: %m"); } + +void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length) { + size_t padding_length; + char *padding; + + assert(rta); + assert(!data || data_length > 0); + + /* fill in the attribute */ + rta->rta_type = type; + rta->rta_len = RTA_LENGTH(data_length); + if (data) + /* we don't deal with the case where the user lies about the type + * and gives us too little data (so don't do that) + */ + padding = mempcpy(RTA_DATA(rta), data, data_length); + + else + /* if no data was passed, make sure we still initialize the padding + note that we can have data_length > 0 (used by some containers) */ + padding = RTA_DATA(rta); + + /* make sure also the padding at the end of the message is initialized */ + padding_length = (char *) rta + RTA_SPACE(data_length) - padding; + memzero(padding, padding_length); +} + +int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length) { + struct rtattr *new_rta, *sub_rta; + size_t message_length; + + assert(rta); + assert(!data || data_length > 0); + + /* get the new message size (with padding at the end) */ + message_length = RTA_ALIGN(rta ? (*rta)->rta_len : 0) + RTA_SPACE(data_length); + + /* buffer should be smaller than both one page or 8K to be accepted by the kernel */ + if (message_length > MIN(page_size(), 8192UL)) + return -ENOBUFS; + + /* realloc to fit the new attribute */ + new_rta = realloc(*rta, message_length); + if (!new_rta) + return -ENOMEM; + *rta = new_rta; + + /* get pointer to the attribute we are about to add */ + sub_rta = (struct rtattr *) ((uint8_t *) *rta + RTA_ALIGN((*rta)->rta_len)); + + rtattr_append_attribute_internal(sub_rta, type, data, data_length); + + /* update rta_len */ + (*rta)->rta_len = message_length; + + return 0; +} diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index 8c43fafec7..1782786841 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -77,3 +77,6 @@ int rtnl_log_create_error(int r); int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data); int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data); + +void rtattr_append_attribute_internal(struct rtattr *rta, unsigned short type, const void *data, size_t data_length); +int rtattr_append_attribute(struct rtattr **rta, unsigned short type, const void *data, size_t data_length);