Replace most uses of assert by ovs_assert.
[sliver-openvswitch.git] / lib / netlink.c
index 725bba0..c8e5905 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,7 +16,6 @@
 
 #include <config.h>
 #include "netlink.h"
-#include <assert.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <sys/types.h>
@@ -25,6 +24,7 @@
 #include "netlink-protocol.h"
 #include "ofpbuf.h"
 #include "timeval.h"
+#include "unaligned.h"
 #include "vlog.h"
 
 VLOG_DEFINE_THIS_MODULE(netlink);
@@ -87,26 +87,6 @@ nl_msg_reserve(struct ofpbuf *msg, size_t size)
     ofpbuf_prealloc_tailroom(msg, NLMSG_ALIGN(size));
 }
 
-static uint32_t
-get_nlmsg_seq(void)
-{
-    /* Next nlmsghdr sequence number.
-     *
-     * This implementation uses sequence numbers that are unique process-wide,
-     * to avoid a hypothetical race: send request, close socket, open new
-     * socket that reuses the old socket's PID value, send request on new
-     * socket, receive reply from kernel to old socket but with same PID and
-     * sequence number.  (This race could be avoided other ways, e.g. by
-     * preventing PIDs from being quickly reused). */
-    static uint32_t next_seq;
-
-    if (next_seq == 0) {
-        /* Pick initial sequence number. */
-        next_seq = getpid() ^ time_wall();
-    }
-    return next_seq++;
-}
-
 /* Puts a nlmsghdr at the beginning of 'msg', which must be initially empty.
  * Uses the given 'type' and 'flags'.  'expected_payload' should be
  * an estimate of the number of payload bytes to be supplied; if the size of
@@ -120,8 +100,9 @@ get_nlmsg_seq(void)
  * is often NLM_F_REQUEST indicating that a request is being made, commonly
  * or'd with NLM_F_ACK to request an acknowledgement.
  *
- * Sets the new nlmsghdr's nlmsg_pid field to 0 for now.  nl_sock_send() will
- * fill it in just before sending the message.
+ * Sets the new nlmsghdr's nlmsg_len, nlmsg_seq, and nlmsg_pid fields to 0 for
+ * now.  Functions that send Netlink messages will fill these in just before
+ * sending the message.
  *
  * nl_msg_put_genlmsghdr() is more convenient for composing a Generic Netlink
  * message. */
