discover/network: Allow for arbitrary-sized netlink messages
authorJeremy Kerr <jk@ozlabs.org>
Fri, 25 Oct 2013 06:34:30 +0000 (14:34 +0800)
committerJeremy Kerr <jk@ozlabs.org>
Fri, 1 Nov 2013 07:31:59 +0000 (15:31 +0800)
Currently, we drop any netlink data beyond our 4096-byte buffer. This
means that we can only parse a limited number of network interfaces.

This change uses recvmsg with MSG_PEEK to determine the size of an
incoming netlink message before doing the actual recvmsg. This way, we
can realloc our buffer to suit.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
discover/network.c

index edb7358b5c36618cc6ee46d94cb7e1fb2f789483..d39f7a7c0ef57af83ae39b26e4bcb10acf4b26dd 100644 (file)
@@ -25,6 +25,7 @@
 
 #define HWADDR_SIZE    6
 #define PIDFILE_BASE   (LOCAL_STATE_DIR "/petitboot/")
+#define INITIAL_BUFSIZE        4096
 
 #define for_each_nlmsg(buf, nlmsg, len) \
        for (nlmsg = (struct nlmsghdr *)buf; \
@@ -58,6 +59,8 @@ struct network {
        struct device_handler   *handler;
        struct waiter           *waiter;
        int                     netlink_sd;
+       void                    *netlink_buf;
+       unsigned int            netlink_buf_size;
        bool                    manual_config;
        bool                    dry_run;
 };
@@ -116,6 +119,10 @@ static int network_init_netlink(struct network *network)
                return -1;
        }
 
+       network->netlink_buf_size = INITIAL_BUFSIZE;
+       network->netlink_buf = talloc_array(network, char,
+                               network->netlink_buf_size);
+
        return 0;
 }
 
@@ -426,19 +433,48 @@ static int network_netlink_process(void *arg)
 {
        struct network *network = arg;
        struct nlmsghdr *nlmsg;
+       struct msghdr msg;
+       struct iovec iov;
        unsigned int len;
-       char buf[4096];
-       int rc;
+       int rc, flags;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = &iov;
+       msg.msg_iovlen = 1;
+
+       flags = MSG_PEEK;
+
+retry:
+       iov.iov_len = network->netlink_buf_size;
+       iov.iov_base = network->netlink_buf;
+
+       rc = recvmsg(network->netlink_sd, &msg, flags);
 
-       rc = recv(network->netlink_sd, buf, sizeof(buf), 0);
        if (rc < 0) {
-               perror("netlink recv");
+               perror("netlink recv header");
                return -1;
        }
 
        len = rc;
 
-       for_each_nlmsg(buf, nlmsg, len)
+       /* if the netlink message was larger than our buffer, realloc
+        * before reading again */
+       if (len > network->netlink_buf_size || msg.msg_flags & MSG_TRUNC) {
+               network->netlink_buf_size *= 2;
+               network->netlink_buf = talloc_realloc(network,
+                                       network->netlink_buf,
+                                       char *,
+                                       network->netlink_buf_size);
+               goto retry;
+       }
+
+       /* otherwise, we're good to read the entire message without PEEK */
+       if (flags == MSG_PEEK) {
+               flags = 0;
+               goto retry;
+       }
+
+       for_each_nlmsg(network->netlink_buf, nlmsg, len)
                network_handle_nlmsg(network, nlmsg);
 
        return 0;