X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fofp-util.c;h=cc3c9fdf98ae1a93ce700ee652b38f3460449fef;hb=b5e5143b6a808d3463b205c36d49cdd9a9a9e4c3;hp=e29450f895cf0e367fad1e07b46253b41bdef72f;hpb=08f94c0e1a47518e73f954af8f61eb0df31ac8bd;p=sliver-openvswitch.git diff --git a/lib/ofp-util.c b/lib/ofp-util.c index e29450f89..cc3c9fdf9 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira Networks. + * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,8 +28,8 @@ #include "classifier.h" #include "dynamic-string.h" #include "learn.h" -#include "multipath.h" #include "meta-flow.h" +#include "multipath.h" #include "netdev.h" #include "nx-match.h" #include "ofp-errors.h" @@ -64,26 +64,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 +96,66 @@ 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 == 8); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); /* 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. */ + /* Wildcard fields that aren't defined by ofp10_match or tun_id. */ wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_ECN | FWW_NW_TTL - | FWW_ND_TARGET | FWW_IPV6_LABEL); + | FWW_IPV6_LABEL); - 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,12 @@ 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(OFP_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 +194,53 @@ 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); } 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 +262,265 @@ 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)) { + /* Metadata field not yet supported because we haven't decided how to + * map it onto our existing fields (or whether to add a new field). */ + return OFPERR_OFPBMC_BAD_FIELD; + } + + 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; + + /* Metadata field not yet supported */ + match->metadata_mask = htonll(UINT64_MAX); + + 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 +529,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 @@ -587,6 +855,10 @@ ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length, OFPST_QUEUE, "OFPST_QUEUE request", sizeof(struct ofp_queue_stats_request), 0 }, + { OFPUTIL_OFPST_PORT_DESC_REQUEST, OFP10_VERSION, + OFPST_PORT_DESC, "OFPST_PORT_DESC request", + sizeof(struct ofp_stats_msg), 0 }, + { 0, 0, OFPST_VENDOR, "OFPST_VENDOR request", sizeof(struct ofp_vendor_stats_msg), 1 }, @@ -644,6 +916,10 @@ ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length, OFPST_QUEUE, "OFPST_QUEUE reply", sizeof(struct ofp_stats_msg), sizeof(struct ofp_queue_stats) }, + { OFPUTIL_OFPST_PORT_DESC_REPLY, OFP10_VERSION, + OFPST_PORT_DESC, "OFPST_PORT_DESC reply", + sizeof(struct ofp_stats_msg), sizeof(struct ofp10_phy_port) }, + { 0, 0, OFPST_VENDOR, "OFPST_VENDOR reply", sizeof(struct ofp_vendor_stats_msg), 1 }, @@ -698,7 +974,10 @@ ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length, { OFPUTIL_OFPT_FEATURES_REPLY, OFP10_VERSION, OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY", - sizeof(struct ofp_switch_features), sizeof(struct ofp_phy_port) }, + sizeof(struct ofp_switch_features), sizeof(struct ofp10_phy_port) }, + { OFPUTIL_OFPT_FEATURES_REPLY, OFP11_VERSION, + OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY", + sizeof(struct ofp_switch_features), sizeof(struct ofp11_port) }, { OFPUTIL_OFPT_GET_CONFIG_REQUEST, OFP10_VERSION, OFPT_GET_CONFIG_REQUEST, "OFPT_GET_CONFIG_REQUEST", @@ -722,7 +1001,10 @@ ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length, { OFPUTIL_OFPT_PORT_STATUS, OFP10_VERSION, OFPT_PORT_STATUS, "OFPT_PORT_STATUS", - sizeof(struct ofp_port_status), 0 }, + sizeof(struct ofp_port_status) + sizeof(struct ofp10_phy_port), 0 }, + { OFPUTIL_OFPT_PORT_STATUS, OFP11_VERSION, + OFPT_PORT_STATUS, "OFPT_PORT_STATUS", + sizeof(struct ofp_port_status) + sizeof(struct ofp11_port), 0 }, { OFPUTIL_OFPT_PACKET_OUT, OFP10_VERSION, OFPT10_PACKET_OUT, "OFPT_PACKET_OUT", @@ -734,7 +1016,10 @@ ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length, { OFPUTIL_OFPT_PORT_MOD, OFP10_VERSION, OFPT10_PORT_MOD, "OFPT_PORT_MOD", - sizeof(struct ofp_port_mod), 0 }, + sizeof(struct ofp10_port_mod), 0 }, + { OFPUTIL_OFPT_PORT_MOD, OFP11_VERSION, + OFPT11_PORT_MOD, "OFPT_PORT_MOD", + sizeof(struct ofp11_port_mod), 0 }, { 0, OFP10_VERSION, OFPT10_STATS_REQUEST, "OFPT_STATS_REQUEST", @@ -878,6 +1163,22 @@ ofputil_protocol_from_ofp_version(int version) } } +/* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION or + * OFP11_VERSION) that corresponds to 'protocol'. */ +uint8_t +ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol) +{ + switch (protocol) { + case OFPUTIL_P_OF10: + case OFPUTIL_P_OF10_TID: + case OFPUTIL_P_NXM: + case OFPUTIL_P_NXM_TID: + return OFP10_VERSION; + } + + NOT_REACHED(); +} + /* Returns true if 'protocol' is a single OFPUTIL_P_* value, false * otherwise. */ bool @@ -1141,10 +1442,15 @@ ofputil_usable_protocols(const struct cls_rule *rule) { const struct flow_wildcards *wc = &rule->wc; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 8); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11); - /* 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; } @@ -1189,6 +1495,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))) { @@ -1360,18 +1671,19 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, * 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); /* 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); @@ -1396,17 +1708,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, /* 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); @@ -1428,10 +1735,7 @@ 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) @@ -1452,8 +1756,8 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, 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; + 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); @@ -1469,14 +1773,10 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, 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); @@ -1515,7 +1815,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; } } @@ -1533,7 +1835,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); @@ -1620,7 +1922,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; @@ -1634,7 +1936,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; @@ -1731,8 +2033,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, } 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); @@ -1825,7 +2127,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, ofs->length = htons(len); 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); @@ -1861,7 +2163,7 @@ 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(nx_put_match(msg, false, &fs->rule, 0, 0)); nfs->cookie = fs->cookie; nfs->packet_count = htonll(fs->packet_count); nfs->byte_count = htonll(fs->byte_count); @@ -1921,8 +2223,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); @@ -1977,7 +2279,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; @@ -1995,7 +2297,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; @@ -2017,7 +2319,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) { @@ -2049,7 +2351,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; } @@ -2130,7 +2432,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); @@ -2199,7 +2501,7 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, po->buffer_id = ntohl(opo->buffer_id); po->in_port = ntohs(opo->in_port); if (po->in_port >= OFPP_MAX && po->in_port != OFPP_LOCAL - && po->in_port != OFPP_NONE) { + && po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) { VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16, po->in_port); return OFPERR_NXBRC_BAD_IN_PORT; @@ -2237,25 +2539,523 @@ BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */ BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */ /* NETDEV_F_ bits 11...15 are OFPPF10_ bits 7...11: */ -BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == (OFPPF_COPPER << 4)); -BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == (OFPPF_FIBER << 4)); -BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == (OFPPF_AUTONEG << 4)); -BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == (OFPPF_PAUSE << 4)); -BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == (OFPPF_PAUSE_ASYM << 4)); - -enum netdev_features -ofputil_netdev_port_features_from_ofp10(ovs_be32 ofp10_) +BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == (OFPPF10_COPPER << 4)); +BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == (OFPPF10_FIBER << 4)); +BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == (OFPPF10_AUTONEG << 4)); +BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == (OFPPF10_PAUSE << 4)); +BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == (OFPPF10_PAUSE_ASYM << 4)); + +static enum netdev_features +netdev_port_features_from_ofp10(ovs_be32 ofp10_) { uint32_t ofp10 = ntohl(ofp10_); return (ofp10 & 0x7f) | ((ofp10 & 0xf80) << 4); } -ovs_be32 -ofputil_netdev_port_features_to_ofp10(enum netdev_features features) +static ovs_be32 +netdev_port_features_to_ofp10(enum netdev_features features) { return htonl((features & 0x7f) | ((features & 0xf800) >> 4)); } +BUILD_ASSERT_DECL((int) NETDEV_F_10MB_HD == OFPPF_10MB_HD); /* bit 0 */ +BUILD_ASSERT_DECL((int) NETDEV_F_10MB_FD == OFPPF_10MB_FD); /* bit 1 */ +BUILD_ASSERT_DECL((int) NETDEV_F_100MB_HD == OFPPF_100MB_HD); /* bit 2 */ +BUILD_ASSERT_DECL((int) NETDEV_F_100MB_FD == OFPPF_100MB_FD); /* bit 3 */ +BUILD_ASSERT_DECL((int) NETDEV_F_1GB_HD == OFPPF_1GB_HD); /* bit 4 */ +BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */ +BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */ +BUILD_ASSERT_DECL((int) NETDEV_F_40GB_FD == OFPPF11_40GB_FD); /* bit 7 */ +BUILD_ASSERT_DECL((int) NETDEV_F_100GB_FD == OFPPF11_100GB_FD); /* bit 8 */ +BUILD_ASSERT_DECL((int) NETDEV_F_1TB_FD == OFPPF11_1TB_FD); /* bit 9 */ +BUILD_ASSERT_DECL((int) NETDEV_F_OTHER == OFPPF11_OTHER); /* bit 10 */ +BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == OFPPF11_COPPER); /* bit 11 */ +BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == OFPPF11_FIBER); /* bit 12 */ +BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == OFPPF11_AUTONEG); /* bit 13 */ +BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == OFPPF11_PAUSE); /* bit 14 */ +BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == OFPPF11_PAUSE_ASYM);/* bit 15 */ + +static enum netdev_features +netdev_port_features_from_ofp11(ovs_be32 ofp11) +{ + return ntohl(ofp11) & 0xffff; +} + +static ovs_be32 +netdev_port_features_to_ofp11(enum netdev_features features) +{ + return htonl(features & 0xffff); +} + +static enum ofperr +ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp, + const struct ofp10_phy_port *opp) +{ + memset(pp, 0, sizeof *pp); + + pp->port_no = ntohs(opp->port_no); + memcpy(pp->hw_addr, opp->hw_addr, OFP_ETH_ALEN); + ovs_strlcpy(pp->name, opp->name, OFP_MAX_PORT_NAME_LEN); + + pp->config = ntohl(opp->config) & OFPPC10_ALL; + pp->state = ntohl(opp->state) & OFPPS10_ALL; + + pp->curr = netdev_port_features_from_ofp10(opp->curr); + pp->advertised = netdev_port_features_from_ofp10(opp->advertised); + pp->supported = netdev_port_features_from_ofp10(opp->supported); + pp->peer = netdev_port_features_from_ofp10(opp->peer); + + pp->curr_speed = netdev_features_to_bps(pp->curr) / 1000; + pp->max_speed = netdev_features_to_bps(pp->supported) / 1000; + + return 0; +} + +static enum ofperr +ofputil_decode_ofp11_port(struct ofputil_phy_port *pp, + const struct ofp11_port *op) +{ + enum ofperr error; + + memset(pp, 0, sizeof *pp); + + error = ofputil_port_from_ofp11(op->port_no, &pp->port_no); + if (error) { + return error; + } + memcpy(pp->hw_addr, op->hw_addr, OFP_ETH_ALEN); + ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN); + + pp->config = ntohl(op->config) & OFPPC11_ALL; + pp->state = ntohl(op->state) & OFPPC11_ALL; + + pp->curr = netdev_port_features_from_ofp11(op->curr); + pp->advertised = netdev_port_features_from_ofp11(op->advertised); + pp->supported = netdev_port_features_from_ofp11(op->supported); + pp->peer = netdev_port_features_from_ofp11(op->peer); + + pp->curr_speed = ntohl(op->curr_speed); + pp->max_speed = ntohl(op->max_speed); + + return 0; +} + +static size_t +ofputil_get_phy_port_size(uint8_t ofp_version) +{ + return ofp_version == OFP10_VERSION ? sizeof(struct ofp10_phy_port) + : sizeof(struct ofp11_port); +} + +static void +ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp, + struct ofp10_phy_port *opp) +{ + memset(opp, 0, sizeof *opp); + + opp->port_no = htons(pp->port_no); + memcpy(opp->hw_addr, pp->hw_addr, ETH_ADDR_LEN); + ovs_strlcpy(opp->name, pp->name, OFP_MAX_PORT_NAME_LEN); + + opp->config = htonl(pp->config & OFPPC10_ALL); + opp->state = htonl(pp->state & OFPPS10_ALL); + + opp->curr = netdev_port_features_to_ofp10(pp->curr); + opp->advertised = netdev_port_features_to_ofp10(pp->advertised); + opp->supported = netdev_port_features_to_ofp10(pp->supported); + opp->peer = netdev_port_features_to_ofp10(pp->peer); +} + +static void +ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp, + struct ofp11_port *op) +{ + memset(op, 0, sizeof *op); + + op->port_no = ofputil_port_to_ofp11(pp->port_no); + memcpy(op->hw_addr, pp->hw_addr, ETH_ADDR_LEN); + ovs_strlcpy(op->name, pp->name, OFP_MAX_PORT_NAME_LEN); + + op->config = htonl(pp->config & OFPPC11_ALL); + op->state = htonl(pp->state & OFPPS11_ALL); + + op->curr = netdev_port_features_to_ofp11(pp->curr); + op->advertised = netdev_port_features_to_ofp11(pp->advertised); + op->supported = netdev_port_features_to_ofp11(pp->supported); + op->peer = netdev_port_features_to_ofp11(pp->peer); + + op->curr_speed = htonl(pp->curr_speed); + op->max_speed = htonl(pp->max_speed); +} + +static void +ofputil_put_phy_port(uint8_t ofp_version, const struct ofputil_phy_port *pp, + struct ofpbuf *b) +{ + if (ofp_version == OFP10_VERSION) { + struct ofp10_phy_port *opp; + if (b->size + sizeof *opp <= UINT16_MAX) { + opp = ofpbuf_put_uninit(b, sizeof *opp); + ofputil_encode_ofp10_phy_port(pp, opp); + } + } else { + struct ofp11_port *op; + if (b->size + sizeof *op <= UINT16_MAX) { + op = ofpbuf_put_uninit(b, sizeof *op); + ofputil_encode_ofp11_port(pp, op); + } + } +} + +void +ofputil_append_port_desc_stats_reply(uint8_t ofp_version, + const struct ofputil_phy_port *pp, + struct list *replies) +{ + if (ofp_version == OFP10_VERSION) { + struct ofp10_phy_port *opp; + + opp = ofputil_append_stats_reply(sizeof *opp, replies); + ofputil_encode_ofp10_phy_port(pp, opp); + } else { + struct ofp11_port *op; + + op = ofputil_append_stats_reply(sizeof *op, replies); + ofputil_encode_ofp11_port(pp, op); + } +} + +/* ofputil_switch_features */ + +#define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \ + OFPC_IP_REASM | OFPC_QUEUE_STATS | OFPC_ARP_MATCH_IP) +BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_STATS == OFPC_FLOW_STATS); +BUILD_ASSERT_DECL((int) OFPUTIL_C_TABLE_STATS == OFPC_TABLE_STATS); +BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_STATS == OFPC_PORT_STATS); +BUILD_ASSERT_DECL((int) OFPUTIL_C_IP_REASM == OFPC_IP_REASM); +BUILD_ASSERT_DECL((int) OFPUTIL_C_QUEUE_STATS == OFPC_QUEUE_STATS); +BUILD_ASSERT_DECL((int) OFPUTIL_C_ARP_MATCH_IP == OFPC_ARP_MATCH_IP); + +struct ofputil_action_bit_translation { + enum ofputil_action_bitmap ofputil_bit; + int of_bit; +}; + +static const struct ofputil_action_bit_translation of10_action_bits[] = { + { OFPUTIL_A_OUTPUT, OFPAT10_OUTPUT }, + { OFPUTIL_A_SET_VLAN_VID, OFPAT10_SET_VLAN_VID }, + { OFPUTIL_A_SET_VLAN_PCP, OFPAT10_SET_VLAN_PCP }, + { OFPUTIL_A_STRIP_VLAN, OFPAT10_STRIP_VLAN }, + { OFPUTIL_A_SET_DL_SRC, OFPAT10_SET_DL_SRC }, + { OFPUTIL_A_SET_DL_DST, OFPAT10_SET_DL_DST }, + { OFPUTIL_A_SET_NW_SRC, OFPAT10_SET_NW_SRC }, + { OFPUTIL_A_SET_NW_DST, OFPAT10_SET_NW_DST }, + { OFPUTIL_A_SET_NW_TOS, OFPAT10_SET_NW_TOS }, + { OFPUTIL_A_SET_TP_SRC, OFPAT10_SET_TP_SRC }, + { OFPUTIL_A_SET_TP_DST, OFPAT10_SET_TP_DST }, + { OFPUTIL_A_ENQUEUE, OFPAT10_ENQUEUE }, + { 0, 0 }, +}; + +static const struct ofputil_action_bit_translation of11_action_bits[] = { + { OFPUTIL_A_OUTPUT, OFPAT11_OUTPUT }, + { OFPUTIL_A_SET_VLAN_VID, OFPAT11_SET_VLAN_VID }, + { OFPUTIL_A_SET_VLAN_PCP, OFPAT11_SET_VLAN_PCP }, + { OFPUTIL_A_SET_DL_SRC, OFPAT11_SET_DL_SRC }, + { OFPUTIL_A_SET_DL_DST, OFPAT11_SET_DL_DST }, + { OFPUTIL_A_SET_NW_SRC, OFPAT11_SET_NW_SRC }, + { OFPUTIL_A_SET_NW_DST, OFPAT11_SET_NW_DST }, + { OFPUTIL_A_SET_NW_TOS, OFPAT11_SET_NW_TOS }, + { OFPUTIL_A_SET_NW_ECN, OFPAT11_SET_NW_ECN }, + { OFPUTIL_A_SET_TP_SRC, OFPAT11_SET_TP_SRC }, + { OFPUTIL_A_SET_TP_DST, OFPAT11_SET_TP_DST }, + { OFPUTIL_A_COPY_TTL_OUT, OFPAT11_COPY_TTL_OUT }, + { OFPUTIL_A_COPY_TTL_IN, OFPAT11_COPY_TTL_IN }, + { OFPUTIL_A_SET_MPLS_LABEL, OFPAT11_SET_MPLS_LABEL }, + { OFPUTIL_A_SET_MPLS_TC, OFPAT11_SET_MPLS_TC }, + { OFPUTIL_A_SET_MPLS_TTL, OFPAT11_SET_MPLS_TTL }, + { OFPUTIL_A_DEC_MPLS_TTL, OFPAT11_DEC_MPLS_TTL }, + { OFPUTIL_A_PUSH_VLAN, OFPAT11_PUSH_VLAN }, + { OFPUTIL_A_POP_VLAN, OFPAT11_POP_VLAN }, + { OFPUTIL_A_PUSH_MPLS, OFPAT11_PUSH_MPLS }, + { OFPUTIL_A_POP_MPLS, OFPAT11_POP_MPLS }, + { OFPUTIL_A_SET_QUEUE, OFPAT11_SET_QUEUE }, + { OFPUTIL_A_GROUP, OFPAT11_GROUP }, + { OFPUTIL_A_SET_NW_TTL, OFPAT11_SET_NW_TTL }, + { OFPUTIL_A_DEC_NW_TTL, OFPAT11_DEC_NW_TTL }, + { 0, 0 }, +}; + +static enum ofputil_action_bitmap +decode_action_bits(ovs_be32 of_actions, + const struct ofputil_action_bit_translation *x) +{ + enum ofputil_action_bitmap ofputil_actions; + + ofputil_actions = 0; + for (; x->ofputil_bit; x++) { + if (of_actions & htonl(1u << x->of_bit)) { + ofputil_actions |= x->ofputil_bit; + } + } + return ofputil_actions; +} + +/* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an + * abstract representation in '*features'. Initializes '*b' to iterate over + * the OpenFlow port structures following 'osf' with later calls to + * ofputil_pull_phy_port(). Returns 0 if successful, otherwise an + * OFPERR_* value. */ +enum ofperr +ofputil_decode_switch_features(const struct ofp_switch_features *osf, + struct ofputil_switch_features *features, + struct ofpbuf *b) +{ + ofpbuf_use_const(b, osf, ntohs(osf->header.length)); + ofpbuf_pull(b, sizeof *osf); + + features->datapath_id = ntohll(osf->datapath_id); + features->n_buffers = ntohl(osf->n_buffers); + features->n_tables = osf->n_tables; + + features->capabilities = ntohl(osf->capabilities) & OFPC_COMMON; + + if (b->size % ofputil_get_phy_port_size(osf->header.version)) { + return OFPERR_OFPBRC_BAD_LEN; + } + + if (osf->header.version == OFP10_VERSION) { + if (osf->capabilities & htonl(OFPC10_STP)) { + features->capabilities |= OFPUTIL_C_STP; + } + features->actions = decode_action_bits(osf->actions, of10_action_bits); + } else if (osf->header.version == OFP11_VERSION) { + if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) { + features->capabilities |= OFPUTIL_C_GROUP_STATS; + } + features->actions = decode_action_bits(osf->actions, of11_action_bits); + } else { + return OFPERR_OFPBRC_BAD_VERSION; + } + + return 0; +} + +/* Returns true if the maximum number of ports are in 'osf'. */ +static bool +max_ports_in_features(const struct ofp_switch_features *osf) +{ + size_t pp_size = ofputil_get_phy_port_size(osf->header.version); + return ntohs(osf->header.length) + pp_size > UINT16_MAX; +} + +/* Given a buffer 'b' that contains a Features Reply message, checks if + * it contains the maximum number of ports that will fit. If so, it + * returns true and removes the ports from the message. The caller + * should then send an OFPST_PORT_DESC stats request to get the ports, + * since the switch may have more ports than could be represented in the + * Features Reply. Otherwise, returns false. + */ +bool +ofputil_switch_features_ports_trunc(struct ofpbuf *b) +{ + struct ofp_switch_features *osf = b->data; + + if (max_ports_in_features(osf)) { + /* Remove all the ports. */ + b->size = sizeof(*osf); + update_openflow_length(b); + + return true; + } + + return false; +} + +static ovs_be32 +encode_action_bits(enum ofputil_action_bitmap ofputil_actions, + const struct ofputil_action_bit_translation *x) +{ + uint32_t of_actions; + + of_actions = 0; + for (; x->ofputil_bit; x++) { + if (ofputil_actions & x->ofputil_bit) { + of_actions |= 1 << x->of_bit; + } + } + return htonl(of_actions); +} + +/* Returns a buffer owned by the caller that encodes 'features' in the format + * required by 'protocol' with the given 'xid'. The caller should append port + * information to the buffer with subsequent calls to + * ofputil_put_switch_features_port(). */ +struct ofpbuf * +ofputil_encode_switch_features(const struct ofputil_switch_features *features, + enum ofputil_protocol protocol, ovs_be32 xid) +{ + struct ofp_switch_features *osf; + struct ofpbuf *b; + + osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, xid, &b); + osf->header.version = ofputil_protocol_to_ofp_version(protocol); + osf->datapath_id = htonll(features->datapath_id); + osf->n_buffers = htonl(features->n_buffers); + osf->n_tables = features->n_tables; + + osf->capabilities = htonl(features->capabilities & OFPC_COMMON); + if (osf->header.version == OFP10_VERSION) { + if (features->capabilities & OFPUTIL_C_STP) { + osf->capabilities |= htonl(OFPC10_STP); + } + osf->actions = encode_action_bits(features->actions, of10_action_bits); + } else { + if (features->capabilities & OFPUTIL_C_GROUP_STATS) { + osf->capabilities |= htonl(OFPC11_GROUP_STATS); + } + osf->actions = encode_action_bits(features->actions, of11_action_bits); + } + + return b; +} + +/* Encodes 'pp' into the format required by the switch_features message already + * in 'b', which should have been returned by ofputil_encode_switch_features(), + * and appends the encoded version to 'b'. */ +void +ofputil_put_switch_features_port(const struct ofputil_phy_port *pp, + struct ofpbuf *b) +{ + const struct ofp_switch_features *osf = b->data; + + ofputil_put_phy_port(osf->header.version, pp, b); +} + +/* ofputil_port_status */ + +/* Decodes the OpenFlow "port status" message in '*ops' into an abstract form + * in '*ps'. Returns 0 if successful, otherwise an OFPERR_* value. */ +enum ofperr +ofputil_decode_port_status(const struct ofp_port_status *ops, + struct ofputil_port_status *ps) +{ + struct ofpbuf b; + int retval; + + if (ops->reason != OFPPR_ADD && + ops->reason != OFPPR_DELETE && + ops->reason != OFPPR_MODIFY) { + return OFPERR_NXBRC_BAD_REASON; + } + ps->reason = ops->reason; + + ofpbuf_use_const(&b, ops, ntohs(ops->header.length)); + ofpbuf_pull(&b, sizeof *ops); + retval = ofputil_pull_phy_port(ops->header.version, &b, &ps->desc); + assert(retval != EOF); + return retval; +} + +/* Converts the abstract form of a "port status" message in '*ps' into an + * OpenFlow message suitable for 'protocol', and returns that encoded form in + * a buffer owned by the caller. */ +struct ofpbuf * +ofputil_encode_port_status(const struct ofputil_port_status *ps, + enum ofputil_protocol protocol) +{ + struct ofp_port_status *ops; + struct ofpbuf *b; + + b = ofpbuf_new(sizeof *ops + sizeof(struct ofp11_port)); + ops = put_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, htonl(0), b); + ops->header.version = ofputil_protocol_to_ofp_version(protocol); + ops->reason = ps->reason; + ofputil_put_phy_port(ops->header.version, &ps->desc, b); + update_openflow_length(b); + return b; +} + +/* ofputil_port_mod */ + +/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in + * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */ +enum ofperr +ofputil_decode_port_mod(const struct ofp_header *oh, + struct ofputil_port_mod *pm) +{ + if (oh->version == OFP10_VERSION) { + const struct ofp10_port_mod *opm = (const struct ofp10_port_mod *) oh; + + if (oh->length != htons(sizeof *opm)) { + return OFPERR_OFPBRC_BAD_LEN; + } + + pm->port_no = ntohs(opm->port_no); + memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN); + pm->config = ntohl(opm->config) & OFPPC10_ALL; + pm->mask = ntohl(opm->mask) & OFPPC10_ALL; + pm->advertise = netdev_port_features_from_ofp10(opm->advertise); + } else if (oh->version == OFP11_VERSION) { + const struct ofp11_port_mod *opm = (const struct ofp11_port_mod *) oh; + enum ofperr error; + + if (oh->length != htons(sizeof *opm)) { + return OFPERR_OFPBRC_BAD_LEN; + } + + error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no); + if (error) { + return error; + } + + memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN); + pm->config = ntohl(opm->config) & OFPPC11_ALL; + pm->mask = ntohl(opm->mask) & OFPPC11_ALL; + pm->advertise = netdev_port_features_from_ofp11(opm->advertise); + } else { + return OFPERR_OFPBRC_BAD_VERSION; + } + + pm->config &= pm->mask; + return 0; +} + +/* Converts the abstract form of a "port 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_port_mod(const struct ofputil_port_mod *pm, + enum ofputil_protocol protocol) +{ + uint8_t ofp_version = ofputil_protocol_to_ofp_version(protocol); + struct ofpbuf *b; + + if (ofp_version == OFP10_VERSION) { + struct ofp10_port_mod *opm; + + opm = make_openflow(sizeof *opm, OFPT10_PORT_MOD, &b); + opm->port_no = htons(pm->port_no); + memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN); + opm->config = htonl(pm->config & OFPPC10_ALL); + opm->mask = htonl(pm->mask & OFPPC10_ALL); + opm->advertise = netdev_port_features_to_ofp10(pm->advertise); + } else if (ofp_version == OFP11_VERSION) { + struct ofp11_port_mod *opm; + + opm = make_openflow(sizeof *opm, OFPT11_PORT_MOD, &b); + opm->port_no = htonl(pm->port_no); + memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN); + opm->config = htonl(pm->config & OFPPC11_ALL); + opm->mask = htonl(pm->mask & OFPPC11_ALL); + opm->advertise = netdev_port_features_to_ofp11(pm->advertise); + } else { + NOT_REACHED(); + } + + return b; +} + struct ofpbuf * ofputil_encode_packet_out(const struct ofputil_packet_out *po) { @@ -2597,7 +3397,7 @@ make_flow_mod(uint16_t command, const struct cls_rule *rule, ofm->header.length = htons(size); ofm->cookie = 0; ofm->priority = htons(MIN(rule->priority, UINT16_MAX)); - ofputil_cls_rule_to_match(rule, &ofm->match); + ofputil_cls_rule_to_ofp10_match(rule, &ofm->match); ofm->command = htons(command); return out; } @@ -2614,32 +3414,6 @@ make_add_flow(const struct cls_rule *rule, uint32_t 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) @@ -2778,6 +3552,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; @@ -2851,6 +3626,31 @@ ofputil_format_port(uint16_t port, struct ds *s) ds_put_cstr(s, name); } +/* 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 + * port and returns 0. If no ports remain to be decoded, returns EOF. + * On an error, returns a positive OFPERR_* value. */ +int +ofputil_pull_phy_port(uint8_t ofp_version, struct ofpbuf *b, + struct ofputil_phy_port *pp) +{ + if (ofp_version == OFP10_VERSION) { + const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp); + return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF; + } else { + const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op); + return op ? ofputil_decode_ofp11_port(pp, op) : EOF; + } +} + +/* Given a buffer 'b' that contains an array of OpenFlow ports of type + * 'ofp_version', returns the number of elements. */ +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) { @@ -3204,11 +4004,13 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) bool action_outputs_to_port(const union ofp_action *action, ovs_be16 port) { - switch (ntohs(action->type)) { - case OFPAT10_OUTPUT: + switch (ofputil_decode_action(action)) { + case OFPUTIL_OFPAT10_OUTPUT: return action->output.port == port; - case OFPAT10_ENQUEUE: + case OFPUTIL_OFPAT10_ENQUEUE: return ((const struct ofp_action_enqueue *) action)->port == port; + case OFPUTIL_NXAST_CONTROLLER: + return port == htons(OFPP_CONTROLLER); default: return false; } @@ -3299,7 +4101,7 @@ ofputil_normalize_rule(struct cls_rule *rule) wc.wildcards |= FWW_IPV6_LABEL; } if (!(may_match & MAY_ND_TARGET)) { - wc.wildcards |= FWW_ND_TARGET; + wc.nd_target_mask = in6addr_any; } /* Log any changes. */ @@ -3415,7 +4217,8 @@ ofputil_parse_key_value(char **stringp, char **keyp, char **valuep) for (value_len = 0; level > 0; value_len++) { switch (value[value_len]) { case '\0': - ovs_fatal(0, "unbalanced parentheses in argument to %s", key); + level = 0; + break; case '(': level++;