chiark / gitweb /
sd-rtnl: fix size check in sd_rtnl_message_append_string()
[elogind.git] / src / libsystemd / sd-rtnl / rtnl-message.c
index 44ad303198a72942a40466829da479fd038ce76a..165e84d7a0a1d2cdb11e31f305eed6ba86ab2c5f 100644 (file)
@@ -114,6 +114,24 @@ int sd_rtnl_message_route_set_dst_prefixlen(sd_rtnl_message *m, unsigned char pr
         return 0;
 }
 
+int sd_rtnl_message_route_set_src_prefixlen(sd_rtnl_message *m, unsigned char prefixlen) {
+        struct rtmsg *rtm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+
+        rtm = NLMSG_DATA(m->hdr);
+
+        if ((rtm->rtm_family == AF_INET && prefixlen > 32) ||
+            (rtm->rtm_family == AF_INET6 && prefixlen > 128))
+                return -ERANGE;
+
+        rtm->rtm_src_len = prefixlen;
+
+        return 0;
+}
+
 int sd_rtnl_message_route_set_scope(sd_rtnl_message *m, unsigned char scope) {
         struct rtmsg *rtm;
 
@@ -128,6 +146,51 @@ int sd_rtnl_message_route_set_scope(sd_rtnl_message *m, unsigned char scope) {
         return 0;
 }
 
+int sd_rtnl_message_route_get_family(sd_rtnl_message *m, int *family) {
+        struct rtmsg *rtm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(family, -EINVAL);
+
+        rtm = NLMSG_DATA(m->hdr);
+
+        *family = rtm->rtm_family;
+
+        return 0;
+}
+
+int sd_rtnl_message_route_get_dst_prefixlen(sd_rtnl_message *m, unsigned char *dst_len) {
+        struct rtmsg *rtm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(dst_len, -EINVAL);
+
+        rtm = NLMSG_DATA(m->hdr);
+
+        *dst_len = rtm->rtm_dst_len;
+
+        return 0;
+}
+
+int sd_rtnl_message_route_get_src_prefixlen(sd_rtnl_message *m, unsigned char *src_len) {
+        struct rtmsg *rtm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_route(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(src_len, -EINVAL);
+
+        rtm = NLMSG_DATA(m->hdr);
+
+        *src_len = rtm->rtm_src_len;
+
+        return 0;
+}
+
 int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret,
                               uint16_t nlmsg_type, int rtm_family,
                               unsigned char rtm_protocol) {
@@ -135,7 +198,8 @@ int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret,
         int r;
 
         assert_return(rtnl_message_type_is_route(nlmsg_type), -EINVAL);
-        assert_return(rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL);
+        assert_return((nlmsg_type == RTM_GETROUTE && rtm_family == AF_UNSPEC) ||
+                      rtm_family == AF_INET || rtm_family == AF_INET6, -EINVAL);
         assert_return(ret, -EINVAL);
 
         r = message_new(rtnl, ret, nlmsg_type);
@@ -156,6 +220,59 @@ int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret,
         return 0;
 }
 
+int sd_rtnl_message_neigh_get_family(sd_rtnl_message *m, int *family) {
+        struct ndmsg *ndm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(family, -EINVAL);
+
+        ndm = NLMSG_DATA(m->hdr);
+
+        *family = ndm->ndm_family;
+
+        return 0;
+}
+
+int sd_rtnl_message_neigh_get_ifindex(sd_rtnl_message *m, int *index) {
+        struct ndmsg *ndm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL);
+        assert_return(index, -EINVAL);
+
+        ndm = NLMSG_DATA(m->hdr);
+
+        *index = ndm->ndm_ifindex;
+
+        return 0;
+}
+
+int sd_rtnl_message_new_neigh(sd_rtnl *rtnl, sd_rtnl_message **ret, uint16_t nlmsg_type, int index, int ndm_family) {
+        struct ndmsg *ndm;
+        int r;
+
+        assert_return(rtnl_message_type_is_neigh(nlmsg_type), -EINVAL);
+        assert_return(ndm_family == AF_INET || ndm_family == AF_INET6, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        r = message_new(rtnl, ret, nlmsg_type);
+        if (r < 0)
+                return r;
+
+        if (nlmsg_type == RTM_NEWNEIGH)
+                (*ret)->hdr->nlmsg_flags |= NLM_F_CREATE | NLM_F_APPEND;
+
+        ndm = NLMSG_DATA((*ret)->hdr);
+
+        ndm->ndm_family = ndm_family;
+        ndm->ndm_ifindex = index;
+
+        return 0;
+}
+
 int sd_rtnl_message_link_set_flags(sd_rtnl_message *m, unsigned flags, unsigned change) {
         struct ifinfomsg *ifi;
 
@@ -227,9 +344,10 @@ int sd_rtnl_message_new_link(sd_rtnl *rtnl, sd_rtnl_message **ret,
 int sd_rtnl_message_request_dump(sd_rtnl_message *m, int dump) {
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
-        assert_return(m->hdr->nlmsg_type == RTM_GETLINK ||
-                      m->hdr->nlmsg_type == RTM_GETADDR ||
-                      m->hdr->nlmsg_type == RTM_GETROUTE,
+        assert_return(m->hdr->nlmsg_type == RTM_GETLINK  ||
+                      m->hdr->nlmsg_type == RTM_GETADDR  ||
+                      m->hdr->nlmsg_type == RTM_GETROUTE ||
+                      m->hdr->nlmsg_type == RTM_GETNEIGH,
                       -EINVAL);
 
         if (dump)
@@ -582,8 +700,8 @@ int sd_rtnl_message_append_string(sd_rtnl_message *m, unsigned short type, const
                 size = (size_t)r;
 
         if (size) {
-                length = strnlen(data, size);
-                if (length >= size)
+                length = strnlen(data, size+1);
+                if (length > size)
                         return -EINVAL;
         } else
                 length = strlen(data);
@@ -815,6 +933,8 @@ int sd_rtnl_message_read_string(sd_rtnl_message *m, unsigned short type, const c
         int r;
         void *attr_data;
 
+        assert_return(m, -EINVAL);
+
         r = message_attribute_has_type(m, type, NLA_STRING);
         if (r < 0)
                 return r;
@@ -825,7 +945,8 @@ int sd_rtnl_message_read_string(sd_rtnl_message *m, unsigned short type, const c
         else if (strnlen(attr_data, r) >= (size_t) r)
                 return -EIO;
 
-        *data = (const char *) attr_data;
+        if (data)
+                *data = (const char *) attr_data;
 
         return 0;
 }
@@ -834,6 +955,8 @@ int sd_rtnl_message_read_u8(sd_rtnl_message *m, unsigned short type, uint8_t *da
         int r;
         void *attr_data;
 
+        assert_return(m, -EINVAL);
+
         r = message_attribute_has_type(m, type, NLA_U8);
         if (r < 0)
                 return r;
@@ -844,7 +967,8 @@ int sd_rtnl_message_read_u8(sd_rtnl_message *m, unsigned short type, uint8_t *da
         else if ((size_t) r < sizeof(uint8_t))
                 return -EIO;
 
-        *data = *(uint8_t *) attr_data;
+        if (data)
+                *data = *(uint8_t *) attr_data;
 
         return 0;
 }
@@ -853,6 +977,8 @@ int sd_rtnl_message_read_u16(sd_rtnl_message *m, unsigned short type, uint16_t *
         int r;
         void *attr_data;
 
+        assert_return(m, -EINVAL);
+
         r = message_attribute_has_type(m, type, NLA_U16);
         if (r < 0)
                 return r;
@@ -863,7 +989,8 @@ int sd_rtnl_message_read_u16(sd_rtnl_message *m, unsigned short type, uint16_t *
         else if ((size_t) r < sizeof(uint16_t))
                 return -EIO;
 
-        *data = *(uint16_t *) attr_data;
+        if (data)
+                *data = *(uint16_t *) attr_data;
 
         return 0;
 }
@@ -872,6 +999,8 @@ int sd_rtnl_message_read_u32(sd_rtnl_message *m, unsigned short type, uint32_t *
         int r;
         void *attr_data;
 
+        assert_return(m, -EINVAL);
+
         r = message_attribute_has_type(m, type, NLA_U32);
         if (r < 0)
                 return r;
@@ -882,7 +1011,8 @@ int sd_rtnl_message_read_u32(sd_rtnl_message *m, unsigned short type, uint32_t *
         else if ((size_t)r < sizeof(uint32_t))
                 return -EIO;
 
-        *data = *(uint32_t *) attr_data;
+        if (data)
+                *data = *(uint32_t *) attr_data;
 
         return 0;
 }
@@ -891,6 +1021,8 @@ int sd_rtnl_message_read_ether_addr(sd_rtnl_message *m, unsigned short type, str
         int r;
         void *attr_data;
 
+        assert_return(m, -EINVAL);
+
         r = message_attribute_has_type(m, type, NLA_ETHER_ADDR);
         if (r < 0)
                 return r;
@@ -901,7 +1033,8 @@ int sd_rtnl_message_read_ether_addr(sd_rtnl_message *m, unsigned short type, str
         else if ((size_t)r < sizeof(struct ether_addr))
                 return -EIO;
 
-        memcpy(data, attr_data, sizeof(struct ether_addr));
+        if (data)
+                memcpy(data, attr_data, sizeof(struct ether_addr));
 
         return 0;
 }
@@ -910,6 +1043,8 @@ int sd_rtnl_message_read_cache_info(sd_rtnl_message *m, unsigned short type, str
         int r;
         void *attr_data;
 
+        assert_return(m, -EINVAL);
+
         r = message_attribute_has_type(m, type, NLA_CACHE_INFO);
         if (r < 0)
                 return r;
@@ -920,7 +1055,8 @@ int sd_rtnl_message_read_cache_info(sd_rtnl_message *m, unsigned short type, str
         else if ((size_t)r < sizeof(struct ifa_cacheinfo))
                 return -EIO;
 
-        memcpy(info, attr_data, sizeof(struct ifa_cacheinfo));
+        if (info)
+                memcpy(info, attr_data, sizeof(struct ifa_cacheinfo));
 
         return 0;
 }
@@ -929,6 +1065,8 @@ int sd_rtnl_message_read_in_addr(sd_rtnl_message *m, unsigned short type, struct
         int r;
         void *attr_data;
 
+        assert_return(m, -EINVAL);
+
         r = message_attribute_has_type(m, type, NLA_IN_ADDR);
         if (r < 0)
                 return r;
@@ -939,7 +1077,8 @@ int sd_rtnl_message_read_in_addr(sd_rtnl_message *m, unsigned short type, struct
         else if ((size_t)r < sizeof(struct in_addr))
                 return -EIO;
 
-        memcpy(data, attr_data, sizeof(struct in_addr));
+        if (data)
+                memcpy(data, attr_data, sizeof(struct in_addr));
 
         return 0;
 }
@@ -948,6 +1087,8 @@ int sd_rtnl_message_read_in6_addr(sd_rtnl_message *m, unsigned short type, struc
         int r;
         void *attr_data;
 
+        assert_return(m, -EINVAL);
+
         r = message_attribute_has_type(m, type, NLA_IN_ADDR);
         if (r < 0)
                 return r;
@@ -958,7 +1099,8 @@ int sd_rtnl_message_read_in6_addr(sd_rtnl_message *m, unsigned short type, struc
         else if ((size_t)r < sizeof(struct in6_addr))
                 return -EIO;
 
-        memcpy(data, attr_data, sizeof(struct in6_addr));
+        if (data)
+                memcpy(data, attr_data, sizeof(struct in6_addr));
 
         return 0;
 }
@@ -1052,13 +1194,20 @@ uint32_t rtnl_message_get_serial(sd_rtnl_message *m) {
         return m->hdr->nlmsg_seq;
 }
 
+int sd_rtnl_message_is_error(sd_rtnl_message *m) {
+        assert_return(m, 0);
+        assert_return(m->hdr, 0);
+
+        return m->hdr->nlmsg_type == NLMSG_ERROR;
+}
+
 int sd_rtnl_message_get_errno(sd_rtnl_message *m) {
         struct nlmsgerr *err;
 
         assert_return(m, -EINVAL);
         assert_return(m->hdr, -EINVAL);
 
-        if (m->hdr->nlmsg_type != NLMSG_ERROR)
+        if (!sd_rtnl_message_is_error(m))
                 return 0;
 
         err = NLMSG_DATA(m->hdr);
@@ -1238,7 +1387,7 @@ int socket_read_message(sd_rtnl *rtnl) {
                 }
         }
 
-        for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len); new_msg = NLMSG_NEXT(new_msg, len)) {
+        for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
                 _cleanup_rtnl_message_unref_ sd_rtnl_message *m = NULL;
                 const NLType *nl_type;
 
@@ -1253,7 +1402,8 @@ int socket_read_message(sd_rtnl *rtnl) {
                 if (new_msg->nlmsg_type == NLMSG_DONE) {
                         /* finished reading multi-part message */
                         done = true;
-                        break;
+
+                        continue;
                 }
 
                 /* check that we support this message type */
@@ -1267,8 +1417,10 @@ int socket_read_message(sd_rtnl *rtnl) {
                 }
 
                 /* check that the size matches the message type */
-                if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size))
+                if (new_msg->nlmsg_len < NLMSG_LENGTH(nl_type->size)) {
+                        log_debug("sd-rtnl: message larger than expected, dropping");
                         continue;
+                }
 
                 r = message_new_empty(rtnl, &m);
                 if (r < 0)