X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetlink.c;h=5414239c794ecf4254f0830a7c2c62943fc1231d;hb=8d0abb5ef553bc4a43f0df7f7398142b9600294b;hp=39d703aed6bbb399287383eae04d3510ab5eca1b;hpb=2fe27d5ad27f3c7879ea696209bcf9702d9b7109;p=sliver-openvswitch.git diff --git a/lib/netlink.c b/lib/netlink.c index 39d703aed..5414239c7 100644 --- a/lib/netlink.c +++ b/lib/netlink.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010 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. @@ -25,6 +25,7 @@ #include "netlink-protocol.h" #include "ofpbuf.h" #include "timeval.h" +#include "unaligned.h" #include "vlog.h" VLOG_DEFINE_THIS_MODULE(netlink); @@ -87,26 +88,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 +101,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. */ @@ -138,7 +120,7 @@ nl_msg_put_nlmsghdr(struct ofpbuf *msg, 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; } @@ -200,6 +182,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 @@ -243,30 +248,54 @@ nl_msg_put_u8(struct ofpbuf *msg, uint16_t type, uint8_t value) nl_msg_put_unspec(msg, type, &value, sizeof value); } -/* Appends a Netlink attribute of the given 'type' and the given 16-bit 'value' - * to 'msg'. */ +/* Appends a Netlink attribute of the given 'type' and the given 16-bit host + * byte order 'value' to 'msg'. */ void nl_msg_put_u16(struct ofpbuf *msg, uint16_t type, uint16_t value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } -/* Appends a Netlink attribute of the given 'type' and the given 32-bit 'value' - * to 'msg'. */ +/* Appends a Netlink attribute of the given 'type' and the given 32-bit host + * byte order 'value' to 'msg'. */ void nl_msg_put_u32(struct ofpbuf *msg, uint16_t type, uint32_t value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } -/* Appends a Netlink attribute of the given 'type' and the given 64-bit 'value' - * to 'msg'. */ +/* Appends a Netlink attribute of the given 'type' and the given 64-bit host + * byte order 'value' to 'msg'. */ void nl_msg_put_u64(struct ofpbuf *msg, uint16_t type, uint64_t value) { nl_msg_put_unspec(msg, type, &value, sizeof value); } +/* Appends a Netlink attribute of the given 'type' and the given 16-bit network + * byte order 'value' to 'msg'. */ +void +nl_msg_put_be16(struct ofpbuf *msg, uint16_t type, ovs_be16 value) +{ + nl_msg_put_unspec(msg, type, &value, sizeof value); +} + +/* Appends a Netlink attribute of the given 'type' and the given 32-bit network + * byte order 'value' to 'msg'. */ +void +nl_msg_put_be32(struct ofpbuf *msg, uint16_t type, ovs_be32 value) +{ + nl_msg_put_unspec(msg, type, &value, sizeof value); +} + +/* Appends a Netlink attribute of the given 'type' and the given 64-bit network + * byte order 'value' to 'msg'. */ +void +nl_msg_put_be64(struct ofpbuf *msg, uint16_t type, ovs_be64 value) +{ + nl_msg_put_unspec(msg, type, &value, sizeof value); +} + /* Appends a Netlink attribute of the given 'type' and the given * null-terminated string 'value' to 'msg'. */ void @@ -275,6 +304,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 @@ -334,6 +462,14 @@ nl_msg_next(struct ofpbuf *buffer, struct ofpbuf *msg) /* Attributes. */ +/* Returns the bits of 'nla->nla_type' that are significant for determining its + * type. */ +int +nl_attr_type(const struct nlattr *nla) +{ + return nla->nla_type & NLA_TYPE_MASK; +} + /* Returns the first byte in the payload of attribute 'nla'. */ const void * nl_attr_get(const struct nlattr *nla) @@ -379,7 +515,7 @@ nl_attr_get_u8(const struct nlattr *nla) return NL_ATTR_GET_AS(nla, uint8_t); } -/* Returns the 16-bit value in 'nla''s payload. +/* Returns the 16-bit host byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 2 bytes long. */ uint16_t @@ -388,7 +524,7 @@ nl_attr_get_u16(const struct nlattr *nla) return NL_ATTR_GET_AS(nla, uint16_t); } -/* Returns the 32-bit value in 'nla''s payload. +/* Returns the 32-bit host byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 4 bytes long. */ uint32_t @@ -397,13 +533,42 @@ nl_attr_get_u32(const struct nlattr *nla) return NL_ATTR_GET_AS(nla, uint32_t); } -/* Returns the 64-bit value in 'nla''s payload. +/* Returns the 64-bit host byte order value in 'nla''s payload. * * Asserts that 'nla''s payload is at least 8 bytes long. */ 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. + * + * Asserts that 'nla''s payload is at least 2 bytes long. */ +ovs_be16 +nl_attr_get_be16(const struct nlattr *nla) +{ + return NL_ATTR_GET_AS(nla, ovs_be16); +} + +/* Returns the 32-bit network byte order value in 'nla''s payload. + * + * Asserts that 'nla''s payload is at least 4 bytes long. */ +ovs_be32 +nl_attr_get_be32(const struct nlattr *nla) +{ + return NL_ATTR_GET_AS(nla, ovs_be32); +} + +/* Returns the 64-bit network byte order value in 'nla''s payload. + * + * Asserts that 'nla''s payload is at least 8 bytes long. */ +ovs_be64 +nl_attr_get_be64(const struct nlattr *nla) +{ + 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. @@ -436,6 +601,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 @@ -448,91 +658,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, nla->nla_type, aligned_len, - (char*)tail - (char*)p); - return false; - } - type = nla->nla_type; + 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, "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; } @@ -549,3 +713,39 @@ nl_parse_nested(const struct nlattr *nla, const struct nl_policy policy[], nl_attr_get_nested(nla, &buf); return nl_policy_parse(&buf, 0, policy, attrs, n_attrs); } + +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) { + return nla; + } + } + return NULL; +} + +/* Returns the first Netlink attribute within 'buf' with the specified 'type', + * skipping a header of 'hdr_len' bytes at the beginning of 'buf'. + * + * This function does not validate the attribute's length. */ +const struct nlattr * +nl_attr_find(const struct ofpbuf *buf, size_t hdr_len, uint16_t type) +{ + const uint8_t *start = (const uint8_t *) buf->data + hdr_len; + return nl_attr_find__((const struct nlattr *) start, buf->size - hdr_len, + type); +} + +/* Returns the first Netlink attribute within 'nla' with the specified + * 'type'. + * + * This function does not validate the attribute's length. */ +const struct nlattr * +nl_attr_find_nested(const struct nlattr *nla, uint16_t type) +{ + return nl_attr_find__(nl_attr_get(nla), nl_attr_get_size(nla), type); +}