X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=lib%2Fofp-util.c;h=ad3fb369219fe8f666219719be1ef1fa40f4d9a4;hb=e43928f2862b83a3c13e8662490a22fa25405be5;hp=6622b833d4072e2b287e45f3ba046f5dd3446497;hpb=81a76618be9ea195a1e4a881ba9591728891d10b;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 6622b833..ad3fb369 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -16,6 +16,7 @@ #include #include "ofp-print.h" +#include #include #include #include @@ -139,7 +140,7 @@ ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch, uint32_t ofpfw = ntohl(ofmatch->wildcards) & OFPFW10_ALL; /* Initialize match->wc. */ - memset(match->flow.zeros, 0, sizeof match->flow.zeros); + memset(&match->flow, 0, sizeof match->flow); ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc); /* Initialize most of match->flow. */ @@ -305,7 +306,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, uint16_t wc = ntohl(ofmatch->wildcards); uint8_t dl_src_mask[ETH_ADDR_LEN]; uint8_t dl_dst_mask[ETH_ADDR_LEN]; - bool ipv4, arp; + bool ipv4, arp, rarp; int i; match_init_catchall(match); @@ -370,6 +371,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, ipv4 = match->flow.dl_type == htons(ETH_TYPE_IP); arp = match->flow.dl_type == htons(ETH_TYPE_ARP); + rarp = match->flow.dl_type == htons(ETH_TYPE_RARP); if (ipv4 && !(wc & OFPFW11_NW_TOS)) { if (ofmatch->nw_tos & ~IP_DSCP_MASK) { @@ -380,7 +382,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, match_set_nw_dscp(match, ofmatch->nw_tos); } - if (ipv4 || arp) { + if (ipv4 || arp || rarp) { if (!(wc & OFPFW11_NW_PROTO)) { match_set_nw_proto(match, ofmatch->nw_proto); } @@ -579,15 +581,15 @@ struct proto_abbrev { /* Most users really don't care about some of the differences between * protocols. These abbreviations help with that. */ static const struct proto_abbrev proto_abbrevs[] = { - { OFPUTIL_P_ANY, "any" }, - { OFPUTIL_P_OF10_ANY, "OpenFlow10" }, - { OFPUTIL_P_NXM_ANY, "NXM" }, + { OFPUTIL_P_ANY, "any" }, + { OFPUTIL_P_OF10_STD_ANY, "OpenFlow10" }, + { OFPUTIL_P_OF10_NXM_ANY, "NXM" }, }; #define N_PROTO_ABBREVS ARRAY_SIZE(proto_abbrevs) enum ofputil_protocol ofputil_flow_dump_protocols[] = { - OFPUTIL_P_NXM, - OFPUTIL_P_OF10, + OFPUTIL_P_OF10_NXM, + OFPUTIL_P_OF10_STD, }; size_t ofputil_n_flow_dump_protocols = ARRAY_SIZE(ofputil_flow_dump_protocols); @@ -601,9 +603,9 @@ ofputil_protocol_from_ofp_version(enum ofp_version version) { switch (version) { case OFP10_VERSION: - return OFPUTIL_P_OF10; + return OFPUTIL_P_OF10_STD; case OFP12_VERSION: - return OFPUTIL_P_OF12; + return OFPUTIL_P_OF12_OXM; case OFP11_VERSION: default: return 0; @@ -616,12 +618,12 @@ enum ofp_version ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol) { switch (protocol) { - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: return OFP10_VERSION; - case OFPUTIL_P_OF12: + case OFPUTIL_P_OF12_OXM: return OFP12_VERSION; } @@ -650,16 +652,16 @@ enum ofputil_protocol ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable) { switch (protocol) { - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: - return enable ? OFPUTIL_P_OF10_TID : OFPUTIL_P_OF10; + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: + return enable ? OFPUTIL_P_OF10_STD_TID : OFPUTIL_P_OF10_STD; - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: - return enable ? OFPUTIL_P_NXM_TID : OFPUTIL_P_NXM; + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: + return enable ? OFPUTIL_P_OF10_NXM_TID : OFPUTIL_P_OF10_NXM; - case OFPUTIL_P_OF12: - return OFPUTIL_P_OF12; + case OFPUTIL_P_OF12_OXM: + return OFPUTIL_P_OF12_OXM; default: NOT_REACHED(); @@ -684,16 +686,16 @@ ofputil_protocol_set_base(enum ofputil_protocol cur, bool tid = (cur & OFPUTIL_P_TID) != 0; switch (new_base) { - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: - return ofputil_protocol_set_tid(OFPUTIL_P_OF10, tid); + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: + return ofputil_protocol_set_tid(OFPUTIL_P_OF10_STD, tid); - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: - return ofputil_protocol_set_tid(OFPUTIL_P_NXM, tid); + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: + return ofputil_protocol_set_tid(OFPUTIL_P_OF10_NXM, tid); - case OFPUTIL_P_OF12: - return ofputil_protocol_set_tid(OFPUTIL_P_OF12, tid); + case OFPUTIL_P_OF12_OXM: + return ofputil_protocol_set_tid(OFPUTIL_P_OF12_OXM, tid); default: NOT_REACHED(); @@ -711,20 +713,20 @@ ofputil_protocol_to_string(enum ofputil_protocol protocol) /* Use a "switch" statement for single-bit names so that we get a compiler * warning if we forget any. */ switch (protocol) { - case OFPUTIL_P_NXM: + case OFPUTIL_P_OF10_NXM: return "NXM-table_id"; - case OFPUTIL_P_NXM_TID: + case OFPUTIL_P_OF10_NXM_TID: return "NXM+table_id"; - case OFPUTIL_P_OF10: + case OFPUTIL_P_OF10_STD: return "OpenFlow10-table_id"; - case OFPUTIL_P_OF10_TID: + case OFPUTIL_P_OF10_STD_TID: return "OpenFlow10+table_id"; - case OFPUTIL_P_OF12: - return NULL; + case OFPUTIL_P_OF12_OXM: + return "OXM"; } /* Check abbreviations. */ @@ -845,6 +847,71 @@ ofputil_protocols_from_string(const char *s) return protocols; } +static enum ofp_version +ofputil_version_from_string(const char *s) +{ + if (!strcasecmp(s, "OpenFlow10")) { + return OFP10_VERSION; + } + if (!strcasecmp(s, "OpenFlow11")) { + return OFP11_VERSION; + } + if (!strcasecmp(s, "OpenFlow12")) { + return OFP12_VERSION; + } + VLOG_FATAL("Unknown OpenFlow version: \"%s\"", s); +} + +static bool +is_delimiter(char c) +{ + return isspace(c) || c == ','; +} + +uint32_t +ofputil_versions_from_string(const char *s) +{ + size_t i = 0; + uint32_t bitmap = 0; + + while (s[i]) { + size_t j; + enum ofp_version version; + char *key; + + if (is_delimiter(s[i])) { + i++; + continue; + } + j = 0; + while (s[i + j] && !is_delimiter(s[i + j])) { + j++; + } + key = xmemdup0(s + i, j); + version = ofputil_version_from_string(key); + free(key); + bitmap |= 1u << version; + i += j; + } + + return bitmap; +} + +const char * +ofputil_version_to_string(enum ofp_version ofp_version) +{ + switch (ofp_version) { + case OFP10_VERSION: + return "OpenFlow10"; + case OFP11_VERSION: + return "OpenFlow11"; + case OFP12_VERSION: + return "OpenFlow12"; + default: + NOT_REACHED(); + } +} + bool ofputil_packet_in_format_is_valid(enum nx_packet_in_format packet_in_format) { @@ -902,109 +969,274 @@ ofputil_usable_protocols(const struct match *match) BUILD_ASSERT_DECL(FLOW_WC_SEQ == 17); - /* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */ + /* NXM, OXM, and OF1.1 support bitwise matching on ethernet addresses. */ if (!eth_mask_is_exact(wc->masks.dl_src) && !eth_addr_is_zero(wc->masks.dl_src)) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } if (!eth_mask_is_exact(wc->masks.dl_dst) && !eth_addr_is_zero(wc->masks.dl_dst)) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* NXM and OF1.1+ support matching metadata. */ + /* NXM, OXM, and OF1.1+ support matching metadata. */ if (wc->masks.metadata != htonll(0)) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports matching ARP hardware addresses. */ + /* NXM and OXM support matching ARP hardware addresses. */ if (!eth_addr_is_zero(wc->masks.arp_sha) || !eth_addr_is_zero(wc->masks.arp_tha)) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports matching IPv6 traffic. */ + /* NXM and OXM support matching IPv6 traffic. */ if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports matching registers. */ + /* NXM and OXM support matching registers. */ if (!regs_fully_wildcarded(wc)) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports matching tun_id. */ - if (wc->masks.tun_id != htonll(0)) { - return OFPUTIL_P_NXM_ANY; + /* NXM and OXM support matching tun_id. */ + if (wc->masks.tunnel.tun_id != htonll(0)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports matching fragments. */ + /* NXM and OXM support matching fragments. */ if (wc->masks.nw_frag) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports matching IPv6 flow label. */ + /* NXM and OXM support matching IPv6 flow label. */ if (wc->masks.ipv6_label) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports matching IP ECN bits. */ + /* NXM and OXM support matching IP ECN bits. */ if (wc->masks.nw_tos & IP_ECN_MASK) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports matching IP TTL/hop limit. */ + /* NXM and OXM support matching IP TTL/hop limit. */ if (wc->masks.nw_ttl) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports non-CIDR IPv4 address masks. */ + /* NXM and OXM support non-CIDR IPv4 address masks. */ if (!ip_is_cidr(wc->masks.nw_src) || !ip_is_cidr(wc->masks.nw_dst)) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } - /* Only NXM supports bitwise matching on transport port. */ + /* NXM and OXM support bitwise matching on transport port. */ if ((wc->masks.tp_src && wc->masks.tp_src != htons(UINT16_MAX)) || (wc->masks.tp_dst && wc->masks.tp_dst != htons(UINT16_MAX))) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } /* Other formats can express this rule. */ return OFPUTIL_P_ANY; } +void +ofputil_format_version(struct ds *msg, enum ofp_version version) +{ + ds_put_format(msg, "0x%02x", version); +} + +void +ofputil_format_version_name(struct ds *msg, enum ofp_version version) +{ + ds_put_cstr(msg, ofputil_version_to_string(version)); +} + +static void +ofputil_format_version_bitmap__(struct ds *msg, uint32_t bitmap, + void (*format_version)(struct ds *msg, + enum ofp_version)) +{ + while (bitmap) { + format_version(msg, raw_ctz(bitmap)); + bitmap = zero_rightmost_1bit(bitmap); + if (bitmap) { + ds_put_cstr(msg, ", "); + } + } +} + +void +ofputil_format_version_bitmap(struct ds *msg, uint32_t bitmap) +{ + ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version); +} + +void +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_versionsp) +{ + uint16_t bitmap_len = ntohs(oheh->length) - sizeof *oheh; + const ovs_be32 *bitmap = (const ovs_be32 *) (oheh + 1); + uint32_t allowed_versions; + + 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; + } + + *allowed_versionsp = allowed_versions; + 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 allowed_versions; + 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); + + ofpmsg_update_length(msg); + } + + 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 * connection if the switch processes the returned message correctly. (If * '*next != want' then the caller will have to iterate.) * - * If 'current == want', returns NULL and stores 'current' in '*next'. */ + * If 'current == want', or if it is not possible to transition from 'current' + * to 'want' (because, for example, 'current' and 'want' use different OpenFlow + * protocol versions), returns NULL and stores 'current' in '*next'. */ struct ofpbuf * ofputil_encode_set_protocol(enum ofputil_protocol current, enum ofputil_protocol want, enum ofputil_protocol *next) { + enum ofp_version cur_version, want_version; enum ofputil_protocol cur_base, want_base; bool cur_tid, want_tid; + cur_version = ofputil_protocol_to_ofp_version(current); + want_version = ofputil_protocol_to_ofp_version(want); + if (cur_version != want_version) { + *next = current; + return NULL; + } + cur_base = ofputil_protocol_to_base(current); want_base = ofputil_protocol_to_base(want); if (cur_base != want_base) { *next = ofputil_protocol_set_base(current, want_base); switch (want_base) { - case OFPUTIL_P_NXM: + case OFPUTIL_P_OF10_NXM: return ofputil_encode_nx_set_flow_format(NXFF_NXM); - case OFPUTIL_P_OF10: + case OFPUTIL_P_OF10_STD: return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10); - case OFPUTIL_P_OF12: + case OFPUTIL_P_OF12_OXM: return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW12); - case OFPUTIL_P_OF10_TID: - case OFPUTIL_P_NXM_TID: + case OFPUTIL_P_OF10_STD_TID: + case OFPUTIL_P_OF10_NXM_TID: NOT_REACHED(); } } @@ -1046,13 +1278,13 @@ ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format) { switch (flow_format) { case NXFF_OPENFLOW10: - return OFPUTIL_P_OF10; + return OFPUTIL_P_OF10_STD; case NXFF_NXM: - return OFPUTIL_P_NXM; + return OFPUTIL_P_OF10_NXM; case NXFF_OPENFLOW12: - return OFPUTIL_P_OF12; + return OFPUTIL_P_OF12_OXM; default: return 0; @@ -1084,12 +1316,13 @@ ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format) } struct ofpbuf * -ofputil_make_set_packet_in_format(enum nx_packet_in_format packet_in_format) +ofputil_make_set_packet_in_format(enum ofp_version ofp_version, + enum nx_packet_in_format packet_in_format) { struct nx_set_packet_in_format *spif; struct ofpbuf *msg; - msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT, OFP10_VERSION, 0); + msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT, ofp_version, 0); spif = ofpbuf_put_zeros(msg, sizeof *spif); spif->format = htonl(packet_in_format); @@ -1156,7 +1389,6 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, fm->cookie_mask = htonll(0); fm->new_cookie = ofm->cookie; } else { - /* XXX */ fm->cookie = ofm->cookie; fm->cookie_mask = ofm->cookie_mask; fm->new_cookie = htonll(UINT64_MAX); @@ -1171,7 +1403,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, return error; } if (ofm->out_group != htonl(OFPG_ANY)) { - return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED; + return OFPERR_OFPFMFC_UNKNOWN; } fm->flags = ntohs(ofm->flags); } else { @@ -1277,13 +1509,17 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, struct ofpbuf *msg; switch (protocol) { - case OFPUTIL_P_OF12: { + case OFPUTIL_P_OF12_OXM: { struct ofp11_flow_mod *ofm; msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, OFP12_VERSION, NXM_TYPICAL_LEN + fm->ofpacts_len); ofm = ofpbuf_put_zeros(msg, sizeof *ofm); - ofm->cookie = fm->new_cookie; + if (fm->command == OFPFC_ADD) { + ofm->cookie = fm->new_cookie; + } else { + ofm->cookie = fm->cookie; + } ofm->cookie_mask = fm->cookie_mask; ofm->table_id = fm->table_id; ofm->command = fm->command; @@ -1299,8 +1535,8 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, break; } - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: { + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: { struct ofp10_flow_mod *ofm; msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION, @@ -1319,8 +1555,8 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, break; } - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: { + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: { struct nx_flow_mod *nfm; int match_len; @@ -1371,9 +1607,9 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms, usable_protocols &= OFPUTIL_P_TID; } - /* Matching of the cookie is only supported through NXM. */ + /* Matching of the cookie is only supported through NXM or OF1.1+. */ if (fm->cookie_mask != htonll(0)) { - usable_protocols &= OFPUTIL_P_NXM_ANY; + usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } } assert(usable_protocols); @@ -1410,7 +1646,7 @@ ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr, return error; } if (ofsr->out_group != htonl(OFPG11_ANY)) { - return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED; + return OFPERR_OFPFMFC_UNKNOWN; } fsr->cookie = ofsr->cookie; fsr->cookie_mask = ofsr->cookie_mask; @@ -1494,7 +1730,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, enum ofpraw raw; switch (protocol) { - case OFPUTIL_P_OF12: { + case OFPUTIL_P_OF12_OXM: { struct ofp11_flow_stats_request *ofsr; raw = (fsr->aggregate @@ -1511,8 +1747,8 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, break; } - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: { + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: { struct ofp10_flow_stats_request *ofsr; raw = (fsr->aggregate @@ -1526,8 +1762,8 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, break; } - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: { + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: { struct nx_flow_stats_request *nfsr; int match_len; @@ -1565,7 +1801,7 @@ ofputil_flow_stats_request_usable_protocols( usable_protocols = ofputil_usable_protocols(&fsr->match); if (fsr->cookie_mask != htonll(0)) { - usable_protocols &= OFPUTIL_P_NXM_ANY; + usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM; } return usable_protocols; } @@ -1913,7 +2149,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, fr->priority = ntohs(ofr->priority); fr->cookie = ofr->cookie; fr->reason = ofr->reason; - /* XXX: ofr->table_id is ignored */ + fr->table_id = ofr->table_id; fr->duration_sec = ntohl(ofr->duration_sec); fr->duration_nsec = ntohl(ofr->duration_nsec); fr->idle_timeout = ntohs(ofr->idle_timeout); @@ -1929,6 +2165,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, fr->priority = ntohs(ofr->priority); fr->cookie = ofr->cookie; fr->reason = ofr->reason; + fr->table_id = 255; fr->duration_sec = ntohl(ofr->duration_sec); fr->duration_nsec = ntohl(ofr->duration_nsec); fr->idle_timeout = ntohs(ofr->idle_timeout); @@ -1937,7 +2174,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, fr->byte_count = ntohll(ofr->byte_count); } else if (raw == OFPRAW_NXT_FLOW_REMOVED) { struct nx_flow_removed *nfr; - int error; + enum ofperr error; nfr = ofpbuf_pull(&b, sizeof *nfr); error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match, @@ -1952,6 +2189,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, fr->priority = ntohs(nfr->priority); fr->cookie = nfr->cookie; fr->reason = nfr->reason; + fr->table_id = 255; fr->duration_sec = ntohl(nfr->duration_sec); fr->duration_nsec = ntohl(nfr->duration_nsec); fr->idle_timeout = ntohs(nfr->idle_timeout); @@ -1975,7 +2213,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, struct ofpbuf *msg; switch (protocol) { - case OFPUTIL_P_OF12: { + case OFPUTIL_P_OF12_OXM: { struct ofp12_flow_removed *ofr; msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED, @@ -1985,7 +2223,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, ofr->cookie = fr->cookie; ofr->priority = htons(fr->priority); ofr->reason = fr->reason; - ofr->table_id = 0; + ofr->table_id = fr->table_id; ofr->duration_sec = htonl(fr->duration_sec); ofr->duration_nsec = htonl(fr->duration_nsec); ofr->idle_timeout = htons(fr->idle_timeout); @@ -1996,8 +2234,8 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, break; } - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: { + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: { struct ofp_flow_removed *ofr; msg = ofpraw_alloc_xid(OFPRAW_OFPT10_FLOW_REMOVED, OFP10_VERSION, @@ -2015,8 +2253,8 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, break; } - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: { + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: { struct nx_flow_removed *nfr; int match_len; @@ -2053,7 +2291,7 @@ ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin, pin->packet_len = b->size; pin->fmd.in_port = match->flow.in_port; - pin->fmd.tun_id = match->flow.tun_id; + pin->fmd.tun_id = match->flow.tunnel.tun_id; pin->fmd.metadata = match->flow.metadata; memcpy(pin->fmd.regs, match->flow.regs, sizeof pin->fmd.regs); } @@ -2168,7 +2406,7 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin, struct ofpbuf *packet; /* Add OFPT_PACKET_IN. */ - if (protocol == OFPUTIL_P_OF12) { + if (protocol == OFPUTIL_P_OF12_OXM) { struct ofp12_packet_in *opi; struct match match; @@ -2279,7 +2517,6 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, const struct ofp_header *oh, struct ofpbuf *ofpacts) { - enum ofperr bad_in_port_err; enum ofpraw raw; struct ofpbuf b; @@ -2301,8 +2538,6 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, if (error) { return error; } - - bad_in_port_err = OFPERR_OFPBMC_BAD_VALUE; } else if (raw == OFPRAW_OFPT10_PACKET_OUT) { enum ofperr error; const struct ofp_packet_out *opo = ofpbuf_pull(&b, sizeof *opo); @@ -2314,8 +2549,6 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, if (error) { return error; } - - bad_in_port_err = OFPERR_NXBRC_BAD_IN_PORT; } else { NOT_REACHED(); } @@ -2324,7 +2557,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, && po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) { VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16, po->in_port); - return bad_in_port_err; + return OFPERR_OFPBRC_BAD_PORT; } po->ofpacts = ofpacts->data; @@ -2419,8 +2652,8 @@ ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp, pp->supported = netdev_port_features_from_ofp10(opp->supported); pp->peer = netdev_port_features_from_ofp10(opp->peer); - pp->curr_speed = netdev_features_to_bps(pp->curr) / 1000; - pp->max_speed = netdev_features_to_bps(pp->supported) / 1000; + pp->curr_speed = netdev_features_to_bps(pp->curr, 0) / 1000; + pp->max_speed = netdev_features_to_bps(pp->supported, 0) / 1000; return 0; } @@ -2938,6 +3171,143 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm, return b; } +/* Table stats. */ + +static void +ofputil_put_ofp10_table_stats(const struct ofp12_table_stats *in, + struct ofpbuf *buf) +{ + struct wc_map { + enum ofp_flow_wildcards wc10; + enum oxm12_ofb_match_fields mf12; + }; + + static const struct wc_map wc_map[] = { + { OFPFW10_IN_PORT, OFPXMT12_OFB_IN_PORT }, + { OFPFW10_DL_VLAN, OFPXMT12_OFB_VLAN_VID }, + { OFPFW10_DL_SRC, OFPXMT12_OFB_ETH_SRC }, + { OFPFW10_DL_DST, OFPXMT12_OFB_ETH_DST}, + { OFPFW10_DL_TYPE, OFPXMT12_OFB_ETH_TYPE }, + { OFPFW10_NW_PROTO, OFPXMT12_OFB_IP_PROTO }, + { OFPFW10_TP_SRC, OFPXMT12_OFB_TCP_SRC }, + { OFPFW10_TP_DST, OFPXMT12_OFB_TCP_DST }, + { OFPFW10_NW_SRC_MASK, OFPXMT12_OFB_IPV4_SRC }, + { OFPFW10_NW_DST_MASK, OFPXMT12_OFB_IPV4_DST }, + { OFPFW10_DL_VLAN_PCP, OFPXMT12_OFB_VLAN_PCP }, + { OFPFW10_NW_TOS, OFPXMT12_OFB_IP_DSCP }, + }; + + struct ofp10_table_stats *out; + const struct wc_map *p; + + out = ofpbuf_put_uninit(buf, sizeof *out); + out->table_id = in->table_id; + strcpy(out->name, in->name); + out->wildcards = 0; + for (p = wc_map; p < &wc_map[ARRAY_SIZE(wc_map)]; p++) { + if (in->wildcards & htonll(1ULL << p->mf12)) { + out->wildcards |= htonl(p->wc10); + } + } + out->max_entries = in->max_entries; + out->active_count = in->active_count; + put_32aligned_be64(&out->lookup_count, in->lookup_count); + put_32aligned_be64(&out->matched_count, in->matched_count); +} + +static ovs_be32 +oxm12_to_ofp11_flow_match_fields(ovs_be64 oxm12) +{ + struct map { + enum ofp11_flow_match_fields fmf11; + enum oxm12_ofb_match_fields mf12; + }; + + static const struct map map[] = { + { OFPFMF11_IN_PORT, OFPXMT12_OFB_IN_PORT }, + { OFPFMF11_DL_VLAN, OFPXMT12_OFB_VLAN_VID }, + { OFPFMF11_DL_VLAN_PCP, OFPXMT12_OFB_VLAN_PCP }, + { OFPFMF11_DL_TYPE, OFPXMT12_OFB_ETH_TYPE }, + { OFPFMF11_NW_TOS, OFPXMT12_OFB_IP_DSCP }, + { OFPFMF11_NW_PROTO, OFPXMT12_OFB_IP_PROTO }, + { OFPFMF11_TP_SRC, OFPXMT12_OFB_TCP_SRC }, + { OFPFMF11_TP_DST, OFPXMT12_OFB_TCP_DST }, + { OFPFMF11_MPLS_LABEL, OFPXMT12_OFB_MPLS_LABEL }, + { OFPFMF11_MPLS_TC, OFPXMT12_OFB_MPLS_TC }, + /* I don't know what OFPFMF11_TYPE means. */ + { OFPFMF11_DL_SRC, OFPXMT12_OFB_ETH_SRC }, + { OFPFMF11_DL_DST, OFPXMT12_OFB_ETH_DST }, + { OFPFMF11_NW_SRC, OFPXMT12_OFB_IPV4_SRC }, + { OFPFMF11_NW_DST, OFPXMT12_OFB_IPV4_DST }, + { OFPFMF11_METADATA, OFPXMT12_OFB_METADATA }, + }; + + const struct map *p; + uint32_t fmf11; + + fmf11 = 0; + for (p = map; p < &map[ARRAY_SIZE(map)]; p++) { + if (oxm12 & htonll(1ULL << p->mf12)) { + fmf11 |= p->fmf11; + } + } + return htonl(fmf11); +} + +static void +ofputil_put_ofp11_table_stats(const struct ofp12_table_stats *in, + struct ofpbuf *buf) +{ + struct ofp11_table_stats *out; + + out = ofpbuf_put_uninit(buf, sizeof *out); + out->table_id = in->table_id; + strcpy(out->name, in->name); + out->wildcards = oxm12_to_ofp11_flow_match_fields(in->wildcards); + out->match = oxm12_to_ofp11_flow_match_fields(in->match); + out->instructions = in->instructions; + out->write_actions = in->write_actions; + out->apply_actions = in->apply_actions; + out->config = in->config; + out->max_entries = in->max_entries; + out->active_count = in->active_count; + out->lookup_count = in->lookup_count; + out->matched_count = in->matched_count; +} + +struct ofpbuf * +ofputil_encode_table_stats_reply(const struct ofp12_table_stats stats[], int n, + const struct ofp_header *request) +{ + struct ofpbuf *reply; + int i; + + reply = ofpraw_alloc_stats_reply(request, n * sizeof *stats); + + switch ((enum ofp_version) request->version) { + case OFP10_VERSION: + for (i = 0; i < n; i++) { + ofputil_put_ofp10_table_stats(&stats[i], reply); + } + break; + + case OFP11_VERSION: + for (i = 0; i < n; i++) { + ofputil_put_ofp11_table_stats(&stats[i], reply); + } + break; + + case OFP12_VERSION: + ofpbuf_put(reply, stats, n * sizeof *stats); + break; + + default: + NOT_REACHED(); + } + + return reply; +} + /* ofputil_flow_monitor_request */ /* Converts an NXST_FLOW_MONITOR request in 'msg' into an abstract @@ -3404,36 +3774,76 @@ ofputil_check_output_port(uint16_t port, int max_ports) OFPUTIL_NAMED_PORT(LOCAL) \ OFPUTIL_NAMED_PORT(NONE) -/* Checks whether 's' is the string representation of an OpenFlow port number, - * either as an integer or a string name (e.g. "LOCAL"). If it is, stores the - * number in '*port' and returns true. Otherwise, returns false. */ +/* Stores the port number represented by 's' into '*portp'. 's' may be an + * integer or, for reserved ports, the standard OpenFlow name for the port + * (e.g. "LOCAL"). + * + * Returns true if successful, false if 's' is not a valid OpenFlow port number + * or name. The caller should issue an error message in this case, because + * this function usually does not. (This gives the caller an opportunity to + * look up the port name another way, e.g. by contacting the switch and listing + * the names of all its ports). + * + * This function accepts OpenFlow 1.0 port numbers. It also accepts a subset + * of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit + * range as described in include/openflow/openflow-1.1.h. */ bool -ofputil_port_from_string(const char *name, uint16_t *port) +ofputil_port_from_string(const char *s, uint16_t *portp) { - struct pair { - const char *name; - uint16_t value; - }; - static const struct pair pairs[] = { -#define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME}, - OFPUTIL_NAMED_PORTS -#undef OFPUTIL_NAMED_PORT - }; - static const int n_pairs = ARRAY_SIZE(pairs); - int i; + unsigned int port32; - if (str_to_int(name, 0, &i) && i >= 0 && i < UINT16_MAX) { - *port = i; - return true; - } + *portp = 0; + if (str_to_uint(s, 10, &port32)) { + if (port32 < OFPP_MAX) { + *portp = port32; + return true; + } else if (port32 < OFPP_FIRST_RESV) { + VLOG_WARN("port %u is a reserved OF1.0 port number that will " + "be translated to %u when talking to an OF1.1 or " + "later controller", port32, port32 + OFPP11_OFFSET); + *portp = port32; + return true; + } else if (port32 <= OFPP_LAST_RESV) { + struct ds s; - for (i = 0; i < n_pairs; i++) { - if (!strcasecmp(name, pairs[i].name)) { - *port = pairs[i].value; + ds_init(&s); + ofputil_format_port(port32, &s); + VLOG_WARN_ONCE("referring to port %s as %u is deprecated for " + "compatibility with future versions of OpenFlow", + ds_cstr(&s), port32); + ds_destroy(&s); + + *portp = port32; + return true; + } else if (port32 < OFPP11_MAX) { + VLOG_WARN("port %u is outside the supported range 0 through " + "%"PRIx16"or 0x%x through 0x%"PRIx32, port32, + UINT16_MAX, (unsigned int) OFPP11_MAX, UINT32_MAX); + return false; + } else { + *portp = port32 - OFPP11_OFFSET; return true; } + } else { + struct pair { + const char *name; + uint16_t value; + }; + static const struct pair pairs[] = { +#define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME}, + OFPUTIL_NAMED_PORTS +#undef OFPUTIL_NAMED_PORT + }; + const struct pair *p; + + for (p = pairs; p < &pairs[ARRAY_SIZE(pairs)]; p++) { + if (!strcasecmp(s, p->name)) { + *portp = p->value; + return true; + } + } + return false; } - return false; } /* Appends to 's' a string representation of the OpenFlow port number 'port'. @@ -3497,9 +3907,9 @@ ofputil_action_code_from_name(const char *name) { static const char *names[OFPUTIL_N_ACTIONS] = { NULL, -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME, -#define OFPAT11_ACTION(ENUM, STRUCT, NAME) NAME, -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, +#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME, +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, +#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, #include "ofp-util.def" }; @@ -3525,9 +3935,10 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) case OFPUTIL_ACTION_INVALID: NOT_REACHED(); -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ +#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ + case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); -#define OFPAT11_ACTION OFPAT10_ACTION #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); #include "ofp-util.def" @@ -3551,7 +3962,8 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) ofputil_init_##ENUM(s); \ return s; \ } -#define OFPAT11_ACTION OFPAT10_ACTION +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ + OFPAT10_ACTION(ENUM, STRUCT, NAME) #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ void \ ofputil_init_##ENUM(struct STRUCT *s) \ @@ -3609,7 +4021,8 @@ ofputil_normalize_match__(struct match *match, bool may_log) may_match |= MAY_ND_TARGET | MAY_ARP_THA; } } - } else if (match->flow.dl_type == htons(ETH_TYPE_ARP)) { + } else if (match->flow.dl_type == htons(ETH_TYPE_ARP) || + match->flow.dl_type == htons(ETH_TYPE_RARP)) { may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA; } else { may_match = 0; @@ -3765,3 +4178,451 @@ ofputil_parse_key_value(char **stringp, char **keyp, char **valuep) *valuep = value; return true; } + +/* Encode a dump ports request for 'port', the encoded message + * will be fore Open Flow version 'ofp_version'. Returns message + * as a struct ofpbuf. Returns encoded message on success, NULL on error */ +struct ofpbuf * +ofputil_encode_dump_ports_request(enum ofp_version ofp_version, int16_t port) +{ + struct ofpbuf *request; + + switch (ofp_version) { + case OFP10_VERSION: { + struct ofp10_port_stats_request *req; + request = ofpraw_alloc(OFPRAW_OFPST10_PORT_REQUEST, ofp_version, 0); + req = ofpbuf_put_zeros(request, sizeof *req); + req->port_no = htons(port); + break; + } + case OFP11_VERSION: + case OFP12_VERSION: { + struct ofp11_port_stats_request *req; + request = ofpraw_alloc(OFPRAW_OFPST11_PORT_REQUEST, ofp_version, 0); + req = ofpbuf_put_zeros(request, sizeof *req); + req->port_no = ofputil_port_to_ofp11(port); + break; + } + default: + NOT_REACHED(); + } + + return request; +} + +static void +ofputil_port_stats_to_ofp10(const struct ofputil_port_stats *ops, + struct ofp10_port_stats *ps10) +{ + ps10->port_no = htons(ops->port_no); + memset(ps10->pad, 0, sizeof ps10->pad); + put_32aligned_be64(&ps10->rx_packets, htonll(ops->stats.rx_packets)); + put_32aligned_be64(&ps10->tx_packets, htonll(ops->stats.tx_packets)); + put_32aligned_be64(&ps10->rx_bytes, htonll(ops->stats.rx_bytes)); + put_32aligned_be64(&ps10->tx_bytes, htonll(ops->stats.tx_bytes)); + put_32aligned_be64(&ps10->rx_dropped, htonll(ops->stats.rx_dropped)); + put_32aligned_be64(&ps10->tx_dropped, htonll(ops->stats.tx_dropped)); + put_32aligned_be64(&ps10->rx_errors, htonll(ops->stats.rx_errors)); + put_32aligned_be64(&ps10->tx_errors, htonll(ops->stats.tx_errors)); + put_32aligned_be64(&ps10->rx_frame_err, htonll(ops->stats.rx_frame_errors)); + put_32aligned_be64(&ps10->rx_over_err, htonll(ops->stats.rx_over_errors)); + put_32aligned_be64(&ps10->rx_crc_err, htonll(ops->stats.rx_crc_errors)); + put_32aligned_be64(&ps10->collisions, htonll(ops->stats.collisions)); +} + +static void +ofputil_port_stats_to_ofp11(const struct ofputil_port_stats *ops, + struct ofp11_port_stats *ps11) +{ + ps11->port_no = ofputil_port_to_ofp11(ops->port_no); + memset(ps11->pad, 0, sizeof ps11->pad); + ps11->rx_packets = htonll(ops->stats.rx_packets); + ps11->tx_packets = htonll(ops->stats.tx_packets); + ps11->rx_bytes = htonll(ops->stats.rx_bytes); + ps11->tx_bytes = htonll(ops->stats.tx_bytes); + ps11->rx_dropped = htonll(ops->stats.rx_dropped); + ps11->tx_dropped = htonll(ops->stats.tx_dropped); + ps11->rx_errors = htonll(ops->stats.rx_errors); + ps11->tx_errors = htonll(ops->stats.tx_errors); + ps11->rx_frame_err = htonll(ops->stats.rx_frame_errors); + ps11->rx_over_err = htonll(ops->stats.rx_over_errors); + ps11->rx_crc_err = htonll(ops->stats.rx_crc_errors); + ps11->collisions = htonll(ops->stats.collisions); +} + +/* Encode a ports stat for 'ops' and append it to 'replies'. */ +void +ofputil_append_port_stat(struct list *replies, + const struct ofputil_port_stats *ops) +{ + struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); + struct ofp_header *oh = msg->data; + + switch ((enum ofp_version)oh->version) { + case OFP12_VERSION: + case OFP11_VERSION: { + struct ofp11_port_stats *reply = ofpmp_append(replies, sizeof *reply); + ofputil_port_stats_to_ofp11(ops, reply); + break; + } + + case OFP10_VERSION: { + struct ofp10_port_stats *reply = ofpmp_append(replies, sizeof *reply); + ofputil_port_stats_to_ofp10(ops, reply); + break; + } + + default: + NOT_REACHED(); + } +} + +static enum ofperr +ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops, + const struct ofp10_port_stats *ps10) +{ + memset(ops, 0, sizeof *ops); + + ops->port_no = ntohs(ps10->port_no); + ops->stats.rx_packets = ntohll(get_32aligned_be64(&ps10->rx_packets)); + ops->stats.tx_packets = ntohll(get_32aligned_be64(&ps10->tx_packets)); + ops->stats.rx_bytes = ntohll(get_32aligned_be64(&ps10->rx_bytes)); + ops->stats.tx_bytes = ntohll(get_32aligned_be64(&ps10->tx_bytes)); + ops->stats.rx_dropped = ntohll(get_32aligned_be64(&ps10->rx_dropped)); + ops->stats.tx_dropped = ntohll(get_32aligned_be64(&ps10->tx_dropped)); + ops->stats.rx_errors = ntohll(get_32aligned_be64(&ps10->rx_errors)); + ops->stats.tx_errors = ntohll(get_32aligned_be64(&ps10->tx_errors)); + ops->stats.rx_frame_errors = + ntohll(get_32aligned_be64(&ps10->rx_frame_err)); + ops->stats.rx_over_errors = ntohll(get_32aligned_be64(&ps10->rx_over_err)); + ops->stats.rx_crc_errors = ntohll(get_32aligned_be64(&ps10->rx_crc_err)); + ops->stats.collisions = ntohll(get_32aligned_be64(&ps10->collisions)); + + return 0; +} + +static enum ofperr +ofputil_port_stats_from_ofp11(struct ofputil_port_stats *ops, + const struct ofp11_port_stats *ps11) +{ + enum ofperr error; + + memset(ops, 0, sizeof *ops); + error = ofputil_port_from_ofp11(ps11->port_no, &ops->port_no); + if (error) { + return error; + } + + ops->stats.rx_packets = ntohll(ps11->rx_packets); + ops->stats.tx_packets = ntohll(ps11->tx_packets); + ops->stats.rx_bytes = ntohll(ps11->rx_bytes); + ops->stats.tx_bytes = ntohll(ps11->tx_bytes); + ops->stats.rx_dropped = ntohll(ps11->rx_dropped); + ops->stats.tx_dropped = ntohll(ps11->tx_dropped); + ops->stats.rx_errors = ntohll(ps11->rx_errors); + ops->stats.tx_errors = ntohll(ps11->tx_errors); + ops->stats.rx_frame_errors = ntohll(ps11->rx_frame_err); + ops->stats.rx_over_errors = ntohll(ps11->rx_over_err); + ops->stats.rx_crc_errors = ntohll(ps11->rx_crc_err); + ops->stats.collisions = ntohll(ps11->collisions); + + return 0; +} + +/* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY + * message 'oh'. */ +size_t +ofputil_count_port_stats(const struct ofp_header *oh) +{ + struct ofpbuf b; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + ofpraw_pull_assert(&b); + + BUILD_ASSERT(sizeof(struct ofp10_port_stats) == + sizeof(struct ofp11_port_stats)); + return b.size / sizeof(struct ofp10_port_stats); +} + +/* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract + * ofputil_port_stats in 'ps'. + * + * Multiple OFPST_PORT_STATS replies can be packed into a single OpenFlow + * message. Calling this function multiple times for a single 'msg' iterates + * through the replies. The caller must initially leave 'msg''s layer pointers + * null and not modify them between calls. + * + * Returns 0 if successful, EOF if no replies were left in this 'msg', + * otherwise a positive errno value. */ +int +ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg) +{ + enum ofperr error; + enum ofpraw raw; + + error = (msg->l2 + ? ofpraw_decode(&raw, msg->l2) + : ofpraw_pull(&raw, msg)); + if (error) { + return error; + } + + if (!msg->size) { + return EOF; + } else if (raw == OFPRAW_OFPST11_PORT_REPLY) { + const struct ofp11_port_stats *ps11; + + ps11 = ofpbuf_try_pull(msg, sizeof *ps11); + if (!ps11) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %zu leftover " + "bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + return ofputil_port_stats_from_ofp11(ps, ps11); + } else if (raw == OFPRAW_OFPST10_PORT_REPLY) { + const struct ofp10_port_stats *ps10; + + ps10 = ofpbuf_try_pull(msg, sizeof *ps10); + if (!ps10) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %zu leftover " + "bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + return ofputil_port_stats_from_ofp10(ps, ps10); + } else { + NOT_REACHED(); + } + +} + +/* Parse a port status request message into a 16 bit OpenFlow 1.0 + * port number and stores the latter in '*ofp10_port'. + * Returns 0 if successful, otherwise an OFPERR_* number. */ +enum ofperr +ofputil_decode_port_stats_request(const struct ofp_header *request, + uint16_t *ofp10_port) +{ + switch ((enum ofp_version)request->version) { + case OFP12_VERSION: + case OFP11_VERSION: { + const struct ofp11_port_stats_request *psr11 = ofpmsg_body(request); + return ofputil_port_from_ofp11(psr11->port_no, ofp10_port); + } + + case OFP10_VERSION: { + const struct ofp10_port_stats_request *psr10 = ofpmsg_body(request); + *ofp10_port = ntohs(psr10->port_no); + return 0; + } + + default: + NOT_REACHED(); + } +} + +/* Parse a queue status request message into 'oqsr'. + * Returns 0 if successful, otherwise an OFPERR_* number. */ +enum ofperr +ofputil_decode_queue_stats_request(const struct ofp_header *request, + struct ofputil_queue_stats_request *oqsr) +{ + switch ((enum ofp_version)request->version) { + case OFP12_VERSION: + case OFP11_VERSION: { + const struct ofp11_queue_stats_request *qsr11 = ofpmsg_body(request); + oqsr->queue_id = ntohl(qsr11->queue_id); + return ofputil_port_from_ofp11(qsr11->port_no, &oqsr->port_no); + } + + case OFP10_VERSION: { + const struct ofp10_queue_stats_request *qsr11 = ofpmsg_body(request); + oqsr->queue_id = ntohl(qsr11->queue_id); + oqsr->port_no = ntohs(qsr11->port_no); + return 0; + } + + default: + NOT_REACHED(); + } +} + +/* Encode a queue statsrequest for 'oqsr', the encoded message + * will be fore Open Flow version 'ofp_version'. Returns message + * as a struct ofpbuf. Returns encoded message on success, NULL on error */ +struct ofpbuf * +ofputil_encode_queue_stats_request(enum ofp_version ofp_version, + const struct ofputil_queue_stats_request *oqsr) +{ + struct ofpbuf *request; + + switch (ofp_version) { + case OFP11_VERSION: + case OFP12_VERSION: { + struct ofp11_queue_stats_request *req; + request = ofpraw_alloc(OFPRAW_OFPST11_QUEUE_REQUEST, ofp_version, 0); + req = ofpbuf_put_zeros(request, sizeof *req); + req->port_no = ofputil_port_to_ofp11(oqsr->port_no); + req->queue_id = htonl(oqsr->queue_id); + break; + } + case OFP10_VERSION: { + struct ofp10_queue_stats_request *req; + request = ofpraw_alloc(OFPRAW_OFPST10_QUEUE_REQUEST, ofp_version, 0); + req = ofpbuf_put_zeros(request, sizeof *req); + req->port_no = htons(oqsr->port_no); + req->queue_id = htonl(oqsr->queue_id); + break; + } + default: + NOT_REACHED(); + } + + return request; +} + +/* Returns the number of queue stats elements in OFPTYPE_QUEUE_STATS_REPLY + * message 'oh'. */ +size_t +ofputil_count_queue_stats(const struct ofp_header *oh) +{ + struct ofpbuf b; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + ofpraw_pull_assert(&b); + + BUILD_ASSERT(sizeof(struct ofp10_queue_stats) == + sizeof(struct ofp11_queue_stats)); + return b.size / sizeof(struct ofp10_queue_stats); +} + +static enum ofperr +ofputil_queue_stats_from_ofp10(struct ofputil_queue_stats *oqs, + const struct ofp10_queue_stats *qs10) +{ + oqs->port_no = ntohs(qs10->port_no); + oqs->queue_id = ntohl(qs10->queue_id); + oqs->stats.tx_bytes = ntohll(get_32aligned_be64(&qs10->tx_bytes)); + oqs->stats.tx_packets = ntohll(get_32aligned_be64(&qs10->tx_packets)); + oqs->stats.tx_errors = ntohll(get_32aligned_be64(&qs10->tx_errors)); + + return 0; +} + +static enum ofperr +ofputil_queue_stats_from_ofp11(struct ofputil_queue_stats *oqs, + const struct ofp11_queue_stats *qs11) +{ + enum ofperr error; + + error = ofputil_port_from_ofp11(qs11->port_no, &oqs->port_no); + if (error) { + return error; + } + + oqs->queue_id = ntohl(qs11->queue_id); + oqs->stats.tx_bytes = ntohll(qs11->tx_bytes); + oqs->stats.tx_packets = ntohll(qs11->tx_packets); + oqs->stats.tx_errors = ntohll(qs11->tx_errors); + + return 0; +} + +/* Converts an OFPST_QUEUE_STATS reply in 'msg' into an abstract + * ofputil_queue_stats in 'qs'. + * + * Multiple OFPST_QUEUE_STATS replies can be packed into a single OpenFlow + * message. Calling this function multiple times for a single 'msg' iterates + * through the replies. The caller must initially leave 'msg''s layer pointers + * null and not modify them between calls. + * + * Returns 0 if successful, EOF if no replies were left in this 'msg', + * otherwise a positive errno value. */ +int +ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg) +{ + enum ofperr error; + enum ofpraw raw; + + error = (msg->l2 + ? ofpraw_decode(&raw, msg->l2) + : ofpraw_pull(&raw, msg)); + if (error) { + return error; + } + + if (!msg->size) { + return EOF; + } else if (raw == OFPRAW_OFPST11_QUEUE_REPLY) { + const struct ofp11_queue_stats *qs11; + + qs11 = ofpbuf_try_pull(msg, sizeof *qs11); + if (!qs11) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_QUEUE reply has %zu leftover " + "bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + return ofputil_queue_stats_from_ofp11(qs, qs11); + } else if (raw == OFPRAW_OFPST10_QUEUE_REPLY) { + const struct ofp10_queue_stats *qs10; + + qs10 = ofpbuf_try_pull(msg, sizeof *qs10); + if (!qs10) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_QUEUE reply has %zu leftover " + "bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + return ofputil_queue_stats_from_ofp10(qs, qs10); + } else { + NOT_REACHED(); + } +} + +static void +ofputil_queue_stats_to_ofp10(const struct ofputil_queue_stats *oqs, + struct ofp10_queue_stats *qs10) +{ + qs10->port_no = htons(oqs->port_no); + memset(qs10->pad, 0, sizeof qs10->pad); + qs10->queue_id = htonl(oqs->queue_id); + put_32aligned_be64(&qs10->tx_bytes, htonll(oqs->stats.tx_bytes)); + put_32aligned_be64(&qs10->tx_packets, htonll(oqs->stats.tx_packets)); + put_32aligned_be64(&qs10->tx_errors, htonll(oqs->stats.tx_errors)); +} + +static void +ofputil_queue_stats_to_ofp11(const struct ofputil_queue_stats *oqs, + struct ofp11_queue_stats *qs11) +{ + qs11->port_no = ofputil_port_to_ofp11(oqs->port_no); + qs11->queue_id = htonl(oqs->queue_id); + qs11->tx_bytes = htonll(oqs->stats.tx_bytes); + qs11->tx_packets = htonll(oqs->stats.tx_packets); + qs11->tx_errors = htonll(oqs->stats.tx_errors); +} + +/* Encode a queue stat for 'oqs' and append it to 'replies'. */ +void +ofputil_append_queue_stat(struct list *replies, + const struct ofputil_queue_stats *oqs) +{ + struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); + struct ofp_header *oh = msg->data; + + switch ((enum ofp_version)oh->version) { + case OFP12_VERSION: + case OFP11_VERSION: { + struct ofp11_queue_stats *reply = ofpmp_append(replies, sizeof *reply);; + ofputil_queue_stats_to_ofp11(oqs, reply); + break; + } + + case OFP10_VERSION: { + struct ofp10_queue_stats *reply = ofpmp_append(replies, sizeof *reply);; + ofputil_queue_stats_to_ofp10(oqs, reply); + break; + } + + default: + NOT_REACHED(); + } +}