X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fofp-util.c;h=a0a372f8cf4d41e338c7777ed3ff2e6dfea0d33a;hb=fe29af4c888d48cc1f16b1a247c2ffb6f0864522;hp=89215926029628b27efb28abc46f9773ad19883f;hpb=75fa58f844031e071dd828162344b493ecc561f0;p=sliver-openvswitch.git diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 892159260..a0a372f8c 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -84,7 +84,7 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) void ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 23); /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); @@ -106,10 +106,10 @@ ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) >> OFPFW10_NW_DST_SHIFT); if (!(ofpfw & OFPFW10_TP_SRC)) { - wc->masks.tp_src = htons(UINT16_MAX); + wc->masks.tp_src = OVS_BE16_MAX; } if (!(ofpfw & OFPFW10_TP_DST)) { - wc->masks.tp_dst = htons(UINT16_MAX); + wc->masks.tp_dst = OVS_BE16_MAX; } if (!(ofpfw & OFPFW10_DL_SRC)) { @@ -119,7 +119,7 @@ ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) memset(wc->masks.dl_dst, 0xff, ETH_ADDR_LEN); } if (!(ofpfw & OFPFW10_DL_TYPE)) { - wc->masks.dl_type = htons(UINT16_MAX); + wc->masks.dl_type = OVS_BE16_MAX; } /* VLAN TCI mask. */ @@ -296,8 +296,8 @@ ofputil_pull_ofp11_match(struct ofpbuf *buf, struct match *match, } } -/* Converts the ofp11_match in 'match' into a struct match in 'match. Returns - * 0 if successful, otherwise an OFPERR_* value. */ +/* Converts the ofp11_match in 'ofmatch' into a struct match in 'match'. + * Returns 0 if successful, otherwise an OFPERR_* value. */ enum ofperr ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, struct match *match) @@ -335,7 +335,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) { /* Match only packets without a VLAN tag. */ match->flow.vlan_tci = htons(0); - match->wc.masks.vlan_tci = htons(UINT16_MAX); + match->wc.masks.vlan_tci = OVS_BE16_MAX; } else { if (ofmatch->dl_vlan == htons(OFPVID11_ANY)) { /* Match any packet with a VLAN tag regardless of VID. */ @@ -420,6 +420,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, case IPPROTO_TCP: case IPPROTO_UDP: + case IPPROTO_SCTP: if (!(wc & (OFPFW11_TP_SRC))) { match_set_tp_src(match, ofmatch->tp_src); } @@ -428,11 +429,6 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, } break; - case IPPROTO_SCTP: - /* We don't support SCTP and it seems that we should tell the - * controller, since OF1.1 implementations are supposed to. */ - return OFPERR_OFPBMC_BAD_FIELD; - default: /* OF1.1 says explicitly to ignore this. */ break; @@ -440,11 +436,11 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, } if (eth_type_mpls(match->flow.dl_type)) { - enum { OFPFW11_MPLS_ALL = OFPFW11_MPLS_LABEL | OFPFW11_MPLS_TC }; - - if ((wc & OFPFW11_MPLS_ALL) != OFPFW11_MPLS_ALL) { - /* MPLS not supported. */ - return OFPERR_OFPBMC_BAD_TAG; + if (!(wc & OFPFW11_MPLS_LABEL)) { + match_set_mpls_label(match, ofmatch->mpls_label); + } + if (!(wc & OFPFW11_MPLS_TC)) { + match_set_mpls_tc(match, ofmatch->mpls_tc); } } @@ -537,9 +533,17 @@ ofputil_match_to_ofp11_match(const struct match *match, ofmatch->tp_dst = match->flow.tp_dst; } - /* MPLS not supported. */ - wc |= OFPFW11_MPLS_LABEL; - wc |= OFPFW11_MPLS_TC; + if (!(match->wc.masks.mpls_lse & htonl(MPLS_LABEL_MASK))) { + wc |= OFPFW11_MPLS_LABEL; + } else { + ofmatch->mpls_label = htonl(mpls_lse_to_label(match->flow.mpls_lse)); + } + + if (!(match->wc.masks.mpls_lse & htonl(MPLS_TC_MASK))) { + wc |= OFPFW11_MPLS_TC; + } else { + ofmatch->mpls_tc = mpls_lse_to_tc(match->flow.mpls_lse); + } ofmatch->metadata = match->flow.metadata; ofmatch->metadata_mask = ~match->wc.masks.metadata; @@ -569,7 +573,7 @@ ofputil_match_typical_len(enum ofputil_protocol protocol) return NXM_TYPICAL_LEN; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -591,7 +595,7 @@ ofputil_put_ofp11_match(struct ofpbuf *b, const struct match *match, case OFPUTIL_P_OF10_STD_TID: case OFPUTIL_P_OF10_NXM: case OFPUTIL_P_OF10_NXM_TID: - NOT_REACHED(); + OVS_NOT_REACHED(); case OFPUTIL_P_OF11_STD: { struct ofp11_match *om; @@ -609,7 +613,7 @@ ofputil_put_ofp11_match(struct ofpbuf *b, const struct match *match, return oxm_put_match(b, match); } - NOT_REACHED(); + OVS_NOT_REACHED(); } /* Given a 'dl_type' value in the format used in struct flow, returns the @@ -711,7 +715,7 @@ ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol) return OFP13_VERSION; } - NOT_REACHED(); + OVS_NOT_REACHED(); } /* Returns a bitmap of OpenFlow versions that are supported by at @@ -786,7 +790,7 @@ ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable) return OFPUTIL_P_OF13_OXM; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -826,7 +830,7 @@ ofputil_protocol_set_base(enum ofputil_protocol cur, return ofputil_protocol_set_tid(OFPUTIL_P_OF13_OXM, tid); default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -914,7 +918,7 @@ ofputil_protocols_to_string(enum ofputil_protocol protocols) goto match; } } - NOT_REACHED(); + OVS_NOT_REACHED(); match: ; } @@ -1067,7 +1071,7 @@ ofputil_version_to_string(enum ofp_version ofp_version) case OFP13_VERSION: return "OpenFlow13"; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -1092,7 +1096,7 @@ ofputil_packet_in_format_to_string(enum nx_packet_in_format packet_in_format) case NXPIF_NXM: return "nxm"; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -1104,152 +1108,6 @@ ofputil_packet_in_format_from_string(const char *s) : -1); } -static bool -regs_fully_wildcarded(const struct flow_wildcards *wc) -{ - int i; - - for (i = 0; i < FLOW_N_REGS; i++) { - if (wc->masks.regs[i] != 0) { - return false; - } - } - return true; -} - -/* Returns a bit-mask of ofputil_protocols that can be used for sending 'match' - * to a switch (e.g. to add or remove a flow). Only NXM can handle tunnel IDs, - * registers, or fixing the Ethernet multicast bit. Otherwise, it's better to - * use OpenFlow 1.0 protocol for backward compatibility. */ -enum ofputil_protocol -ofputil_usable_protocols(const struct match *match) -{ - const struct flow_wildcards *wc = &match->wc; - - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 20); - - /* These tunnel params can't be sent in a flow_mod */ - if (wc->masks.tunnel.ip_ttl - || wc->masks.tunnel.ip_tos || wc->masks.tunnel.flags) { - return OFPUTIL_P_NONE; - } - - /* skb_mark and skb_priority can't be sent in a flow_mod */ - if (wc->masks.skb_mark || wc->masks.skb_priority) { - return OFPUTIL_P_NONE; - } - - /* 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_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - if (!eth_mask_is_exact(wc->masks.dl_dst) - && !eth_addr_is_zero(wc->masks.dl_dst)) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM, OXM, and OF1.1+ support matching metadata. */ - if (wc->masks.metadata != htonll(0)) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* 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_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM and OXM support matching L3 and L4 fields within IPv6. - * - * (arp_sha, arp_tha, nw_frag, and nw_ttl are covered elsewhere so they - * don't need to be included in this test too.) */ - if (match->flow.dl_type == htons(ETH_TYPE_IPV6) - && (!ipv6_mask_is_any(&wc->masks.ipv6_src) - || !ipv6_mask_is_any(&wc->masks.ipv6_dst) - || !ipv6_mask_is_any(&wc->masks.nd_target) - || wc->masks.ipv6_label - || wc->masks.tp_src - || wc->masks.tp_dst - || wc->masks.nw_proto - || wc->masks.nw_tos)) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM and OXM support matching registers. */ - if (!regs_fully_wildcarded(wc)) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM and OXM support matching tun_id, tun_src, and tun_dst. */ - if (wc->masks.tunnel.tun_id != htonll(0) - || wc->masks.tunnel.ip_src != htonl(0) - || wc->masks.tunnel.ip_dst != htonl(0)) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM and OXM support matching fragments. */ - if (wc->masks.nw_frag) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM and OXM support matching IP ECN bits. */ - if (wc->masks.nw_tos & IP_ECN_MASK) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM and OXM support matching IP TTL/hop limit. */ - if (wc->masks.nw_ttl) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* 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_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* 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_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM and OF1.1+ support matching MPLS label */ - if (wc->masks.mpls_lse & htonl(MPLS_LABEL_MASK)) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM and OF1.1+ support matching MPLS TC */ - if (wc->masks.mpls_lse & htonl(MPLS_TC_MASK)) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* NXM and OF1.3+ support matching MPLS stack flag */ - /* Allow for OF1.2 as there doesn't seem to be a - * particularly good reason not to */ - if (wc->masks.mpls_lse & htonl(MPLS_BOS_MASK)) { - return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - - /* Other formats can express this rule. */ - return OFPUTIL_P_ANY; -} - void ofputil_format_version(struct ds *msg, enum ofp_version version) { @@ -1293,7 +1151,7 @@ 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); + const ovs_be32 *bitmap = ALIGNED_CAST(const ovs_be32 *, oheh + 1); uint32_t allowed_versions; if (!bitmap_len || bitmap_len % sizeof *bitmap) { @@ -1372,7 +1230,7 @@ ofputil_decode_hello(const struct ofp_header *oh, uint32_t *allowed_versions) /* 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 +static bool should_send_version_bitmap(uint32_t allowed_versions) { return !is_pow2((allowed_versions >> 1) + 1); @@ -1397,7 +1255,7 @@ ofputil_encode_hello(uint32_t 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); + *ALIGNED_CAST(ovs_be32 *, oheh + 1) = htonl(allowed_versions); ofpmsg_update_length(msg); } @@ -1447,11 +1305,11 @@ ofputil_encode_set_protocol(enum ofputil_protocol current, case OFPUTIL_P_OF13_OXM: /* There is only one variant of each OpenFlow 1.1+ protocol, and we * verified above that we're not trying to change versions. */ - NOT_REACHED(); + OVS_NOT_REACHED(); case OFPUTIL_P_OF10_STD_TID: case OFPUTIL_P_OF10_NXM_TID: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -1520,7 +1378,7 @@ ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format) case NXFF_NXM: return "nxm"; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -1552,6 +1410,74 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id) return msg; } +struct ofputil_flow_mod_flag { + uint16_t raw_flag; + enum ofp_version min_version, max_version; + enum ofputil_flow_mod_flags flag; +}; + +static const struct ofputil_flow_mod_flag ofputil_flow_mod_flags[] = { + { OFPFF_SEND_FLOW_REM, OFP10_VERSION, 0, OFPUTIL_FF_SEND_FLOW_REM }, + { OFPFF_CHECK_OVERLAP, OFP10_VERSION, 0, OFPUTIL_FF_CHECK_OVERLAP }, + { OFPFF10_EMERG, OFP10_VERSION, OFP10_VERSION, + OFPUTIL_FF_EMERG }, + { OFPFF12_RESET_COUNTS, OFP12_VERSION, 0, OFPUTIL_FF_RESET_COUNTS }, + { OFPFF13_NO_PKT_COUNTS, OFP13_VERSION, 0, OFPUTIL_FF_NO_PKT_COUNTS }, + { OFPFF13_NO_BYT_COUNTS, OFP13_VERSION, 0, OFPUTIL_FF_NO_BYT_COUNTS }, + { 0, 0, 0, 0 }, +}; + +static enum ofperr +ofputil_decode_flow_mod_flags(ovs_be16 raw_flags_, + enum ofp_flow_mod_command command, + enum ofp_version version, + enum ofputil_flow_mod_flags *flagsp) +{ + uint16_t raw_flags = ntohs(raw_flags_); + const struct ofputil_flow_mod_flag *f; + + *flagsp = 0; + for (f = ofputil_flow_mod_flags; f->raw_flag; f++) { + if (raw_flags & f->raw_flag + && version >= f->min_version + && (!f->max_version || version <= f->max_version)) { + raw_flags &= ~f->raw_flag; + *flagsp |= f->flag; + } + } + + /* In OF1.0 and OF1.1, "add" always resets counters, and other commands + * never do. + * + * In OF1.2 and later, OFPFF12_RESET_COUNTS controls whether each command + * resets counters. */ + if ((version == OFP10_VERSION || version == OFP11_VERSION) + && command == OFPFC_ADD) { + *flagsp |= OFPUTIL_FF_RESET_COUNTS; + } + + return raw_flags ? OFPERR_OFPFMFC_BAD_FLAGS : 0; +} + +static ovs_be16 +ofputil_encode_flow_mod_flags(enum ofputil_flow_mod_flags flags, + enum ofp_version version) +{ + const struct ofputil_flow_mod_flag *f; + uint16_t raw_flags; + + raw_flags = 0; + for (f = ofputil_flow_mod_flags; f->raw_flag; f++) { + if (f->flag & flags + && version >= f->min_version + && (!f->max_version || version <= f->max_version)) { + raw_flags |= f->raw_flag; + } + } + + return htons(raw_flags); +} + /* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh' into an abstract * flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error * code. @@ -1566,18 +1492,19 @@ enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, const struct ofp_header *oh, enum ofputil_protocol protocol, - struct ofpbuf *ofpacts) + struct ofpbuf *ofpacts, + ofp_port_t max_port, uint8_t max_table) { - uint16_t command; + ovs_be16 raw_flags; + enum ofperr error; struct ofpbuf b; enum ofpraw raw; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); if (raw == OFPRAW_OFPT11_FLOW_MOD) { - /* Standard OpenFlow 1.1 flow_mod. */ + /* Standard OpenFlow 1.1+ flow_mod. */ const struct ofp11_flow_mod *ofm; - enum ofperr error; ofm = ofpbuf_pull(&b, sizeof *ofm); @@ -1586,7 +1513,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, return error; } - error = ofpacts_pull_openflow11_instructions(&b, b.size, ofpacts); + error = ofpacts_pull_openflow_instructions(&b, b.size, oh->version, + ofpacts); if (error) { return error; } @@ -1607,11 +1535,23 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, } else { fm->cookie = ofm->cookie; fm->cookie_mask = ofm->cookie_mask; - fm->new_cookie = htonll(UINT64_MAX); + fm->new_cookie = OVS_BE64_MAX; } fm->modify_cookie = false; fm->command = ofm->command; + + /* Get table ID. + * + * OF1.1 entirely forbids table_id == OFPTT_ALL. + * OF1.2+ allows table_id == OFPTT_ALL only for deletes. */ fm->table_id = ofm->table_id; + if (fm->table_id == OFPTT_ALL + && (oh->version == OFP11_VERSION + || (ofm->command != OFPFC_DELETE && + ofm->command != OFPFC_DELETE_STRICT))) { + return OFPERR_OFPFMFC_BAD_TABLE_ID; + } + fm->idle_timeout = ntohs(ofm->idle_timeout); fm->hard_timeout = ntohs(ofm->hard_timeout); fm->buffer_id = ntohl(ofm->buffer_id); @@ -1619,17 +1559,20 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, if (error) { return error; } + fm->out_group = ntohl(ofm->out_group); + if ((ofm->command == OFPFC_DELETE || ofm->command == OFPFC_DELETE_STRICT) && ofm->out_group != htonl(OFPG_ANY)) { return OFPERR_OFPFMFC_UNKNOWN; } - fm->flags = ntohs(ofm->flags); + raw_flags = ofm->flags; } else { + uint16_t command; + if (raw == OFPRAW_OFPT10_FLOW_MOD) { /* Standard OpenFlow 1.0 flow_mod. */ const struct ofp10_flow_mod *ofm; - enum ofperr error; /* Get the ofp10_flow_mod. */ ofm = ofpbuf_pull(&b, sizeof *ofm); @@ -1639,7 +1582,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, ofputil_normalize_match(&fm->match); /* Now get the actions. */ - error = ofpacts_pull_openflow10(&b, b.size, ofpacts); + error = ofpacts_pull_openflow_actions(&b, b.size, oh->version, + ofpacts); if (error) { return error; } @@ -1659,11 +1603,11 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, fm->hard_timeout = ntohs(ofm->hard_timeout); fm->buffer_id = ntohl(ofm->buffer_id); fm->out_port = u16_to_ofp(ntohs(ofm->out_port)); - fm->flags = ntohs(ofm->flags); + fm->out_group = OFPG11_ANY; + raw_flags = ofm->flags; } else if (raw == OFPRAW_NXT_FLOW_MOD) { /* Nicira extended flow_mod. */ const struct nx_flow_mod *nfm; - enum ofperr error; /* Dissect the message. */ nfm = ofpbuf_pull(&b, sizeof *nfm); @@ -1672,7 +1616,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, if (error) { return error; } - error = ofpacts_pull_openflow10(&b, b.size, ofpacts); + error = ofpacts_pull_openflow_actions(&b, b.size, oh->version, + ofpacts); if (error) { return error; } @@ -1690,24 +1635,13 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, fm->hard_timeout = ntohs(nfm->hard_timeout); fm->buffer_id = ntohl(nfm->buffer_id); fm->out_port = u16_to_ofp(ntohs(nfm->out_port)); - fm->flags = ntohs(nfm->flags); + fm->out_group = OFPG11_ANY; + raw_flags = nfm->flags; } else { - NOT_REACHED(); - } - - if (fm->flags & OFPFF10_EMERG) { - /* We do not support the OpenFlow 1.0 emergency flow cache, which - * is not required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. - * - * OpenFlow 1.0 specifies the error code to use when idle_timeout - * or hard_timeout is nonzero. Otherwise, there is no good error - * code, so just state that the flow table is full. */ - return (fm->hard_timeout || fm->idle_timeout - ? OFPERR_OFPFMFC_BAD_EMERG_TIMEOUT - : OFPERR_OFPFMFC_TABLE_FULL); + OVS_NOT_REACHED(); } - fm->modify_cookie = fm->new_cookie != htonll(UINT64_MAX); + fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX; if (protocol & OFPUTIL_P_TID) { fm->command = command & 0xff; fm->table_id = command >> 8; @@ -1720,7 +1654,27 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, fm->ofpacts = ofpacts->data; fm->ofpacts_len = ofpacts->size; - return 0; + error = ofputil_decode_flow_mod_flags(raw_flags, fm->command, + oh->version, &fm->flags); + if (error) { + return error; + } + + if (fm->flags & OFPUTIL_FF_EMERG) { + /* We do not support the OpenFlow 1.0 emergency flow cache, which + * is not required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. + * + * OpenFlow 1.0 specifies the error code to use when idle_timeout + * or hard_timeout is nonzero. Otherwise, there is no good error + * code, so just state that the flow table is full. */ + return (fm->hard_timeout || fm->idle_timeout + ? OFPERR_OFPFMFC_BAD_EMERG_TIMEOUT + : OFPERR_OFPFMFC_TABLE_FULL); + } + + return ofpacts_check_consistency(fm->ofpacts, fm->ofpacts_len, + &fm->match.flow, max_port, + fm->table_id, max_table, protocol); } static enum ofperr @@ -1744,13 +1698,17 @@ ofputil_pull_bands(struct ofpbuf *msg, size_t len, uint16_t *n_bands, } mb = ofpbuf_put_uninit(bands, sizeof *mb); mb->type = ntohs(ombh->type); + if (mb->type != OFPMBT13_DROP && mb->type != OFPMBT13_DSCP_REMARK) { + return OFPERR_OFPMMFC_BAD_BAND; + } mb->rate = ntohl(ombh->rate); mb->burst_size = ntohl(ombh->burst_size); mb->prec_level = (mb->type == OFPMBT13_DSCP_REMARK) ? ((struct ofp13_meter_band_dscp_remark *)ombh)->prec_level : 0; n++; len -= ombh_len; - ombh = (struct ofp13_meter_band_header *)(((char *)ombh) + ombh_len); + ombh = ALIGNED_CAST(struct ofp13_meter_band_header *, + (char *) ombh + ombh_len); } if (len) { return OFPERR_OFPBRC_BAD_LEN; @@ -1773,6 +1731,11 @@ ofputil_decode_meter_mod(const struct ofp_header *oh, /* Translate the message. */ mm->command = ntohs(omm->command); + if (mm->command != OFPMC13_ADD && + mm->command != OFPMC13_MODIFY && + mm->command != OFPMC13_DELETE) { + return OFPERR_OFPMMFC_BAD_COMMAND; + } mm->meter.meter_id = ntohl(omm->meter_id); if (mm->command == OFPMC13_DELETE) { @@ -1783,6 +1746,10 @@ ofputil_decode_meter_mod(const struct ofp_header *oh, enum ofperr error; mm->meter.flags = ntohs(omm->flags); + if (mm->meter.flags & OFPMF13_KBPS && + mm->meter.flags & OFPMF13_PKTPS) { + return OFPERR_OFPMMFC_BAD_FLAGS; + } mm->meter.bands = bands->data; error = ofputil_pull_bands(&b, b.size, &mm->meter.n_bands, bands); @@ -1934,7 +1901,7 @@ ofputil_decode_meter_config(struct ofpbuf *msg, omc = ofpbuf_try_pull(msg, sizeof *omc); if (!omc) { VLOG_WARN_RL(&bad_ofmsg_rl, - "OFPMP_METER_CONFIG reply has %zu leftover bytes at end", + "OFPMP_METER_CONFIG reply has %"PRIuSIZE" leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } @@ -2010,7 +1977,7 @@ ofputil_decode_meter_stats(struct ofpbuf *msg, oms = ofpbuf_try_pull(msg, sizeof *oms); if (!oms) { VLOG_WARN_RL(&bad_ofmsg_rl, - "OFPMP_METER reply has %zu leftover bytes at end", + "OFPMP_METER reply has %"PRIuSIZE" leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } @@ -2102,6 +2069,8 @@ struct ofpbuf * ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, enum ofputil_protocol protocol) { + enum ofp_version version = ofputil_protocol_to_ofp_version(protocol); + ovs_be16 raw_flags = ofputil_encode_flow_mod_flags(fm->flags, version); struct ofpbuf *msg; switch (protocol) { @@ -2112,9 +2081,7 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, int tailroom; tailroom = ofputil_match_typical_len(protocol) + fm->ofpacts_len; - msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, - ofputil_protocol_to_ofp_version(protocol), - tailroom); + msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, version, tailroom); ofm = ofpbuf_put_zeros(msg, sizeof *ofm); if ((protocol == OFPUTIL_P_OF11_STD && (fm->command == OFPFC_MODIFY || @@ -2126,17 +2093,25 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, ofm->cookie = fm->cookie; } ofm->cookie_mask = fm->cookie_mask; - ofm->table_id = fm->table_id; + if (fm->table_id != OFPTT_ALL + || (protocol != OFPUTIL_P_OF11_STD + && (fm->command == OFPFC_DELETE || + fm->command == OFPFC_DELETE_STRICT))) { + ofm->table_id = fm->table_id; + } else { + ofm->table_id = 0; + } ofm->command = fm->command; ofm->idle_timeout = htons(fm->idle_timeout); ofm->hard_timeout = htons(fm->hard_timeout); ofm->priority = htons(fm->priority); ofm->buffer_id = htonl(fm->buffer_id); ofm->out_port = ofputil_port_to_ofp11(fm->out_port); - ofm->out_group = htonl(OFPG11_ANY); - ofm->flags = htons(fm->flags); + ofm->out_group = htonl(fm->out_group); + ofm->flags = raw_flags; ofputil_put_ofp11_match(msg, &fm->match, protocol); - ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg); + ofpacts_put_openflow_instructions(fm->ofpacts, fm->ofpacts_len, msg, + version); break; } @@ -2155,8 +2130,9 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, ofm->priority = htons(fm->priority); ofm->buffer_id = htonl(fm->buffer_id); ofm->out_port = htons(ofp_to_u16(fm->out_port)); - ofm->flags = htons(fm->flags); - ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg); + ofm->flags = raw_flags; + ofpacts_put_openflow_actions(fm->ofpacts, fm->ofpacts_len, msg, + version); break; } @@ -2177,53 +2153,21 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, nfm->priority = htons(fm->priority); nfm->buffer_id = htonl(fm->buffer_id); nfm->out_port = htons(ofp_to_u16(fm->out_port)); - nfm->flags = htons(fm->flags); + nfm->flags = raw_flags; nfm->match_len = htons(match_len); - ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg); + ofpacts_put_openflow_actions(fm->ofpacts, fm->ofpacts_len, msg, + version); break; } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } ofpmsg_update_length(msg); return msg; } -/* Returns a bitmask with a 1-bit for each protocol that could be used to - * send all of the 'n_fm's flow table modification requests in 'fms', and a - * 0-bit for each protocol that is inadequate. - * - * (The return value will have at least one 1-bit.) */ -enum ofputil_protocol -ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms, - size_t n_fms) -{ - enum ofputil_protocol usable_protocols; - size_t i; - - usable_protocols = OFPUTIL_P_ANY; - for (i = 0; i < n_fms; i++) { - const struct ofputil_flow_mod *fm = &fms[i]; - - usable_protocols &= ofputil_usable_protocols(&fm->match); - if (fm->table_id != 0xff) { - usable_protocols &= OFPUTIL_P_TID; - } - - /* Matching of the cookie is only supported through NXM or OF1.1+. */ - if (fm->cookie_mask != htonll(0)) { - usable_protocols &= (OFPUTIL_P_OF10_NXM_ANY - | OFPUTIL_P_OF11_STD - | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM); - } - } - - return usable_protocols; -} - static enum ofperr ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr, const struct ofp10_flow_stats_request *ofsr, @@ -2232,6 +2176,7 @@ ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr, fsr->aggregate = aggregate; ofputil_match_from_ofp10_match(&ofsr->match, &fsr->match); fsr->out_port = u16_to_ofp(ntohs(ofsr->out_port)); + fsr->out_group = OFPG11_ANY; fsr->table_id = ofsr->table_id; fsr->cookie = fsr->cookie_mask = htonll(0); @@ -2252,9 +2197,7 @@ ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr, if (error) { return error; } - if (ofsr->out_group != htonl(OFPG11_ANY)) { - return OFPERR_OFPFMFC_UNKNOWN; - } + fsr->out_group = ntohl(ofsr->out_group); fsr->cookie = ofsr->cookie; fsr->cookie_mask = ofsr->cookie_mask; error = ofputil_pull_ofp11_match(b, &fsr->match, NULL); @@ -2284,11 +2227,289 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr, fsr->aggregate = aggregate; fsr->out_port = u16_to_ofp(ntohs(nfsr->out_port)); + fsr->out_group = OFPG11_ANY; fsr->table_id = nfsr->table_id; return 0; } +/* Constructs and returns an OFPT_QUEUE_GET_CONFIG request for the specified + * 'port', suitable for OpenFlow version 'version'. */ +struct ofpbuf * +ofputil_encode_queue_get_config_request(enum ofp_version version, + ofp_port_t port) +{ + struct ofpbuf *request; + + if (version == OFP10_VERSION) { + struct ofp10_queue_get_config_request *qgcr10; + + request = ofpraw_alloc(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST, + version, 0); + qgcr10 = ofpbuf_put_zeros(request, sizeof *qgcr10); + qgcr10->port = htons(ofp_to_u16(port)); + } else { + struct ofp11_queue_get_config_request *qgcr11; + + request = ofpraw_alloc(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST, + version, 0); + qgcr11 = ofpbuf_put_zeros(request, sizeof *qgcr11); + qgcr11->port = ofputil_port_to_ofp11(port); + } + + return request; +} + +/* Parses OFPT_QUEUE_GET_CONFIG request 'oh', storing the port specified by the + * request into '*port'. Returns 0 if successful, otherwise an OpenFlow error + * code. */ +enum ofperr +ofputil_decode_queue_get_config_request(const struct ofp_header *oh, + ofp_port_t *port) +{ + const struct ofp10_queue_get_config_request *qgcr10; + const struct ofp11_queue_get_config_request *qgcr11; + enum ofpraw raw; + struct ofpbuf b; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + raw = ofpraw_pull_assert(&b); + + switch ((int) raw) { + case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST: + qgcr10 = b.data; + *port = u16_to_ofp(ntohs(qgcr10->port)); + return 0; + + case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST: + qgcr11 = b.data; + return ofputil_port_from_ofp11(qgcr11->port, port); + } + + OVS_NOT_REACHED(); +} + +/* Constructs and returns the beginning of a reply to + * OFPT_QUEUE_GET_CONFIG_REQUEST 'oh'. The caller may append information about + * individual queues with ofputil_append_queue_get_config_reply(). */ +struct ofpbuf * +ofputil_encode_queue_get_config_reply(const struct ofp_header *oh) +{ + struct ofp10_queue_get_config_reply *qgcr10; + struct ofp11_queue_get_config_reply *qgcr11; + struct ofpbuf *reply; + enum ofperr error; + struct ofpbuf b; + enum ofpraw raw; + ofp_port_t port; + + error = ofputil_decode_queue_get_config_request(oh, &port); + ovs_assert(!error); + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + raw = ofpraw_pull_assert(&b); + + switch ((int) raw) { + case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REQUEST: + reply = ofpraw_alloc_reply(OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY, + oh, 0); + qgcr10 = ofpbuf_put_zeros(reply, sizeof *qgcr10); + qgcr10->port = htons(ofp_to_u16(port)); + break; + + case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REQUEST: + reply = ofpraw_alloc_reply(OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY, + oh, 0); + qgcr11 = ofpbuf_put_zeros(reply, sizeof *qgcr11); + qgcr11->port = ofputil_port_to_ofp11(port); + break; + + default: + OVS_NOT_REACHED(); + } + + return reply; +} + +static void +put_queue_rate(struct ofpbuf *reply, enum ofp_queue_properties property, + uint16_t rate) +{ + if (rate != UINT16_MAX) { + struct ofp_queue_prop_rate *oqpr; + + oqpr = ofpbuf_put_zeros(reply, sizeof *oqpr); + oqpr->prop_header.property = htons(property); + oqpr->prop_header.len = htons(sizeof *oqpr); + oqpr->rate = htons(rate); + } +} + +/* Appends a queue description for 'queue_id' to the + * OFPT_QUEUE_GET_CONFIG_REPLY already in 'oh'. */ +void +ofputil_append_queue_get_config_reply(struct ofpbuf *reply, + const struct ofputil_queue_config *oqc) +{ + const struct ofp_header *oh = reply->data; + size_t start_ofs, len_ofs; + ovs_be16 *len; + + start_ofs = reply->size; + if (oh->version < OFP12_VERSION) { + struct ofp10_packet_queue *opq10; + + opq10 = ofpbuf_put_zeros(reply, sizeof *opq10); + opq10->queue_id = htonl(oqc->queue_id); + len_ofs = (char *) &opq10->len - (char *) reply->data; + } else { + struct ofp11_queue_get_config_reply *qgcr11; + struct ofp12_packet_queue *opq12; + ovs_be32 port; + + qgcr11 = reply->l3; + port = qgcr11->port; + + opq12 = ofpbuf_put_zeros(reply, sizeof *opq12); + opq12->port = port; + opq12->queue_id = htonl(oqc->queue_id); + len_ofs = (char *) &opq12->len - (char *) reply->data; + } + + put_queue_rate(reply, OFPQT_MIN_RATE, oqc->min_rate); + put_queue_rate(reply, OFPQT_MAX_RATE, oqc->max_rate); + + len = ofpbuf_at(reply, len_ofs, sizeof *len); + *len = htons(reply->size - start_ofs); +} + +/* Decodes the initial part of an OFPT_QUEUE_GET_CONFIG_REPLY from 'reply' and + * stores in '*port' the port that the reply is about. The caller may call + * ofputil_pull_queue_get_config_reply() to obtain information about individual + * queues included in the reply. Returns 0 if successful, otherwise an + * ofperr.*/ +enum ofperr +ofputil_decode_queue_get_config_reply(struct ofpbuf *reply, ofp_port_t *port) +{ + const struct ofp10_queue_get_config_reply *qgcr10; + const struct ofp11_queue_get_config_reply *qgcr11; + enum ofpraw raw; + + raw = ofpraw_pull_assert(reply); + switch ((int) raw) { + case OFPRAW_OFPT10_QUEUE_GET_CONFIG_REPLY: + qgcr10 = ofpbuf_pull(reply, sizeof *qgcr10); + *port = u16_to_ofp(ntohs(qgcr10->port)); + return 0; + + case OFPRAW_OFPT11_QUEUE_GET_CONFIG_REPLY: + qgcr11 = ofpbuf_pull(reply, sizeof *qgcr11); + return ofputil_port_from_ofp11(qgcr11->port, port); + } + + OVS_NOT_REACHED(); +} + +static enum ofperr +parse_queue_rate(const struct ofp_queue_prop_header *hdr, uint16_t *rate) +{ + const struct ofp_queue_prop_rate *oqpr; + + if (hdr->len == htons(sizeof *oqpr)) { + oqpr = (const struct ofp_queue_prop_rate *) hdr; + *rate = ntohs(oqpr->rate); + return 0; + } else { + return OFPERR_OFPBRC_BAD_LEN; + } +} + +/* Decodes information about a queue from the OFPT_QUEUE_GET_CONFIG_REPLY in + * 'reply' and stores it in '*queue'. ofputil_decode_queue_get_config_reply() + * must already have pulled off the main header. + * + * This function returns EOF if the last queue has already been decoded, 0 if a + * queue was successfully decoded into '*queue', or an ofperr if there was a + * problem decoding 'reply'. */ +int +ofputil_pull_queue_get_config_reply(struct ofpbuf *reply, + struct ofputil_queue_config *queue) +{ + const struct ofp_header *oh; + unsigned int opq_len; + unsigned int len; + + if (!reply->size) { + return EOF; + } + + queue->min_rate = UINT16_MAX; + queue->max_rate = UINT16_MAX; + + oh = reply->l2; + if (oh->version < OFP12_VERSION) { + const struct ofp10_packet_queue *opq10; + + opq10 = ofpbuf_try_pull(reply, sizeof *opq10); + if (!opq10) { + return OFPERR_OFPBRC_BAD_LEN; + } + queue->queue_id = ntohl(opq10->queue_id); + len = ntohs(opq10->len); + opq_len = sizeof *opq10; + } else { + const struct ofp12_packet_queue *opq12; + + opq12 = ofpbuf_try_pull(reply, sizeof *opq12); + if (!opq12) { + return OFPERR_OFPBRC_BAD_LEN; + } + queue->queue_id = ntohl(opq12->queue_id); + len = ntohs(opq12->len); + opq_len = sizeof *opq12; + } + + if (len < opq_len || len > reply->size + opq_len || len % 8) { + return OFPERR_OFPBRC_BAD_LEN; + } + len -= opq_len; + + while (len > 0) { + const struct ofp_queue_prop_header *hdr; + unsigned int property; + unsigned int prop_len; + enum ofperr error = 0; + + hdr = ofpbuf_at_assert(reply, 0, sizeof *hdr); + prop_len = ntohs(hdr->len); + if (prop_len < sizeof *hdr || prop_len > reply->size || prop_len % 8) { + return OFPERR_OFPBRC_BAD_LEN; + } + + property = ntohs(hdr->property); + switch (property) { + case OFPQT_MIN_RATE: + error = parse_queue_rate(hdr, &queue->min_rate); + break; + + case OFPQT_MAX_RATE: + error = parse_queue_rate(hdr, &queue->max_rate); + break; + + default: + VLOG_INFO_RL(&bad_ofmsg_rl, "unknown queue property %u", property); + break; + } + if (error) { + return error; + } + + ofpbuf_pull(reply, prop_len); + len -= prop_len; + } + return 0; +} + /* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE * request 'oh', into an abstract flow_stats_request in 'fsr'. Returns 0 if * successful, otherwise an OpenFlow error code. */ @@ -2322,7 +2543,7 @@ ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr, default: /* Hey, the caller lied. */ - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -2350,7 +2571,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr); ofsr->table_id = fsr->table_id; ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port); - ofsr->out_group = htonl(OFPG11_ANY); + ofsr->out_group = htonl(fsr->out_group); ofsr->cookie = fsr->cookie; ofsr->cookie_mask = fsr->cookie_mask; ofputil_put_ofp11_match(msg, &fsr->match, protocol); @@ -2393,30 +2614,12 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } return msg; } -/* Returns a bitmask with a 1-bit for each protocol that could be used to - * accurately encode 'fsr', and a 0-bit for each protocol that is inadequate. - * - * (The return value will have at least one 1-bit.) */ -enum ofputil_protocol -ofputil_flow_stats_request_usable_protocols( - const struct ofputil_flow_stats_request *fsr) -{ - enum ofputil_protocol usable_protocols; - - usable_protocols = ofputil_usable_protocols(&fsr->match); - if (fsr->cookie_mask != htonll(0)) { - usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM - | OFPUTIL_P_OF13_OXM; - } - return usable_protocols; -} - /* Converts an OFPST_FLOW or NXST_FLOW reply in 'msg' into an abstract * ofputil_flow_stats in 'fs'. * @@ -2443,6 +2646,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, bool flow_age_extension, struct ofpbuf *ofpacts) { + const struct ofp_header *oh; enum ofperr error; enum ofpraw raw; @@ -2452,6 +2656,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, if (error) { return error; } + oh = msg->l2; if (!msg->size) { return EOF; @@ -2463,7 +2668,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, ofs = ofpbuf_try_pull(msg, sizeof *ofs); if (!ofs) { - VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %zu leftover " + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %"PRIuSIZE" leftover " "bytes at end", msg->size); return EINVAL; } @@ -2471,7 +2676,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, length = ntohs(ofs->length); if (length < sizeof *ofs) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid " - "length %zu", length); + "length %"PRIuSIZE, length); return EINVAL; } @@ -2480,8 +2685,9 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, return EINVAL; } - if (ofpacts_pull_openflow11_instructions(msg, length - sizeof *ofs - - padded_match_len, ofpacts)) { + if (ofpacts_pull_openflow_instructions(msg, length - sizeof *ofs - + padded_match_len, oh->version, + ofpacts)) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions"); return EINVAL; } @@ -2492,9 +2698,17 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, fs->duration_nsec = ntohl(ofs->duration_nsec); fs->idle_timeout = ntohs(ofs->idle_timeout); fs->hard_timeout = ntohs(ofs->hard_timeout); - fs->flags = (raw == OFPRAW_OFPST13_FLOW_REPLY) ? ntohs(ofs->flags) : 0; - fs->idle_age = -1; - fs->hard_age = -1; + if (raw == OFPRAW_OFPST13_FLOW_REPLY) { + error = ofputil_decode_flow_mod_flags(ofs->flags, -1, oh->version, + &fs->flags); + if (error) { + return error; + } + } else { + fs->flags = 0; + } + fs->idle_age = -1; + fs->hard_age = -1; fs->cookie = ofs->cookie; fs->packet_count = ntohll(ofs->packet_count); fs->byte_count = ntohll(ofs->byte_count); @@ -2504,7 +2718,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, ofs = ofpbuf_try_pull(msg, sizeof *ofs); if (!ofs) { - VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %zu leftover " + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %"PRIuSIZE" leftover " "bytes at end", msg->size); return EINVAL; } @@ -2512,11 +2726,12 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, length = ntohs(ofs->length); if (length < sizeof *ofs) { VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid " - "length %zu", length); + "length %"PRIuSIZE, length); return EINVAL; } - if (ofpacts_pull_openflow10(msg, length - sizeof *ofs, ofpacts)) { + if (ofpacts_pull_openflow_actions(msg, length - sizeof *ofs, + oh->version, ofpacts)) { return EINVAL; } @@ -2539,7 +2754,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, nfs = ofpbuf_try_pull(msg, sizeof *nfs); if (!nfs) { - VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply has %zu leftover " + VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply has %"PRIuSIZE" leftover " "bytes at end", msg->size); return EINVAL; } @@ -2547,8 +2762,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, length = ntohs(nfs->length); match_len = ntohs(nfs->match_len); if (length < sizeof *nfs + ROUND_UP(match_len, 8)) { - VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply with match_len=%zu " - "claims invalid length %zu", match_len, length); + VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply with match_len=%"PRIuSIZE" " + "claims invalid length %"PRIuSIZE, match_len, length); return EINVAL; } if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL)) { @@ -2556,7 +2771,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, } actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8); - if (ofpacts_pull_openflow10(msg, actions_len, ofpacts)) { + if (ofpacts_pull_openflow_actions(msg, actions_len, oh->version, + ofpacts)) { return EINVAL; } @@ -2581,7 +2797,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, fs->byte_count = ntohll(nfs->byte_count); fs->flags = 0; } else { - NOT_REACHED(); + OVS_NOT_REACHED(); } fs->ofpacts = ofpacts->data; @@ -2602,7 +2818,7 @@ unknown_to_zero(uint64_t count) /* Appends an OFPST_FLOW or NXST_FLOW reply that contains the data in 'fs' to * those already present in the list of ofpbufs in 'replies'. 'replies' should - * have been initialized with ofputil_start_stats_reply(). */ + * have been initialized with ofpmp_init(). */ void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, struct list *replies) @@ -2610,6 +2826,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); size_t start_ofs = reply->size; enum ofpraw raw; + enum ofp_version version = ((struct ofp_header *)reply->data)->version; ofpraw_decode_partial(&raw, reply->data, reply->size); if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) { @@ -2617,8 +2834,8 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, ofpbuf_put_uninit(reply, sizeof *ofs); oxm_put_match(reply, &fs->match); - ofpacts_put_openflow11_instructions(fs->ofpacts, fs->ofpacts_len, - reply); + ofpacts_put_openflow_instructions(fs->ofpacts, fs->ofpacts_len, reply, + version); ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs); ofs->length = htons(reply->size - start_ofs); @@ -2629,7 +2846,11 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, ofs->priority = htons(fs->priority); ofs->idle_timeout = htons(fs->idle_timeout); ofs->hard_timeout = htons(fs->hard_timeout); - ofs->flags = (raw == OFPRAW_OFPST13_FLOW_REPLY) ? htons(fs->flags) : 0; + if (raw == OFPRAW_OFPST13_FLOW_REPLY) { + ofs->flags = ofputil_encode_flow_mod_flags(fs->flags, version); + } else { + ofs->flags = 0; + } memset(ofs->pad2, 0, sizeof ofs->pad2); ofs->cookie = fs->cookie; ofs->packet_count = htonll(unknown_to_zero(fs->packet_count)); @@ -2638,8 +2859,8 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, struct ofp10_flow_stats *ofs; ofpbuf_put_uninit(reply, sizeof *ofs); - ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply); - + ofpacts_put_openflow_actions(fs->ofpacts, fs->ofpacts_len, reply, + version); ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs); ofs->length = htons(reply->size - start_ofs); ofs->table_id = fs->table_id; @@ -2662,8 +2883,8 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, ofpbuf_put_uninit(reply, sizeof *nfs); match_len = nx_put_match(reply, &fs->match, 0, 0); - ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply); - + ofpacts_put_openflow_actions(fs->ofpacts, fs->ofpacts_len, reply, + version); nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs); nfs->length = htons(reply->size - start_ofs); nfs->table_id = fs->table_id; @@ -2684,7 +2905,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, nfs->packet_count = htonll(fs->packet_count); nfs->byte_count = htonll(fs->byte_count); } else { - NOT_REACHED(); + OVS_NOT_REACHED(); } ofpmp_postappend(replies, start_ofs); @@ -2813,7 +3034,7 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, fr->packet_count = ntohll(nfr->packet_count); fr->byte_count = ntohll(nfr->byte_count); } else { - NOT_REACHED(); + OVS_NOT_REACHED(); } return 0; @@ -2897,7 +3118,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } return msg; @@ -2916,6 +3137,7 @@ ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin, pin->fmd.tun_dst = match->flow.tunnel.ip_dst; pin->fmd.metadata = match->flow.metadata; memcpy(pin->fmd.regs, match->flow.regs, sizeof pin->fmd.regs); + pin->fmd.pkt_mark = match->flow.pkt_mark; } enum ofperr @@ -2926,6 +3148,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, struct ofpbuf b; memset(pin, 0, sizeof *pin); + pin->cookie = OVS_BE64_MAX; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); @@ -2973,6 +3196,23 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, pin->reason = opi->reason; pin->buffer_id = ntohl(opi->buffer_id); pin->total_len = ntohs(opi->total_len); + } else if (raw == OFPRAW_OFPT11_PACKET_IN) { + const struct ofp11_packet_in *opi; + enum ofperr error; + + opi = ofpbuf_pull(&b, sizeof *opi); + + pin->packet = b.data; + pin->packet_len = b.size; + + pin->buffer_id = ntohl(opi->buffer_id); + error = ofputil_port_from_ofp11(opi->in_port, &pin->fmd.in_port); + if (error) { + return error; + } + pin->total_len = ntohs(opi->total_len); + pin->reason = opi->reason; + pin->table_id = opi->table_id; } else if (raw == OFPRAW_NXT_PACKET_IN) { const struct nx_packet_in *npi; struct match match; @@ -2998,7 +3238,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, ofputil_decode_packet_in_finish(pin, &match, &b); } else { - NOT_REACHED(); + OVS_NOT_REACHED(); } return 0; @@ -3030,9 +3270,127 @@ ofputil_packet_in_to_match(const struct ofputil_packet_in *pin, } } + if (pin->fmd.pkt_mark != 0) { + match_set_pkt_mark(match, pin->fmd.pkt_mark); + } + match_set_in_port(match, pin->fmd.in_port); } +static struct ofpbuf * +ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin) +{ + struct ofp10_packet_in *opi; + struct ofpbuf *packet; + + packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION, + htonl(0), pin->packet_len); + opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data)); + opi->total_len = htons(pin->total_len); + opi->in_port = htons(ofp_to_u16(pin->fmd.in_port)); + opi->reason = pin->reason; + opi->buffer_id = htonl(pin->buffer_id); + + ofpbuf_put(packet, pin->packet, pin->packet_len); + + return packet; +} + +static struct ofpbuf * +ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin) +{ + struct nx_packet_in *npi; + struct ofpbuf *packet; + struct match match; + size_t match_len; + + ofputil_packet_in_to_match(pin, &match); + + /* The final argument is just an estimate of the space required. */ + packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION, + htonl(0), (sizeof(struct flow_metadata) * 2 + + 2 + pin->packet_len)); + ofpbuf_put_zeros(packet, sizeof *npi); + match_len = nx_put_match(packet, &match, 0, 0); + ofpbuf_put_zeros(packet, 2); + ofpbuf_put(packet, pin->packet, pin->packet_len); + + npi = packet->l3; + npi->buffer_id = htonl(pin->buffer_id); + npi->total_len = htons(pin->total_len); + npi->reason = pin->reason; + npi->table_id = pin->table_id; + npi->cookie = pin->cookie; + npi->match_len = htons(match_len); + + return packet; +} + +static struct ofpbuf * +ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin) +{ + struct ofp11_packet_in *opi; + struct ofpbuf *packet; + + packet = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION, + htonl(0), pin->packet_len); + opi = ofpbuf_put_zeros(packet, sizeof *opi); + opi->buffer_id = htonl(pin->buffer_id); + opi->in_port = ofputil_port_to_ofp11(pin->fmd.in_port); + opi->in_phy_port = opi->in_port; + opi->total_len = htons(pin->total_len); + opi->reason = pin->reason; + opi->table_id = pin->table_id; + + ofpbuf_put(packet, pin->packet, pin->packet_len); + + return packet; +} + +static struct ofpbuf * +ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin, + enum ofputil_protocol protocol) +{ + struct ofp13_packet_in *opi; + struct match match; + enum ofpraw packet_in_raw; + enum ofp_version packet_in_version; + size_t packet_in_size; + struct ofpbuf *packet; + + if (protocol == OFPUTIL_P_OF12_OXM) { + packet_in_raw = OFPRAW_OFPT12_PACKET_IN; + packet_in_version = OFP12_VERSION; + packet_in_size = sizeof (struct ofp12_packet_in); + } else { + packet_in_raw = OFPRAW_OFPT13_PACKET_IN; + packet_in_version = OFP13_VERSION; + packet_in_size = sizeof (struct ofp13_packet_in); + } + + ofputil_packet_in_to_match(pin, &match); + + /* The final argument is just an estimate of the space required. */ + packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version, + htonl(0), (sizeof(struct flow_metadata) * 2 + + 2 + pin->packet_len)); + ofpbuf_put_zeros(packet, packet_in_size); + oxm_put_match(packet, &match); + ofpbuf_put_zeros(packet, 2); + ofpbuf_put(packet, pin->packet, pin->packet_len); + + opi = packet->l3; + opi->pi.buffer_id = htonl(pin->buffer_id); + opi->pi.total_len = htons(pin->total_len); + opi->pi.reason = pin->reason; + opi->pi.table_id = pin->table_id; + if (protocol == OFPUTIL_P_OF13_OXM) { + opi->cookie = pin->cookie; + } + + return packet; +} + /* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message * in the format specified by 'packet_in_format'. */ struct ofpbuf * @@ -3040,86 +3398,32 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin, enum ofputil_protocol protocol, enum nx_packet_in_format packet_in_format) { - size_t send_len = MIN(pin->send_len, pin->packet_len); struct ofpbuf *packet; - /* Add OFPT_PACKET_IN. */ - if (protocol == OFPUTIL_P_OF13_OXM || protocol == OFPUTIL_P_OF12_OXM) { - struct ofp13_packet_in *opi; - struct match match; - enum ofpraw packet_in_raw; - enum ofp_version packet_in_version; - size_t packet_in_size; + switch (protocol) { + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: + packet = (packet_in_format == NXPIF_NXM + ? ofputil_encode_nx_packet_in(pin) + : ofputil_encode_ofp10_packet_in(pin)); + break; - if (protocol == OFPUTIL_P_OF12_OXM) { - packet_in_raw = OFPRAW_OFPT12_PACKET_IN; - packet_in_version = OFP12_VERSION; - packet_in_size = sizeof (struct ofp12_packet_in); - } else { - packet_in_raw = OFPRAW_OFPT13_PACKET_IN; - packet_in_version = OFP13_VERSION; - packet_in_size = sizeof (struct ofp13_packet_in); - } + case OFPUTIL_P_OF11_STD: + packet = ofputil_encode_ofp11_packet_in(pin); + break; - ofputil_packet_in_to_match(pin, &match); - - /* The final argument is just an estimate of the space required. */ - packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version, - htonl(0), (sizeof(struct flow_metadata) * 2 - + 2 + send_len)); - ofpbuf_put_zeros(packet, packet_in_size); - oxm_put_match(packet, &match); - ofpbuf_put_zeros(packet, 2); - ofpbuf_put(packet, pin->packet, send_len); - - opi = packet->l3; - opi->pi.buffer_id = htonl(pin->buffer_id); - opi->pi.total_len = htons(pin->total_len); - opi->pi.reason = pin->reason; - opi->pi.table_id = pin->table_id; - if (protocol == OFPUTIL_P_OF13_OXM) { - opi->cookie = pin->cookie; - } - } else if (packet_in_format == NXPIF_OPENFLOW10) { - struct ofp10_packet_in *opi; - - packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION, - htonl(0), send_len); - opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data)); - opi->total_len = htons(pin->total_len); - opi->in_port = htons(ofp_to_u16(pin->fmd.in_port)); - opi->reason = pin->reason; - opi->buffer_id = htonl(pin->buffer_id); - - ofpbuf_put(packet, pin->packet, send_len); - } else if (packet_in_format == NXPIF_NXM) { - struct nx_packet_in *npi; - struct match match; - size_t match_len; - - ofputil_packet_in_to_match(pin, &match); - - /* The final argument is just an estimate of the space required. */ - packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION, - htonl(0), (sizeof(struct flow_metadata) * 2 - + 2 + send_len)); - ofpbuf_put_zeros(packet, sizeof *npi); - match_len = nx_put_match(packet, &match, 0, 0); - ofpbuf_put_zeros(packet, 2); - ofpbuf_put(packet, pin->packet, send_len); - - npi = packet->l3; - npi->buffer_id = htonl(pin->buffer_id); - npi->total_len = htons(pin->total_len); - npi->reason = pin->reason; - npi->table_id = pin->table_id; - npi->cookie = pin->cookie; - npi->match_len = htons(match_len); - } else { - NOT_REACHED(); + case OFPUTIL_P_OF12_OXM: + case OFPUTIL_P_OF13_OXM: + packet = ofputil_encode_ofp12_packet_in(pin, protocol); + break; + + default: + OVS_NOT_REACHED(); } - ofpmsg_update_length(packet); + ofpmsg_update_length(packet); return packet; } @@ -3194,8 +3498,8 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, return error; } - error = ofpacts_pull_openflow11_actions(&b, ntohs(opo->actions_len), - ofpacts); + error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len), + oh->version, ofpacts); if (error) { return error; } @@ -3206,12 +3510,13 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, po->buffer_id = ntohl(opo->buffer_id); po->in_port = u16_to_ofp(ntohs(opo->in_port)); - error = ofpacts_pull_openflow10(&b, ntohs(opo->actions_len), ofpacts); + error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len), + oh->version, ofpacts); if (error) { return error; } } else { - NOT_REACHED(); + OVS_NOT_REACHED(); } if (ofp_to_u16(po->in_port) >= ofp_to_u16(OFPP_MAX) @@ -3360,7 +3665,7 @@ ofputil_get_phy_port_size(enum ofp_version ofp_version) case OFP13_VERSION: return sizeof(struct ofp11_port); default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -3431,7 +3736,7 @@ ofputil_put_phy_port(enum ofp_version ofp_version, } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -3460,7 +3765,7 @@ ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version, } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -3651,7 +3956,7 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features, raw = OFPRAW_OFPT13_FEATURES_REPLY; break; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } b = ofpraw_alloc_xid(raw, version, xid, 0); osf = ofpbuf_put_zeros(b, sizeof *osf); @@ -3679,7 +3984,7 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features, } break; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } return b; @@ -3752,7 +4057,7 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps, break; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } b = ofpraw_alloc_xid(raw, version, htonl(0), 0); @@ -3762,7 +4067,7 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps, ofpmsg_update_length(b); return b; } - + /* ofputil_port_mod */ /* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in @@ -3844,9 +4149,68 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm, opm->advertise = netdev_port_features_to_ofp11(pm->advertise); break; } + default: + OVS_NOT_REACHED(); + } + + return b; +} + +/* ofputil_table_mod */ + +/* Decodes the OpenFlow "table mod" message in '*oh' into an abstract form in + * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */ +enum ofperr +ofputil_decode_table_mod(const struct ofp_header *oh, + struct ofputil_table_mod *pm) +{ + enum ofpraw raw; + struct ofpbuf b; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + raw = ofpraw_pull_assert(&b); + + if (raw == OFPRAW_OFPT11_TABLE_MOD) { + const struct ofp11_table_mod *otm = b.data; + + pm->table_id = otm->table_id; + pm->config = ntohl(otm->config); + } else { + return OFPERR_OFPBRC_BAD_TYPE; + } + + return 0; +} + +/* Converts the abstract form of a "table mod" message in '*pm' into an OpenFlow + * message suitable for 'protocol', and returns that encoded form in a buffer + * owned by the caller. */ +struct ofpbuf * +ofputil_encode_table_mod(const struct ofputil_table_mod *pm, + enum ofputil_protocol protocol) +{ + enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); + struct ofpbuf *b; + + switch (ofp_version) { + case OFP10_VERSION: { + ovs_fatal(0, "table mod needs OpenFlow 1.1 or later " + "(\'-O OpenFlow11\')"); + break; + } + case OFP11_VERSION: + case OFP12_VERSION: + case OFP13_VERSION: { + struct ofp11_table_mod *otm; + b = ofpraw_alloc(OFPRAW_OFPT11_TABLE_MOD, ofp_version, 0); + otm = ofpbuf_put_zeros(b, sizeof *otm); + otm->table_id = pm->table_id; + otm->config = htonl(pm->config); + break; + } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } return b; @@ -3881,7 +4245,7 @@ ofputil_decode_role_message(const struct ofp_header *oh, rr->role = ntohl(orr->role); if (raw == OFPRAW_OFPT12_ROLE_REQUEST ? orr->role == htonl(OFPCR12_ROLE_NOCHANGE) - : orr->generation_id == htonll(UINT64_MAX)) { + : orr->generation_id == OVS_BE64_MAX) { rr->have_generation_id = false; rr->generation_id = 0; } else { @@ -3906,7 +4270,7 @@ ofputil_decode_role_message(const struct ofp_header *oh, rr->have_generation_id = false; rr->generation_id = 0; } else { - NOT_REACHED(); + OVS_NOT_REACHED(); } return 0; @@ -3943,12 +4307,57 @@ ofputil_encode_role_reply(const struct ofp_header *request, nrr = ofpbuf_put_zeros(buf, sizeof *nrr); nrr->role = htonl(rr->role - 1); } else { - NOT_REACHED(); + OVS_NOT_REACHED(); } return buf; } +struct ofpbuf * +ofputil_encode_role_status(const struct ofputil_role_status *status, + enum ofputil_protocol protocol) +{ + struct ofpbuf *buf; + enum ofp_version version; + struct ofp14_role_status *rstatus; + + version = ofputil_protocol_to_ofp_version(protocol); + buf = ofpraw_alloc_xid(OFPRAW_OFPT14_ROLE_STATUS, version, htonl(0), 0); + rstatus = ofpbuf_put_zeros(buf, sizeof *rstatus); + rstatus->role = htonl(status->role); + rstatus->reason = status->reason; + rstatus->generation_id = htonll(status->generation_id); + + return buf; +} + +enum ofperr +ofputil_decode_role_status(const struct ofp_header *oh, + struct ofputil_role_status *rs) +{ + struct ofpbuf b; + enum ofpraw raw; + const struct ofp14_role_status *r; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + raw = ofpraw_pull_assert(&b); + ovs_assert(raw == OFPRAW_OFPT14_ROLE_STATUS); + + r = b.l3; + if (r->role != htonl(OFPCR12_ROLE_NOCHANGE) && + r->role != htonl(OFPCR12_ROLE_EQUAL) && + r->role != htonl(OFPCR12_ROLE_MASTER) && + r->role != htonl(OFPCR12_ROLE_SLAVE)) { + return OFPERR_OFPRRFC_BAD_ROLE; + } + + rs->role = ntohl(r->role); + rs->generation_id = ntohll(r->generation_id); + rs->reason = r->reason; + + return 0; +} + /* Table stats. */ static void @@ -4053,6 +4462,19 @@ ofputil_put_ofp11_table_stats(const struct ofp12_table_stats *in, out->matched_count = in->matched_count; } +static void +ofputil_put_ofp12_table_stats(const struct ofp12_table_stats *in, + struct ofpbuf *buf) +{ + struct ofp12_table_stats *out = ofpbuf_put(buf, in, sizeof *in); + + /* Trim off OF1.3-only capabilities. */ + out->match &= htonll(OFPXMT12_MASK); + out->wildcards &= htonll(OFPXMT12_MASK); + out->write_setfields &= htonll(OFPXMT12_MASK); + out->apply_setfields &= htonll(OFPXMT12_MASK); +} + static void ofputil_put_ofp13_table_stats(const struct ofp12_table_stats *in, struct ofpbuf *buf) @@ -4078,31 +4500,27 @@ ofputil_encode_table_stats_reply(const struct ofp12_table_stats stats[], int n, reply = ofpraw_alloc_stats_reply(request, n * sizeof *stats); - switch ((enum ofp_version) request->version) { - case OFP10_VERSION: - for (i = 0; i < n; i++) { + for (i = 0; i < n; i++) { + switch ((enum ofp_version) request->version) { + case OFP10_VERSION: ofputil_put_ofp10_table_stats(&stats[i], reply); - } - break; + break; - case OFP11_VERSION: - for (i = 0; i < n; i++) { + case OFP11_VERSION: ofputil_put_ofp11_table_stats(&stats[i], reply); - } - break; + break; - case OFP12_VERSION: - ofpbuf_put(reply, stats, n * sizeof *stats); - break; + case OFP12_VERSION: + ofputil_put_ofp12_table_stats(&stats[i], reply); + break; - case OFP13_VERSION: - for (i = 0; i < n; i++) { + case OFP13_VERSION: ofputil_put_ofp13_table_stats(&stats[i], reply); - } - break; + break; - default: - NOT_REACHED(); + default: + OVS_NOT_REACHED(); + } } return reply; @@ -4138,7 +4556,7 @@ ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq, nfmr = ofpbuf_try_pull(msg, sizeof *nfmr); if (!nfmr) { - VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR request has %zu " + VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR request has %"PRIuSIZE" " "leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } @@ -4210,6 +4628,7 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update, { struct nx_flow_update_header *nfuh; unsigned int length; + struct ofp_header *oh; if (!msg->l2) { msg->l2 = msg->data; @@ -4224,6 +4643,8 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update, goto bad_len; } + oh = msg->l2; + nfuh = msg->data; update->event = ntohs(nfuh->event); length = ntohs(nfuh->length); @@ -4272,7 +4693,8 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update, } actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8); - error = ofpacts_pull_openflow10(msg, actions_len, ofpacts); + error = ofpacts_pull_openflow_actions(msg, actions_len, oh->version, + ofpacts); if (error) { return error; } @@ -4288,7 +4710,7 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update, } bad_len: - VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR reply has %zu " + VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR reply has %"PRIuSIZE" " "leftover bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } @@ -4332,9 +4754,11 @@ ofputil_append_flow_update(const struct ofputil_flow_update *update, struct nx_flow_update_header *nfuh; struct ofpbuf *msg; size_t start_ofs; + enum ofp_version version; msg = ofpbuf_from_list(list_back(replies)); start_ofs = msg->size; + version = ((struct ofp_header *)msg->l2)->version; if (update->event == NXFME_ABBREV) { struct nx_flow_update_abbrev *nfua; @@ -4347,8 +4771,8 @@ ofputil_append_flow_update(const struct ofputil_flow_update *update, ofpbuf_put_zeros(msg, sizeof *nfuf); match_len = nx_put_match(msg, update->match, htonll(0), htonll(0)); - ofpacts_put_openflow10(update->ofpacts, update->ofpacts_len, msg); - + ofpacts_put_openflow_actions(update->ofpacts, update->ofpacts_len, msg, + version); nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf); nfuf->reason = htons(update->reason); nfuf->priority = htons(update->priority); @@ -4387,7 +4811,8 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po, msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size); ofpbuf_put_zeros(msg, sizeof *opo); actions_ofs = msg->size; - ofpacts_put_openflow10(po->ofpacts, po->ofpacts_len, msg); + ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg, + ofp_version); opo = msg->l3; opo->buffer_id = htonl(po->buffer_id); @@ -4404,8 +4829,8 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po, msg = ofpraw_alloc(OFPRAW_OFPT11_PACKET_OUT, ofp_version, size); ofpbuf_put_zeros(msg, sizeof *opo); - len = ofpacts_put_openflow11_actions(po->ofpacts, po->ofpacts_len, msg); - + len = ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg, + ofp_version); opo = msg->l3; opo->buffer_id = htonl(po->buffer_id); opo->in_port = ofputil_port_to_ofp11(po->in_port); @@ -4414,7 +4839,7 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po, } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } if (po->buffer_id == UINT32_MAX) { @@ -4467,7 +4892,7 @@ ofputil_encode_barrier_request(enum ofp_version ofp_version) break; default: - NOT_REACHED(); + OVS_NOT_REACHED(); } return ofpraw_alloc(type, ofp_version, 0); @@ -4483,7 +4908,7 @@ ofputil_frag_handling_to_string(enum ofp_config_flags flags) case OFPC_FRAG_NX_MATCH: return "nx-match"; } - NOT_REACHED(); + OVS_NOT_REACHED(); } bool @@ -4542,31 +4967,6 @@ ofputil_port_to_ofp11(ofp_port_t ofp10_port) : ofp_to_u16(ofp10_port) + OFPP11_OFFSET); } -/* Checks that 'port' is a valid output port for the OFPAT10_OUTPUT action, given - * that the switch will never have more than 'max_ports' ports. Returns 0 if - * 'port' is valid, otherwise an OpenFlow return code. */ -enum ofperr -ofputil_check_output_port(ofp_port_t port, ofp_port_t max_ports) -{ - switch (port) { - case OFPP_IN_PORT: - case OFPP_TABLE: - case OFPP_NORMAL: - case OFPP_FLOOD: - case OFPP_ALL: - case OFPP_CONTROLLER: - case OFPP_NONE: - case OFPP_LOCAL: - return 0; - - default: - if (ofp_to_u16(port) < ofp_to_u16(max_ports)) { - return 0; - } - return OFPERR_OFPBAC_BAD_OUT_PORT; - } -} - #define OFPUTIL_NAMED_PORTS \ OFPUTIL_NAMED_PORT(IN_PORT) \ OFPUTIL_NAMED_PORT(TABLE) \ @@ -4682,6 +5082,65 @@ ofputil_port_to_string(ofp_port_t port, } } +/* Stores the group id represented by 's' into '*group_idp'. 's' may be an + * integer or, for reserved group IDs, the standard OpenFlow name for the group + * (either "ANY" or "ALL"). + * + * Returns true if successful, false if 's' is not a valid OpenFlow group ID or + * name. */ +bool +ofputil_group_from_string(const char *s, uint32_t *group_idp) +{ + if (!strcasecmp(s, "any")) { + *group_idp = OFPG11_ANY; + } else if (!strcasecmp(s, "all")) { + *group_idp = OFPG11_ALL; + } else if (!str_to_uint(s, 10, group_idp)) { + VLOG_WARN("%s is not a valid group ID. (Valid group IDs are " + "32-bit nonnegative integers or the keywords ANY or " + "ALL.)", s); + return false; + } + + return true; +} + +/* Appends to 's' a string representation of the OpenFlow group ID 'group_id'. + * Most groups' string representation is just the number, but for special + * groups, e.g. OFPG11_ALL, it is the name, e.g. "ALL". */ +void +ofputil_format_group(uint32_t group_id, struct ds *s) +{ + char name[MAX_GROUP_NAME_LEN]; + + ofputil_group_to_string(group_id, name, sizeof name); + ds_put_cstr(s, name); +} + + +/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string + * representation of OpenFlow group ID 'group_id'. Most group are represented + * as just their number, but special groups, e.g. OFPG11_ALL, are represented + * by name, e.g. "ALL". */ +void +ofputil_group_to_string(uint32_t group_id, + char namebuf[MAX_GROUP_NAME_LEN + 1], size_t bufsize) +{ + switch (group_id) { + case OFPG11_ALL: + ovs_strlcpy(namebuf, "ALL", bufsize); + break; + + case OFPG11_ANY: + ovs_strlcpy(namebuf, "ANY", bufsize); + break; + + default: + snprintf(namebuf, bufsize, "%"PRIu32, group_id); + break; + } +} + /* Given a buffer 'b' that contains an array of OpenFlow ports of type * 'ofp_version', tries to pull the first element from the array. If * successful, initializes '*pp' with an abstract representation of the @@ -4703,7 +5162,7 @@ ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b, return op ? ofputil_decode_ofp11_port(pp, op) : EOF; } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -4714,22 +5173,21 @@ size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b) return b->size / ofputil_get_phy_port_size(ofp_version); } -/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if - * 'name' is "output" then the return value is OFPUTIL_OFPAT10_OUTPUT), or -1 if - * 'name' is not the name of any action. - * - * ofp-util.def lists the mapping from names to action. */ -int -ofputil_action_code_from_name(const char *name) -{ - static const char *const names[OFPUTIL_N_ACTIONS] = { - NULL, +/* ofp-util.def lists the mapping from names to action. */ +static const char *const names[OFPUTIL_N_ACTIONS] = { + NULL, #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" - }; +}; +/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if + * 'name' is "output" then the return value is OFPUTIL_OFPAT10_OUTPUT), or -1 + * if 'name' is not the name of any action. */ +int +ofputil_action_code_from_name(const char *name) +{ const char *const *p; for (p = names; p < &names[ARRAY_SIZE(names)]; p++) { @@ -4740,6 +5198,15 @@ ofputil_action_code_from_name(const char *name) return -1; } +/* Returns name corresponding to the 'enum ofputil_action_code', + * or "Unkonwn action", if the name is not available. */ +const char * +ofputil_action_name_from_code(enum ofputil_action_code code) +{ + return code < (int)OFPUTIL_N_ACTIONS && names[code] ? names[code] + : "Unknown action"; +} + /* Appends an action of the type specified by 'code' to 'buf' and returns the * action. Initializes the parts of 'action' that identify it as having type * and length 'sizeof *action' and zeros the rest. For actions that @@ -4750,7 +5217,7 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) { switch (code) { case OFPUTIL_ACTION_INVALID: - NOT_REACHED(); + OVS_NOT_REACHED(); #define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); @@ -4760,7 +5227,7 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); #include "ofp-util.def" } - NOT_REACHED(); + OVS_NOT_REACHED(); } #define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ @@ -4823,13 +5290,15 @@ ofputil_normalize_match__(struct match *match, bool may_log) may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR; if (match->flow.nw_proto == IPPROTO_TCP || match->flow.nw_proto == IPPROTO_UDP || + match->flow.nw_proto == IPPROTO_SCTP || match->flow.nw_proto == IPPROTO_ICMP) { may_match |= MAY_TP_ADDR; } } else if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) { may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6; if (match->flow.nw_proto == IPPROTO_TCP || - match->flow.nw_proto == IPPROTO_UDP) { + match->flow.nw_proto == IPPROTO_UDP || + match->flow.nw_proto == IPPROTO_SCTP) { may_match |= MAY_TP_ADDR; } else if (match->flow.nw_proto == IPPROTO_ICMPV6) { may_match |= MAY_TP_ADDR; @@ -4878,7 +5347,6 @@ ofputil_normalize_match__(struct match *match, bool may_log) } if (!(may_match & MAY_MPLS)) { wc.masks.mpls_lse = htonl(0); - wc.masks.mpls_depth = 0; } /* Log any changes. */ @@ -5029,7 +5497,7 @@ ofputil_encode_dump_ports_request(enum ofp_version ofp_version, ofp_port_t port) break; } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } return request; @@ -5113,7 +5581,7 @@ ofputil_append_port_stat(struct list *replies, } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -5183,6 +5651,21 @@ ofputil_port_stats_from_ofp13(struct ofputil_port_stats *ops, return error; } +static size_t +ofputil_get_port_stats_size(enum ofp_version ofp_version) +{ + switch (ofp_version) { + case OFP10_VERSION: + return sizeof(struct ofp10_port_stats); + case OFP11_VERSION: + case OFP12_VERSION: + return sizeof(struct ofp11_port_stats); + case OFP13_VERSION: + return sizeof(struct ofp13_port_stats); + default: + OVS_NOT_REACHED(); + } +} /* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY * message 'oh'. */ @@ -5194,9 +5677,7 @@ ofputil_count_port_stats(const struct ofp_header *oh) 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); + return b.size / ofputil_get_port_stats_size(oh->version); } /* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract @@ -5249,11 +5730,11 @@ ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg) } return ofputil_port_stats_from_ofp10(ps, ps10); } else { - NOT_REACHED(); + OVS_NOT_REACHED(); } bad_len: - VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %zu leftover " + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %"PRIuSIZE" leftover " "bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } @@ -5280,23 +5761,588 @@ ofputil_decode_port_stats_request(const struct ofp_header *request, } default: - NOT_REACHED(); + OVS_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) +/* Frees all of the "struct ofputil_bucket"s in the 'buckets' list. */ +void +ofputil_bucket_list_destroy(struct list *buckets) { - switch ((enum ofp_version)request->version) { - case OFP13_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); + struct ofputil_bucket *bucket, *next_bucket; + + LIST_FOR_EACH_SAFE (bucket, next_bucket, list_node, buckets) { + list_remove(&bucket->list_node); + free(bucket->ofpacts); + free(bucket); + } +} + +/* Returns an OpenFlow group stats request for OpenFlow version 'ofp_version', + * that requests stats for group 'group_id'. (Use OFPG_ALL to request stats + * for all groups.) + * + * Group statistics include packet and byte counts for each group. */ +struct ofpbuf * +ofputil_encode_group_stats_request(enum ofp_version ofp_version, + uint32_t group_id) +{ + struct ofpbuf *request; + + switch (ofp_version) { + case OFP10_VERSION: + ovs_fatal(0, "dump-group-stats needs OpenFlow 1.1 or later " + "(\'-O OpenFlow11\')"); + case OFP11_VERSION: + case OFP12_VERSION: + case OFP13_VERSION: { + struct ofp11_group_stats_request *req; + request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_REQUEST, ofp_version, 0); + req = ofpbuf_put_zeros(request, sizeof *req); + req->group_id = htonl(group_id); + break; + } + default: + OVS_NOT_REACHED(); + } + + return request; +} + +/* Returns an OpenFlow group description request for OpenFlow version + * 'ofp_version', that requests stats for group 'group_id'. (Use OFPG_ALL to + * request stats for all groups.) + * + * Group descriptions include the bucket and action configuration for each + * group. */ +struct ofpbuf * +ofputil_encode_group_desc_request(enum ofp_version ofp_version) +{ + struct ofpbuf *request; + + switch (ofp_version) { + case OFP10_VERSION: + ovs_fatal(0, "dump-groups needs OpenFlow 1.1 or later " + "(\'-O OpenFlow11\')"); + case OFP11_VERSION: + case OFP12_VERSION: + case OFP13_VERSION: { + request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST, ofp_version, 0); + break; + } + default: + OVS_NOT_REACHED(); + } + + return request; +} + +static void * +ofputil_group_stats_to_ofp11(const struct ofputil_group_stats *ogs, + size_t base_len, struct list *replies) +{ + struct ofp11_bucket_counter *bc11; + struct ofp11_group_stats *gs11; + size_t length; + int i; + + length = base_len + sizeof(struct ofp11_bucket_counter) * ogs->n_buckets; + + gs11 = ofpmp_append(replies, length); + memset(gs11, 0, base_len); + gs11->length = htons(length); + gs11->group_id = htonl(ogs->group_id); + gs11->ref_count = htonl(ogs->ref_count); + gs11->packet_count = htonll(ogs->packet_count); + gs11->byte_count = htonll(ogs->byte_count); + + bc11 = (void *) (((uint8_t *) gs11) + base_len); + for (i = 0; i < ogs->n_buckets; i++) { + const struct bucket_counter *obc = &ogs->bucket_stats[i]; + + bc11[i].packet_count = htonll(obc->packet_count); + bc11[i].byte_count = htonll(obc->byte_count); + } + + return gs11; +} + +static void +ofputil_append_of13_group_stats(const struct ofputil_group_stats *ogs, + struct list *replies) +{ + struct ofp13_group_stats *gs13; + + gs13 = ofputil_group_stats_to_ofp11(ogs, sizeof *gs13, replies); + gs13->duration_sec = htonl(ogs->duration_sec); + gs13->duration_nsec = htonl(ogs->duration_nsec); +} + +/* Encodes 'ogs' properly for the format of the list of group statistics + * replies already begun in 'replies' and appends it to the list. 'replies' + * must have originally been initialized with ofpmp_init(). */ +void +ofputil_append_group_stats(struct list *replies, + const struct ofputil_group_stats *ogs) +{ + struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); + struct ofp_header *oh = msg->data; + + switch ((enum ofp_version)oh->version) { + case OFP11_VERSION: + case OFP12_VERSION: + ofputil_group_stats_to_ofp11(ogs, sizeof(struct ofp11_group_stats), + replies); + break; + + case OFP13_VERSION: + ofputil_append_of13_group_stats(ogs, replies); + break; + + case OFP10_VERSION: + default: + OVS_NOT_REACHED(); + } +} + +/* Returns an OpenFlow group features request for OpenFlow version + * 'ofp_version'. */ +struct ofpbuf * +ofputil_encode_group_features_request(enum ofp_version ofp_version) +{ + struct ofpbuf *request = NULL; + + switch (ofp_version) { + case OFP10_VERSION: + case OFP11_VERSION: + ovs_fatal(0, "dump-group-features needs OpenFlow 1.2 or later " + "(\'-O OpenFlow12\')"); + case OFP12_VERSION: + case OFP13_VERSION: { + request = ofpraw_alloc(OFPRAW_OFPST12_GROUP_FEATURES_REQUEST, + ofp_version, 0); + break; + } + default: + OVS_NOT_REACHED(); + } + + return request; +} + +/* Returns a OpenFlow message that encodes 'features' properly as a reply to + * group features request 'request'. */ +struct ofpbuf * +ofputil_encode_group_features_reply( + const struct ofputil_group_features *features, + const struct ofp_header *request) +{ + struct ofp12_group_features_stats *ogf; + struct ofpbuf *reply; + + reply = ofpraw_alloc_xid(OFPRAW_OFPST12_GROUP_FEATURES_REPLY, + request->version, request->xid, 0); + ogf = ofpbuf_put_zeros(reply, sizeof *ogf); + ogf->types = htonl(features->types); + ogf->capabilities = htonl(features->capabilities); + ogf->max_groups[0] = htonl(features->max_groups[0]); + ogf->max_groups[1] = htonl(features->max_groups[1]); + ogf->max_groups[2] = htonl(features->max_groups[2]); + ogf->max_groups[3] = htonl(features->max_groups[3]); + ogf->actions[0] = htonl(features->actions[0]); + ogf->actions[1] = htonl(features->actions[1]); + ogf->actions[2] = htonl(features->actions[2]); + ogf->actions[3] = htonl(features->actions[3]); + + return reply; +} + +/* Decodes group features reply 'oh' into 'features'. */ +void +ofputil_decode_group_features_reply(const struct ofp_header *oh, + struct ofputil_group_features *features) +{ + const struct ofp12_group_features_stats *ogf = ofpmsg_body(oh); + + features->types = ntohl(ogf->types); + features->capabilities = ntohl(ogf->capabilities); + features->max_groups[0] = ntohl(ogf->max_groups[0]); + features->max_groups[1] = ntohl(ogf->max_groups[1]); + features->max_groups[2] = ntohl(ogf->max_groups[2]); + features->max_groups[3] = ntohl(ogf->max_groups[3]); + features->actions[0] = ntohl(ogf->actions[0]); + features->actions[1] = ntohl(ogf->actions[1]); + features->actions[2] = ntohl(ogf->actions[2]); + features->actions[3] = ntohl(ogf->actions[3]); +} + +/* Parse a group status request message into a 32 bit OpenFlow 1.1 + * group ID and stores the latter in '*group_id'. + * Returns 0 if successful, otherwise an OFPERR_* number. */ +enum ofperr +ofputil_decode_group_stats_request(const struct ofp_header *request, + uint32_t *group_id) +{ + const struct ofp11_group_stats_request *gsr11 = ofpmsg_body(request); + *group_id = ntohl(gsr11->group_id); + return 0; +} + +/* Converts a group stats reply in 'msg' into an abstract ofputil_group_stats + * in 'gs'. Assigns freshly allocated memory to gs->bucket_stats for the + * caller to eventually free. + * + * Multiple group 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_group_stats_reply(struct ofpbuf *msg, + struct ofputil_group_stats *gs) +{ + struct ofp11_bucket_counter *obc; + struct ofp11_group_stats *ogs11; + enum ofpraw raw; + enum ofperr error; + size_t base_len; + size_t length; + size_t i; + + gs->bucket_stats = NULL; + error = (msg->l2 + ? ofpraw_decode(&raw, msg->l2) + : ofpraw_pull(&raw, msg)); + if (error) { + return error; + } + + if (!msg->size) { + return EOF; + } + + if (raw == OFPRAW_OFPST11_GROUP_REPLY) { + base_len = sizeof *ogs11; + ogs11 = ofpbuf_try_pull(msg, sizeof *ogs11); + gs->duration_sec = gs->duration_nsec = UINT32_MAX; + } else if (raw == OFPRAW_OFPST13_GROUP_REPLY) { + struct ofp13_group_stats *ogs13; + + base_len = sizeof *ogs13; + ogs13 = ofpbuf_try_pull(msg, sizeof *ogs13); + if (ogs13) { + ogs11 = &ogs13->gs; + gs->duration_sec = ntohl(ogs13->duration_sec); + gs->duration_nsec = ntohl(ogs13->duration_nsec); + } else { + ogs11 = NULL; + } + } else { + OVS_NOT_REACHED(); + } + + if (!ogs11) { + VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply has %"PRIuSIZE" leftover bytes at end", + ofpraw_get_name(raw), msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + length = ntohs(ogs11->length); + if (length < sizeof base_len) { + VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply claims invalid length %"PRIuSIZE, + ofpraw_get_name(raw), length); + return OFPERR_OFPBRC_BAD_LEN; + } + + gs->group_id = ntohl(ogs11->group_id); + gs->ref_count = ntohl(ogs11->ref_count); + gs->packet_count = ntohll(ogs11->packet_count); + gs->byte_count = ntohll(ogs11->byte_count); + + gs->n_buckets = (length - base_len) / sizeof *obc; + obc = ofpbuf_try_pull(msg, gs->n_buckets * sizeof *obc); + if (!obc) { + VLOG_WARN_RL(&bad_ofmsg_rl, "%s reply has %"PRIuSIZE" leftover bytes at end", + ofpraw_get_name(raw), msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + + gs->bucket_stats = xmalloc(gs->n_buckets * sizeof *gs->bucket_stats); + for (i = 0; i < gs->n_buckets; i++) { + gs->bucket_stats[i].packet_count = ntohll(obc[i].packet_count); + gs->bucket_stats[i].byte_count = ntohll(obc[i].byte_count); + } + + return 0; +} + +/* Appends a group stats reply that contains the data in 'gds' to those already + * present in the list of ofpbufs in 'replies'. 'replies' should have been + * initialized with ofpmp_init(). */ +void +ofputil_append_group_desc_reply(const struct ofputil_group_desc *gds, + struct list *buckets, + struct list *replies) +{ + struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); + struct ofp11_group_desc_stats *ogds; + struct ofputil_bucket *bucket; + size_t start_ogds; + enum ofp_version version = ((struct ofp_header *)reply->data)->version; + + start_ogds = reply->size; + ofpbuf_put_zeros(reply, sizeof *ogds); + LIST_FOR_EACH (bucket, list_node, buckets) { + struct ofp11_bucket *ob; + size_t start_ob; + + start_ob = reply->size; + ofpbuf_put_zeros(reply, sizeof *ob); + ofpacts_put_openflow_actions(bucket->ofpacts, bucket->ofpacts_len, + reply, version); + ob = ofpbuf_at_assert(reply, start_ob, sizeof *ob); + ob->len = htons(reply->size - start_ob); + ob->weight = htons(bucket->weight); + ob->watch_port = ofputil_port_to_ofp11(bucket->watch_port); + ob->watch_group = htonl(bucket->watch_group); + } + ogds = ofpbuf_at_assert(reply, start_ogds, sizeof *ogds); + ogds->length = htons(reply->size - start_ogds); + ogds->type = gds->type; + ogds->group_id = htonl(gds->group_id); + + ofpmp_postappend(replies, start_ogds); +} + +static enum ofperr +ofputil_pull_buckets(struct ofpbuf *msg, size_t buckets_length, + enum ofp_version version, struct list *buckets) +{ + struct ofp11_bucket *ob; + + list_init(buckets); + while (buckets_length > 0) { + struct ofputil_bucket *bucket; + struct ofpbuf ofpacts; + enum ofperr error; + size_t ob_len; + + ob = (buckets_length >= sizeof *ob + ? ofpbuf_try_pull(msg, sizeof *ob) + : NULL); + if (!ob) { + VLOG_WARN_RL(&bad_ofmsg_rl, "buckets end with %"PRIuSIZE" leftover bytes", + buckets_length); + } + + ob_len = ntohs(ob->len); + if (ob_len < sizeof *ob) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length " + "%"PRIuSIZE" is not valid", ob_len); + return OFPERR_OFPGMFC_BAD_BUCKET; + } else if (ob_len > buckets_length) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message bucket length " + "%"PRIuSIZE" exceeds remaining buckets data size %"PRIuSIZE, + ob_len, buckets_length); + return OFPERR_OFPGMFC_BAD_BUCKET; + } + buckets_length -= ob_len; + + ofpbuf_init(&ofpacts, 0); + error = ofpacts_pull_openflow_actions(msg, ob_len - sizeof *ob, + version, &ofpacts); + if (error) { + ofpbuf_uninit(&ofpacts); + ofputil_bucket_list_destroy(buckets); + return error; + } + + bucket = xzalloc(sizeof *bucket); + bucket->weight = ntohs(ob->weight); + error = ofputil_port_from_ofp11(ob->watch_port, &bucket->watch_port); + if (error) { + ofpbuf_uninit(&ofpacts); + ofputil_bucket_list_destroy(buckets); + return OFPERR_OFPGMFC_BAD_WATCH; + } + bucket->watch_group = ntohl(ob->watch_group); + bucket->ofpacts = ofpbuf_steal_data(&ofpacts); + bucket->ofpacts_len = ofpacts.size; + list_push_back(buckets, &bucket->list_node); + } + + return 0; +} + +/* Converts a group description reply in 'msg' into an abstract + * ofputil_group_desc in 'gd'. + * + * Multiple group description 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_group_desc_reply(struct ofputil_group_desc *gd, + struct ofpbuf *msg, enum ofp_version version) +{ + struct ofp11_group_desc_stats *ogds; + size_t length; + + if (!msg->l2) { + ofpraw_pull_assert(msg); + } + + if (!msg->size) { + return EOF; + } + + ogds = ofpbuf_try_pull(msg, sizeof *ogds); + if (!ogds) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply has %"PRIuSIZE" " + "leftover bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + gd->type = ogds->type; + gd->group_id = ntohl(ogds->group_id); + + length = ntohs(ogds->length); + if (length < sizeof *ogds || length - sizeof *ogds > msg->size) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST11_GROUP_DESC reply claims invalid " + "length %"PRIuSIZE, length); + return OFPERR_OFPBRC_BAD_LEN; + } + + return ofputil_pull_buckets(msg, length - sizeof *ogds, version, + &gd->buckets); +} + +/* Converts abstract group mod 'gm' into a message for OpenFlow version + * 'ofp_version' and returns the message. */ +struct ofpbuf * +ofputil_encode_group_mod(enum ofp_version ofp_version, + const struct ofputil_group_mod *gm) +{ + struct ofpbuf *b; + struct ofp11_group_mod *ogm; + size_t start_ogm; + size_t start_bucket; + struct ofputil_bucket *bucket; + struct ofp11_bucket *ob; + + switch (ofp_version) { + case OFP10_VERSION: { + if (gm->command == OFPGC11_ADD) { + ovs_fatal(0, "add-group needs OpenFlow 1.1 or later " + "(\'-O OpenFlow11\')"); + } else if (gm->command == OFPGC11_MODIFY) { + ovs_fatal(0, "mod-group needs OpenFlow 1.1 or later " + "(\'-O OpenFlow11\')"); + } else { + ovs_fatal(0, "del-groups needs OpenFlow 1.1 or later " + "(\'-O OpenFlow11\')"); + } + } + + case OFP11_VERSION: + case OFP12_VERSION: + case OFP13_VERSION: { + b = ofpraw_alloc(OFPRAW_OFPT11_GROUP_MOD, ofp_version, 0); + start_ogm = b->size; + ofpbuf_put_zeros(b, sizeof *ogm); + + LIST_FOR_EACH (bucket, list_node, &gm->buckets) { + start_bucket = b->size; + ofpbuf_put_zeros(b, sizeof *ob); + if (bucket->ofpacts && bucket->ofpacts_len) { + ofpacts_put_openflow_actions(bucket->ofpacts, + bucket->ofpacts_len, b, + ofp_version); + } + ob = ofpbuf_at_assert(b, start_bucket, sizeof *ob); + ob->len = htons(b->size - start_bucket);; + ob->weight = htons(bucket->weight); + ob->watch_port = ofputil_port_to_ofp11(bucket->watch_port); + ob->watch_group = htonl(bucket->watch_group); + } + ogm = ofpbuf_at_assert(b, start_ogm, sizeof *ogm); + ogm->command = htons(gm->command); + ogm->type = gm->type; + ogm->group_id = htonl(gm->group_id); + + break; + } + + default: + OVS_NOT_REACHED(); + } + + return b; +} + +/* Converts OpenFlow group mod message 'oh' into an abstract group mod in + * 'gm'. Returns 0 if successful, otherwise an OpenFlow error code. */ +enum ofperr +ofputil_decode_group_mod(const struct ofp_header *oh, + struct ofputil_group_mod *gm) +{ + const struct ofp11_group_mod *ogm; + struct ofpbuf msg; + struct ofputil_bucket *bucket; + enum ofperr err; + + ofpbuf_use_const(&msg, oh, ntohs(oh->length)); + ofpraw_pull_assert(&msg); + + ogm = ofpbuf_pull(&msg, sizeof *ogm); + gm->command = ntohs(ogm->command); + gm->type = ogm->type; + gm->group_id = ntohl(ogm->group_id); + + err = ofputil_pull_buckets(&msg, msg.size, oh->version, &gm->buckets); + if (err) { + return err; + } + + LIST_FOR_EACH (bucket, list_node, &gm->buckets) { + switch (gm->type) { + case OFPGT11_ALL: + case OFPGT11_INDIRECT: + if (ofputil_bucket_has_liveness(bucket)) { + return OFPERR_OFPGMFC_WATCH_UNSUPPORTED; + } + break; + case OFPGT11_SELECT: + break; + case OFPGT11_FF: + if (!ofputil_bucket_has_liveness(bucket)) { + return OFPERR_OFPGMFC_INVALID_GROUP; + } + break; + default: + OVS_NOT_REACHED(); + } + } + + return 0; +} + +/* 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 OFP13_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: { @@ -5311,7 +6357,7 @@ ofputil_decode_queue_stats_request(const struct ofp_header *request, } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } } @@ -5346,12 +6392,28 @@ ofputil_encode_queue_stats_request(enum ofp_version ofp_version, break; } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } return request; } +static size_t +ofputil_get_queue_stats_size(enum ofp_version ofp_version) +{ + switch (ofp_version) { + case OFP10_VERSION: + return sizeof(struct ofp10_queue_stats); + case OFP11_VERSION: + case OFP12_VERSION: + return sizeof(struct ofp11_queue_stats); + case OFP13_VERSION: + return sizeof(struct ofp13_queue_stats); + default: + OVS_NOT_REACHED(); + } +} + /* Returns the number of queue stats elements in OFPTYPE_QUEUE_STATS_REPLY * message 'oh'. */ size_t @@ -5362,9 +6424,7 @@ ofputil_count_queue_stats(const struct ofp_header *oh) 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); + return b.size / ofputil_get_queue_stats_size(oh->version); } static enum ofperr @@ -5373,9 +6433,10 @@ ofputil_queue_stats_from_ofp10(struct ofputil_queue_stats *oqs, { oqs->port_no = u16_to_ofp(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)); + oqs->tx_bytes = ntohll(get_32aligned_be64(&qs10->tx_bytes)); + oqs->tx_packets = ntohll(get_32aligned_be64(&qs10->tx_packets)); + oqs->tx_errors = ntohll(get_32aligned_be64(&qs10->tx_errors)); + oqs->duration_sec = oqs->duration_nsec = UINT32_MAX; return 0; } @@ -5392,9 +6453,10 @@ ofputil_queue_stats_from_ofp11(struct ofputil_queue_stats *oqs, } 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); + oqs->tx_bytes = ntohll(qs11->tx_bytes); + oqs->tx_packets = ntohll(qs11->tx_packets); + oqs->tx_errors = ntohll(qs11->tx_errors); + oqs->duration_sec = oqs->duration_nsec = UINT32_MAX; return 0; } @@ -5403,11 +6465,10 @@ static enum ofperr ofputil_queue_stats_from_ofp13(struct ofputil_queue_stats *oqs, const struct ofp13_queue_stats *qs13) { - enum ofperr error - = ofputil_queue_stats_from_ofp11(oqs, &qs13->qs); + enum ofperr error = ofputil_queue_stats_from_ofp11(oqs, &qs13->qs); if (!error) { - /* FIXME: Get qs13->duration_sec and qs13->duration_nsec, - * Add to netdev_queue_stats? */ + oqs->duration_sec = ntohl(qs13->duration_sec); + oqs->duration_nsec = ntohl(qs13->duration_nsec); } return error; @@ -5463,11 +6524,11 @@ ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg) } return ofputil_queue_stats_from_ofp10(qs, qs10); } else { - NOT_REACHED(); + OVS_NOT_REACHED(); } bad_len: - VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_QUEUE reply has %zu leftover " + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_QUEUE reply has %"PRIuSIZE" leftover " "bytes at end", msg->size); return OFPERR_OFPBRC_BAD_LEN; } @@ -5479,9 +6540,9 @@ ofputil_queue_stats_to_ofp10(const struct ofputil_queue_stats *oqs, qs10->port_no = htons(ofp_to_u16(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)); + put_32aligned_be64(&qs10->tx_bytes, htonll(oqs->tx_bytes)); + put_32aligned_be64(&qs10->tx_packets, htonll(oqs->tx_packets)); + put_32aligned_be64(&qs10->tx_errors, htonll(oqs->tx_errors)); } static void @@ -5490,9 +6551,9 @@ ofputil_queue_stats_to_ofp11(const struct ofputil_queue_stats *oqs, { 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); + qs11->tx_bytes = htonll(oqs->tx_bytes); + qs11->tx_packets = htonll(oqs->tx_packets); + qs11->tx_errors = htonll(oqs->tx_errors); } static void @@ -5500,10 +6561,13 @@ ofputil_queue_stats_to_ofp13(const struct ofputil_queue_stats *oqs, struct ofp13_queue_stats *qs13) { ofputil_queue_stats_to_ofp11(oqs, &qs13->qs); - /* OF 1.3 adds duration fields */ - /* FIXME: Need to implement queue alive duration (sec + nsec) */ - qs13->duration_sec = htonl(~0); - qs13->duration_nsec = htonl(~0); + if (oqs->duration_sec != UINT32_MAX) { + qs13->duration_sec = htonl(oqs->duration_sec); + qs13->duration_nsec = htonl(oqs->duration_nsec); + } else { + qs13->duration_sec = OVS_BE32_MAX; + qs13->duration_nsec = OVS_BE32_MAX; + } } /* Encode a queue stat for 'oqs' and append it to 'replies'. */ @@ -5535,6 +6599,6 @@ ofputil_append_queue_stat(struct list *replies, } default: - NOT_REACHED(); + OVS_NOT_REACHED(); } }