@@ -131,14 +112,14 @@ nl_msg_put_nlmsghdr(struct ofpbuf *msg,
 {
     struct nlmsghdr *nlmsghdr;
 
-    assert(msg->size == 0);
+    ovs_assert(msg->size == 0);
 
     nl_msg_reserve(msg, NLMSG_HDRLEN + expected_payload);
     nlmsghdr = nl_msg_put_uninit(msg, NLMSG_HDRLEN);
     nlmsghdr->nlmsg_len = 0;
     nlmsghdr->nlmsg_type = type;
     nlmsghdr->nlmsg_flags = flags;
-    nlmsghdr->nlmsg_seq = get_nlmsg_seq();
+    nlmsghdr->nlmsg_seq = 0;
     nlmsghdr->nlmsg_pid = 0;
 }
 
@@ -170,7 +151,7 @@ nl_msg_put_genlmsghdr(struct ofpbuf *msg, size_t expected_payload,
     struct genlmsghdr *genlmsghdr;
 
     nl_msg_put_nlmsghdr(msg, GENL_HDRLEN + expected_payload, family, flags);
-    assert(msg->size == NLMSG_HDRLEN);
+    ovs_assert(msg->size == NLMSG_HDRLEN);
     genlmsghdr = nl_msg_put_uninit(msg, GENL_HDRLEN);
     genlmsghdr->cmd = cmd;
     genlmsghdr->version = version;
@@ -200,6 +181,29 @@ nl_msg_put_uninit(struct ofpbuf *msg, size_t size)
     return p;
 }
 
+/* Prepends the 'size' bytes of data in 'p', plus Netlink padding if needed, to
+ * the head end of 'msg'.  Data in 'msg' is reallocated and copied if
+ * necessary. */
+void
+nl_msg_push(struct ofpbuf *msg, const void *data, size_t size)
+{
+    memcpy(nl_msg_push_uninit(msg, size), data, size);
+}
+
+/* Prepends 'size' bytes of data, plus Netlink padding if needed, to the head
+ * end of 'msg', reallocating and copying its data if necessary.  Returns a
+ * pointer to the first byte of the new data, which is left uninitialized. */
+void *
+nl_msg_push_uninit(struct ofpbuf *msg, size_t size)
+{
+    size_t pad = NLMSG_ALIGN(size) - size;
+    char *p = ofpbuf_push_uninit(msg, size + pad);
+    if (pad) {
+        memset(p + size, 0, pad);
+    }
+    return p;
+}
+
 /* Appends a Netlink attribute of the given 'type' and room for 'size' bytes of
  * data as its payload, plus Netlink padding if needed, to the tail end of
  * 'msg', reallocating and copying its data if necessary.  Returns a pointer to
@@ -209,7 +213,7 @@ nl_msg_put_unspec_uninit(struct ofpbuf *msg, uint16_t type, size_t size)
 {
     size_t total_size = NLA_HDRLEN + size;
     struct nlattr* nla = nl_msg_put_uninit(msg, total_size);
-    assert(NLA_ALIGN(total_size) <= UINT16_MAX);
+    ovs_assert(NLA_ALIGN(total_size) <= UINT16_MAX);
     nla->nla_len = total_size;
     nla->nla_type = type;
     return nla + 1;
@@ -299,6 +303,105 @@ nl_msg_put_string(struct ofpbuf *msg, uint16_t type, const char *value)
     nl_msg_put_unspec(msg, type, value, strlen(value) + 1);
 }
 
+/* Prepends a Netlink attribute of the given 'type' and room for 'size' bytes
+ * of data as its payload, plus Netlink padding if needed, to the head end of
+ * 'msg', reallocating and copying its data if necessary.  Returns a pointer to
+ * the first byte of data in the attribute, which is left uninitialized. */
+void *
+nl_msg_push_unspec_uninit(struct ofpbuf *msg, uint16_t type, size_t size)
+{
+    size_t total_size = NLA_HDRLEN + size;
+    struct nlattr* nla = nl_msg_push_uninit(msg, total_size);
+    ovs_assert(NLA_ALIGN(total_size) <= UINT16_MAX);
+    nla->nla_len = total_size;
+    nla->nla_type = type;
+    return nla + 1;
+}
+
+/* Prepends a Netlink attribute of the given 'type' and the 'size' bytes of
+ * 'data' as its payload, to the head end of 'msg', reallocating and copying
+ * its data if necessary.  Returns a pointer to the first byte of data in the
+ * attribute, which is left uninitialized. */
+void
+nl_msg_push_unspec(struct ofpbuf *msg, uint16_t type,
+                  const void *data, size_t size)
+{
+    memcpy(nl_msg_push_unspec_uninit(msg, type, size), data, size);
+}
+
+/* Prepends a Netlink attribute of the given 'type' and no payload to 'msg'.
+ * (Some Netlink protocols use the presence or absence of an attribute as a
+ * Boolean flag.) */
+void
+nl_msg_push_flag(struct ofpbuf *msg, uint16_t type)
+{
+    nl_msg_push_unspec(msg, type, NULL, 0);
+}
+
+/* Prepends a Netlink attribute of the given 'type' and the given 8-bit 'value'
+ * to 'msg'. */
+void
+nl_msg_push_u8(struct ofpbuf *msg, uint16_t type, uint8_t value)
+{
+    nl_msg_push_unspec(msg, type, &value, sizeof value);
+}
+
+/* Prepends a Netlink attribute of the given 'type' and the given 16-bit host
+ * byte order 'value' to 'msg'. */
+void
+nl_msg_push_u16(struct ofpbuf *msg, uint16_t type, uint16_t value)
+{
+    nl_msg_push_unspec(msg, type, &value, sizeof value);
+}
+
+/* Prepends a Netlink attribute of the given 'type' and the given 32-bit host
+ * byte order 'value' to 'msg'. */
+void
+nl_msg_push_u32(struct ofpbuf *msg, uint16_t type, uint32_t value)
+{
+    nl_msg_push_unspec(msg, type, &value, sizeof value);
+}
+
+/* Prepends a Netlink attribute of the given 'type' and the given 64-bit host
+ * byte order 'value' to 'msg'. */
+void
+nl_msg_push_u64(struct ofpbuf *msg, uint16_t type, uint64_t value)
+{
+    nl_msg_push_unspec(msg, type, &value, sizeof value);
+}
+
+/* Prepends a Netlink attribute of the given 'type' and the given 16-bit
+ * network byte order 'value' to 'msg'. */
+void
+nl_msg_push_be16(struct ofpbuf *msg, uint16_t type, ovs_be16 value)
+{
+    nl_msg_push_unspec(msg, type, &value, sizeof value);
+}
+
+/* Prepends a Netlink attribute of the given 'type' and the given 32-bit
+ * network byte order 'value' to 'msg'. */
+void
+nl_msg_push_be32(struct ofpbuf *msg, uint16_t type, ovs_be32 value)
+{
+    nl_msg_push_unspec(msg, type, &value, sizeof value);
+}
+
+/* Prepends a Netlink attribute of the given 'type' and the given 64-bit
+ * network byte order 'value' to 'msg'. */
+void
+nl_msg_push_be64(struct ofpbuf *msg, uint16_t type, ovs_be64 value)
+{
+    nl_msg_push_unspec(msg, type, &value, sizeof value);
+}
+
+/* Prepends a Netlink attribute of the given 'type' and the given
+ * null-terminated string 'value' to 'msg'. */
+void
+nl_msg_push_string(struct ofpbuf *msg, uint16_t type, const char *value)
+{
+    nl_msg_push_unspec(msg, type, value, strlen(value) + 1);
+}
+
 /* Adds the header for nested Netlink attributes to 'msg', with the specified
  * 'type', and returns the header's offset within 'msg'.  The caller should add
  * the content for the nested Netlink attribute to 'msg' (e.g. using the other
@@ -370,7 +473,7 @@ nl_attr_type(const struct nlattr *nla)
 const void *
 nl_attr_get(const struct nlattr *nla)
 {
-    assert(nla->nla_len >= NLA_HDRLEN);
+    ovs_assert(nla->nla_len >= NLA_HDRLEN);
     return nla + 1;
 }
 
@@ -378,7 +481,7 @@ nl_attr_get(const struct nlattr *nla)
 size_t
 nl_attr_get_size(const struct nlattr *nla)
 {
-    assert(nla->nla_len >= NLA_HDRLEN);
+    ovs_assert(nla->nla_len >= NLA_HDRLEN);
     return nla->nla_len - NLA_HDRLEN;
 }
 
@@ -387,7 +490,7 @@ nl_attr_get_size(const struct nlattr *nla)
 const void *
 nl_attr_get_unspec(const struct nlattr *nla, size_t size)
 {
-    assert(nla->nla_len >= NLA_HDRLEN + size);
+    ovs_assert(nla->nla_len >= NLA_HDRLEN + size);
     return nla + 1;
 }
 
@@ -435,7 +538,8 @@ nl_attr_get_u32(const struct nlattr *nla)
 uint64_t
 nl_attr_get_u64(const struct nlattr *nla)
 {
-    return NL_ATTR_GET_AS(nla, uint64_t);
+    const ovs_32aligned_u64 *x = nl_attr_get_unspec(nla, sizeof *x);
+    return get_32aligned_u64(x);
 }
 
 /* Returns the 16-bit network byte order value in 'nla''s payload.
@@ -462,7 +566,8 @@ nl_attr_get_be32(const struct nlattr *nla)
 ovs_be64
 nl_attr_get_be64(const struct nlattr *nla)
 {
-    return NL_ATTR_GET_AS(nla, ovs_be64);
+    const ovs_32aligned_be64 *x = nl_attr_get_unspec(nla, sizeof *x);
+    return get_32aligned_be64(x);
 }
 
 /* Returns the null-terminated string value in 'nla''s payload.
@@ -471,8 +576,8 @@ nl_attr_get_be64(const struct nlattr *nla)
 const char *
 nl_attr_get_string(const struct nlattr *nla)
 {
-    assert(nla->nla_len > NLA_HDRLEN);
-    assert(memchr(nl_attr_get(nla), '\0', nla->nla_len - NLA_HDRLEN) != NULL);
+    ovs_assert(nla->nla_len > NLA_HDRLEN);
+    ovs_assert(memchr(nl_attr_get(nla), '\0', nla->nla_len - NLA_HDRLEN));
     return nl_attr_get(nla);
 }
 
@@ -495,6 +600,51 @@ static const size_t attr_len_range[][2] = {
     [NL_A_NESTED] = { 0, SIZE_MAX },
 };
 
+bool
+nl_attr_validate(const struct nlattr *nla, const struct nl_policy *policy)
+{
+    uint16_t type = nl_attr_type(nla);
+    size_t min_len;
+    size_t max_len;
+    size_t len;
+
+    if (policy->type == NL_A_NO_ATTR) {
+        return true;
+    }
+
+    /* Figure out min and max length. */
+    min_len = policy->min_len;
+    if (!min_len) {
+        min_len = attr_len_range[policy->type][0];
+    }
+    max_len = policy->max_len;
+    if (!max_len) {
+        max_len = attr_len_range[policy->type][1];
+    }
+
+    /* Verify length. */
+    len = nl_attr_get_size(nla);
+    if (len < min_len || len > max_len) {
+        VLOG_DBG_RL(&rl, "attr %"PRIu16" length %zu not in "
+                    "allowed range %zu...%zu", type, len, min_len, max_len);
+        return false;
+    }
+
+    /* Strings must be null terminated and must not have embedded nulls. */
+    if (policy->type == NL_A_STRING) {
+        if (((char *) nla)[nla->nla_len - 1]) {
+            VLOG_DBG_RL(&rl, "attr %"PRIu16" lacks null at end", type);
+            return false;
+        }
+        if (memchr(nla + 1, '\0', len - 1) != NULL) {
+            VLOG_DBG_RL(&rl, "attr %"PRIu16" has bad length", type);
+            return false;
+        }
+    }
+
+    return true;
+}
+
 /* Parses the 'msg' starting at the given 'nla_offset' as a sequence of Netlink
  * attributes.  'policy[i]', for 0 <= i < n_attrs, specifies how the attribute
  * with nla_type == i is parsed; a pointer to attribute i is stored in
@@ -507,94 +657,45 @@ nl_policy_parse(const struct ofpbuf *msg, size_t nla_offset,
                 const struct nl_policy policy[],
                 struct nlattr *attrs[], size_t n_attrs)
 {
-    void *p, *tail;
-    size_t n_required;
+    struct nlattr *nla;
+    size_t left;
     size_t i;
 
-    n_required = 0;
-    for (i = 0; i < n_attrs; i++) {
-        attrs[i] = NULL;
-
-        assert(policy[i].type < N_NL_ATTR_TYPES);
-        if (policy[i].type != NL_A_NO_ATTR
-            && policy[i].type != NL_A_FLAG
-            && !policy[i].optional) {
-            n_required++;
-        }
-    }
+    memset(attrs, 0, n_attrs * sizeof *attrs);
 
-    p = ofpbuf_at(msg, nla_offset, 0);
-    if (p == NULL) {
+    if (msg->size < nla_offset) {
         VLOG_DBG_RL(&rl, "missing headers in nl_policy_parse");
         return false;
     }
-    tail = ofpbuf_tail(msg);
-
-    while (p < tail) {
-        size_t offset = (char*)p - (char*)msg->data;
-        struct nlattr *nla = p;
-        size_t len, aligned_len;
-        uint16_t type;
-
-        /* Make sure its claimed length is plausible. */
-        if (nla->nla_len < NLA_HDRLEN) {
-            VLOG_DBG_RL(&rl, "%zu: attr shorter than NLA_HDRLEN (%"PRIu16")",
-                        offset, nla->nla_len);
-            return false;
-        }
-        len = nla->nla_len - NLA_HDRLEN;
-        aligned_len = NLA_ALIGN(len);
-        if (aligned_len > (char*)tail - (char*)p) {
-            VLOG_DBG_RL(&rl, "%zu: attr %"PRIu16" aligned data len (%zu) "
-                        "> bytes left (%tu)",
-                        offset, nl_attr_type(nla), aligned_len,
-                        (char*)tail - (char*)p);
-            return false;
-        }
 
