X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fnetlink.c;h=bbd6b6d4136e2568a8905124cf4a7822a03c18d6;hb=0ef165ecb57943e17a8ee8270df68ffb8d032e29;hp=1e1ec61f6c1fc3d06718031acf4dc2f630c2b006;hpb=506564b8cf00ea42d3b9b818a6b7a33800f07d31;p=sliver-openvswitch.git diff --git a/lib/netlink.c b/lib/netlink.c index 1e1ec61f6..bbd6b6d41 100644 --- a/lib/netlink.c +++ b/lib/netlink.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 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,12 +16,12 @@ #include #include "netlink.h" -#include #include #include #include #include #include "coverage.h" +#include "flow.h" #include "netlink-protocol.h" #include "ofpbuf.h" #include "timeval.h" @@ -66,8 +66,8 @@ nl_msg_nlmsgerr(const struct ofpbuf *msg, int *errorp) struct nlmsgerr *err = ofpbuf_at(msg, NLMSG_HDRLEN, sizeof *err); int code = EPROTO; if (!err) { - VLOG_ERR_RL(&rl, "received invalid nlmsgerr (%zd bytes < %zd)", - msg->size, NLMSG_HDRLEN + sizeof *err); + VLOG_ERR_RL(&rl, "received invalid nlmsgerr (%"PRIu32"d bytes < %"PRIuSIZE"d)", + ofpbuf_size(msg), NLMSG_HDRLEN + sizeof *err); } else if (err->error <= 0 && err->error > INT_MIN) { code = -err->error; } @@ -88,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 @@ -121,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. */ @@ -132,14 +113,14 @@ nl_msg_put_nlmsghdr(struct ofpbuf *msg, { struct nlmsghdr *nlmsghdr; - assert(msg->size == 0); + ovs_assert(ofpbuf_size(msg) == 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; } @@ -171,7 +152,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(ofpbuf_size(msg) == NLMSG_HDRLEN); genlmsghdr = nl_msg_put_uninit(msg, GENL_HDRLEN); genlmsghdr->cmd = cmd; genlmsghdr->version = version; @@ -193,7 +174,7 @@ nl_msg_put(struct ofpbuf *msg, const void *data, size_t size) void * nl_msg_put_uninit(struct ofpbuf *msg, size_t size) { - size_t pad = NLMSG_ALIGN(size) - size; + size_t pad = PAD_SIZE(size, NLMSG_ALIGNTO); char *p = ofpbuf_put_uninit(msg, size + pad); if (pad) { memset(p + size, 0, pad); @@ -216,7 +197,7 @@ nl_msg_push(struct ofpbuf *msg, const void *data, size_t size) void * nl_msg_push_uninit(struct ofpbuf *msg, size_t size) { - size_t pad = NLMSG_ALIGN(size) - size; + size_t pad = PAD_SIZE(size, NLMSG_ALIGNTO); char *p = ofpbuf_push_uninit(msg, size + pad); if (pad) { memset(p + size, 0, pad); @@ -233,12 +214,24 @@ 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; } +/* 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 + * the first byte of data in the attribute, which is zeroed. */ +void * +nl_msg_put_unspec_zero(struct ofpbuf *msg, uint16_t type, size_t size) +{ + void *data = nl_msg_put_unspec_uninit(msg, type, size); + memset(data, 0, size); + return data; +} + /* Appends a Netlink attribute of the given 'type' and the 'size' bytes of * 'data' as its payload, to the tail end of 'msg', reallocating and copying * its data if necessary. Returns a pointer to the first byte of data in the @@ -315,6 +308,15 @@ 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 odp_port_t + * 'value' to 'msg'. */ +void +nl_msg_put_odp_port(struct ofpbuf *msg, uint16_t type, odp_port_t value) +{ + nl_msg_put_u32(msg, type, odp_to_u32(value)); +} + + /* Appends a Netlink attribute of the given 'type' and the given * null-terminated string 'value' to 'msg'. */ void @@ -332,7 +334,7 @@ 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); + ovs_assert(!nl_attr_oversized(size)); nla->nla_len = total_size; nla->nla_type = type; return nla + 1; @@ -430,7 +432,7 @@ nl_msg_push_string(struct ofpbuf *msg, uint16_t type, const char *value) size_t nl_msg_start_nested(struct ofpbuf *msg, uint16_t type) { - size_t offset = msg->size; + size_t offset = ofpbuf_size(msg); nl_msg_put_unspec(msg, type, NULL, 0); return offset; } @@ -441,7 +443,7 @@ void nl_msg_end_nested(struct ofpbuf *msg, size_t offset) { struct nlattr *attr = ofpbuf_at_assert(msg, offset, sizeof *attr); - attr->nla_len = msg->size - offset; + attr->nla_len = ofpbuf_size(msg) - offset; } /* Appends a nested Netlink attribute of the given 'type', with the 'size' @@ -457,27 +459,37 @@ nl_msg_put_nested(struct ofpbuf *msg, /* If 'buffer' begins with a valid "struct nlmsghdr", pulls the header and its * payload off 'buffer', stores header and payload in 'msg->data' and - * 'msg->size', and returns a pointer to the header. + * 'ofpbuf_size(msg)', and returns a pointer to the header. * * If 'buffer' does not begin with a "struct nlmsghdr" or begins with one that * is invalid, returns NULL without modifying 'buffer'. */ struct nlmsghdr * nl_msg_next(struct ofpbuf *buffer, struct ofpbuf *msg) { - if (buffer->size >= sizeof(struct nlmsghdr)) { + if (ofpbuf_size(buffer) >= sizeof(struct nlmsghdr)) { struct nlmsghdr *nlmsghdr = nl_msg_nlmsghdr(buffer); size_t len = nlmsghdr->nlmsg_len; - if (len >= sizeof *nlmsghdr && len <= buffer->size) { + if (len >= sizeof *nlmsghdr && len <= ofpbuf_size(buffer)) { ofpbuf_use_const(msg, nlmsghdr, len); ofpbuf_pull(buffer, len); return nlmsghdr; } } - msg->data = NULL; - msg->size = 0; + ofpbuf_set_data(msg, NULL); + ofpbuf_set_size(msg, 0); return NULL; } + +/* Returns true if a Netlink attribute with a payload that is 'payload_size' + * bytes long would be oversized, that is, if it's not possible to create an + * nlattr of that size because its size wouldn't fit in the 16-bit nla_len + * field. */ +bool +nl_attr_oversized(size_t payload_size) +{ + return NL_ATTR_SIZE(payload_size) > UINT16_MAX; +} /* Attributes. */ @@ -493,7 +505,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; } @@ -501,7 +513,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; } @@ -510,7 +522,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; } @@ -590,14 +602,23 @@ nl_attr_get_be64(const struct nlattr *nla) return get_32aligned_be64(x); } +/* Returns the 32-bit odp_port_t value in 'nla''s payload. + * + * Asserts that 'nla''s payload is at least 4 bytes long. */ +odp_port_t +nl_attr_get_odp_port(const struct nlattr *nla) +{ + return u32_to_odp(nl_attr_get_u32(nla)); +} + /* Returns the null-terminated string value in 'nla''s payload. * * Asserts that 'nla''s payload contains a null-terminated string. */ 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); } @@ -608,17 +629,86 @@ nl_attr_get_nested(const struct nlattr *nla, struct ofpbuf *nested) ofpbuf_use_const(nested, nl_attr_get(nla), nl_attr_get_size(nla)); } -/* Default minimum and maximum payload sizes for each type of attribute. */ -static const size_t attr_len_range[][2] = { - [0 ... N_NL_ATTR_TYPES - 1] = { 0, SIZE_MAX }, - [NL_A_U8] = { 1, 1 }, - [NL_A_U16] = { 2, 2 }, - [NL_A_U32] = { 4, 4 }, - [NL_A_U64] = { 8, 8 }, - [NL_A_STRING] = { 1, SIZE_MAX }, - [NL_A_FLAG] = { 0, SIZE_MAX }, - [NL_A_NESTED] = { 0, SIZE_MAX }, -}; +/* Default minimum payload size for each type of attribute. */ +static size_t +min_attr_len(enum nl_attr_type type) +{ + switch (type) { + case NL_A_NO_ATTR: return 0; + case NL_A_UNSPEC: return 0; + case NL_A_U8: return 1; + case NL_A_U16: return 2; + case NL_A_U32: return 4; + case NL_A_U64: return 8; + case NL_A_STRING: return 1; + case NL_A_FLAG: return 0; + case NL_A_NESTED: return 0; + case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED(); + } +} + +/* Default maximum payload size for each type of attribute. */ +static size_t +max_attr_len(enum nl_attr_type type) +{ + switch (type) { + case NL_A_NO_ATTR: return SIZE_MAX; + case NL_A_UNSPEC: return SIZE_MAX; + case NL_A_U8: return 1; + case NL_A_U16: return 2; + case NL_A_U32: return 4; + case NL_A_U64: return 8; + case NL_A_STRING: return SIZE_MAX; + case NL_A_FLAG: return SIZE_MAX; + case NL_A_NESTED: return SIZE_MAX; + case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED(); + } +} + +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 = min_attr_len(policy->type); + } + max_len = policy->max_len; + if (!max_len) { + max_len = max_attr_len(policy->type); + } + + /* Verify length. */ + len = nl_attr_get_size(nla); + if (len < min_len || len > max_len) { + VLOG_DBG_RL(&rl, "attr %"PRIu16" length %"PRIuSIZE" not in " + "allowed range %"PRIuSIZE"...%"PRIuSIZE, 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 @@ -633,77 +723,42 @@ nl_policy_parse(const struct ofpbuf *msg, size_t nla_offset, struct nlattr *attrs[], size_t n_attrs) { struct nlattr *nla; - size_t n_required; size_t left; size_t i; - n_required = 0; - for (i = 0; i < n_attrs; i++) { - attrs[i] = NULL; + memset(attrs, 0, n_attrs * sizeof *attrs); - 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++; - } - } - - if (msg->size < nla_offset) { + if (ofpbuf_size(msg) < nla_offset) { VLOG_DBG_RL(&rl, "missing headers in nl_policy_parse"); return false; } - 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); + NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, nla_offset, 0), + ofpbuf_size(msg) - 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. */ } } 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); - 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 %"PRIuSIZE" missing", i); + return false; + } } return true; } @@ -729,7 +784,7 @@ nl_attr_find__(const struct nlattr *attrs, size_t size, uint16_t type) 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; } } @@ -743,8 +798,7 @@ nl_attr_find__(const struct nlattr *attrs, size_t size, uint16_t type) 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, + return nl_attr_find__(ofpbuf_at(buf, hdr_len, 0), ofpbuf_size(buf) - hdr_len, type); }