/*
- * 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.
}
/* 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
#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 == 12);
/* 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_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;
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. */
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);
}
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)
{
: 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
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 },
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 },
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,
{
const struct flow_wildcards *wc = &rule->wc;
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 10);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 12);
+
+ /* 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;
+ }
- /* Only NXM supports separately wildcards the Ethernet multicast bit. */
- if (!(wc->wildcards & FWW_DL_DST) != !(wc->wildcards & FWW_ETH_MCAST)) {
+ /* NXM and OF1.1+ support matching metadata. */
+ if (wc->metadata_mask != htonll(0)) {
return OFPUTIL_P_NXM_ANY;
}
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))) {
* 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);
/* 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);
}
/* 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)
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;
+ 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);
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);
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;
}
}
(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);
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;
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;
}
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);
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);
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);
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);
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;
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;
return msg;
}
-int
+enum ofperr
ofputil_decode_packet_in(struct ofputil_packet_in *pin,
const struct ofp_header *oh)
{
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;
}
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);
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],
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);
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;
return 0;
}
-static int
-ofputil_pull_phy_port(uint8_t ofp_version, struct ofpbuf *b,
- struct ofputil_phy_port *pp)
+static size_t
+ofputil_get_phy_port_size(uint8_t ofp_version)
{
- 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;
- }
+ return ofp_version == OFP10_VERSION ? sizeof(struct ofp10_phy_port)
+ : sizeof(struct ofp11_port);
}
static void
}
}
}
+
+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);
+ }
+}
\f
/* ofputil_switch_features */
/* 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_switch_features_port(). Returns 0 if successful, otherwise an
+ * ofputil_pull_phy_port(). Returns 0 if successful, otherwise an
* OFPERR_* value. */
enum ofperr
ofputil_decode_switch_features(const struct ofp_switch_features *osf,
{
ofpbuf_use_const(b, osf, ntohs(osf->header.length));
ofpbuf_pull(b, sizeof *osf);
- b->l2 = (struct ofputil_switch_features *) 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 (osf->header.version == OFP10_VERSION) {
- if (b->size % sizeof(struct ofp10_phy_port)) {
- return OFPERR_OFPBRC_BAD_LEN;
- }
+ 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 (b->size % sizeof(struct ofp11_port)) {
- return OFPERR_OFPBRC_BAD_LEN;
- }
-
if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) {
features->capabilities |= OFPUTIL_C_GROUP_STATS;
}
return 0;
}
-/* Given a buffer 'b' that was initialized by a previous successful call to
- * ofputil_decode_switch_features(), tries to decode an OpenFlow port structure
- * following the main switch features information. If successful, initializes
- * '*pp' with an abstract representation of the port and returns 0. If no
- * ports remained to be decoded, returns EOF. On an error, returns a positive
- * OFPERR_* value. */
-int
-ofputil_pull_switch_features_port(struct ofpbuf *b,
- struct ofputil_phy_port *pp)
+/* Returns true if the maximum number of ports are in 'osf'. */
+static bool
+max_ports_in_features(const struct ofp_switch_features *osf)
{
- const struct ofp_switch_features *osf = b->l2;
- return ofputil_pull_phy_port(osf->header.version, b, pp);
+ size_t pp_size = ofputil_get_phy_port_size(osf->header.version);
+ return ntohs(osf->header.length) + pp_size > UINT16_MAX;
}
-/* Returns the number of OpenFlow port structures that follow the main switch
- * features information in '*osf'. The return value is only guaranteed to be
- * accurate if '*osf' is well-formed, that is, if
- * ofputil_decode_switch_features() can process '*osf' successfully. */
-size_t
-ofputil_count_phy_ports(const struct ofp_switch_features *osf)
+/* 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)
{
- size_t ports_len = ntohs(osf->header.length) - sizeof *osf;
- return (osf->header.version == OFP10_VERSION
- ? ports_len / sizeof(struct ofp10_phy_port)
- : ports_len / sizeof(struct ofp11_port));
+ 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
}
msg = ofpbuf_new(size);
- opo = put_openflow(sizeof *opo, OFPT10_PACKET_OUT, msg);
+ opo = put_openflow(sizeof *opo, OFPT_PACKET_OUT, msg);
opo->buffer_id = htonl(po->buffer_id);
opo->in_port = htons(po->in_port);
opo->actions_len = htons(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.type = OFPT_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);
+ ofputil_cls_rule_to_ofp10_match(rule, &ofm->match);
ofm->command = htons(command);
return out;
}
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)
case OFPP_FLOOD:
case OFPP_ALL:
case OFPP_CONTROLLER:
+ case OFPP_NONE:
case OFPP_LOCAL:
return 0;
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)
{
error = 0;
switch ((enum ofputil_action_code) code) {
+ case OFPUTIL_ACTION_INVALID:
+ NOT_REACHED();
+
case OFPUTIL_OFPAT10_OUTPUT:
error = ofputil_check_output_port(ntohs(a->output.port),
max_ports);
ofputil_action_code_from_name(const char *name)
{
static const char *names[OFPUTIL_N_ACTIONS] = {
+ NULL,
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME,
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
#include "ofp-util.def"
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 NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
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;
}
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++;