netlink: Use NL_ATTR_FOR_EACH in nl_policy_parse().
[sliver-openvswitch.git] / lib / netlink.c
index ec35849..1e1ec61 100644 (file)
@@ -201,6 +201,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
@@ -300,6 +323,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);
+    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
@@ -510,8 +632,9 @@ 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;
+    struct nlattr *nla;
     size_t n_required;
+    size_t left;
     size_t i;
 
     n_required = 0;
@@ -526,36 +649,17 @@ nl_policy_parse(const struct ofpbuf *msg, size_t nla_offset,
         }
     }
 
-    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) {
+        size_t offset = (char*)nla - (char*)msg->data;
+        size_t len = nl_attr_get_size(nla);
+        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;
@@ -592,7 +696,10 @@ nl_policy_parse(const struct ofpbuf *msg, size_t nla_offset,
         } else {
             /* Skip attribute type that we don't care about. */
         }
-        p = (char*)p + NLA_ALIGN(nla->nla_len);
+    }
+    if (left) {
+        VLOG_DBG_RL(&rl, "attributes followed by garbage");
+        return false;
     }
     if (n_required) {
         VLOG_DBG_RL(&rl, "%zu required attrs missing", n_required);
@@ -615,7 +722,7 @@ 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;