Allow encoding and decoding of version bitmap in hello messages as specified in Open Flow 1.3.1.
Signed-off-by: Simon Horman <ho...@verge.net.au> --- v3 * As suggested by Ben Pfaff - Skip unknown hello elements - Use bitmap directly in the form of a uint32_t instead of creating a struct ofp_hello v2 * Initial post --- include/openflow/openflow-common.h | 11 ++++ lib/ofp-print.c | 27 ++++++++- lib/ofp-util.c | 117 ++++++++++++++++++++++++++++++++++++ lib/ofp-util.h | 4 ++ lib/vconn.c | 47 +++++++++------ tests/ofp-print.at | 23 +++++++ 6 files changed, 209 insertions(+), 20 deletions(-) diff --git a/include/openflow/openflow-common.h b/include/openflow/openflow-common.h index 0bca0d2..462b2fc 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 8654783..b4f0028 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -901,6 +901,29 @@ ofp_print_error(struct ds *string, enum ofperr error) } static void +ofp_print_hello(struct ds *string, const struct ofp_header *oh) +{ + enum ofperr error; + struct ofpbuf b; + uint32_t allowed_versions; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + error = ofputil_decode_hello(&allowed_versions, &b); + if (error) { + ofp_print_error(string, error); + return; + } + + ds_put_cstr(string, "\n version bitmap: "); + ofputil_format_version_bitmap(string, allowed_versions); + + if (b.size) { + ds_put_char(string, '\n'); + ds_put_hex_dump(string, b.data, b.size, 0, true); + } +} + +static void ofp_print_error_msg(struct ds *string, const struct ofp_header *oh) { size_t len = ntohs(oh->length); @@ -1726,9 +1749,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 cc70017..b415128 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -1135,6 +1135,123 @@ ofputil_format_version_bitmap_names(struct ds *msg, uint32_t bitmap) ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version_name); } +static enum ofperr +ofputil_decode_hello_bitmap(uint32_t *allowed_versions, struct ofpbuf *msg) +{ + const struct ofp_hello_elem_header *oheh = msg->data; + uint16_t elem_len = ntohs(oheh->length); + size_t padded_elem_len = ROUND_UP(elem_len, 8); + size_t version_len; + ovs_be32 *bitmap; + + if (msg->size < padded_elem_len || !elem_len || + elem_len % sizeof *bitmap) { + return OFPERR_OFPET_HELLO_FAILED; + } + + oheh = ofpbuf_pull(msg, sizeof *oheh); + bitmap = ofpbuf_pull(msg, padded_elem_len - sizeof *oheh); + version_len = elem_len - sizeof *oheh; + if (version_len) { + /* N.B: Only use the first 32bit 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]); + } + + return 0; +} + +enum ofperr +ofputil_decode_hello(uint32_t *allowed_versions, struct ofpbuf *msg) +{ + const struct ofp_header *oh = msg->data; + enum ofp_version ofp_version = oh->version; + bool have_version_bitmap = false; + + ofpraw_pull_assert(msg); + *allowed_versions = 0; + + while (1) { + const struct ofp_hello_elem_header *oheh = msg->data; + + if (msg->size < sizeof *oheh) { + break; + } + + if (oheh->type == htons(OFPHET_VERSIONBITMAP)) { + enum ofperr err; + + have_version_bitmap = true; + err = ofputil_decode_hello_bitmap(allowed_versions, msg); + if (err) { + return err; + } + } else { + size_t padded_elem_len = ROUND_UP(ntohs(oheh->length), 8); + + if (!padded_elem_len || msg->size < padded_elem_len) { + break; + } + + /* Skip unknown element */ + ofpbuf_pull(msg, padded_elem_len); + } + } + + if (!have_version_bitmap) { + /* Construct bitmap based on version in OpenFlow header */ + *allowed_versions = ofputil_version_bitmap_set_range1(OFP10_VERSION, + ofp_version); + } + + return 0; +} + +static inline bool +should_send_version_bitmap(uint32_t allowed_versions, + enum ofp_version ofp_version) +{ + size_t i; + + /* No need to send version bitmap if all bits up to ofp_version are one */ + for (i = OFP10_VERSION; i <= ofp_version; i++) { + if (!ofputil_version_bitmap_is_set(allowed_versions, i)) { + return true; + } + } + + return false; +} + +/* Create an OFPT_HELLO message according to + * 'ofp_version' and returns the message. */ +struct ofpbuf * +ofputil_encode_hello(uint32_t allowed_versions) +{ + enum ofp_version ofp_version; + struct ofpbuf *msg; + + ofp_version = ofputil_version_bitmap_scanr(allowed_versions); + msg = ofpraw_alloc(OFPRAW_OFPT_HELLO, ofp_version, 0); + + if (should_send_version_bitmap(allowed_versions, ofp_version)) { + struct ofp_hello_elem_header *oheh; + uint16_t map_len; + + map_len = VERSION_BITMAP_W / 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 4838e20..f6a69af 100644 --- a/lib/ofp-util.h +++ b/lib/ofp-util.h @@ -150,6 +150,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); +enum ofperr ofputil_decode_hello(uint32_t *allowed_versions, + struct ofpbuf *msg); +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 db293c5..e548c20 100644 --- a/lib/vconn.c +++ b/lib/vconn.c @@ -395,12 +395,12 @@ vcs_connecting(struct vconn *vconn) } static void -vcs_send_hello(struct vconn *vconn, enum ofp_version max_version) +vcs_send_hello(struct vconn *vconn) { struct ofpbuf *b; int retval; - b = ofpraw_alloc(OFPRAW_OFPT_HELLO, max_version, 0); + b = ofputil_encode_hello(vconn->allowed_versions); retval = do_send(vconn, b); if (!retval) { vconn->state = VCS_RECV_HELLO; @@ -439,39 +439,52 @@ 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; error = ofptype_decode(&type, b->data); if (!error && type == OFPTYPE_HELLO) { - if (b->size > sizeof *oh) { + uint32_t peer_allowed_versions; + uint32_t allowed_versions; + + error = ofputil_decode_hello(&peer_allowed_versions, b); + if (error) { + VLOG_WARN_RL(&bad_ofmsg_rl, "%s: ***decode error: %s***\n", + vconn->name, ofperr_get_name(error)); + return; + } + + if (b->size) { struct ds msg = DS_EMPTY_INITIALIZER; - ds_put_format(&msg, "%s: extra-long hello:\n", vconn->name); + ds_put_format(&msg, "%s: trailing 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); } - vconn->version = - ofputil_version_bitmap_scanr(vconn->allowed_versions); + allowed_versions = peer_allowed_versions & vconn->allowed_versions; + vconn->version = ofputil_version_bitmap_scanr(allowed_versions); + if (vconn->version == VERSION_BITMAP_W) { struct ds msg = DS_EMPTY_INITIALIZER; + ds_put_format(&msg, "%s: we support ", vconn->name); format_versions(&msg, vconn->allowed_versions); - VLOG_WARN_RL(&bad_ofmsg_rl, - "%s: version negotiation failed: we support " - "%s but peer " "supports no later than " - "version 0x%02"PRIx8, vconn->name, - ds_cstr(&msg), oh->version); + ds_put_cstr(&msg, ". but peer supports "); + format_versions(&msg, peer_allowed_versions); + ds_put_char(&msg, '.'); + VLOG_WARN_RL(&bad_ofmsg_rl, "%s", ds_cstr(&msg)); ds_destroy(&msg); vconn->state = VCS_SEND_ERROR; } else { struct ds msg = DS_EMPTY_INITIALIZER; + ds_put_format(&msg, "%s: negotiated OpenFlow version 0x%02x " + "(we support ", vconn->name, vconn->version); format_versions(&msg, vconn->allowed_versions); - VLOG_DBG("%s: negotiated OpenFlow version 0x%02x " - "(we support %s, peer no later than " - "version 0x%02"PRIx8")", vconn->name, - vconn->version, ds_cstr(&msg), oh->version); + ds_put_cstr(&msg, ". peer supports "); + format_versions(&msg, peer_allowed_versions); + ds_put_cstr(&msg, ".)"); + VLOG_DBG("%s", ds_cstr(&msg)); ds_destroy(&msg); vconn->state = VCS_CONNECTED; } @@ -537,7 +550,7 @@ vconn_connect(struct vconn *vconn) break; case VCS_SEND_HELLO: - vcs_send_hello(vconn, max_version); + vcs_send_hello(vconn); break; case VCS_RECV_HELLO: diff --git a/tests/ofp-print.at b/tests/ofp-print.at index 6133fff..ee26e75 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,10 +54,32 @@ AT_KEYWORDS([ofp-print]) AT_CHECK([ovs-ofctl ofp-print 0100001300000000657874726120646174610a], [0], [dnl OFPT_HELLO (xid=0x0): + version bitmap: 0x01 00000000 65 78 74 72 61 20 64 61-74 61 0a |extra data. | ]) 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 f1"], [0], +[dnl +OFPT_HELLO (xid=0x0): + version bitmap: 0x00, 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 f1 61 62 63"], [0], +[dnl +OFPT_HELLO (xid=0x0): + version bitmap: 0x00, 0x04, 0x05, 0x06, 0x07 +00000000 61 62 63 |abc | +]) +AT_CLEANUP + dnl OFPT_ERROR tests are in ofp-errors.at. AT_SETUP([OFPT_ECHO_REQUEST, empty payload]) -- 1.7.10.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev