chiark / gitweb /
Fix some format strings for enums, they are signed
[elogind.git] / src / libsystemd / sd-rtnl / rtnl-message.c
index 44ad303198a72942a40466829da479fd038ce76a..484613351d6d7de2c1267634e5d7fde8b83e311c 100644 (file)
@@ -43,7 +43,7 @@ static int message_new_empty(sd_rtnl *rtnl, sd_rtnl_message **ret) {
 
         assert_return(ret, -EINVAL);
 
-        /* Note that 'rtnl' is curretly unused, if we start using it internally
+        /* Note that 'rtnl' is currently unused, if we start using it internally
            we must take care to avoid problems due to mutual references between
            busses and their queued messages. See sd-bus.
          */
@@ -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,113 @@ int sd_rtnl_message_new_route(sd_rtnl *rtnl, sd_rtnl_message **ret,
         return 0;
 }
 
+int sd_rtnl_message_neigh_set_flags(sd_rtnl_message *m, uint8_t flags) {
+        struct ndmsg *ndm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL);
+
+        ndm = NLMSG_DATA(m->hdr);
+        ndm->ndm_flags |= flags;
+
+        return 0;
+}
+
+int sd_rtnl_message_neigh_set_state(sd_rtnl_message *m, uint16_t state) {
+        struct ndmsg *ndm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL);
+
+        ndm = NLMSG_DATA(m->hdr);
+        ndm->ndm_state |= state;
+
+        return 0;
+}
+
+int sd_rtnl_message_neigh_get_flags(sd_rtnl_message *m, uint8_t *flags) {
+        struct ndmsg *ndm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL);
+
+        ndm = NLMSG_DATA(m->hdr);
+        *flags = ndm->ndm_flags;
+
+        return 0;
+}
+
+int sd_rtnl_message_neigh_get_state(sd_rtnl_message *m, uint16_t *state) {
+        struct ndmsg *ndm;
+
+        assert_return(m, -EINVAL);
+        assert_return(m->hdr, -EINVAL);
+        assert_return(rtnl_message_type_is_neigh(m->hdr->nlmsg_type), -EINVAL);
+
+        ndm = NLMSG_DATA(m->hdr);
+        *state = ndm->ndm_state;
+
+        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 ||
+                      ndm_family == PF_BRIDGE, -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 +398,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)
@@ -414,7 +586,7 @@ sd_rtnl_message *sd_rtnl_message_ref(sd_rtnl_message *m) {
 }
 
 sd_rtnl_message *sd_rtnl_message_unref(sd_rtnl_message *m) {
-        if (m && REFCNT_DEC(m->n_ref) <= 0) {
+        if (m && REFCNT_DEC(m->n_ref) == 0) {
                 unsigned i;
 
                 free(m->hdr);
@@ -582,8 +754,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 +987,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 +999,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 +1009,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 +1021,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 +1031,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 +1043,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 +1053,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 +1065,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 +1075,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 +1087,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 +1097,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 +1109,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 +1119,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 +1131,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 +1141,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 +1153,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 +1248,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);
@@ -1145,8 +1348,10 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
                 /* no data */
                 if (errno == ENOBUFS)
                         log_debug("rtnl: kernel receive buffer overrun");
+                else if (errno == EAGAIN)
+                        log_debug("rtnl: no data in socket");
 
-                return (errno == EAGAIN) ? 0 : -errno;
+                return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
         } else if (r == 0)
                 /* connection was closed by the kernel */
                 return -ECONNRESET;
@@ -1158,8 +1363,10 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
                         struct ucred *ucred = (void *)CMSG_DATA(cmsg);
 
                         /* from the kernel */
-                        if (ucred->uid == 0 && ucred->pid == 0)
+                        if (ucred->pid == 0)
                                 auth = true;
+                        else
+                                log_debug("rtnl: ignoring message from pid %u", ucred->pid);
                 } else if (cmsg->cmsg_level == SOL_NETLINK &&
                            cmsg->cmsg_type == NETLINK_PKTINFO &&
                            cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
@@ -1170,9 +1377,17 @@ static int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool
                 }
         }
 
-        if (!auth)
+        if (!auth) {
                 /* not from the kernel, ignore */
+                if (peek) {
+                        /* drop the message */
+                        r = recvmsg(fd, &msg, 0);
+                        if (r < 0)
+                                return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
+                }
+
                 return 0;
+        }
 
         if (group)
                 *_group = group;
@@ -1238,7 +1453,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,22 +1468,25 @@ 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 */
                 r = type_system_get_type(NULL, &nl_type, new_msg->nlmsg_type);
                 if (r < 0) {
                         if (r == -ENOTSUP)
-                                log_debug("sd-rtnl: ignored message with unknown type: %u",
+                                log_debug("sd-rtnl: ignored message with unknown type: %i",
                                           new_msg->nlmsg_type);
 
                         continue;
                 }
 
                 /* 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)