X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=lib%2Fofp-util.c;h=8f2217f5766e892b499b40ac937711654f25906b;hb=44d3732d5e172a8528ae2f36c232dfa5a1c18b74;hp=48be7749fc2a076f4c5be9d64ac3fda234ca62df;hpb=5b5a3a674c975ed1ccfc38b4dae705d4fe4df0eb;p=openvswitch diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 48be7749..8f2217f5 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -32,6 +32,7 @@ #include "multipath.h" #include "netdev.h" #include "nx-match.h" +#include "ofp-actions.h" #include "ofp-errors.h" #include "ofp-util.h" #include "ofpbuf.h" @@ -64,26 +65,27 @@ ofputil_wcbits_to_netmask(int wcbits) } /* Given the IP netmask 'netmask', returns the number of bits of the IP address - * that it wildcards, that is, the number of 0-bits in 'netmask'. 'netmask' - * must be a CIDR netmask (see ip_is_cidr()). */ + * that it wildcards, that is, the number of 0-bits in 'netmask', a number + * between 0 and 32 inclusive. + * + * If 'netmask' is not a CIDR netmask (see ip_is_cidr()), the return value will + * still be in the valid range but isn't otherwise meaningful. */ int ofputil_netmask_to_wcbits(ovs_be32 netmask) { return 32 - ip_count_cidr_bits(netmask); } -/* A list of the FWW_* and OFPFW_ bits that have the same value, meaning, and +/* A list of the FWW_* and OFPFW10_ bits that have the same value, meaning, and * name. */ #define WC_INVARIANT_LIST \ WC_INVARIANT_BIT(IN_PORT) \ - WC_INVARIANT_BIT(DL_SRC) \ - WC_INVARIANT_BIT(DL_DST) \ WC_INVARIANT_BIT(DL_TYPE) \ WC_INVARIANT_BIT(NW_PROTO) /* Verify that all of the invariant bits (as defined on WC_INVARIANT_LIST) * actually have the same names and values. */ -#define WC_INVARIANT_BIT(NAME) BUILD_ASSERT_DECL(FWW_##NAME == OFPFW_##NAME); +#define WC_INVARIANT_BIT(NAME) BUILD_ASSERT_DECL(FWW_##NAME == OFPFW10_##NAME); WC_INVARIANT_LIST #undef WC_INVARIANT_BIT @@ -95,65 +97,65 @@ static const flow_wildcards_t WC_INVARIANTS = 0 #undef WC_INVARIANT_BIT ; -/* Converts the wildcard in 'ofpfw' into a flow_wildcards in 'wc' for use in - * struct cls_rule. It is the caller's responsibility to handle the special - * case where the flow match's dl_vlan is set to OFP_VLAN_NONE. */ +/* Converts the OpenFlow 1.0 wildcards in 'ofpfw' (OFPFW10_*) into a + * flow_wildcards in 'wc' for use in struct cls_rule. It is the caller's + * responsibility to handle the special case where the flow match's dl_vlan is + * set to OFP_VLAN_NONE. */ void -ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc) +ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14); /* Initialize most of rule->wc. */ flow_wildcards_init_catchall(wc); wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS; - /* Wildcard fields that aren't defined by ofp_match or tun_id. */ - wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_ECN | FWW_NW_TTL - | FWW_IPV6_LABEL); + /* Wildcard fields that aren't defined by ofp10_match or tun_id. */ + wc->wildcards |= FWW_NW_ECN | FWW_NW_TTL; - if (ofpfw & OFPFW_NW_TOS) { + if (ofpfw & OFPFW10_NW_TOS) { /* OpenFlow 1.0 defines a TOS wildcard, but it's much later in * the enum than we can use. */ wc->wildcards |= FWW_NW_DSCP; } - wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT); - wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT); + wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_SRC_SHIFT); + wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW10_NW_DST_SHIFT); - if (!(ofpfw & OFPFW_TP_SRC)) { + if (!(ofpfw & OFPFW10_TP_SRC)) { wc->tp_src_mask = htons(UINT16_MAX); } - if (!(ofpfw & OFPFW_TP_DST)) { + if (!(ofpfw & OFPFW10_TP_DST)) { wc->tp_dst_mask = htons(UINT16_MAX); } - if (ofpfw & OFPFW_DL_DST) { - /* OpenFlow 1.0 OFPFW_DL_DST covers the whole Ethernet destination, but - * Open vSwitch breaks the Ethernet destination into bits as FWW_DL_DST - * and FWW_ETH_MCAST. */ - wc->wildcards |= FWW_ETH_MCAST; + if (!(ofpfw & OFPFW10_DL_SRC)) { + memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN); + } + if (!(ofpfw & OFPFW10_DL_DST)) { + memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN); } /* VLAN TCI mask. */ - if (!(ofpfw & OFPFW_DL_VLAN_PCP)) { + if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) { wc->vlan_tci_mask |= htons(VLAN_PCP_MASK | VLAN_CFI); } - if (!(ofpfw & OFPFW_DL_VLAN)) { + if (!(ofpfw & OFPFW10_DL_VLAN)) { wc->vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI); } } -/* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given - * 'priority'. */ +/* Converts the ofp10_match in 'match' into a cls_rule in 'rule', with the + * given 'priority'. */ void -ofputil_cls_rule_from_match(const struct ofp_match *match, - unsigned int priority, struct cls_rule *rule) +ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *match, + unsigned int priority, struct cls_rule *rule) { - uint32_t ofpfw = ntohl(match->wildcards) & OFPFW_ALL; + uint32_t ofpfw = ntohl(match->wildcards) & OFPFW10_ALL; /* Initialize rule->priority, rule->wc. */ rule->priority = !ofpfw ? UINT16_MAX : priority; - ofputil_wildcard_from_openflow(ofpfw, &rule->wc); + ofputil_wildcard_from_ofpfw10(ofpfw, &rule->wc); /* Initialize most of rule->flow. */ rule->flow.nw_src = match->nw_src; @@ -168,12 +170,13 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, rule->flow.nw_proto = match->nw_proto; /* Translate VLANs. */ - if (!(ofpfw & OFPFW_DL_VLAN) && match->dl_vlan == htons(OFP_VLAN_NONE)) { + if (!(ofpfw & OFPFW10_DL_VLAN) && + match->dl_vlan == htons(OFP10_VLAN_NONE)) { /* Match only packets without 802.1Q header. * - * When OFPFW_DL_VLAN_PCP is wildcarded, this is obviously correct. + * When OFPFW10_DL_VLAN_PCP is wildcarded, this is obviously correct. * - * If OFPFW_DL_VLAN_PCP is matched, the flow match is contradictory, + * If OFPFW10_DL_VLAN_PCP is matched, the flow match is contradictory, * because we can't have a specific PCP without an 802.1Q header. * However, older versions of OVS treated this as matching packets * withut an 802.1Q header, so we do here too. */ @@ -192,44 +195,54 @@ ofputil_cls_rule_from_match(const struct ofp_match *match, cls_rule_zero_wildcarded_fields(rule); } -/* Convert 'rule' into the OpenFlow match structure 'match'. */ +/* Convert 'rule' into the OpenFlow 1.0 match structure 'match'. */ void -ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match) +ofputil_cls_rule_to_ofp10_match(const struct cls_rule *rule, + struct ofp10_match *match) { const struct flow_wildcards *wc = &rule->wc; uint32_t ofpfw; /* Figure out most OpenFlow wildcards. */ ofpfw = (OVS_FORCE uint32_t) (wc->wildcards & WC_INVARIANTS); - ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT; - ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT; + ofpfw |= (ofputil_netmask_to_wcbits(wc->nw_src_mask) + << OFPFW10_NW_SRC_SHIFT); + ofpfw |= (ofputil_netmask_to_wcbits(wc->nw_dst_mask) + << OFPFW10_NW_DST_SHIFT); if (wc->wildcards & FWW_NW_DSCP) { - ofpfw |= OFPFW_NW_TOS; + ofpfw |= OFPFW10_NW_TOS; } if (!wc->tp_src_mask) { - ofpfw |= OFPFW_TP_SRC; + ofpfw |= OFPFW10_TP_SRC; } if (!wc->tp_dst_mask) { - ofpfw |= OFPFW_TP_DST; + ofpfw |= OFPFW10_TP_DST; + } + if (eth_addr_is_zero(wc->dl_src_mask)) { + ofpfw |= OFPFW10_DL_SRC; + } + if (eth_addr_is_zero(wc->dl_dst_mask)) { + ofpfw |= OFPFW10_DL_DST; } /* Translate VLANs. */ match->dl_vlan = htons(0); match->dl_vlan_pcp = 0; if (rule->wc.vlan_tci_mask == htons(0)) { - ofpfw |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP; + ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP; } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI) && !(rule->flow.vlan_tci & htons(VLAN_CFI))) { - match->dl_vlan = htons(OFP_VLAN_NONE); + match->dl_vlan = htons(OFP10_VLAN_NONE); + ofpfw |= OFPFW10_DL_VLAN_PCP; } else { if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) { - ofpfw |= OFPFW_DL_VLAN; + ofpfw |= OFPFW10_DL_VLAN; } else { match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci)); } if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) { - ofpfw |= OFPFW_DL_VLAN_PCP; + ofpfw |= OFPFW10_DL_VLAN_PCP; } else { match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci); } @@ -251,8 +264,264 @@ ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match) memset(match->pad2, '\0', sizeof match->pad2); } +/* Converts the ofp11_match in 'match' into a cls_rule in 'rule', with the + * given 'priority'. Returns 0 if successful, otherwise an OFPERR_* value. */ +enum ofperr +ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match, + unsigned int priority, + struct cls_rule *rule) +{ + uint16_t wc = ntohl(match->wildcards); + uint8_t dl_src_mask[ETH_ADDR_LEN]; + uint8_t dl_dst_mask[ETH_ADDR_LEN]; + bool ipv4, arp; + int i; + + cls_rule_init_catchall(rule, priority); + + if (!(wc & OFPFW11_IN_PORT)) { + uint16_t ofp_port; + enum ofperr error; + + error = ofputil_port_from_ofp11(match->in_port, &ofp_port); + if (error) { + return OFPERR_OFPBMC_BAD_VALUE; + } + cls_rule_set_in_port(rule, ofp_port); + } + + for (i = 0; i < ETH_ADDR_LEN; i++) { + dl_src_mask[i] = ~match->dl_src_mask[i]; + } + cls_rule_set_dl_src_masked(rule, match->dl_src, dl_src_mask); + + for (i = 0; i < ETH_ADDR_LEN; i++) { + dl_dst_mask[i] = ~match->dl_dst_mask[i]; + } + cls_rule_set_dl_dst_masked(rule, match->dl_dst, dl_dst_mask); + + if (!(wc & OFPFW11_DL_VLAN)) { + if (match->dl_vlan == htons(OFPVID11_NONE)) { + /* Match only packets without a VLAN tag. */ + rule->flow.vlan_tci = htons(0); + rule->wc.vlan_tci_mask = htons(UINT16_MAX); + } else { + if (match->dl_vlan == htons(OFPVID11_ANY)) { + /* Match any packet with a VLAN tag regardless of VID. */ + rule->flow.vlan_tci = htons(VLAN_CFI); + rule->wc.vlan_tci_mask = htons(VLAN_CFI); + } else if (ntohs(match->dl_vlan) < 4096) { + /* Match only packets with the specified VLAN VID. */ + rule->flow.vlan_tci = htons(VLAN_CFI) | match->dl_vlan; + rule->wc.vlan_tci_mask = htons(VLAN_CFI | VLAN_VID_MASK); + } else { + /* Invalid VID. */ + return OFPERR_OFPBMC_BAD_VALUE; + } + + if (!(wc & OFPFW11_DL_VLAN_PCP)) { + if (match->dl_vlan_pcp <= 7) { + rule->flow.vlan_tci |= htons(match->dl_vlan_pcp + << VLAN_PCP_SHIFT); + rule->wc.vlan_tci_mask |= htons(VLAN_PCP_MASK); + } else { + /* Invalid PCP. */ + return OFPERR_OFPBMC_BAD_VALUE; + } + } + } + } + + if (!(wc & OFPFW11_DL_TYPE)) { + cls_rule_set_dl_type(rule, + ofputil_dl_type_from_openflow(match->dl_type)); + } + + ipv4 = rule->flow.dl_type == htons(ETH_TYPE_IP); + arp = rule->flow.dl_type == htons(ETH_TYPE_ARP); + + if (ipv4 && !(wc & OFPFW11_NW_TOS)) { + if (match->nw_tos & ~IP_DSCP_MASK) { + /* Invalid TOS. */ + return OFPERR_OFPBMC_BAD_VALUE; + } + + cls_rule_set_nw_dscp(rule, match->nw_tos); + } + + if (ipv4 || arp) { + if (!(wc & OFPFW11_NW_PROTO)) { + cls_rule_set_nw_proto(rule, match->nw_proto); + } + cls_rule_set_nw_src_masked(rule, match->nw_src, ~match->nw_src_mask); + cls_rule_set_nw_dst_masked(rule, match->nw_dst, ~match->nw_dst_mask); + } + +#define OFPFW11_TP_ALL (OFPFW11_TP_SRC | OFPFW11_TP_DST) + if (ipv4 && (wc & OFPFW11_TP_ALL) != OFPFW11_TP_ALL) { + switch (rule->flow.nw_proto) { + case IPPROTO_ICMP: + /* "A.2.3 Flow Match Structures" in OF1.1 says: + * + * The tp_src and tp_dst fields will be ignored unless the + * network protocol specified is as TCP, UDP or SCTP. + * + * but I'm pretty sure we should support ICMP too, otherwise + * that's a regression from OF1.0. */ + if (!(wc & OFPFW11_TP_SRC)) { + uint16_t icmp_type = ntohs(match->tp_src); + if (icmp_type < 0x100) { + cls_rule_set_icmp_type(rule, icmp_type); + } else { + return OFPERR_OFPBMC_BAD_FIELD; + } + } + if (!(wc & OFPFW11_TP_DST)) { + uint16_t icmp_code = ntohs(match->tp_dst); + if (icmp_code < 0x100) { + cls_rule_set_icmp_code(rule, icmp_code); + } else { + return OFPERR_OFPBMC_BAD_FIELD; + } + } + break; + + case IPPROTO_TCP: + case IPPROTO_UDP: + if (!(wc & (OFPFW11_TP_SRC))) { + cls_rule_set_tp_src(rule, match->tp_src); + } + if (!(wc & (OFPFW11_TP_DST))) { + cls_rule_set_tp_dst(rule, match->tp_dst); + } + 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; + } + } + + if (rule->flow.dl_type == htons(ETH_TYPE_MPLS) || + rule->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST)) { + 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 (match->metadata_mask != htonll(UINT64_MAX)) { + cls_rule_set_metadata_masked(rule, match->metadata, + ~match->metadata_mask); + } + + return 0; +} + +/* Convert 'rule' into the OpenFlow 1.1 match structure 'match'. */ +void +ofputil_cls_rule_to_ofp11_match(const struct cls_rule *rule, + struct ofp11_match *match) +{ + uint32_t wc = 0; + int i; + + memset(match, 0, sizeof *match); + match->omh.type = htons(OFPMT_STANDARD); + match->omh.length = htons(OFPMT11_STANDARD_LENGTH); + + if (rule->wc.wildcards & FWW_IN_PORT) { + wc |= OFPFW11_IN_PORT; + } else { + match->in_port = ofputil_port_to_ofp11(rule->flow.in_port); + } + + + memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN); + for (i = 0; i < ETH_ADDR_LEN; i++) { + match->dl_src_mask[i] = ~rule->wc.dl_src_mask[i]; + } + + memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN); + for (i = 0; i < ETH_ADDR_LEN; i++) { + match->dl_dst_mask[i] = ~rule->wc.dl_dst_mask[i]; + } + + if (rule->wc.vlan_tci_mask == htons(0)) { + wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP; + } else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI) + && !(rule->flow.vlan_tci & htons(VLAN_CFI))) { + match->dl_vlan = htons(OFPVID11_NONE); + wc |= OFPFW11_DL_VLAN_PCP; + } else { + if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) { + match->dl_vlan = htons(OFPVID11_ANY); + } else { + match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci)); + } + + if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) { + wc |= OFPFW11_DL_VLAN_PCP; + } else { + match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci); + } + } + + if (rule->wc.wildcards & FWW_DL_TYPE) { + wc |= OFPFW11_DL_TYPE; + } else { + match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type); + } + + if (rule->wc.wildcards & FWW_NW_DSCP) { + wc |= OFPFW11_NW_TOS; + } else { + match->nw_tos = rule->flow.nw_tos & IP_DSCP_MASK; + } + + if (rule->wc.wildcards & FWW_NW_PROTO) { + wc |= OFPFW11_NW_PROTO; + } else { + match->nw_proto = rule->flow.nw_proto; + } + + match->nw_src = rule->flow.nw_src; + match->nw_src_mask = ~rule->wc.nw_src_mask; + match->nw_dst = rule->flow.nw_dst; + match->nw_dst_mask = ~rule->wc.nw_dst_mask; + + if (!rule->wc.tp_src_mask) { + wc |= OFPFW11_TP_SRC; + } else { + match->tp_src = rule->flow.tp_src; + } + + if (!rule->wc.tp_dst_mask) { + wc |= OFPFW11_TP_DST; + } else { + match->tp_dst = rule->flow.tp_dst; + } + + /* MPLS not supported. */ + wc |= OFPFW11_MPLS_LABEL; + wc |= OFPFW11_MPLS_TC; + + match->metadata = rule->flow.metadata; + match->metadata_mask = ~rule->wc.metadata_mask; + + match->wildcards = htonl(wc); +} + /* Given a 'dl_type' value in the format used in struct flow, returns the - * corresponding 'dl_type' value for use in an OpenFlow ofp_match structure. */ + * corresponding 'dl_type' value for use in an ofp10_match or ofp11_match + * structure. */ ovs_be16 ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type) { @@ -261,7 +530,7 @@ ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type) : flow_dl_type); } -/* Given a 'dl_type' value in the format used in an OpenFlow ofp_match +/* Given a 'dl_type' value in the format used in an ofp10_match or ofp11_match * structure, returns the corresponding 'dl_type' value for use in struct * flow. */ ovs_be16 @@ -409,6 +678,18 @@ ofputil_decode_vendor(const struct ofp_header *oh, size_t length, { OFPUTIL_NXT_SET_CONTROLLER_ID, OFP10_VERSION, NXT_SET_CONTROLLER_ID, "NXT_SET_CONTROLLER_ID", sizeof(struct nx_controller_id), 0 }, + + { OFPUTIL_NXT_FLOW_MONITOR_CANCEL, OFP10_VERSION, + NXT_FLOW_MONITOR_CANCEL, "NXT_FLOW_MONITOR_CANCEL", + sizeof(struct nx_flow_monitor_cancel), 0 }, + + { OFPUTIL_NXT_FLOW_MONITOR_PAUSED, OFP10_VERSION, + NXT_FLOW_MONITOR_PAUSED, "NXT_FLOW_MONITOR_PAUSED", + sizeof(struct nicira_header), 0 }, + + { OFPUTIL_NXT_FLOW_MONITOR_RESUMED, OFP10_VERSION, + NXT_FLOW_MONITOR_RESUMED, "NXT_FLOW_MONITOR_RESUMED", + sizeof(struct nicira_header), 0 }, }; static const struct ofputil_msg_category nxt_category = { @@ -491,6 +772,10 @@ ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length, { OFPUTIL_NXST_AGGREGATE_REQUEST, OFP10_VERSION, NXST_AGGREGATE, "NXST_AGGREGATE request", sizeof(struct nx_aggregate_stats_request), 8 }, + + { OFPUTIL_NXST_FLOW_MONITOR_REQUEST, OFP10_VERSION, + NXST_FLOW_MONITOR, "NXST_FLOW_MONITOR request", + sizeof(struct nicira_stats_msg), 8 }, }; static const struct ofputil_msg_category nxst_request_category = { @@ -524,6 +809,10 @@ ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length, { OFPUTIL_NXST_AGGREGATE_REPLY, OFP10_VERSION, NXST_AGGREGATE, "NXST_AGGREGATE reply", sizeof(struct nx_aggregate_stats_reply), 0 }, + + { OFPUTIL_NXST_FLOW_MONITOR_REPLY, OFP10_VERSION, + NXST_FLOW_MONITOR, "NXST_FLOW_MONITOR reply", + sizeof(struct nicira_stats_msg), 8 }, }; static const struct ofputil_msg_category nxst_reply_category = { @@ -739,11 +1028,11 @@ ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length, sizeof(struct ofp_port_status) + sizeof(struct ofp11_port), 0 }, { OFPUTIL_OFPT_PACKET_OUT, OFP10_VERSION, - OFPT10_PACKET_OUT, "OFPT_PACKET_OUT", + OFPT_PACKET_OUT, "OFPT_PACKET_OUT", sizeof(struct ofp_packet_out), 1 }, { OFPUTIL_OFPT_FLOW_MOD, OFP10_VERSION, - OFPT10_FLOW_MOD, "OFPT_FLOW_MOD", + OFPT_FLOW_MOD, "OFPT_FLOW_MOD", sizeof(struct ofp_flow_mod), 1 }, { OFPUTIL_OFPT_PORT_MOD, OFP10_VERSION, @@ -891,12 +1180,13 @@ ofputil_protocol_from_ofp_version(int version) { switch (version) { case OFP10_VERSION: return OFPUTIL_P_OF10; + case OFP12_VERSION: return OFPUTIL_P_OF12; default: return 0; } } -/* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION or - * OFP11_VERSION) that corresponds to 'protocol'. */ +/* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION, + * OFP11_VERSION or OFP12_VERSION) that corresponds to 'protocol'. */ uint8_t ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol) { @@ -906,6 +1196,8 @@ ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol) case OFPUTIL_P_NXM: case OFPUTIL_P_NXM_TID: return OFP10_VERSION; + case OFPUTIL_P_OF12: + return OFP12_VERSION; } NOT_REACHED(); @@ -941,6 +1233,9 @@ ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable) case OFPUTIL_P_NXM_TID: return enable ? OFPUTIL_P_NXM_TID : OFPUTIL_P_NXM; + case OFPUTIL_P_OF12: + return OFPUTIL_P_OF12; + default: NOT_REACHED(); } @@ -972,6 +1267,9 @@ ofputil_protocol_set_base(enum ofputil_protocol cur, case OFPUTIL_P_NXM_TID: return ofputil_protocol_set_tid(OFPUTIL_P_NXM, tid); + case OFPUTIL_P_OF12: + return ofputil_protocol_set_tid(OFPUTIL_P_OF12, tid); + default: NOT_REACHED(); } @@ -999,6 +1297,9 @@ ofputil_protocol_to_string(enum ofputil_protocol protocol) case OFPUTIL_P_OF10_TID: return "OpenFlow10+table_id"; + + case OFPUTIL_P_OF12: + return NULL; } /* Check abbreviations. */ @@ -1174,15 +1475,26 @@ ofputil_usable_protocols(const struct cls_rule *rule) { const struct flow_wildcards *wc = &rule->wc; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14); - /* Only NXM supports separately wildcards the Ethernet multicast bit. */ - if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) { + /* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */ + if (!eth_mask_is_exact(wc->dl_src_mask) + && !eth_addr_is_zero(wc->dl_src_mask)) { + return OFPUTIL_P_NXM_ANY; + } + if (!eth_mask_is_exact(wc->dl_dst_mask) + && !eth_addr_is_zero(wc->dl_dst_mask)) { + return OFPUTIL_P_NXM_ANY; + } + + /* NXM and OF1.1+ support matching metadata. */ + if (wc->metadata_mask != htonll(0)) { return OFPUTIL_P_NXM_ANY; } /* Only NXM supports matching ARP hardware addresses. */ - if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) { + if (!eth_addr_is_zero(wc->arp_sha_mask) || + !eth_addr_is_zero(wc->arp_tha_mask)) { return OFPUTIL_P_NXM_ANY; } @@ -1208,7 +1520,7 @@ ofputil_usable_protocols(const struct cls_rule *rule) } /* Only NXM supports matching IPv6 flow label. */ - if (!(wc->wildcards & FWW_IPV6_LABEL)) { + if (wc->ipv6_label_mask) { return OFPUTIL_P_NXM_ANY; } @@ -1222,6 +1534,11 @@ ofputil_usable_protocols(const struct cls_rule *rule) return OFPUTIL_P_NXM_ANY; } + /* Only NXM supports non-CIDR IPv4 address masks. */ + if (!ip_is_cidr(wc->nw_src_mask) || !ip_is_cidr(wc->nw_dst_mask)) { + return OFPUTIL_P_NXM_ANY; + } + /* Only NXM supports bitwise matching on transport port. */ if ((wc->tp_src_mask && wc->tp_src_mask != htons(UINT16_MAX)) || (wc->tp_dst_mask && wc->tp_dst_mask != htons(UINT16_MAX))) { @@ -1259,6 +1576,9 @@ ofputil_encode_set_protocol(enum ofputil_protocol current, case OFPUTIL_P_OF10: return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10); + case OFPUTIL_P_OF12: + return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW12); + case OFPUTIL_P_OF10_TID: case OFPUTIL_P_NXM_TID: NOT_REACHED(); @@ -1306,6 +1626,9 @@ ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format) case NXFF_NXM: return OFPUTIL_P_NXM; + case NXFF_OPENFLOW12: + return OFPUTIL_P_OF12; + default: return 0; } @@ -1328,6 +1651,8 @@ ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format) return "openflow10"; case NXFF_NXM: return "nxm"; + case NXFF_OPENFLOW12: + return "openflow12"; default: NOT_REACHED(); } @@ -1362,11 +1687,17 @@ ofputil_make_flow_mod_table_id(bool flow_mod_table_id) * flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error * code. * - * Does not validate the flow_mod actions. */ + * Uses 'ofpacts' to store the abstract OFPACT_* version of 'oh''s actions. + * The caller must initialize 'ofpacts' and retains ownership of it. + * 'fm->ofpacts' will point into the 'ofpacts' buffer. + * + * Does not validate the flow_mod actions. The caller should do that, with + * ofpacts_check(). */ enum ofperr ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, const struct ofp_header *oh, - enum ofputil_protocol protocol) + enum ofputil_protocol protocol, + struct ofpbuf *ofpacts) { const struct ofputil_msg_type *type; uint16_t command; @@ -1381,30 +1712,33 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, uint16_t priority; enum ofperr error; - /* Dissect the message. */ + /* Get the ofp_flow_mod. */ ofm = ofpbuf_pull(&b, sizeof *ofm); - error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions); - if (error) { - return error; - } /* Set priority based on original wildcards. Normally we'd allow * ofputil_cls_rule_from_match() to do this for us, but * ofputil_normalize_rule() can put wildcards where the original flow * didn't have them. */ priority = ntohs(ofm->priority); - if (!(ofm->match.wildcards & htonl(OFPFW_ALL))) { + if (!(ofm->match.wildcards & htonl(OFPFW10_ALL))) { priority = UINT16_MAX; } /* Translate the rule. */ - ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr); + ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr); ofputil_normalize_rule(&fm->cr); + /* Now get the actions. */ + error = ofpacts_pull_openflow10(&b, b.size, ofpacts); + if (error) { + return error; + } + /* Translate the message. */ - fm->cookie = ofm->cookie; - fm->cookie_mask = htonll(UINT64_MAX); command = ntohs(ofm->command); + fm->cookie = htonll(0); + fm->cookie_mask = htonll(0); + fm->new_cookie = ofm->cookie; fm->idle_timeout = ntohs(ofm->idle_timeout); fm->hard_timeout = ntohs(ofm->hard_timeout); fm->buffer_id = ntohl(ofm->buffer_id); @@ -1422,24 +1756,19 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, if (error) { return error; } - error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions); + error = ofpacts_pull_openflow10(&b, b.size, ofpacts); if (error) { return error; } /* Translate the message. */ command = ntohs(nfm->command); - if (command == OFPFC_ADD) { - if (fm->cookie_mask) { - /* The "NXM_NX_COOKIE*" matches are not valid for flow - * additions. Additions must use the "cookie" field of - * the "nx_flow_mod" structure. */ - return OFPERR_NXBRC_NXM_INVALID; - } else { - fm->cookie = nfm->cookie; - fm->cookie_mask = htonll(UINT64_MAX); - } + if ((command & 0xff) == OFPFC_ADD && fm->cookie_mask) { + /* Flow additions may only set a new cookie, not match an + * existing cookie. */ + return OFPERR_NXBRC_NXM_INVALID; } + fm->new_cookie = nfm->cookie; fm->idle_timeout = ntohs(nfm->idle_timeout); fm->hard_timeout = ntohs(nfm->hard_timeout); fm->buffer_id = ntohl(nfm->buffer_id); @@ -1449,6 +1778,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, NOT_REACHED(); } + fm->ofpacts = ofpacts->data; + fm->ofpacts_len = ofpacts->size; if (protocol & OFPUTIL_P_TID) { fm->command = command & 0xff; fm->table_id = command >> 8; @@ -1461,15 +1792,11 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, } /* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to - * 'protocol' and returns the message. - * - * 'flow_mod_table_id' should be true if the NXT_FLOW_MOD_TABLE_ID extension is - * enabled, false otherwise. */ + * 'protocol' and returns the message. */ struct ofpbuf * ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, enum ofputil_protocol protocol) { - size_t actions_len = fm->n_actions * sizeof *fm->actions; struct ofp_flow_mod *ofm; struct nx_flow_mod *nfm; struct ofpbuf *msg; @@ -1483,10 +1810,10 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, switch (protocol) { case OFPUTIL_P_OF10: case OFPUTIL_P_OF10_TID: - msg = ofpbuf_new(sizeof *ofm + actions_len); - ofm = put_openflow(sizeof *ofm, OFPT10_FLOW_MOD, msg); - ofputil_cls_rule_to_match(&fm->cr, &ofm->match); - ofm->cookie = fm->cookie; + msg = ofpbuf_new(sizeof *ofm + fm->ofpacts_len); + ofm = put_openflow(sizeof *ofm, OFPT_FLOW_MOD, msg); + ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match); + ofm->cookie = fm->new_cookie; ofm->command = htons(command); ofm->idle_timeout = htons(fm->idle_timeout); ofm->hard_timeout = htons(fm->hard_timeout); @@ -1498,18 +1825,14 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, case OFPUTIL_P_NXM: case OFPUTIL_P_NXM_TID: - msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len); + msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + fm->ofpacts_len); put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg); nfm = msg->data; nfm->command = htons(command); - if (command == OFPFC_ADD) { - nfm->cookie = fm->cookie; - match_len = nx_put_match(msg, &fm->cr, 0, 0); - } else { - nfm->cookie = 0; - match_len = nx_put_match(msg, &fm->cr, - fm->cookie, fm->cookie_mask); - } + nfm->cookie = fm->new_cookie; + match_len = nx_put_match(msg, false, &fm->cr, + fm->cookie, fm->cookie_mask); + nfm = msg->data; nfm->idle_timeout = htons(fm->idle_timeout); nfm->hard_timeout = htons(fm->hard_timeout); nfm->priority = htons(fm->cr.priority); @@ -1519,11 +1842,14 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, nfm->match_len = htons(match_len); break; + case OFPUTIL_P_OF12: default: NOT_REACHED(); } - ofpbuf_put(msg, fm->actions, actions_len); + if (fm->ofpacts) { + ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg); + } update_openflow_length(msg); return msg; } @@ -1548,7 +1874,9 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms, if (fm->table_id != 0xff) { usable_protocols &= OFPUTIL_P_TID; } - if (fm->command != OFPFC_ADD && fm->cookie_mask != htonll(0)) { + + /* Matching of the cookie is only supported through NXM. */ + if (fm->cookie_mask != htonll(0)) { usable_protocols &= OFPUTIL_P_NXM_ANY; } } @@ -1566,7 +1894,7 @@ ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr, (const struct ofp_flow_stats_request *) oh; fsr->aggregate = aggregate; - ofputil_cls_rule_from_match(&ofsr->match, 0, &fsr->match); + ofputil_cls_rule_from_ofp10_match(&ofsr->match, 0, &fsr->match); fsr->out_port = ntohs(ofsr->out_port); fsr->table_id = ofsr->table_id; fsr->cookie = fsr->cookie_mask = htonll(0); @@ -1653,7 +1981,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, type = fsr->aggregate ? OFPST_AGGREGATE : OFPST_FLOW; ofsr = ofputil_make_stats_request(sizeof *ofsr, type, 0, &msg); - ofputil_cls_rule_to_match(&fsr->match, &ofsr->match); + ofputil_cls_rule_to_ofp10_match(&fsr->match, &ofsr->match); ofsr->table_id = fsr->table_id; ofsr->out_port = htons(fsr->out_port); break; @@ -1667,7 +1995,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, subtype = fsr->aggregate ? NXST_AGGREGATE : NXST_FLOW; ofputil_make_stats_request(sizeof *nfsr, OFPST_VENDOR, subtype, &msg); - match_len = nx_put_match(msg, &fsr->match, + match_len = nx_put_match(msg, false, &fsr->match, fsr->cookie, fsr->cookie_mask); nfsr = msg->data; @@ -1677,6 +2005,7 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, break; } + case OFPUTIL_P_OF12: default: NOT_REACHED(); } @@ -1715,12 +2044,17 @@ ofputil_flow_stats_request_usable_protocols( * 'flow_age_extension' as true so that the contents of 'msg' determine the * 'idle_age' and 'hard_age' members in 'fs'. * + * Uses 'ofpacts' to store the abstract OFPACT_* version of the flow stats + * reply's actions. The caller must initialize 'ofpacts' and retains ownership + * of it. 'fs->ofpacts' will point into the 'ofpacts' buffer. + * * Returns 0 if successful, EOF if no replies were left in this 'msg', * otherwise a positive errno value. */ int ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, struct ofpbuf *msg, - bool flow_age_extension) + bool flow_age_extension, + struct ofpbuf *ofpacts) { const struct ofputil_msg_type *type; int code; @@ -1758,14 +2092,13 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, return EINVAL; } - if (ofputil_pull_actions(msg, length - sizeof *ofs, - &fs->actions, &fs->n_actions)) { + if (ofpacts_pull_openflow10(msg, length - sizeof *ofs, ofpacts)) { return EINVAL; } fs->cookie = get_32aligned_be64(&ofs->cookie); - ofputil_cls_rule_from_match(&ofs->match, ntohs(ofs->priority), - &fs->rule); + ofputil_cls_rule_from_ofp10_match(&ofs->match, ntohs(ofs->priority), + &fs->rule); fs->table_id = ofs->table_id; fs->duration_sec = ntohl(ofs->duration_sec); fs->duration_nsec = ntohl(ofs->duration_nsec); @@ -1777,7 +2110,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count)); } else if (code == OFPUTIL_NXST_FLOW_REPLY) { const struct nx_flow_stats *nfs; - size_t match_len, length; + size_t match_len, actions_len, length; nfs = ofpbuf_try_pull(msg, sizeof *nfs); if (!nfs) { @@ -1798,9 +2131,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, return EINVAL; } - if (ofputil_pull_actions(msg, - length - sizeof *nfs - ROUND_UP(match_len, 8), - &fs->actions, &fs->n_actions)) { + actions_len = length - sizeof *nfs - ROUND_UP(match_len, 8); + if (ofpacts_pull_openflow10(msg, actions_len, ofpacts)) { return EINVAL; } @@ -1826,6 +2158,9 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, NOT_REACHED(); } + fs->ofpacts = ofpacts->data; + fs->ofpacts_len = ofpacts->size; + return 0; } @@ -1846,19 +2181,21 @@ void ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, struct list *replies) { - size_t act_len = fs->n_actions * sizeof *fs->actions; - const struct ofp_stats_msg *osm; + struct ofpbuf *reply = ofpbuf_from_list(list_back(replies)); + const struct ofp_stats_msg *osm = reply->data; + size_t start_ofs = reply->size; - osm = ofpbuf_from_list(list_back(replies))->data; if (osm->type == htons(OFPST_FLOW)) { - size_t len = offsetof(struct ofp_flow_stats, actions) + act_len; struct ofp_flow_stats *ofs; - ofs = ofputil_append_stats_reply(len, replies); - ofs->length = htons(len); + ofpbuf_put_uninit(reply, sizeof *ofs); + ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply); + + ofs = ofpbuf_at_assert(reply, start_ofs, sizeof *ofs); + ofs->length = htons(reply->size - start_ofs); ofs->table_id = fs->table_id; ofs->pad = 0; - ofputil_cls_rule_to_match(&fs->rule, &ofs->match); + ofputil_cls_rule_to_ofp10_match(&fs->rule, &ofs->match); ofs->duration_sec = htonl(fs->duration_sec); ofs->duration_nsec = htonl(fs->duration_nsec); ofs->priority = htons(fs->rule.priority); @@ -1870,17 +2207,16 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, htonll(unknown_to_zero(fs->packet_count))); put_32aligned_be64(&ofs->byte_count, htonll(unknown_to_zero(fs->byte_count))); - memcpy(ofs->actions, fs->actions, act_len); } else if (osm->type == htons(OFPST_VENDOR)) { struct nx_flow_stats *nfs; - struct ofpbuf *msg; - size_t start_len; + int match_len; - msg = ofputil_reserve_stats_reply( - sizeof *nfs + NXM_MAX_LEN + act_len, replies); - start_len = msg->size; + ofpbuf_put_uninit(reply, sizeof *nfs); + match_len = nx_put_match(reply, false, &fs->rule, 0, 0); + ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply); - nfs = ofpbuf_put_uninit(msg, sizeof *nfs); + nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs); + nfs->length = htons(reply->size - start_ofs); nfs->table_id = fs->table_id; nfs->pad = 0; nfs->duration_sec = htonl(fs->duration_sec); @@ -1894,15 +2230,15 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, nfs->hard_age = htons(fs->hard_age < 0 ? 0 : fs->hard_age < UINT16_MAX ? fs->hard_age + 1 : UINT16_MAX); - nfs->match_len = htons(nx_put_match(msg, &fs->rule, 0, 0)); + nfs->match_len = htons(match_len); nfs->cookie = fs->cookie; nfs->packet_count = htonll(fs->packet_count); nfs->byte_count = htonll(fs->byte_count); - ofpbuf_put(msg, fs->actions, act_len); - nfs->length = htons(msg->size - start_len); } else { NOT_REACHED(); } + + ofputil_postappend_stats_reply(start_ofs, replies); } /* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or @@ -1954,8 +2290,8 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, const struct ofp_flow_removed *ofr; ofr = (const struct ofp_flow_removed *) oh; - ofputil_cls_rule_from_match(&ofr->match, ntohs(ofr->priority), - &fr->rule); + ofputil_cls_rule_from_ofp10_match(&ofr->match, ntohs(ofr->priority), + &fr->rule); fr->cookie = ofr->cookie; fr->reason = ofr->reason; fr->duration_sec = ntohl(ofr->duration_sec); @@ -2010,7 +2346,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, ofr = make_openflow_xid(sizeof *ofr, OFPT_FLOW_REMOVED, htonl(0), &msg); - ofputil_cls_rule_to_match(&fr->rule, &ofr->match); + ofputil_cls_rule_to_ofp10_match(&fr->rule, &ofr->match); ofr->cookie = fr->cookie; ofr->priority = htons(fr->rule.priority); ofr->reason = fr->reason; @@ -2028,7 +2364,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, int match_len; make_nxmsg_xid(sizeof *nfr, NXT_FLOW_REMOVED, htonl(0), &msg); - match_len = nx_put_match(msg, &fr->rule, 0, 0); + match_len = nx_put_match(msg, false, &fr->rule, 0, 0); nfr = msg->data; nfr->cookie = fr->cookie; @@ -2043,6 +2379,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, break; } + case OFPUTIL_P_OF12: default: NOT_REACHED(); } @@ -2050,7 +2387,7 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, return msg; } -int +enum ofperr ofputil_decode_packet_in(struct ofputil_packet_in *pin, const struct ofp_header *oh) { @@ -2082,7 +2419,7 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, npi = ofpbuf_pull(&b, sizeof *npi); error = nx_pull_match_loose(&b, ntohs(npi->match_len), 0, &rule, NULL, - NULL); + NULL); if (error) { return error; } @@ -2102,6 +2439,9 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, pin->fmd.tun_id = rule.flow.tun_id; pin->fmd.tun_id_mask = rule.wc.tun_id_mask; + pin->fmd.metadata = rule.flow.metadata; + pin->fmd.metadata_mask = rule.wc.metadata_mask; + memcpy(pin->fmd.regs, rule.flow.regs, sizeof pin->fmd.regs); memcpy(pin->fmd.reg_masks, rule.wc.reg_masks, sizeof pin->fmd.reg_masks); @@ -2154,6 +2494,9 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin, cls_rule_init_catchall(&rule, 0); cls_rule_set_tun_id_masked(&rule, pin->fmd.tun_id, pin->fmd.tun_id_mask); + cls_rule_set_metadata_masked(&rule, pin->fmd.metadata, + pin->fmd.metadata_mask); + for (i = 0; i < FLOW_N_REGS; i++) { cls_rule_set_reg_masked(&rule, i, pin->fmd.regs[i], @@ -2163,7 +2506,7 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin, cls_rule_set_in_port(&rule, pin->fmd.in_port); ofpbuf_put_zeros(packet, sizeof *npi); - match_len = nx_put_match(packet, &rule, 0, 0); + match_len = nx_put_match(packet, false, &rule, 0, 0); ofpbuf_put_zeros(packet, 2); ofpbuf_put(packet, pin->packet, send_len); @@ -2222,9 +2565,18 @@ ofputil_packet_in_reason_from_string(const char *s, return false; } +/* Converts an OFPT_PACKET_OUT in 'opo' into an abstract ofputil_packet_out in + * 'po'. + * + * Uses 'ofpacts' to store the abstract OFPACT_* version of the packet out + * message's actions. The caller must initialize 'ofpacts' and retains + * ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer. + * + * Returns 0 if successful, otherwise an OFPERR_* value. */ enum ofperr ofputil_decode_packet_out(struct ofputil_packet_out *po, - const struct ofp_packet_out *opo) + const struct ofp_packet_out *opo, + struct ofpbuf *ofpacts) { enum ofperr error; struct ofpbuf b; @@ -2241,11 +2593,12 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, ofpbuf_use_const(&b, opo, ntohs(opo->header.length)); ofpbuf_pull(&b, sizeof *opo); - error = ofputil_pull_actions(&b, ntohs(opo->actions_len), - &po->actions, &po->n_actions); + error = ofpacts_pull_openflow10(&b, ntohs(opo->actions_len), ofpacts); if (error) { return error; } + po->ofpacts = ofpacts->data; + po->ofpacts_len = ofpacts->size; if (po->buffer_id == UINT32_MAX) { po->packet = b.data; @@ -2786,30 +3139,293 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm, return b; } + +/* ofputil_flow_monitor_request */ + +/* Converts an NXST_FLOW_MONITOR request in 'msg' into an abstract + * ofputil_flow_monitor_request in 'rq'. + * + * Multiple NXST_FLOW_MONITOR requests can be packed into a single OpenFlow + * message. Calling this function multiple times for a single 'msg' iterates + * through the requests. The caller must initially leave 'msg''s layer + * pointers null and not modify them between calls. + * + * Returns 0 if successful, EOF if no requests were left in this 'msg', + * otherwise an OFPERR_* value. */ +int +ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq, + struct ofpbuf *msg) +{ + struct nx_flow_monitor_request *nfmr; + uint16_t flags; + if (!msg->l2) { + msg->l2 = msg->data; + ofpbuf_pull(msg, sizeof(struct nicira_stats_msg)); + } + + if (!msg->size) { + return EOF; + } + + nfmr = ofpbuf_try_pull(msg, sizeof *nfmr); + if (!nfmr) { + VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR request has %zu " + "leftover bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; + } + + flags = ntohs(nfmr->flags); + if (!(flags & (NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY)) + || flags & ~(NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE + | NXFMF_MODIFY | NXFMF_ACTIONS | NXFMF_OWN)) { + VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR has bad flags %#"PRIx16, + flags); + return OFPERR_NXBRC_FM_BAD_FLAGS; + } + + if (!is_all_zeros(nfmr->zeros, sizeof nfmr->zeros)) { + return OFPERR_NXBRC_MUST_BE_ZERO; + } + + rq->id = ntohl(nfmr->id); + rq->flags = flags; + rq->out_port = ntohs(nfmr->out_port); + rq->table_id = nfmr->table_id; + + return nx_pull_match(msg, ntohs(nfmr->match_len), OFP_DEFAULT_PRIORITY, + &rq->match, NULL, NULL); +} + +void +ofputil_append_flow_monitor_request( + const struct ofputil_flow_monitor_request *rq, struct ofpbuf *msg) +{ + struct nx_flow_monitor_request *nfmr; + size_t start_ofs; + int match_len; + + if (!msg->size) { + ofputil_put_stats_header(alloc_xid(), OFPT10_STATS_REQUEST, + htons(OFPST_VENDOR), + htonl(NXST_FLOW_MONITOR), msg); + } + + start_ofs = msg->size; + ofpbuf_put_zeros(msg, sizeof *nfmr); + match_len = nx_put_match(msg, false, &rq->match, htonll(0), htonll(0)); + + nfmr = ofpbuf_at_assert(msg, start_ofs, sizeof *nfmr); + nfmr->id = htonl(rq->id); + nfmr->flags = htons(rq->flags); + nfmr->out_port = htons(rq->out_port); + nfmr->match_len = htons(match_len); + nfmr->table_id = rq->table_id; +} + +/* Converts an NXST_FLOW_MONITOR reply (also known as a flow update) in 'msg' + * into an abstract ofputil_flow_update in 'update'. The caller must have + * initialized update->match to point to space allocated for a cls_rule. + * + * Uses 'ofpacts' to store the abstract OFPACT_* version of the update's + * actions (except for NXFME_ABBREV, which never includes actions). The caller + * must initialize 'ofpacts' and retains ownership of it. 'update->ofpacts' + * will point into the 'ofpacts' buffer. + * + * Multiple flow updates can be packed into a single OpenFlow message. Calling + * this function multiple times for a single 'msg' iterates through the + * updates. The caller must initially leave 'msg''s layer pointers null and + * not modify them between calls. + * + * Returns 0 if successful, EOF if no updates were left in this 'msg', + * otherwise an OFPERR_* value. */ +int +ofputil_decode_flow_update(struct ofputil_flow_update *update, + struct ofpbuf *msg, struct ofpbuf *ofpacts) +{ + struct nx_flow_update_header *nfuh; + unsigned int length; + + if (!msg->l2) { + msg->l2 = msg->data; + ofpbuf_pull(msg, sizeof(struct nicira_stats_msg)); + } + + if (!msg->size) { + return EOF; + } + + if (msg->size < sizeof(struct nx_flow_update_header)) { + goto bad_len; + } + + nfuh = msg->data; + update->event = ntohs(nfuh->event); + length = ntohs(nfuh->length); + if (length > msg->size || length % 8) { + goto bad_len; + } + + if (update->event == NXFME_ABBREV) { + struct nx_flow_update_abbrev *nfua; + + if (length != sizeof *nfua) { + goto bad_len; + } + + nfua = ofpbuf_pull(msg, sizeof *nfua); + update->xid = nfua->xid; + return 0; + } else if (update->event == NXFME_ADDED + || update->event == NXFME_DELETED + || update->event == NXFME_MODIFIED) { + struct nx_flow_update_full *nfuf; + unsigned int actions_len; + unsigned int match_len; + enum ofperr error; + + if (length < sizeof *nfuf) { + goto bad_len; + } + + nfuf = ofpbuf_pull(msg, sizeof *nfuf); + match_len = ntohs(nfuf->match_len); + if (sizeof *nfuf + match_len > length) { + goto bad_len; + } + + update->reason = ntohs(nfuf->reason); + update->idle_timeout = ntohs(nfuf->idle_timeout); + update->hard_timeout = ntohs(nfuf->hard_timeout); + update->table_id = nfuf->table_id; + update->cookie = nfuf->cookie; + + error = nx_pull_match(msg, match_len, ntohs(nfuf->priority), + update->match, NULL, NULL); + if (error) { + return error; + } + + actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8); + error = ofpacts_pull_openflow10(msg, actions_len, ofpacts); + if (error) { + return error; + } + + update->ofpacts = ofpacts->data; + update->ofpacts_len = ofpacts->size; + return 0; + } else { + VLOG_WARN_RL(&bad_ofmsg_rl, + "NXST_FLOW_MONITOR reply has bad event %"PRIu16, + ntohs(nfuh->event)); + return OFPERR_OFPET_BAD_REQUEST; + } + +bad_len: + VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR reply has %zu " + "leftover bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; +} + +uint32_t +ofputil_decode_flow_monitor_cancel(const struct ofp_header *oh) +{ + return ntohl(((const struct nx_flow_monitor_cancel *) oh)->id); +} + +struct ofpbuf * +ofputil_encode_flow_monitor_cancel(uint32_t id) +{ + struct nx_flow_monitor_cancel *nfmc; + struct ofpbuf *msg; + + nfmc = make_nxmsg(sizeof *nfmc, NXT_FLOW_MONITOR_CANCEL, &msg); + nfmc->id = htonl(id); + return msg; +} + +void +ofputil_start_flow_update(struct list *replies) +{ + struct ofpbuf *msg; + + msg = ofpbuf_new(1024); + ofputil_put_stats_header(htonl(0), OFPT10_STATS_REPLY, + htons(OFPST_VENDOR), + htonl(NXST_FLOW_MONITOR), msg); + + list_init(replies); + list_push_back(replies, &msg->list_node); +} + +void +ofputil_append_flow_update(const struct ofputil_flow_update *update, + struct list *replies) +{ + struct nx_flow_update_header *nfuh; + struct ofpbuf *msg; + size_t start_ofs; + + msg = ofpbuf_from_list(list_back(replies)); + start_ofs = msg->size; + + if (update->event == NXFME_ABBREV) { + struct nx_flow_update_abbrev *nfua; + + nfua = ofpbuf_put_zeros(msg, sizeof *nfua); + nfua->xid = update->xid; + } else { + struct nx_flow_update_full *nfuf; + int match_len; + + ofpbuf_put_zeros(msg, sizeof *nfuf); + match_len = nx_put_match(msg, false, update->match, + htonll(0), htonll(0)); + ofpacts_put_openflow10(update->ofpacts, update->ofpacts_len, msg); + + nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf); + nfuf->reason = htons(update->reason); + nfuf->priority = htons(update->match->priority); + nfuf->idle_timeout = htons(update->idle_timeout); + nfuf->hard_timeout = htons(update->hard_timeout); + nfuf->match_len = htons(match_len); + nfuf->table_id = update->table_id; + nfuf->cookie = update->cookie; + } + + nfuh = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuh); + nfuh->length = htons(msg->size - start_ofs); + nfuh->event = htons(update->event); + + ofputil_postappend_stats_reply(start_ofs, replies); +} + struct ofpbuf * ofputil_encode_packet_out(const struct ofputil_packet_out *po) { struct ofp_packet_out *opo; - size_t actions_len; struct ofpbuf *msg; size_t size; - actions_len = po->n_actions * sizeof *po->actions; - size = sizeof *opo + actions_len; + size = sizeof *opo + po->ofpacts_len; if (po->buffer_id == UINT32_MAX) { size += po->packet_len; } msg = ofpbuf_new(size); - opo = put_openflow(sizeof *opo, OFPT10_PACKET_OUT, msg); + put_openflow(sizeof *opo, OFPT_PACKET_OUT, msg); + ofpacts_put_openflow10(po->ofpacts, po->ofpacts_len, msg); + + opo = msg->data; opo->buffer_id = htonl(po->buffer_id); opo->in_port = htons(po->in_port); - opo->actions_len = htons(actions_len); - ofpbuf_put(msg, po->actions, actions_len); + opo->actions_len = htons(msg->size - sizeof *opo); + if (po->buffer_id == UINT32_MAX) { ofpbuf_put(msg, po->packet, po->packet_len); } + update_openflow_length(msg); return msg; @@ -2957,10 +3573,10 @@ update_openflow_length(struct ofpbuf *buffer) oh->length = htons(buffer->size); } -static void -put_stats__(ovs_be32 xid, uint8_t ofp_type, - ovs_be16 ofpst_type, ovs_be32 nxst_subtype, - struct ofpbuf *msg) +void +ofputil_put_stats_header(ovs_be32 xid, uint8_t ofp_type, + ovs_be16 ofpst_type, ovs_be32 nxst_subtype, + struct ofpbuf *msg) { if (ofpst_type == htons(OFPST_VENDOR)) { struct nicira_stats_msg *nsm; @@ -2993,8 +3609,8 @@ ofputil_make_stats_request(size_t openflow_len, uint16_t ofpst_type, struct ofpbuf *msg; msg = *bufferp = ofpbuf_new(openflow_len); - put_stats__(alloc_xid(), OFPT10_STATS_REQUEST, - htons(ofpst_type), htonl(nxst_subtype), msg); + ofputil_put_stats_header(alloc_xid(), OFPT10_STATS_REQUEST, + htons(ofpst_type), htonl(nxst_subtype), msg); ofpbuf_padto(msg, openflow_len); return msg->data; @@ -3003,13 +3619,16 @@ ofputil_make_stats_request(size_t openflow_len, uint16_t ofpst_type, static void put_stats_reply__(const struct ofp_stats_msg *request, struct ofpbuf *msg) { + ovs_be32 nxst_subtype; + assert(request->header.type == OFPT10_STATS_REQUEST || request->header.type == OFPT10_STATS_REPLY); - put_stats__(request->header.xid, OFPT10_STATS_REPLY, request->type, - (request->type != htons(OFPST_VENDOR) - ? htonl(0) - : ((const struct nicira_stats_msg *) request)->subtype), - msg); + + nxst_subtype = (request->type != htons(OFPST_VENDOR) + ? htonl(0) + : ((const struct nicira_stats_msg *) request)->subtype); + ofputil_put_stats_header(request->header.xid, OFPT10_STATS_REPLY, + request->type, nxst_subtype, msg); } /* Creates a statistics reply message with total length 'openflow_len' @@ -3083,6 +3702,30 @@ ofputil_append_stats_reply(size_t len, struct list *replies) return ofpbuf_put_uninit(ofputil_reserve_stats_reply(len, replies), len); } +/* Sometimes, when composing stats replies, it's difficult to predict how long + * an individual reply chunk will be before actually encoding it into the reply + * buffer. This function allows easy handling of this case: just encode the + * reply, then use this function to break the message into two pieces if it + * exceeds the OpenFlow message limit. + * + * In detail, if the final stats message in 'replies' is too long for OpenFlow, + * this function breaks it into two separate stats replies, the first one with + * the first 'start_ofs' bytes, the second one containing the bytes from that + * offset onward. */ +void +ofputil_postappend_stats_reply(size_t start_ofs, struct list *replies) +{ + struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); + + assert(start_ofs <= UINT16_MAX); + if (msg->size > UINT16_MAX) { + size_t len = msg->size - start_ofs; + memcpy(ofputil_append_stats_reply(len, replies), + (const uint8_t *) msg->data + start_ofs, len); + msg->size = start_ofs; + } +} + /* Returns the first byte past the ofp_stats_msg header in 'oh'. */ const void * ofputil_stats_body(const struct ofp_header *oh) @@ -3115,84 +3758,6 @@ ofputil_nxstats_body_len(const struct ofp_header *oh) return ntohs(oh->length) - sizeof(struct nicira_stats_msg); } -struct ofpbuf * -make_flow_mod(uint16_t command, const struct cls_rule *rule, - size_t actions_len) -{ - struct ofp_flow_mod *ofm; - size_t size = sizeof *ofm + actions_len; - struct ofpbuf *out = ofpbuf_new(size); - ofm = ofpbuf_put_zeros(out, sizeof *ofm); - ofm->header.version = OFP10_VERSION; - ofm->header.type = OFPT10_FLOW_MOD; - ofm->header.length = htons(size); - ofm->cookie = 0; - ofm->priority = htons(MIN(rule->priority, UINT16_MAX)); - ofputil_cls_rule_to_match(rule, &ofm->match); - ofm->command = htons(command); - return out; -} - -struct ofpbuf * -make_add_flow(const struct cls_rule *rule, uint32_t buffer_id, - uint16_t idle_timeout, size_t actions_len) -{ - struct ofpbuf *out = make_flow_mod(OFPFC_ADD, rule, actions_len); - struct ofp_flow_mod *ofm = out->data; - ofm->idle_timeout = htons(idle_timeout); - ofm->hard_timeout = htons(OFP_FLOW_PERMANENT); - ofm->buffer_id = htonl(buffer_id); - return out; -} - -struct ofpbuf * -make_del_flow(const struct cls_rule *rule) -{ - struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, rule, 0); - struct ofp_flow_mod *ofm = out->data; - ofm->out_port = htons(OFPP_NONE); - return out; -} - -struct ofpbuf * -make_add_simple_flow(const struct cls_rule *rule, - uint32_t buffer_id, uint16_t out_port, - uint16_t idle_timeout) -{ - if (out_port != OFPP_NONE) { - struct ofp_action_output *oao; - struct ofpbuf *buffer; - - buffer = make_add_flow(rule, buffer_id, idle_timeout, sizeof *oao); - ofputil_put_OFPAT10_OUTPUT(buffer)->port = htons(out_port); - return buffer; - } else { - return make_add_flow(rule, buffer_id, idle_timeout, 0); - } -} - -struct ofpbuf * -make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason, - const struct ofpbuf *payload, int max_send_len) -{ - struct ofp_packet_in *opi; - struct ofpbuf *buf; - int send_len; - - send_len = MIN(max_send_len, payload->size); - buf = ofpbuf_new(sizeof *opi + send_len); - opi = put_openflow_xid(offsetof(struct ofp_packet_in, data), - OFPT_PACKET_IN, 0, buf); - opi->buffer_id = htonl(buffer_id); - opi->total_len = htons(payload->size); - opi->in_port = htons(in_port); - opi->reason = reason; - ofpbuf_put(buf, payload->data, send_len); - update_openflow_length(buf); - - return buf; -} - /* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */ struct ofpbuf * make_echo_request(void) @@ -3309,6 +3874,7 @@ ofputil_check_output_port(uint16_t port, int max_ports) case OFPP_FLOOD: case OFPP_ALL: case OFPP_CONTROLLER: + case OFPP_NONE: case OFPP_LOCAL: return 0; @@ -3407,277 +3973,6 @@ size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b) return b->size / ofputil_get_phy_port_size(ofp_version); } -static enum ofperr -check_resubmit_table(const struct nx_action_resubmit *nar) -{ - if (nar->pad[0] || nar->pad[1] || nar->pad[2]) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - return 0; -} - -static enum ofperr -check_output_reg(const struct nx_action_output_reg *naor, - const struct flow *flow) -{ - struct mf_subfield src; - size_t i; - - for (i = 0; i < sizeof naor->zero; i++) { - if (naor->zero[i]) { - return OFPERR_OFPBAC_BAD_ARGUMENT; - } - } - - nxm_decode(&src, naor->src, naor->ofs_nbits); - return mf_check_src(&src, flow); -} - -enum ofperr -validate_actions(const union ofp_action *actions, size_t n_actions, - const struct flow *flow, int max_ports) -{ - const union ofp_action *a; - size_t left; - - OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) { - enum ofperr error; - uint16_t port; - int code; - - code = ofputil_decode_action(a); - if (code < 0) { - error = -code; - VLOG_WARN_RL(&bad_ofmsg_rl, - "action decoding error at offset %td (%s)", - (a - actions) * sizeof *a, ofperr_get_name(error)); - - return error; - } - - error = 0; - switch ((enum ofputil_action_code) code) { - case OFPUTIL_OFPAT10_OUTPUT: - error = ofputil_check_output_port(ntohs(a->output.port), - max_ports); - break; - - case OFPUTIL_OFPAT10_SET_VLAN_VID: - if (a->vlan_vid.vlan_vid & ~htons(0xfff)) { - error = OFPERR_OFPBAC_BAD_ARGUMENT; - } - break; - - case OFPUTIL_OFPAT10_SET_VLAN_PCP: - if (a->vlan_pcp.vlan_pcp & ~7) { - error = OFPERR_OFPBAC_BAD_ARGUMENT; - } - break; - - case OFPUTIL_OFPAT10_ENQUEUE: - port = ntohs(((const struct ofp_action_enqueue *) a)->port); - if (port >= max_ports && port != OFPP_IN_PORT - && port != OFPP_LOCAL) { - error = OFPERR_OFPBAC_BAD_OUT_PORT; - } - break; - - case OFPUTIL_NXAST_REG_MOVE: - error = nxm_check_reg_move((const struct nx_action_reg_move *) a, - flow); - break; - - case OFPUTIL_NXAST_REG_LOAD: - error = nxm_check_reg_load((const struct nx_action_reg_load *) a, - flow); - break; - - case OFPUTIL_NXAST_MULTIPATH: - error = multipath_check((const struct nx_action_multipath *) a, - flow); - break; - - case OFPUTIL_NXAST_AUTOPATH: - error = autopath_check((const struct nx_action_autopath *) a, - flow); - break; - - case OFPUTIL_NXAST_BUNDLE: - case OFPUTIL_NXAST_BUNDLE_LOAD: - error = bundle_check((const struct nx_action_bundle *) a, - max_ports, flow); - break; - - case OFPUTIL_NXAST_OUTPUT_REG: - error = check_output_reg((const struct nx_action_output_reg *) a, - flow); - break; - - case OFPUTIL_NXAST_RESUBMIT_TABLE: - error = check_resubmit_table( - (const struct nx_action_resubmit *) a); - break; - - case OFPUTIL_NXAST_LEARN: - error = learn_check((const struct nx_action_learn *) a, flow); - break; - - case OFPUTIL_NXAST_CONTROLLER: - if (((const struct nx_action_controller *) a)->zero) { - error = OFPERR_NXBAC_MUST_BE_ZERO; - } - break; - - case OFPUTIL_OFPAT10_STRIP_VLAN: - case OFPUTIL_OFPAT10_SET_NW_SRC: - case OFPUTIL_OFPAT10_SET_NW_DST: - case OFPUTIL_OFPAT10_SET_NW_TOS: - case OFPUTIL_OFPAT10_SET_TP_SRC: - case OFPUTIL_OFPAT10_SET_TP_DST: - case OFPUTIL_OFPAT10_SET_DL_SRC: - case OFPUTIL_OFPAT10_SET_DL_DST: - case OFPUTIL_NXAST_RESUBMIT: - case OFPUTIL_NXAST_SET_TUNNEL: - case OFPUTIL_NXAST_SET_QUEUE: - case OFPUTIL_NXAST_POP_QUEUE: - case OFPUTIL_NXAST_NOTE: - case OFPUTIL_NXAST_SET_TUNNEL64: - case OFPUTIL_NXAST_EXIT: - case OFPUTIL_NXAST_DEC_TTL: - case OFPUTIL_NXAST_FIN_TIMEOUT: - break; - } - - if (error) { - VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)", - (a - actions) * sizeof *a, ofperr_get_name(error)); - return error; - } - } - if (left) { - VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu", - (n_actions - left) * sizeof *a); - return OFPERR_OFPBAC_BAD_LEN; - } - return 0; -} - -struct ofputil_action { - int code; - unsigned int min_len; - unsigned int max_len; -}; - -static const struct ofputil_action action_bad_type - = { -OFPERR_OFPBAC_BAD_TYPE, 0, UINT_MAX }; -static const struct ofputil_action action_bad_len - = { -OFPERR_OFPBAC_BAD_LEN, 0, UINT_MAX }; -static const struct ofputil_action action_bad_vendor - = { -OFPERR_OFPBAC_BAD_VENDOR, 0, UINT_MAX }; - -static const struct ofputil_action * -ofputil_decode_ofpat_action(const union ofp_action *a) -{ - enum ofp10_action_type type = ntohs(a->type); - - switch (type) { -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ - case ENUM: { \ - static const struct ofputil_action action = { \ - OFPUTIL_##ENUM, \ - sizeof(struct STRUCT), \ - sizeof(struct STRUCT) \ - }; \ - return &action; \ - } -#include "ofp-util.def" - - case OFPAT10_VENDOR: - default: - return &action_bad_type; - } -} - -static const struct ofputil_action * -ofputil_decode_nxast_action(const union ofp_action *a) -{ - const struct nx_action_header *nah = (const struct nx_action_header *) a; - enum nx_action_subtype subtype = ntohs(nah->subtype); - - switch (subtype) { -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ - case ENUM: { \ - static const struct ofputil_action action = { \ - OFPUTIL_##ENUM, \ - sizeof(struct STRUCT), \ - EXTENSIBLE ? UINT_MAX : sizeof(struct STRUCT) \ - }; \ - return &action; \ - } -#include "ofp-util.def" - - case NXAST_SNAT__OBSOLETE: - case NXAST_DROP_SPOOFED_ARP__OBSOLETE: - default: - return &action_bad_type; - } -} - -/* Parses 'a' to determine its type. Returns a nonnegative OFPUTIL_OFPAT10_* or - * OFPUTIL_NXAST_* constant if successful, otherwise a negative OFPERR_* error - * code. - * - * The caller must have already verified that 'a''s length is correct (that is, - * a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no - * longer than the amount of space allocated to 'a'). - * - * This function verifies that 'a''s length is correct for the type of action - * that it represents. */ -int -ofputil_decode_action(const union ofp_action *a) -{ - const struct ofputil_action *action; - uint16_t len = ntohs(a->header.len); - - if (a->type != htons(OFPAT10_VENDOR)) { - action = ofputil_decode_ofpat_action(a); - } else { - switch (ntohl(a->vendor.vendor)) { - case NX_VENDOR_ID: - if (len < sizeof(struct nx_action_header)) { - return -OFPERR_OFPBAC_BAD_LEN; - } - action = ofputil_decode_nxast_action(a); - break; - default: - action = &action_bad_vendor; - break; - } - } - - return (len >= action->min_len && len <= action->max_len - ? action->code - : -OFPERR_OFPBAC_BAD_LEN); -} - -/* Parses 'a' and returns its type as an OFPUTIL_OFPAT10_* or OFPUTIL_NXAST_* - * constant. The caller must have already validated that 'a' is a valid action - * understood by Open vSwitch (e.g. by a previous successful call to - * ofputil_decode_action()). */ -enum ofputil_action_code -ofputil_decode_action_unsafe(const union ofp_action *a) -{ - const struct ofputil_action *action; - - if (a->type != htons(OFPAT10_VENDOR)) { - action = ofputil_decode_ofpat_action(a); - } else { - action = ofputil_decode_nxast_action(a); - } - - return action->code; -} - /* 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. @@ -3687,7 +3982,9 @@ int ofputil_action_code_from_name(const char *name) { static const char *names[OFPUTIL_N_ACTIONS] = { -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME, + NULL, +#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME, +#define OFPAT11_ACTION(ENUM, STRUCT, NAME) NAME, #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, #include "ofp-util.def" }; @@ -3711,8 +4008,12 @@ void * ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) { switch (code) { + case OFPUTIL_ACTION_INVALID: + NOT_REACHED(); + #define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); +#define OFPAT11_ACTION OFPAT10_ACTION #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); #include "ofp-util.def" @@ -3736,6 +4037,7 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) ofputil_init_##ENUM(s); \ return s; \ } +#define OFPAT11_ACTION OFPAT10_ACTION #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ void \ ofputil_init_##ENUM(struct STRUCT *s) \ @@ -3756,20 +4058,6 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) } #include "ofp-util.def" -/* Returns true if 'action' outputs to 'port', false otherwise. */ -bool -action_outputs_to_port(const union ofp_action *action, ovs_be16 port) -{ - switch (ntohs(action->type)) { - case OFPAT10_OUTPUT: - return action->output.port == port; - case OFPAT10_ENQUEUE: - return ((const struct ofp_action_enqueue *) action)->port == port; - default: - return false; - } -} - /* "Normalizes" the wildcards in 'rule'. That means: * * 1. If the type of level N is known, then only the valid fields for that @@ -3845,14 +4133,14 @@ ofputil_normalize_rule(struct cls_rule *rule) wc.wildcards |= FWW_NW_TTL; } if (!(may_match & MAY_ARP_SHA)) { - wc.wildcards |= FWW_ARP_SHA; + memset(wc.arp_sha_mask, 0, ETH_ADDR_LEN); } if (!(may_match & MAY_ARP_THA)) { - wc.wildcards |= FWW_ARP_THA; + memset(wc.arp_tha_mask, 0, ETH_ADDR_LEN); } if (!(may_match & MAY_IPV6)) { wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any; - wc.wildcards |= FWW_IPV6_LABEL; + wc.ipv6_label_mask = htonl(0); } if (!(may_match & MAY_ND_TARGET)) { wc.nd_target_mask = in6addr_any; @@ -3877,57 +4165,6 @@ ofputil_normalize_rule(struct cls_rule *rule) } } -/* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if - * successful, otherwise an OpenFlow error. - * - * If successful, the first action is stored in '*actionsp' and the number of - * "union ofp_action" size elements into '*n_actionsp'. Otherwise NULL and 0 - * are stored, respectively. - * - * This function does not check that the actions are valid (the caller should - * do so, with validate_actions()). The caller is also responsible for making - * sure that 'b->data' is initially aligned appropriately for "union - * ofp_action". */ -enum ofperr -ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len, - union ofp_action **actionsp, size_t *n_actionsp) -{ - if (actions_len % OFP_ACTION_ALIGN != 0) { - VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u " - "is not a multiple of %d", actions_len, OFP_ACTION_ALIGN); - goto error; - } - - *actionsp = ofpbuf_try_pull(b, actions_len); - if (*actionsp == NULL) { - VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u " - "exceeds remaining message length (%zu)", - actions_len, b->size); - goto error; - } - - *n_actionsp = actions_len / OFP_ACTION_ALIGN; - return 0; - -error: - *actionsp = NULL; - *n_actionsp = 0; - return OFPERR_OFPBRC_BAD_LEN; -} - -bool -ofputil_actions_equal(const union ofp_action *a, size_t n_a, - const union ofp_action *b, size_t n_b) -{ - return n_a == n_b && (!n_a || !memcmp(a, b, n_a * sizeof *a)); -} - -union ofp_action * -ofputil_actions_clone(const union ofp_action *actions, size_t n) -{ - return n ? xmemdup(actions, n * sizeof *actions) : NULL; -} - /* Parses a key or a key-value pair from '*stringp'. * * On success: Stores the key into '*keyp'. Stores the value, if present, into