From: Simon Horman Date: Mon, 5 Nov 2012 06:04:55 +0000 (-0800) Subject: ofp-util: Add version bitmap support to hello messages. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=openvswitch;a=commitdiff_plain;h=de6c85b0a2e61105d288c23b718f6599761c2a2e ofp-util: Add version bitmap support to hello messages. Allow encoding and decoding of version bitmap in hello messages as specified in Open Flow 1.3.1. Signed-off-by: Simon Horman [blp@nicira.com simplified and generalized decode/encode functions] Signed-off-by: Ben Pfaff --- diff --git a/include/openflow/openflow-common.h b/include/openflow/openflow-common.h index 0bca0d26..462b2fcf 100644 --- a/include/openflow/openflow-common.h +++ b/include/openflow/openflow-common.h @@ -334,4 +334,15 @@ enum ofp_group { OFPG_ANY = 0xffffffff /* Wildcard, for flow stats requests. */ }; +enum ofp_hello_elem_type { + OFPHET_VERSIONBITMAP = 1, /* Bitmap of version supported. */ +}; + +/* Common header for all Hello Elements */ +struct ofp_hello_elem_header { + ovs_be16 type; /* One of OFPHET_*. */ + ovs_be16 length; /* Length in bytes of this element. */ +}; +OFP_ASSERT(sizeof(struct ofp_hello_elem_header) == 4); + #endif /* openflow/openflow-common.h */ diff --git a/lib/ofp-print.c b/lib/ofp-print.c index cafc665d..c1b50e2c 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -903,6 +903,23 @@ ofp_print_error(struct ds *string, enum ofperr error) ds_put_format(string, "***decode error: %s***\n", ofperr_get_name(error)); } +static void +ofp_print_hello(struct ds *string, const struct ofp_header *oh) +{ + uint32_t allowed_versions; + bool ok; + + ok = ofputil_decode_hello(oh, &allowed_versions); + + ds_put_cstr(string, "\n version bitmap: "); + ofputil_format_version_bitmap(string, allowed_versions); + + if (!ok) { + ds_put_cstr(string, "\n unknown data in hello:\n"); + ds_put_hex_dump(string, oh, ntohs(oh->length), 0, true); + } +} + static void ofp_print_error_msg(struct ds *string, const struct ofp_header *oh) { @@ -1729,9 +1746,7 @@ ofp_to_string__(const struct ofp_header *oh, enum ofpraw raw, ofp_header_to_string__(oh, raw, string); switch (ofptype_from_ofpraw(raw)) { case OFPTYPE_HELLO: - ds_put_char(string, '\n'); - ds_put_hex_dump(string, oh + 1, ntohs(oh->length) - sizeof *oh, - 0, true); + ofp_print_hello(string, oh); break; case OFPTYPE_ERROR: diff --git a/lib/ofp-util.c b/lib/ofp-util.c index afb7dcdf..146e538a 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -1078,6 +1078,119 @@ ofputil_format_version_bitmap_names(struct ds *msg, uint32_t bitmap) ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version_name); } +static bool +ofputil_decode_hello_bitmap(const struct ofp_hello_elem_header *oheh, + uint32_t *allowed_versions) +{ + uint16_t bitmap_len = ntohs(oheh->length) - sizeof *oheh; + const ovs_be32 *bitmap = (const ovs_be32 *) (oheh + 1); + + if (!bitmap_len || bitmap_len % sizeof *bitmap) { + return false; + } + + /* Only use the first 32-bit element of the bitmap as that is all the + * current implementation supports. Subsequent elements are ignored which + * should have no effect on session negotiation until Open vSwtich supports + * wire-protocol versions greater than 31. + */ + *allowed_versions = ntohl(bitmap[0]); + + if (*allowed_versions & 1) { + /* There's no OpenFlow version 0. */ + VLOG_WARN_RL(&bad_ofmsg_rl, "peer claims to support invalid OpenFlow " + "version 0x00"); + *allowed_versions &= ~1u; + } + + if (!*allowed_versions) { + VLOG_WARN_RL(&bad_ofmsg_rl, "peer does not support any OpenFlow " + "version (between 0x01 and 0x1f)"); + return false; + } + + return true; +} + +static uint32_t +version_bitmap_from_version(uint8_t ofp_version) +{ + return ((ofp_version < 32 ? 1u << ofp_version : 0) - 1) << 1; +} + +/* Decodes OpenFlow OFPT_HELLO message 'oh', storing into '*allowed_versions' + * the set of OpenFlow versions for which 'oh' announces support. + * + * Because of how OpenFlow defines OFPT_HELLO messages, this function is always + * successful, and thus '*allowed_versions' is always initialized. However, it + * returns false if 'oh' contains some data that could not be fully understood, + * true if 'oh' was completely parsed. */ +bool +ofputil_decode_hello(const struct ofp_header *oh, uint32_t *allowed_versions) +{ + struct ofpbuf msg; + bool ok = true; + + ofpbuf_use_const(&msg, oh, ntohs(oh->length)); + ofpbuf_pull(&msg, sizeof *oh); + + *allowed_versions = version_bitmap_from_version(oh->version); + while (msg.size) { + const struct ofp_hello_elem_header *oheh; + unsigned int len; + + if (msg.size < sizeof *oheh) { + return false; + } + + oheh = msg.data; + len = ntohs(oheh->length); + if (len < sizeof *oheh || !ofpbuf_try_pull(&msg, ROUND_UP(len, 8))) { + return false; + } + + if (oheh->type != htons(OFPHET_VERSIONBITMAP) + || !ofputil_decode_hello_bitmap(oheh, allowed_versions)) { + ok = false; + } + } + + return ok; +} + +/* Returns true if 'allowed_versions' needs to be accompanied by a version + * bitmap to be correctly expressed in an OFPT_HELLO message. */ +static inline bool +should_send_version_bitmap(uint32_t allowed_versions) +{ + return !is_pow2((allowed_versions >> 1) + 1); +} + +/* Create an OFPT_HELLO message that expresses support for the OpenFlow + * versions in the 'allowed_versions' bitmaps and returns the message. */ +struct ofpbuf * +ofputil_encode_hello(uint32_t allowed_versions) +{ + enum ofp_version ofp_version; + struct ofpbuf *msg; + + ofp_version = leftmost_1bit_idx(allowed_versions); + msg = ofpraw_alloc(OFPRAW_OFPT_HELLO, ofp_version, 0); + + if (should_send_version_bitmap(allowed_versions)) { + struct ofp_hello_elem_header *oheh; + uint16_t map_len; + + map_len = sizeof(uint32_t) / CHAR_BIT; + oheh = ofpbuf_put_zeros(msg, ROUND_UP(map_len + sizeof *oheh, 8)); + oheh->type = htons(OFPHET_VERSIONBITMAP); + oheh->length = htons(map_len + sizeof *oheh); + *(ovs_be32 *)(oheh + 1) = htonl(allowed_versions); + } + + return msg; +} + /* Returns an OpenFlow message that, sent on an OpenFlow connection whose * protocol is 'current', at least partly transitions the protocol to 'want'. * Stores in '*next' the protocol that will be in effect on the OpenFlow diff --git a/lib/ofp-util.h b/lib/ofp-util.h index ad9f2bbb..4bd5a001 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -124,6 +124,10 @@ enum ofputil_protocol ofputil_protocols_from_string(const char *s); const char *ofputil_version_to_string(enum ofp_version ofp_version); uint32_t ofputil_versions_from_string(const char *s); +bool ofputil_decode_hello(const struct ofp_header *, + uint32_t *allowed_versions); +struct ofpbuf *ofputil_encode_hello(uint32_t version_bitmap); + struct ofpbuf *ofputil_encode_set_protocol(enum ofputil_protocol current, enum ofputil_protocol want, enum ofputil_protocol *next); diff --git a/lib/vconn.c b/lib/vconn.c index 24a369d8..6c77d060 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -406,8 +406,7 @@ vcs_send_hello(struct vconn *vconn) struct ofpbuf *b; int retval; - b = ofpraw_alloc(OFPRAW_OFPT_HELLO, - leftmost_1bit_idx(vconn->allowed_versions), 0); + b = ofputil_encode_hello(vconn->allowed_versions); retval = do_send(vconn, b); if (!retval) { vconn->state = VCS_RECV_HELLO; @@ -450,7 +449,6 @@ vcs_recv_hello(struct vconn *vconn) retval = do_recv(vconn, &b); if (!retval) { - const struct ofp_header *oh = b->data; enum ofptype type; enum ofperr error; @@ -459,9 +457,10 @@ vcs_recv_hello(struct vconn *vconn) char *peer_s, *local_s; uint32_t common_versions; - if (b->size > sizeof *oh) { + if (!ofputil_decode_hello(b->data, &vconn->peer_versions)) { struct ds msg = DS_EMPTY_INITIALIZER; - ds_put_format(&msg, "%s: extra-long hello:\n", vconn->name); + ds_put_format(&msg, "%s: unknown data in hello:\n", + vconn->name); ds_put_hex_dump(&msg, b->data, b->size, 0, true); VLOG_WARN_RL(&bad_ofmsg_rl, "%s", ds_cstr(&msg)); ds_destroy(&msg); diff --git a/tests/ofp-print.at b/tests/ofp-print.at index db972a9e..980b57dc 100644 --- a/tests/ofp-print.at +++ b/tests/ofp-print.at @@ -45,6 +45,7 @@ AT_SETUP([OFPT_HELLO - ordinary]) AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print 0100000800000000], [0], [dnl OFPT_HELLO (xid=0x0): + version bitmap: 0x01 ]) AT_CLEANUP @@ -53,7 +54,33 @@ AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print 0100001300000000657874726120646174610a], [0], [dnl OFPT_HELLO (xid=0x0): -00000000 65 78 74 72 61 20 64 61-74 61 0a |extra data. | + version bitmap: 0x01 + unknown data in hello: +00000000 01 00 00 13 00 00 00 00-65 78 74 72 61 20 64 61 |........extra da| +00000010 74 61 0a |ta. | +]) +AT_CLEANUP + +AT_SETUP([OFPT_HELLO with version bitmap]) +AT_KEYWORDS([ofp-print]) +AT_CHECK([ovs-ofctl ofp-print "01 00 00 10 00 00 00 00 00 01 00 08 00 00 00 f0"], [0], +[dnl +OFPT_HELLO (xid=0x0): + version bitmap: 0x04, 0x05, 0x06, 0x07 +]) +AT_CLEANUP + +AT_SETUP([OFPT_HELLO with version bitmap and extra data]) +AT_KEYWORDS([ofp-print]) +AT_CHECK([ovs-ofctl ofp-print "\ +01 00 00 1b 00 00 00 00 ff ff 00 06 01 02 00 00 \ +00 01 00 08 00 00 00 f0 61 62 63"], [0], +[dnl +OFPT_HELLO (xid=0x0): + version bitmap: 0x04, 0x05, 0x06, 0x07 + unknown data in hello: +00000000 01 00 00 1b 00 00 00 00-ff ff 00 06 01 02 00 00 |................| +00000010 00 01 00 08 00 00 00 f0-61 62 63 |........abc | ]) AT_CLEANUP