-        type = nl_attr_type(nla);
+    NL_ATTR_FOR_EACH (nla, left,
+                      (struct nlattr *) ((char *) msg->data + nla_offset),
+                      msg->size - nla_offset)
+    {
+        uint16_t type = nl_attr_type(nla);
         if (type < n_attrs && policy[type].type != NL_A_NO_ATTR) {
             const struct nl_policy *e = &policy[type];
-            size_t min_len, max_len;
-
-            /* Validate length and content. */
-            min_len = e->min_len ? e->min_len : attr_len_range[e->type][0];
-            max_len = e->max_len ? e->max_len : attr_len_range[e->type][1];
-            if (len < min_len || len > max_len) {
-                VLOG_DBG_RL(&rl, "%zu: attr %"PRIu16" length %zu not in "
-                            "allowed range %zu...%zu",
-                            offset, type, len, min_len, max_len);
+            if (!nl_attr_validate(nla, e)) {
                 return false;
             }
-            if (e->type == NL_A_STRING) {
-                if (((char *) nla)[nla->nla_len - 1]) {
-                    VLOG_DBG_RL(&rl, "%zu: attr %"PRIu16" lacks null at end",
-                                offset, type);
-                    return false;
-                }
-                if (memchr(nla + 1, '\0', len - 1) != NULL) {
-                    VLOG_DBG_RL(&rl, "%zu: attr %"PRIu16" has bad length",
-                                offset, type);
-                    return false;
-                }
-            }
-            if (!e->optional && attrs[type] == NULL) {
-                assert(n_required > 0);
-                --n_required;
-            }
             if (attrs[type]) {
-                VLOG_DBG_RL(&rl, "%zu: duplicate attr %"PRIu16, offset, type);
+                VLOG_DBG_RL(&rl, "duplicate attr %"PRIu16, type);
             }
             attrs[type] = nla;
-        } else {
-            /* Skip attribute type that we don't care about. */
         }
-        p = (char*)p + NLA_ALIGN(nla->nla_len);
     }
-    if (n_required) {
-        VLOG_DBG_RL(&rl, "%zu required attrs missing", n_required);
+    if (left) {
+        VLOG_DBG_RL(&rl, "attributes followed by garbage");
         return false;
     }
+
+    for (i = 0; i < n_attrs; i++) {
+        const struct nl_policy *e = &policy[i];
+        if (!e->optional && e->type != NL_A_NO_ATTR && !attrs[i]) {
+            VLOG_DBG_RL(&rl, "required attr %zu missing", i);
+            return false;
+        }
+    }
     return true;
 }
 
@@ -612,14 +713,14 @@ nl_parse_nested(const struct nlattr *nla, const struct nl_policy policy[],
     return nl_policy_parse(&buf, 0, policy, attrs, n_attrs);
 }
 
-static const struct nlattr *
+const struct nlattr *
 nl_attr_find__(const struct nlattr *attrs, size_t size, uint16_t type)
 {
     const struct nlattr *nla;
     size_t left;
 
     NL_ATTR_FOR_EACH (nla, left, attrs, size) {
-        if (nl_attr_type (nla) == type) {
+        if (nl_attr_type(nla) == type) {
             return nla;
         }
     }