chiark / gitweb /
sd-netlink: respect attribute type flags
authorTom Gundersen <teg@jklm.no>
Tue, 23 Jun 2015 11:13:20 +0000 (13:13 +0200)
committerSven Eden <yamakuzure@gmx.net>
Tue, 14 Mar 2017 09:04:58 +0000 (10:04 +0100)
Though currently unused by us, netlink attribute types support embedding flags to indicate
if the type is encoded in network byte-order and if it is a nested attribute. Read out
these flags when parsing the message.

We will now swap the byteorder in case it is non-native when reading out integers (though
this is not needed by any of the types we currently support). We do not enforce the NESTED
flag, as the kernel gets this wrong in many cases.

src/libsystemd/sd-netlink/netlink-internal.h
src/libsystemd/sd-netlink/netlink-message.c

index ff663f9..4aa7583 100644 (file)
@@ -72,10 +72,6 @@ struct sd_netlink {
         unsigned rqueue_partial_size;
         size_t rqueue_partial_allocated;
 
-        sd_netlink_message **wqueue;
-        unsigned wqueue_size;
-        size_t wqueue_allocated;
-
         struct nlmsghdr *rbuffer;
         size_t rbuffer_allocated;
 
@@ -96,18 +92,27 @@ struct sd_netlink {
         sd_event *event;
 };
 
+struct netlink_attribute {
+        size_t offset; /* offset from hdr to attirubte */
+        bool nested:1;
+        bool net_byteorder:1;
+};
+
+struct netlink_container {
+        const struct NLTypeSystem *type_system; /* the type system of the container */
+        size_t offset; /* offset from hdr to the start of the container */
+        struct netlink_attribute *attributes;
+        unsigned short n_attributes; /* number of attributes in container */
+};
+
 struct sd_netlink_message {
         RefCount n_ref;
 
         sd_netlink *rtnl;
 
         struct nlmsghdr *hdr;
-        const struct NLTypeSystem *(container_type_system[RTNL_CONTAINER_DEPTH]); /* the type of the container and all its parents */
-        size_t container_offsets[RTNL_CONTAINER_DEPTH]; /* offset from hdr to each container's start */
+        struct netlink_container containers[RTNL_CONTAINER_DEPTH];
         unsigned n_containers; /* number of containers */
-        size_t next_rta_offset; /* offset from hdr to next rta */
-        size_t *rta_offset_tb[RTNL_CONTAINER_DEPTH];
-        unsigned short rta_tb_size[RTNL_CONTAINER_DEPTH];
         bool sealed:1;
         bool broadcast:1;
 
@@ -115,21 +120,17 @@ struct sd_netlink_message {
 };
 
 int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type);
+int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret);
 
+int socket_open(int family);
+int socket_bind(sd_netlink *nl);
+int socket_join_broadcast_group(sd_netlink *nl, unsigned group);
 int socket_write_message(sd_netlink *nl, sd_netlink_message *m);
 int socket_read_message(sd_netlink *nl);
 
 int rtnl_rqueue_make_room(sd_netlink *rtnl);
 int rtnl_rqueue_partial_make_room(sd_netlink *rtnl);
 
-int rtnl_message_read_internal(sd_netlink_message *m, unsigned short type, void **data);
-int rtnl_message_parse(sd_netlink_message *m,
-                       size_t **rta_offset_tb,
-                       unsigned short *rta_tb_size,
-                       int max,
-                       struct rtattr *rta,
-                       unsigned int rt_len);
-
 /* Make sure callbacks don't destroy the rtnl connection */
 #define RTNL_DONT_DESTROY(rtnl) \
         _cleanup_netlink_unref_ _unused_ sd_netlink *_dont_destroy_##rtnl = sd_netlink_ref(rtnl)
index 3f8bf01..b0ed2f2 100644 (file)
 #include "netlink-internal.h"
 #include "netlink-types.h"
 
-#define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->container_offsets[i]) : NULL)
+#define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset) : NULL)
 #define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers ++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr;
 
 #define RTA_TYPE(rta) ((rta)->rta_type & NLA_TYPE_MASK)
