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 */
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)
{
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:
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
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);
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;
retval = do_recv(vconn, &b);
if (!retval) {
- const struct ofp_header *oh = b->data;
enum ofptype type;
enum ofperr error;
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);
AT_KEYWORDS([ofp-print])
AT_CHECK([ovs-ofctl ofp-print 0100000800000000], [0], [dnl
OFPT_HELLO (xid=0x0):
+ version bitmap: 0x01
])
AT_CLEANUP
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