+#define RTA_FLAGS(rta) ((rta)->rta_type & ~NLA_TYPE_MASK)
 
 int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) {
         sd_netlink_message *m;
@@ -72,6 +73,9 @@ int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) {
         if (r < 0)
                 return r;
 
+        if (type_get_type(nl_type) != NETLINK_TYPE_NESTED)
+                return -EINVAL;
+
         r = message_new_empty(rtnl, &m);
         if (r < 0)
                 return r;
@@ -85,8 +89,7 @@ int message_new(sd_netlink *rtnl, sd_netlink_message **ret, uint16_t type) {
 
         m->hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
 
-        if (type_get_type(nl_type) == NETLINK_TYPE_NESTED)
-                type_get_type_system(nl_type, &m->container_type_system[0]);
+        type_get_type_system(nl_type, &m->containers[0].type_system);
         m->hdr->nlmsg_len = size;
         m->hdr->nlmsg_type = type;
 
@@ -127,7 +130,7 @@ sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) {
                 free(m->hdr);
 
                 for (i = 0; i <= m->n_containers; i++)
-                        free(m->rta_offset_tb[i]);
+                        free(m->containers[i].attributes);
 
                 sd_netlink_message_unref(m->next);
 
@@ -221,7 +224,7 @@ static int message_attribute_has_type(sd_netlink_message *m, size_t *out_size, u
 
         assert(m);
 
-        r = type_system_get_type(m->container_type_system[m->n_containers], &type, attribute_type);
+        r = type_system_get_type(m->containers[m->n_containers].type_system, &type, attribute_type);
         if (r < 0)
                 return r;
 
@@ -404,18 +407,18 @@ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type
                 if (r < 0)
                         return r;
 
-                r = type_system_get_type_system_union(m->container_type_system[m->n_containers], &type_system_union, type);
+                r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type);
                 if (r < 0)
                         return r;
 
                 r = type_system_union_protocol_get_type_system(type_system_union,
-                                                               &m->container_type_system[m->n_containers + 1],
+                                                               &m->containers[m->n_containers + 1].type_system,
                                                                family);
                 if (r < 0)
                         return r;
         } else {
-                r = type_system_get_type_system(m->container_type_system[m->n_containers],
-                                                &m->container_type_system[m->n_containers + 1],
+                r = type_system_get_type_system(m->containers[m->n_containers].type_system,
+                                                &m->containers[m->n_containers + 1].type_system,
                                                 type);
                 if (r < 0)
                         return r;
@@ -425,7 +428,7 @@ int sd_netlink_message_open_container(sd_netlink_message *m, unsigned short type
         if (r < 0)
                 return r;
 
-        m->container_offsets[m->n_containers ++] = r;
+        m->containers[m->n_containers ++].offset = r;
 
         return 0;
 }
@@ -437,12 +440,12 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor
         assert_return(m, -EINVAL);
         assert_return(!m->sealed, -EPERM);
 
-        r = type_system_get_type_system_union(m->container_type_system[m->n_containers], &type_system_union, type);
+        r = type_system_get_type_system_union(m->containers[m->n_containers].type_system, &type_system_union, type);
         if (r < 0)
                 return r;
 
         r = type_system_union_get_type_system(type_system_union,
-                                              &m->container_type_system[m->n_containers + 1],
+                                              &m->containers[m->n_containers + 1].type_system,
                                               key);
         if (r < 0)
                 return r;
@@ -452,11 +455,11 @@ int sd_netlink_message_open_container_union(sd_netlink_message *m, unsigned shor
                 return r;
 
         /* do we evere need non-null size */
-        r = add_rtattr(m, type, NULL, 0);
+        r = add_rtattr(m, type | NLA_F_NESTED, NULL, 0);
         if (r < 0)
                 return r;
 
-        m->container_offsets[m->n_containers ++] = r;
+        m->containers[m->n_containers ++].offset = r;
 
         return 0;
 }
@@ -467,29 +470,35 @@ int sd_netlink_message_close_container(sd_netlink_message *m) {
         assert_return(!m->sealed, -EPERM);
         assert_return(m->n_containers > 0, -EINVAL);
 
-        m->container_type_system[m->n_containers] = NULL;
+        m->containers[m->n_containers].type_system = NULL;
         m->n_containers --;
 
         return 0;
 }
 
-int rtnl_message_read_internal(sd_netlink_message *m, unsigned short type, void **data) {
+static int netlink_message_read_internal(sd_netlink_message *m, unsigned short type, void **data, bool *net_byteorder) {
+        struct netlink_attribute *attribute;
         struct rtattr *rta;
 
         assert_return(m, -EINVAL);
         assert_return(m->sealed, -EPERM);
         assert_return(data, -EINVAL);
         assert(m->n_containers <= RTNL_CONTAINER_DEPTH);
-        assert(m->rta_offset_tb[m->n_containers]);
-        assert(type < m->rta_tb_size[m->n_containers]);
+        assert(m->containers[m->n_containers].attributes);
+        assert(type < m->containers[m->n_containers].n_attributes);
 
-        if(!m->rta_offset_tb[m->n_containers][type])
+        attribute = &m->containers[m->n_containers].attributes[type];
+
+        if(!attribute->offset)
                 return -ENODATA;
 
-        rta = (struct rtattr*)((uint8_t *) m->hdr + m->rta_offset_tb[m->n_containers][type]);
+        rta = (struct rtattr*)((uint8_t *) m->hdr + attribute->offset);
 
         *data = RTA_DATA(rta);
 
+        if (net_byteorder)
+                *net_byteorder = attribute->net_byteorder;
+
         return RTA_PAYLOAD(rta);
 }
 
@@ -503,7 +512,7 @@ int sd_netlink_message_read_string(sd_netlink_message *m, unsigned short type, c
         if (r < 0)
                 return r;
 
-        r = rtnl_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if (strnlen(attr_data, r) >= (size_t) r)
@@ -525,7 +534,7 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8
         if (r < 0)
                 return r;
 
-        r = rtnl_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t) r < sizeof(uint8_t))
@@ -538,8 +547,9 @@ int sd_netlink_message_read_u8(sd_netlink_message *m, unsigned short type, uint8
 }
 
 int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint16_t *data) {
-        int r;
         void *attr_data;
+        bool net_byteorder;
+        int r;
 
         assert_return(m, -EINVAL);
 
@@ -547,21 +557,26 @@ int sd_netlink_message_read_u16(sd_netlink_message *m, unsigned short type, uint
         if (r < 0)
                 return r;
 
-        r = rtnl_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
         if (r < 0)
                 return r;
         else if ((size_t) r < sizeof(uint16_t))
                 return -EIO;
 
-        if (data)
-                *data = *(uint16_t *) attr_data;
+        if (data) {
+                if (net_byteorder)
+                        *data = be16toh(*(uint16_t *) attr_data);
+                else
+                        *data = *(uint16_t *) attr_data;
+        }
 
         return 0;
 }
 
 int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint32_t *data) {
-        int r;
         void *attr_data;
+        bool net_byteorder;
+        int r;
 
         assert_return(m, -EINVAL);
 
@@ -569,14 +584,18 @@ int sd_netlink_message_read_u32(sd_netlink_message *m, unsigned short type, uint
         if (r < 0)
                 return r;
 
-        r = rtnl_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, &net_byteorder);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(uint32_t))
                 return -EIO;
 
-        if (data)
-                *data = *(uint32_t *) attr_data;
+        if (data) {
+                if (net_byteorder)
+                        *data = be32toh(*(uint32_t *) attr_data);
+                else
+                        *data = *(uint32_t *) attr_data;
+        }
 
         return 0;
 }
@@ -591,7 +610,7 @@ int sd_netlink_message_read_ether_addr(sd_netlink_message *m, unsigned short typ
         if (r < 0)
                 return r;
 
-        r = rtnl_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(struct ether_addr))
@@ -613,7 +632,7 @@ int sd_netlink_message_read_cache_info(sd_netlink_message *m, unsigned short typ
         if (r < 0)
                 return r;
 
-        r = rtnl_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(struct ifa_cacheinfo))
@@ -635,7 +654,7 @@ int sd_netlink_message_read_in_addr(sd_netlink_message *m, unsigned short type,
         if (r < 0)
                 return r;
 
-        r = rtnl_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(struct in_addr))
@@ -657,7 +676,7 @@ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type,
         if (r < 0)
                 return r;
 
-        r = rtnl_message_read_internal(m, type, &attr_data);
+        r = netlink_message_read_internal(m, type, &attr_data, NULL);
         if (r < 0)
                 return r;
         else if ((size_t)r < sizeof(struct in6_addr))
@@ -669,6 +688,42 @@ int sd_netlink_message_read_in6_addr(sd_netlink_message *m, unsigned short type,
         return 0;
 }
 
+static int netlink_container_parse(sd_netlink_message *m,
+                                   struct netlink_container *container,
+                                   int count,
+                                   struct rtattr *rta,
+                                   unsigned int rt_len) {
+        _cleanup_free_ struct netlink_attribute *attributes = NULL;
+
+        attributes = new0(struct netlink_attribute, count);
+        if(!attributes)
+                return -ENOMEM;
+
+        for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) {
+                unsigned short type;
+
+                type = RTA_TYPE(rta);
+
+                /* if the kernel is newer than the headers we used
+                   when building, we ignore out-of-range attributes */
+                if (type >= count)
+                        continue;
+
+                if (attributes[type].offset)
+                        log_debug("rtnl: message parse - overwriting repeated attribute");
+
+                attributes[type].offset = (uint8_t *) rta - (uint8_t *) m->hdr;
+                attributes[type].nested = RTA_FLAGS(rta) & NLA_F_NESTED;
+                attributes[type].net_byteorder = RTA_FLAGS(rta) & NLA_F_NET_BYTEORDER;
+        }
+
+        container->attributes = attributes;
+        attributes = NULL;
+        container->n_attributes = count;
+
+        return 0;
+}
+
 int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short type_id) {
         const NLType *nl_type;
         const NLTypeSystem *type_system;
@@ -680,7 +735,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
         assert_return(m, -EINVAL);
         assert_return(m->n_containers < RTNL_CONTAINER_DEPTH, -EINVAL);
 
-        r = type_system_get_type(m->container_type_system[m->n_containers],
+        r = type_system_get_type(m->containers[m->n_containers].type_system,
                                  &nl_type,
                                  type_id);
         if (r < 0)
@@ -689,7 +744,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
         type = type_get_type(nl_type);
 
         if (type == NETLINK_TYPE_NESTED) {
-                r = type_system_get_type_system(m->container_type_system[m->n_containers],
+                r = type_system_get_type_system(m->containers[m->n_containers].type_system,
                                                 &type_system,
                                                 type_id);
                 if (r < 0)
@@ -697,7 +752,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
         } else if (type == NETLINK_TYPE_UNION) {
                 const NLTypeSystemUnion *type_system_union;
 
-                r = type_system_get_type_system_union(m->container_type_system[m->n_containers],
+                r = type_system_get_type_system_union(m->containers[m->n_containers].type_system,
                                                       &type_system_union,
                                                       type_id);
                 if (r < 0)
@@ -742,7 +797,7 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
         } else
                 return -EINVAL;
 
-        r = rtnl_message_read_internal(m, type_id, &container);
+        r = netlink_message_read_internal(m, type_id, &container, NULL);
         if (r < 0)
                 return r;
         else
@@ -750,18 +805,17 @@ int sd_netlink_message_enter_container(sd_netlink_message *m, unsigned short typ
 
         m->n_containers ++;
 
-        r = rtnl_message_parse(m,
-                               &m->rta_offset_tb[m->n_containers],
-                               &m->rta_tb_size[m->n_containers],
-                               type_system_get_count(type_system),
-                               container,
-                               size);
+        r = netlink_container_parse(m,
+                                    &m->containers[m->n_containers],
+                                    type_system_get_count(type_system),
+                                    container,
+                                    size);
         if (r < 0) {
                 m->n_containers --;
                 return r;
         }
 
-        m->container_type_system[m->n_containers] = type_system;
+        m->containers[m->n_containers].type_system = type_system;
 
         return 0;
 }
@@ -771,9 +825,9 @@ int sd_netlink_message_exit_container(sd_netlink_message *m) {
         assert_return(m->sealed, -EINVAL);
         assert_return(m->n_containers > 0, -EINVAL);
 
-        free(m->rta_offset_tb[m->n_containers]);
-        m->rta_offset_tb[m->n_containers] = NULL;
-        m->container_type_system[m->n_containers] = NULL;
+        free(m->containers[m->n_containers].attributes);
+        m->containers[m->n_containers].attributes = NULL;
+        m->containers[m->n_containers].type_system = NULL;
 
         m->n_containers --;
 
@@ -808,41 +862,6 @@ int sd_netlink_message_get_errno(sd_netlink_message *m) {
         return err->error;
 }
 
-int rtnl_message_parse(sd_netlink_message *m,
-                       size_t **rta_offset_tb,
-                       unsigned short *rta_tb_size,
-                       int count,
-                       struct rtattr *rta,
-                       unsigned int rt_len) {
-        unsigned short type;
-        size_t *tb;
-
-        tb = new0(size_t, count);
-        if(!tb)
-                return -ENOMEM;
-
-        *rta_tb_size = count;
-
-        for (; RTA_OK(rta, rt_len); rta = RTA_NEXT(rta, rt_len)) {
-                type = RTA_TYPE(rta);
-
-                /* if the kernel is newer than the headers we used
-                   when building, we ignore out-of-range attributes
-                 */
-                if (type >= count)
-                        continue;
-
-                if (tb[type])
-                        log_debug("rtnl: message parse - overwriting repeated attribute");
-
-                tb[type] = (uint8_t *) rta - (uint8_t *) m->hdr;
-        }
-
-        *rta_offset_tb = tb;
-
-        return 0;
-}
-
 int sd_netlink_message_rewind(sd_netlink_message *m) {
         const NLType *nl_type;
         uint16_t type;
@@ -857,15 +876,13 @@ int sd_netlink_message_rewind(sd_netlink_message *m) {
                 rtnl_message_seal(m);
 
         for (i = 1; i <= m->n_containers; i++) {
-                free(m->rta_offset_tb[i]);
-                m->rta_offset_tb[i] = NULL;
-                m->rta_tb_size[i] = 0;
-                m->container_type_system[i] = NULL;
+                free(m->containers[i].attributes);
+                m->containers[i].attributes = NULL;
         }
 
         m->n_containers = 0;
 
-        if (m->rta_offset_tb[0]) {
+        if (m->containers[0].attributes) {
                 /* top-level attributes have already been parsed */
                 return 0;
         }
@@ -884,14 +901,13 @@ int sd_netlink_message_rewind(sd_netlink_message *m) {
 
                 type_get_type_system(nl_type, &type_system);
 
-                m->container_type_system[0] = type_system;
+                m->containers[0].type_system = type_system;
 
-                r = rtnl_message_parse(m,
-                                       &m->rta_offset_tb[m->n_containers],
-                                       &m->rta_tb_size[m->n_containers],
-                                       type_system_get_count(type_system),
-                                       (struct rtattr*)((uint8_t*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
-                                       NLMSG_PAYLOAD(m->hdr, size));
+                r = netlink_container_parse(m,
+                                            &m->containers[m->n_containers],
+                                            type_system_get_count(type_system),
+                                            (struct rtattr*)((uint8_t*)NLMSG_DATA(m->hdr) + NLMSG_ALIGN(size)),
+                                            NLMSG_PAYLOAD(m->hdr, size));
                 if (r < 0)
                         return r;
         }