X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=lib%2Fofp-util.c;h=c66cd40079bf6c7160af7e843a3cbfd6bc3d3765;hb=305b76debf72120672a8ba81d3356b6dccb1da9a;hp=1fe30b469582e00178545e3abdb88f204a12e2c5;hpb=aa6305ea678ab889d7418c90371c72ead2c8e1e8;p=sliver-openvswitch.git diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 1fe30b469..c66cd4007 100644 --- a/lib/ofp-util.c +++ b/lib/ofp-util.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc. + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013 Nicira, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ #include #include "ofp-print.h" +#include #include #include #include @@ -77,102 +78,86 @@ ofputil_netmask_to_wcbits(ovs_be32 netmask) return 32 - ip_count_cidr_bits(netmask); } -/* 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_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 == OFPFW10_##NAME); - WC_INVARIANT_LIST -#undef WC_INVARIANT_BIT - -/* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all - * OR'd together. */ -static const flow_wildcards_t WC_INVARIANTS = 0 -#define WC_INVARIANT_BIT(NAME) | FWW_##NAME - WC_INVARIANT_LIST -#undef WC_INVARIANT_BIT -; - /* 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 + * flow_wildcards in 'wc' for use in struct match. 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_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc) { - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18); - /* Initialize most of rule->wc. */ + /* Initialize most of wc. */ flow_wildcards_init_catchall(wc); - wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS; - /* Wildcard fields that aren't defined by ofp10_match. */ - wc->wildcards |= FWW_NW_ECN | FWW_NW_TTL; + if (!(ofpfw & OFPFW10_IN_PORT)) { + wc->masks.in_port = UINT16_MAX; + } - 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; + if (!(ofpfw & OFPFW10_NW_TOS)) { + wc->masks.nw_tos |= IP_DSCP_MASK; } - 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 & OFPFW10_NW_PROTO)) { + wc->masks.nw_proto = UINT8_MAX; + } + wc->masks.nw_src = ofputil_wcbits_to_netmask(ofpfw + >> OFPFW10_NW_SRC_SHIFT); + wc->masks.nw_dst = ofputil_wcbits_to_netmask(ofpfw + >> OFPFW10_NW_DST_SHIFT); if (!(ofpfw & OFPFW10_TP_SRC)) { - wc->tp_src_mask = htons(UINT16_MAX); + wc->masks.tp_src = htons(UINT16_MAX); } if (!(ofpfw & OFPFW10_TP_DST)) { - wc->tp_dst_mask = htons(UINT16_MAX); + wc->masks.tp_dst = htons(UINT16_MAX); } if (!(ofpfw & OFPFW10_DL_SRC)) { - memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN); + memset(wc->masks.dl_src, 0xff, ETH_ADDR_LEN); } if (!(ofpfw & OFPFW10_DL_DST)) { - memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN); + memset(wc->masks.dl_dst, 0xff, ETH_ADDR_LEN); + } + if (!(ofpfw & OFPFW10_DL_TYPE)) { + wc->masks.dl_type = htons(UINT16_MAX); } /* VLAN TCI mask. */ if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) { - wc->vlan_tci_mask |= htons(VLAN_PCP_MASK | VLAN_CFI); + wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI); } if (!(ofpfw & OFPFW10_DL_VLAN)) { - wc->vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI); + wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI); } } -/* Converts the ofp10_match in 'match' into a cls_rule in 'rule', with the - * given 'priority'. */ +/* Converts the ofp10_match in 'ofmatch' into a struct match in 'match'. */ void -ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *match, - unsigned int priority, struct cls_rule *rule) -{ - uint32_t ofpfw = ntohl(match->wildcards) & OFPFW10_ALL; - - /* Initialize rule->priority, rule->wc. */ - rule->priority = !ofpfw ? UINT16_MAX : priority; - ofputil_wildcard_from_ofpfw10(ofpfw, &rule->wc); - - /* Initialize most of rule->flow. */ - rule->flow.nw_src = match->nw_src; - rule->flow.nw_dst = match->nw_dst; - rule->flow.in_port = ntohs(match->in_port); - rule->flow.dl_type = ofputil_dl_type_from_openflow(match->dl_type); - rule->flow.tp_src = match->tp_src; - rule->flow.tp_dst = match->tp_dst; - memcpy(rule->flow.dl_src, match->dl_src, ETH_ADDR_LEN); - memcpy(rule->flow.dl_dst, match->dl_dst, ETH_ADDR_LEN); - rule->flow.nw_tos = match->nw_tos & IP_DSCP_MASK; - rule->flow.nw_proto = match->nw_proto; +ofputil_match_from_ofp10_match(const struct ofp10_match *ofmatch, + struct match *match) +{ + uint32_t ofpfw = ntohl(ofmatch->wildcards) & OFPFW10_ALL; + + /* Initialize match->wc. */ + memset(&match->flow, 0, sizeof match->flow); + ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc); + + /* Initialize most of match->flow. */ + match->flow.nw_src = ofmatch->nw_src; + match->flow.nw_dst = ofmatch->nw_dst; + match->flow.in_port = ntohs(ofmatch->in_port); + match->flow.dl_type = ofputil_dl_type_from_openflow(ofmatch->dl_type); + match->flow.tp_src = ofmatch->tp_src; + match->flow.tp_dst = ofmatch->tp_dst; + memcpy(match->flow.dl_src, ofmatch->dl_src, ETH_ADDR_LEN); + memcpy(match->flow.dl_dst, ofmatch->dl_dst, ETH_ADDR_LEN); + match->flow.nw_tos = ofmatch->nw_tos & IP_DSCP_MASK; + match->flow.nw_proto = ofmatch->nw_proto; /* Translate VLANs. */ if (!(ofpfw & OFPFW10_DL_VLAN) && - match->dl_vlan == htons(OFP10_VLAN_NONE)) { + ofmatch->dl_vlan == htons(OFP10_VLAN_NONE)) { /* Match only packets without 802.1Q header. * * When OFPFW10_DL_VLAN_PCP is wildcarded, this is obviously correct. @@ -181,175 +166,196 @@ ofputil_cls_rule_from_ofp10_match(const struct ofp10_match *match, * 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. */ - rule->flow.vlan_tci = htons(0); - rule->wc.vlan_tci_mask = htons(0xffff); + match->flow.vlan_tci = htons(0); + match->wc.masks.vlan_tci = htons(0xffff); } else { ovs_be16 vid, pcp, tci; - vid = match->dl_vlan & htons(VLAN_VID_MASK); - pcp = htons((match->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK); + vid = ofmatch->dl_vlan & htons(VLAN_VID_MASK); + pcp = htons((ofmatch->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK); tci = vid | pcp | htons(VLAN_CFI); - rule->flow.vlan_tci = tci & rule->wc.vlan_tci_mask; + match->flow.vlan_tci = tci & match->wc.masks.vlan_tci; } /* Clean up. */ - cls_rule_zero_wildcarded_fields(rule); + match_zero_wildcarded_fields(match); } -/* Convert 'rule' into the OpenFlow 1.0 match structure 'match'. */ +/* Convert 'match' into the OpenFlow 1.0 match structure 'ofmatch'. */ void -ofputil_cls_rule_to_ofp10_match(const struct cls_rule *rule, - struct ofp10_match *match) +ofputil_match_to_ofp10_match(const struct match *match, + struct ofp10_match *ofmatch) { - const struct flow_wildcards *wc = &rule->wc; + const struct flow_wildcards *wc = &match->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 = 0; + if (!wc->masks.in_port) { + ofpfw |= OFPFW10_IN_PORT; + } + if (!wc->masks.dl_type) { + ofpfw |= OFPFW10_DL_TYPE; + } + if (!wc->masks.nw_proto) { + ofpfw |= OFPFW10_NW_PROTO; + } + ofpfw |= (ofputil_netmask_to_wcbits(wc->masks.nw_src) << OFPFW10_NW_SRC_SHIFT); - ofpfw |= (ofputil_netmask_to_wcbits(wc->nw_dst_mask) + ofpfw |= (ofputil_netmask_to_wcbits(wc->masks.nw_dst) << OFPFW10_NW_DST_SHIFT); - if (wc->wildcards & FWW_NW_DSCP) { + if (!(wc->masks.nw_tos & IP_DSCP_MASK)) { ofpfw |= OFPFW10_NW_TOS; } - if (!wc->tp_src_mask) { + if (!wc->masks.tp_src) { ofpfw |= OFPFW10_TP_SRC; } - if (!wc->tp_dst_mask) { + if (!wc->masks.tp_dst) { ofpfw |= OFPFW10_TP_DST; } - if (eth_addr_is_zero(wc->dl_src_mask)) { + if (eth_addr_is_zero(wc->masks.dl_src)) { ofpfw |= OFPFW10_DL_SRC; } - if (eth_addr_is_zero(wc->dl_dst_mask)) { + if (eth_addr_is_zero(wc->masks.dl_dst)) { ofpfw |= OFPFW10_DL_DST; } /* Translate VLANs. */ - match->dl_vlan = htons(0); - match->dl_vlan_pcp = 0; - if (rule->wc.vlan_tci_mask == htons(0)) { + ofmatch->dl_vlan = htons(0); + ofmatch->dl_vlan_pcp = 0; + if (match->wc.masks.vlan_tci == htons(0)) { 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(OFP10_VLAN_NONE); + } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI) + && !(match->flow.vlan_tci & htons(VLAN_CFI))) { + ofmatch->dl_vlan = htons(OFP10_VLAN_NONE); ofpfw |= OFPFW10_DL_VLAN_PCP; } else { - if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) { + if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) { ofpfw |= OFPFW10_DL_VLAN; } else { - match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci)); + ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci)); } - if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) { + if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) { ofpfw |= OFPFW10_DL_VLAN_PCP; } else { - match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci); + ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci); } } /* Compose most of the match structure. */ - match->wildcards = htonl(ofpfw); - match->in_port = htons(rule->flow.in_port); - memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN); - memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN); - match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type); - match->nw_src = rule->flow.nw_src; - match->nw_dst = rule->flow.nw_dst; - match->nw_tos = rule->flow.nw_tos & IP_DSCP_MASK; - match->nw_proto = rule->flow.nw_proto; - match->tp_src = rule->flow.tp_src; - match->tp_dst = rule->flow.tp_dst; - memset(match->pad1, '\0', sizeof match->pad1); - memset(match->pad2, '\0', sizeof match->pad2); + ofmatch->wildcards = htonl(ofpfw); + ofmatch->in_port = htons(match->flow.in_port); + memcpy(ofmatch->dl_src, match->flow.dl_src, ETH_ADDR_LEN); + memcpy(ofmatch->dl_dst, match->flow.dl_dst, ETH_ADDR_LEN); + ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type); + ofmatch->nw_src = match->flow.nw_src; + ofmatch->nw_dst = match->flow.nw_dst; + ofmatch->nw_tos = match->flow.nw_tos & IP_DSCP_MASK; + ofmatch->nw_proto = match->flow.nw_proto; + ofmatch->tp_src = match->flow.tp_src; + ofmatch->tp_dst = match->flow.tp_dst; + memset(ofmatch->pad1, '\0', sizeof ofmatch->pad1); + memset(ofmatch->pad2, '\0', sizeof ofmatch->pad2); } enum ofperr -ofputil_pull_ofp11_match(struct ofpbuf *buf, unsigned int priority, - struct cls_rule *rule) +ofputil_pull_ofp11_match(struct ofpbuf *buf, struct match *match, + uint16_t *padded_match_len) { - struct ofp11_match_header *omh; - struct ofp11_match *om; + struct ofp11_match_header *omh = buf->data; + uint16_t match_len; - if (buf->size < sizeof(struct ofp11_match_header)) { + if (buf->size < sizeof *omh) { return OFPERR_OFPBMC_BAD_LEN; } - omh = buf->data; + match_len = ntohs(omh->length); + switch (ntohs(omh->type)) { - case OFPMT_STANDARD: - if (omh->length != htons(sizeof *om) || buf->size < sizeof *om) { + case OFPMT_STANDARD: { + struct ofp11_match *om; + + if (match_len != sizeof *om || buf->size < sizeof *om) { return OFPERR_OFPBMC_BAD_LEN; } om = ofpbuf_pull(buf, sizeof *om); - return ofputil_cls_rule_from_ofp11_match(om, priority, rule); + if (padded_match_len) { + *padded_match_len = match_len; + } + return ofputil_match_from_ofp11_match(om, match); + } + + case OFPMT_OXM: + if (padded_match_len) { + *padded_match_len = ROUND_UP(match_len, 8); + } + return oxm_pull_match(buf, match); default: return OFPERR_OFPBMC_BAD_TYPE; } } -/* Converts the ofp11_match in 'match' into a cls_rule in 'rule', with the - * given 'priority'. Returns 0 if successful, otherwise an OFPERR_* value. */ +/* Converts the ofp11_match in 'match' into a struct match in 'match. 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) +ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch, + struct match *match) { - uint16_t wc = ntohl(match->wildcards); + uint16_t wc = ntohl(ofmatch->wildcards); uint8_t dl_src_mask[ETH_ADDR_LEN]; uint8_t dl_dst_mask[ETH_ADDR_LEN]; - bool ipv4, arp; + bool ipv4, arp, rarp; int i; - cls_rule_init_catchall(rule, priority); + match_init_catchall(match); if (!(wc & OFPFW11_IN_PORT)) { uint16_t ofp_port; enum ofperr error; - error = ofputil_port_from_ofp11(match->in_port, &ofp_port); + error = ofputil_port_from_ofp11(ofmatch->in_port, &ofp_port); if (error) { return OFPERR_OFPBMC_BAD_VALUE; } - cls_rule_set_in_port(rule, ofp_port); + match_set_in_port(match, ofp_port); } for (i = 0; i < ETH_ADDR_LEN; i++) { - dl_src_mask[i] = ~match->dl_src_mask[i]; + dl_src_mask[i] = ~ofmatch->dl_src_mask[i]; } - cls_rule_set_dl_src_masked(rule, match->dl_src, dl_src_mask); + match_set_dl_src_masked(match, ofmatch->dl_src, dl_src_mask); for (i = 0; i < ETH_ADDR_LEN; i++) { - dl_dst_mask[i] = ~match->dl_dst_mask[i]; + dl_dst_mask[i] = ~ofmatch->dl_dst_mask[i]; } - cls_rule_set_dl_dst_masked(rule, match->dl_dst, dl_dst_mask); + match_set_dl_dst_masked(match, ofmatch->dl_dst, dl_dst_mask); if (!(wc & OFPFW11_DL_VLAN)) { - if (match->dl_vlan == htons(OFPVID11_NONE)) { + if (ofmatch->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); + match->flow.vlan_tci = htons(0); + match->wc.masks.vlan_tci = htons(UINT16_MAX); } else { - if (match->dl_vlan == htons(OFPVID11_ANY)) { + if (ofmatch->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->flow.vlan_tci = htons(VLAN_CFI); + match->wc.masks.vlan_tci = htons(VLAN_CFI); + } else if (ntohs(ofmatch->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); + match->flow.vlan_tci = htons(VLAN_CFI) | ofmatch->dl_vlan; + match->wc.masks.vlan_tci = 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); + if (ofmatch->dl_vlan_pcp <= 7) { + match->flow.vlan_tci |= htons(ofmatch->dl_vlan_pcp + << VLAN_PCP_SHIFT); + match->wc.masks.vlan_tci |= htons(VLAN_PCP_MASK); } else { /* Invalid PCP. */ return OFPERR_OFPBMC_BAD_VALUE; @@ -359,33 +365,34 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match, } if (!(wc & OFPFW11_DL_TYPE)) { - cls_rule_set_dl_type(rule, - ofputil_dl_type_from_openflow(match->dl_type)); + match_set_dl_type(match, + ofputil_dl_type_from_openflow(ofmatch->dl_type)); } - ipv4 = rule->flow.dl_type == htons(ETH_TYPE_IP); - arp = rule->flow.dl_type == htons(ETH_TYPE_ARP); + ipv4 = match->flow.dl_type == htons(ETH_TYPE_IP); + arp = match->flow.dl_type == htons(ETH_TYPE_ARP); + rarp = match->flow.dl_type == htons(ETH_TYPE_RARP); if (ipv4 && !(wc & OFPFW11_NW_TOS)) { - if (match->nw_tos & ~IP_DSCP_MASK) { + if (ofmatch->nw_tos & ~IP_DSCP_MASK) { /* Invalid TOS. */ return OFPERR_OFPBMC_BAD_VALUE; } - cls_rule_set_nw_dscp(rule, match->nw_tos); + match_set_nw_dscp(match, ofmatch->nw_tos); } - if (ipv4 || arp) { + if (ipv4 || arp || rarp) { if (!(wc & OFPFW11_NW_PROTO)) { - cls_rule_set_nw_proto(rule, match->nw_proto); + match_set_nw_proto(match, ofmatch->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); + match_set_nw_src_masked(match, ofmatch->nw_src, ~ofmatch->nw_src_mask); + match_set_nw_dst_masked(match, ofmatch->nw_dst, ~ofmatch->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) { + switch (match->flow.nw_proto) { case IPPROTO_ICMP: /* "A.2.3 Flow Match Structures" in OF1.1 says: * @@ -395,17 +402,17 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match, * 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); + uint16_t icmp_type = ntohs(ofmatch->tp_src); if (icmp_type < 0x100) { - cls_rule_set_icmp_type(rule, icmp_type); + match_set_icmp_type(match, icmp_type); } else { return OFPERR_OFPBMC_BAD_FIELD; } } if (!(wc & OFPFW11_TP_DST)) { - uint16_t icmp_code = ntohs(match->tp_dst); + uint16_t icmp_code = ntohs(ofmatch->tp_dst); if (icmp_code < 0x100) { - cls_rule_set_icmp_code(rule, icmp_code); + match_set_icmp_code(match, icmp_code); } else { return OFPERR_OFPBMC_BAD_FIELD; } @@ -415,10 +422,10 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match, case IPPROTO_TCP: case IPPROTO_UDP: if (!(wc & (OFPFW11_TP_SRC))) { - cls_rule_set_tp_src(rule, match->tp_src); + match_set_tp_src(match, ofmatch->tp_src); } if (!(wc & (OFPFW11_TP_DST))) { - cls_rule_set_tp_dst(rule, match->tp_dst); + match_set_tp_dst(match, ofmatch->tp_dst); } break; @@ -433,8 +440,8 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match, } } - if (rule->flow.dl_type == htons(ETH_TYPE_MPLS) || - rule->flow.dl_type == htons(ETH_TYPE_MPLS_MCAST)) { + if (match->flow.dl_type == htons(ETH_TYPE_MPLS) || + match->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) { @@ -443,105 +450,103 @@ ofputil_cls_rule_from_ofp11_match(const struct ofp11_match *match, } } - if (match->metadata_mask != htonll(UINT64_MAX)) { - cls_rule_set_metadata_masked(rule, match->metadata, - ~match->metadata_mask); - } + match_set_metadata_masked(match, ofmatch->metadata, + ~ofmatch->metadata_mask); return 0; } -/* Convert 'rule' into the OpenFlow 1.1 match structure 'match'. */ +/* Convert 'match' into the OpenFlow 1.1 match structure 'ofmatch'. */ void -ofputil_cls_rule_to_ofp11_match(const struct cls_rule *rule, - struct ofp11_match *match) +ofputil_match_to_ofp11_match(const struct match *match, + struct ofp11_match *ofmatch) { uint32_t wc = 0; int i; - memset(match, 0, sizeof *match); - match->omh.type = htons(OFPMT_STANDARD); - match->omh.length = htons(OFPMT11_STANDARD_LENGTH); + memset(ofmatch, 0, sizeof *ofmatch); + ofmatch->omh.type = htons(OFPMT_STANDARD); + ofmatch->omh.length = htons(OFPMT11_STANDARD_LENGTH); - if (rule->wc.wildcards & FWW_IN_PORT) { + if (!match->wc.masks.in_port) { wc |= OFPFW11_IN_PORT; } else { - match->in_port = ofputil_port_to_ofp11(rule->flow.in_port); + ofmatch->in_port = ofputil_port_to_ofp11(match->flow.in_port); } - memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN); + memcpy(ofmatch->dl_src, match->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]; + ofmatch->dl_src_mask[i] = ~match->wc.masks.dl_src[i]; } - memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN); + memcpy(ofmatch->dl_dst, match->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]; + ofmatch->dl_dst_mask[i] = ~match->wc.masks.dl_dst[i]; } - if (rule->wc.vlan_tci_mask == htons(0)) { + if (match->wc.masks.vlan_tci == 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); + } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI) + && !(match->flow.vlan_tci & htons(VLAN_CFI))) { + ofmatch->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); + if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) { + ofmatch->dl_vlan = htons(OFPVID11_ANY); } else { - match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci)); + ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci)); } - if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) { + if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) { wc |= OFPFW11_DL_VLAN_PCP; } else { - match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci); + ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci); } } - if (rule->wc.wildcards & FWW_DL_TYPE) { + if (!match->wc.masks.dl_type) { wc |= OFPFW11_DL_TYPE; } else { - match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type); + ofmatch->dl_type = ofputil_dl_type_to_openflow(match->flow.dl_type); } - if (rule->wc.wildcards & FWW_NW_DSCP) { + if (!(match->wc.masks.nw_tos & IP_DSCP_MASK)) { wc |= OFPFW11_NW_TOS; } else { - match->nw_tos = rule->flow.nw_tos & IP_DSCP_MASK; + ofmatch->nw_tos = match->flow.nw_tos & IP_DSCP_MASK; } - if (rule->wc.wildcards & FWW_NW_PROTO) { + if (!match->wc.masks.nw_proto) { wc |= OFPFW11_NW_PROTO; } else { - match->nw_proto = rule->flow.nw_proto; + ofmatch->nw_proto = match->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; + ofmatch->nw_src = match->flow.nw_src; + ofmatch->nw_src_mask = ~match->wc.masks.nw_src; + ofmatch->nw_dst = match->flow.nw_dst; + ofmatch->nw_dst_mask = ~match->wc.masks.nw_dst; - if (!rule->wc.tp_src_mask) { + if (!match->wc.masks.tp_src) { wc |= OFPFW11_TP_SRC; } else { - match->tp_src = rule->flow.tp_src; + ofmatch->tp_src = match->flow.tp_src; } - if (!rule->wc.tp_dst_mask) { + if (!match->wc.masks.tp_dst) { wc |= OFPFW11_TP_DST; } else { - match->tp_dst = rule->flow.tp_dst; + ofmatch->tp_dst = match->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; + ofmatch->metadata = match->flow.metadata; + ofmatch->metadata_mask = ~match->wc.masks.metadata; - match->wildcards = htonl(wc); + ofmatch->wildcards = htonl(wc); } /* Given a 'dl_type' value in the format used in struct flow, returns the @@ -576,55 +581,104 @@ struct proto_abbrev { /* Most users really don't care about some of the differences between * protocols. These abbreviations help with that. */ static const struct proto_abbrev proto_abbrevs[] = { - { OFPUTIL_P_ANY, "any" }, - { OFPUTIL_P_OF10_ANY, "OpenFlow10" }, - { OFPUTIL_P_NXM_ANY, "NXM" }, + { OFPUTIL_P_ANY, "any" }, + { OFPUTIL_P_OF10_STD_ANY, "OpenFlow10" }, + { OFPUTIL_P_OF10_NXM_ANY, "NXM" }, + { OFPUTIL_P_ANY_OXM, "OXM" }, }; #define N_PROTO_ABBREVS ARRAY_SIZE(proto_abbrevs) enum ofputil_protocol ofputil_flow_dump_protocols[] = { - OFPUTIL_P_NXM, - OFPUTIL_P_OF10, + OFPUTIL_P_OF13_OXM, + OFPUTIL_P_OF12_OXM, + OFPUTIL_P_OF10_NXM, + OFPUTIL_P_OF10_STD, }; size_t ofputil_n_flow_dump_protocols = ARRAY_SIZE(ofputil_flow_dump_protocols); -/* Returns the ofputil_protocol that is initially in effect on an OpenFlow - * connection that has negotiated the given 'version'. 'version' should - * normally be an 8-bit OpenFlow version identifier (e.g. 0x01 for OpenFlow - * 1.0, 0x02 for OpenFlow 1.1). Returns 0 if 'version' is not supported or - * outside the valid range. */ +/* Returns the set of ofputil_protocols that are supported with the given + * OpenFlow 'version'. 'version' should normally be an 8-bit OpenFlow version + * identifier (e.g. 0x01 for OpenFlow 1.0, 0x02 for OpenFlow 1.1). Returns 0 + * if 'version' is not supported or outside the valid range. */ enum ofputil_protocol -ofputil_protocol_from_ofp_version(enum ofp_version version) +ofputil_protocols_from_ofp_version(enum ofp_version version) { switch (version) { case OFP10_VERSION: - return OFPUTIL_P_OF10; + return OFPUTIL_P_OF10_STD_ANY | OFPUTIL_P_OF10_NXM_ANY; case OFP12_VERSION: - return OFPUTIL_P_OF12; + return OFPUTIL_P_OF12_OXM; + case OFP13_VERSION: + return OFPUTIL_P_OF13_OXM; case OFP11_VERSION: default: return 0; } } +/* Returns the ofputil_protocol that is initially in effect on an OpenFlow + * connection that has negotiated the given 'version'. 'version' should + * normally be an 8-bit OpenFlow version identifier (e.g. 0x01 for OpenFlow + * 1.0, 0x02 for OpenFlow 1.1). Returns 0 if 'version' is not supported or + * outside the valid range. */ +enum ofputil_protocol +ofputil_protocol_from_ofp_version(enum ofp_version version) +{ + return rightmost_1bit(ofputil_protocols_from_ofp_version(version)); +} + /* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION, - * OFP11_VERSION or OFP12_VERSION) that corresponds to 'protocol'. */ + * etc.) that corresponds to 'protocol'. */ enum ofp_version ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol) { switch (protocol) { - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: return OFP10_VERSION; - case OFPUTIL_P_OF12: + case OFPUTIL_P_OF12_OXM: return OFP12_VERSION; + case OFPUTIL_P_OF13_OXM: + return OFP13_VERSION; } NOT_REACHED(); } +/* Returns a bitmap of OpenFlow versions that are supported by at + * least one of the 'protocols'. */ +uint32_t +ofputil_protocols_to_version_bitmap(enum ofputil_protocol protocols) +{ + uint32_t bitmap = 0; + + for (; protocols; protocols = zero_rightmost_1bit(protocols)) { + enum ofputil_protocol protocol = rightmost_1bit(protocols); + + bitmap |= 1u << ofputil_protocol_to_ofp_version(protocol); + } + + return bitmap; +} + +/* Returns the set of protocols that are supported on top of the + * OpenFlow versions included in 'bitmap'. */ +enum ofputil_protocol +ofputil_protocols_from_version_bitmap(uint32_t bitmap) +{ + enum ofputil_protocol protocols = 0; + + for (; bitmap; bitmap = zero_rightmost_1bit(bitmap)) { + enum ofp_version version = rightmost_1bit_idx(bitmap); + + protocols |= ofputil_protocols_from_ofp_version(version); + } + + return protocols; +} + /* Returns true if 'protocol' is a single OFPUTIL_P_* value, false * otherwise. */ bool @@ -647,16 +701,19 @@ enum ofputil_protocol ofputil_protocol_set_tid(enum ofputil_protocol protocol, bool enable) { switch (protocol) { - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: - return enable ? OFPUTIL_P_OF10_TID : OFPUTIL_P_OF10; + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: + return enable ? OFPUTIL_P_OF10_STD_TID : OFPUTIL_P_OF10_STD; - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: - return enable ? OFPUTIL_P_NXM_TID : OFPUTIL_P_NXM; + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: + return enable ? OFPUTIL_P_OF10_NXM_TID : OFPUTIL_P_OF10_NXM; - case OFPUTIL_P_OF12: - return OFPUTIL_P_OF12; + case OFPUTIL_P_OF12_OXM: + return OFPUTIL_P_OF12_OXM; + + case OFPUTIL_P_OF13_OXM: + return OFPUTIL_P_OF13_OXM; default: NOT_REACHED(); @@ -681,16 +738,19 @@ ofputil_protocol_set_base(enum ofputil_protocol cur, bool tid = (cur & OFPUTIL_P_TID) != 0; switch (new_base) { - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: - return ofputil_protocol_set_tid(OFPUTIL_P_OF10, tid); + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: + return ofputil_protocol_set_tid(OFPUTIL_P_OF10_STD, tid); + + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: + return ofputil_protocol_set_tid(OFPUTIL_P_OF10_NXM, tid); - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: - return ofputil_protocol_set_tid(OFPUTIL_P_NXM, tid); + case OFPUTIL_P_OF12_OXM: + return ofputil_protocol_set_tid(OFPUTIL_P_OF12_OXM, tid); - case OFPUTIL_P_OF12: - return ofputil_protocol_set_tid(OFPUTIL_P_OF12, tid); + case OFPUTIL_P_OF13_OXM: + return ofputil_protocol_set_tid(OFPUTIL_P_OF13_OXM, tid); default: NOT_REACHED(); @@ -708,20 +768,23 @@ ofputil_protocol_to_string(enum ofputil_protocol protocol) /* Use a "switch" statement for single-bit names so that we get a compiler * warning if we forget any. */ switch (protocol) { - case OFPUTIL_P_NXM: + case OFPUTIL_P_OF10_NXM: return "NXM-table_id"; - case OFPUTIL_P_NXM_TID: + case OFPUTIL_P_OF10_NXM_TID: return "NXM+table_id"; - case OFPUTIL_P_OF10: + case OFPUTIL_P_OF10_STD: return "OpenFlow10-table_id"; - case OFPUTIL_P_OF10_TID: + case OFPUTIL_P_OF10_STD_TID: return "OpenFlow10+table_id"; - case OFPUTIL_P_OF12: - return NULL; + case OFPUTIL_P_OF12_OXM: + return "OXM-OpenFlow12"; + + case OFPUTIL_P_OF13_OXM: + return "OXM-OpenFlow13"; } /* Check abbreviations. */ @@ -744,7 +807,7 @@ ofputil_protocols_to_string(enum ofputil_protocol protocols) { struct ds s; - assert(!(protocols & ~OFPUTIL_P_ANY)); + ovs_assert(!(protocols & ~OFPUTIL_P_ANY)); if (protocols == 0) { return xstrdup("none"); } @@ -842,6 +905,96 @@ ofputil_protocols_from_string(const char *s) return protocols; } +static int +ofputil_version_from_string(const char *s) +{ + if (!strcasecmp(s, "OpenFlow10")) { + return OFP10_VERSION; + } + if (!strcasecmp(s, "OpenFlow11")) { + return OFP11_VERSION; + } + if (!strcasecmp(s, "OpenFlow12")) { + return OFP12_VERSION; + } + if (!strcasecmp(s, "OpenFlow13")) { + return OFP13_VERSION; + } + return 0; +} + +static bool +is_delimiter(char c) +{ + return isspace(c) || c == ','; +} + +uint32_t +ofputil_versions_from_string(const char *s) +{ + size_t i = 0; + uint32_t bitmap = 0; + + while (s[i]) { + size_t j; + int version; + char *key; + + if (is_delimiter(s[i])) { + i++; + continue; + } + j = 0; + while (s[i + j] && !is_delimiter(s[i + j])) { + j++; + } + key = xmemdup0(s + i, j); + version = ofputil_version_from_string(key); + if (!version) { + VLOG_FATAL("Unknown OpenFlow version: \"%s\"", key); + } + free(key); + bitmap |= 1u << version; + i += j; + } + + return bitmap; +} + +uint32_t +ofputil_versions_from_strings(char ** const s, size_t count) +{ + uint32_t bitmap = 0; + + while (count--) { + int version = ofputil_version_from_string(s[count]); + if (!version) { + VLOG_WARN("Unknown OpenFlow version: \"%s\"", s[count]); + } else { + bitmap |= 1u << version; + } + } + + return bitmap; +} + +const char * +ofputil_version_to_string(enum ofp_version ofp_version) +{ + switch (ofp_version) { + case OFP10_VERSION: + return "OpenFlow10"; + case OFP11_VERSION: + return "OpenFlow11"; + case OFP12_VERSION: + return "OpenFlow12"; + case OFP13_VERSION: + return "OpenFlow13"; + default: + NOT_REACHED(); + } +} + bool ofputil_packet_in_format_is_valid(enum nx_packet_in_format packet_in_format) { @@ -881,128 +1034,328 @@ regs_fully_wildcarded(const struct flow_wildcards *wc) int i; for (i = 0; i < FLOW_N_REGS; i++) { - if (wc->reg_masks[i] != 0) { + if (wc->masks.regs[i] != 0) { return false; } } return true; } -/* Returns a bit-mask of ofputil_protocols that can be used for sending 'rule' +static bool +tun_parms_fully_wildcarded(const struct flow_wildcards *wc) +{ + return (!wc->masks.tunnel.ip_src && + !wc->masks.tunnel.ip_dst && + !wc->masks.tunnel.ip_ttl && + !wc->masks.tunnel.ip_tos && + !wc->masks.tunnel.flags); +} + +/* Returns a bit-mask of ofputil_protocols that can be used for sending 'match' * to a switch (e.g. to add or remove a flow). Only NXM can handle tunnel IDs, * registers, or fixing the Ethernet multicast bit. Otherwise, it's better to * use OpenFlow 1.0 protocol for backward compatibility. */ enum ofputil_protocol -ofputil_usable_protocols(const struct cls_rule *rule) +ofputil_usable_protocols(const struct match *match) { - const struct flow_wildcards *wc = &rule->wc; + const struct flow_wildcards *wc = &match->wc; - BUILD_ASSERT_DECL(FLOW_WC_SEQ == 14); + BUILD_ASSERT_DECL(FLOW_WC_SEQ == 18); - /* 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; + /* tunnel params other than tun_id can't be sent in a flow_mod */ + if (!tun_parms_fully_wildcarded(wc)) { + return OFPUTIL_P_NONE; } - if (!eth_mask_is_exact(wc->dl_dst_mask) - && !eth_addr_is_zero(wc->dl_dst_mask)) { - return OFPUTIL_P_NXM_ANY; + + /* skb_mark and skb_priority can't be sent in a flow_mod */ + if (wc->masks.skb_mark || wc->masks.skb_priority) { + return OFPUTIL_P_NONE; + } + + /* NXM, OXM, and OF1.1 support bitwise matching on ethernet addresses. */ + if (!eth_mask_is_exact(wc->masks.dl_src) + && !eth_addr_is_zero(wc->masks.dl_src)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; + } + if (!eth_mask_is_exact(wc->masks.dl_dst) + && !eth_addr_is_zero(wc->masks.dl_dst)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* NXM and OF1.1+ support matching metadata. */ - if (wc->metadata_mask != htonll(0)) { - return OFPUTIL_P_NXM_ANY; + /* NXM, OXM, and OF1.1+ support matching metadata. */ + if (wc->masks.metadata != htonll(0)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* Only NXM supports matching ARP hardware addresses. */ - if (!eth_addr_is_zero(wc->arp_sha_mask) || - !eth_addr_is_zero(wc->arp_tha_mask)) { - return OFPUTIL_P_NXM_ANY; + /* NXM and OXM support matching ARP hardware addresses. */ + if (!eth_addr_is_zero(wc->masks.arp_sha) || + !eth_addr_is_zero(wc->masks.arp_tha)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* Only NXM supports matching IPv6 traffic. */ - if (!(wc->wildcards & FWW_DL_TYPE) - && (rule->flow.dl_type == htons(ETH_TYPE_IPV6))) { - return OFPUTIL_P_NXM_ANY; + /* NXM and OXM support matching IPv6 traffic. */ + if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* Only NXM supports matching registers. */ + /* NXM and OXM support matching registers. */ if (!regs_fully_wildcarded(wc)) { - return OFPUTIL_P_NXM_ANY; + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* Only NXM supports matching tun_id. */ - if (wc->tun_id_mask != htonll(0)) { - return OFPUTIL_P_NXM_ANY; + /* NXM and OXM support matching tun_id. */ + if (wc->masks.tunnel.tun_id != htonll(0)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* Only NXM supports matching fragments. */ - if (wc->nw_frag_mask) { - return OFPUTIL_P_NXM_ANY; + /* NXM and OXM support matching fragments. */ + if (wc->masks.nw_frag) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* Only NXM supports matching IPv6 flow label. */ - if (wc->ipv6_label_mask) { - return OFPUTIL_P_NXM_ANY; + /* NXM and OXM support matching IPv6 flow label. */ + if (wc->masks.ipv6_label) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* Only NXM supports matching IP ECN bits. */ - if (!(wc->wildcards & FWW_NW_ECN)) { - return OFPUTIL_P_NXM_ANY; + /* NXM and OXM support matching IP ECN bits. */ + if (wc->masks.nw_tos & IP_ECN_MASK) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* Only NXM supports matching IP TTL/hop limit. */ - if (!(wc->wildcards & FWW_NW_TTL)) { - return OFPUTIL_P_NXM_ANY; + /* NXM and OXM support matching IP TTL/hop limit. */ + if (wc->masks.nw_ttl) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* 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; + /* NXM and OXM support non-CIDR IPv4 address masks. */ + if (!ip_is_cidr(wc->masks.nw_src) || !ip_is_cidr(wc->masks.nw_dst)) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } - /* 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))) { - return OFPUTIL_P_NXM_ANY; + /* NXM and OXM support bitwise matching on transport port. */ + if ((wc->masks.tp_src && wc->masks.tp_src != htons(UINT16_MAX)) || + (wc->masks.tp_dst && wc->masks.tp_dst != htons(UINT16_MAX))) { + return OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } /* Other formats can express this rule. */ return OFPUTIL_P_ANY; } +void +ofputil_format_version(struct ds *msg, enum ofp_version version) +{ + ds_put_format(msg, "0x%02x", version); +} + +void +ofputil_format_version_name(struct ds *msg, enum ofp_version version) +{ + ds_put_cstr(msg, ofputil_version_to_string(version)); +} + +static void +ofputil_format_version_bitmap__(struct ds *msg, uint32_t bitmap, + void (*format_version)(struct ds *msg, + enum ofp_version)) +{ + while (bitmap) { + format_version(msg, raw_ctz(bitmap)); + bitmap = zero_rightmost_1bit(bitmap); + if (bitmap) { + ds_put_cstr(msg, ", "); + } + } +} + +void +ofputil_format_version_bitmap(struct ds *msg, uint32_t bitmap) +{ + ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version); +} + +void +ofputil_format_version_bitmap_names(struct ds *msg, uint32_t bitmap) +{ + ofputil_format_version_bitmap__(msg, bitmap, ofputil_format_version_name); +} + +static bool +ofputil_decode_hello_bitmap(const struct ofp_hello_elem_header *oheh, + uint32_t *allowed_versionsp) +{ + uint16_t bitmap_len = ntohs(oheh->length) - sizeof *oheh; + const ovs_be32 *bitmap = (const ovs_be32 *) (oheh + 1); + uint32_t allowed_versions; + + if (!bitmap_len || bitmap_len % sizeof *bitmap) { + return false; + } + + /* Only use the first 32-bit element of the bitmap as that is all the + * current implementation supports. Subsequent elements are ignored which + * should have no effect on session negotiation until Open vSwtich supports + * wire-protocol versions greater than 31. + */ + allowed_versions = ntohl(bitmap[0]); + + if (allowed_versions & 1) { + /* There's no OpenFlow version 0. */ + VLOG_WARN_RL(&bad_ofmsg_rl, "peer claims to support invalid OpenFlow " + "version 0x00"); + allowed_versions &= ~1u; + } + + if (!allowed_versions) { + VLOG_WARN_RL(&bad_ofmsg_rl, "peer does not support any OpenFlow " + "version (between 0x01 and 0x1f)"); + return false; + } + + *allowed_versionsp = allowed_versions; + return true; +} + +static uint32_t +version_bitmap_from_version(uint8_t ofp_version) +{ + return ((ofp_version < 32 ? 1u << ofp_version : 0) - 1) << 1; +} + +/* Decodes OpenFlow OFPT_HELLO message 'oh', storing into '*allowed_versions' + * the set of OpenFlow versions for which 'oh' announces support. + * + * Because of how OpenFlow defines OFPT_HELLO messages, this function is always + * successful, and thus '*allowed_versions' is always initialized. However, it + * returns false if 'oh' contains some data that could not be fully understood, + * true if 'oh' was completely parsed. */ +bool +ofputil_decode_hello(const struct ofp_header *oh, uint32_t *allowed_versions) +{ + struct ofpbuf msg; + bool ok = true; + + ofpbuf_use_const(&msg, oh, ntohs(oh->length)); + ofpbuf_pull(&msg, sizeof *oh); + + *allowed_versions = version_bitmap_from_version(oh->version); + while (msg.size) { + const struct ofp_hello_elem_header *oheh; + unsigned int len; + + if (msg.size < sizeof *oheh) { + return false; + } + + oheh = msg.data; + len = ntohs(oheh->length); + if (len < sizeof *oheh || !ofpbuf_try_pull(&msg, ROUND_UP(len, 8))) { + return false; + } + + if (oheh->type != htons(OFPHET_VERSIONBITMAP) + || !ofputil_decode_hello_bitmap(oheh, allowed_versions)) { + ok = false; + } + } + + return ok; +} + +/* Returns true if 'allowed_versions' needs to be accompanied by a version + * bitmap to be correctly expressed in an OFPT_HELLO message. */ +static inline bool +should_send_version_bitmap(uint32_t allowed_versions) +{ + return !is_pow2((allowed_versions >> 1) + 1); +} + +/* Create an OFPT_HELLO message that expresses support for the OpenFlow + * versions in the 'allowed_versions' bitmaps and returns the message. */ +struct ofpbuf * +ofputil_encode_hello(uint32_t allowed_versions) +{ + enum ofp_version ofp_version; + struct ofpbuf *msg; + + ofp_version = leftmost_1bit_idx(allowed_versions); + msg = ofpraw_alloc(OFPRAW_OFPT_HELLO, ofp_version, 0); + + if (should_send_version_bitmap(allowed_versions)) { + struct ofp_hello_elem_header *oheh; + uint16_t map_len; + + map_len = sizeof allowed_versions; + oheh = ofpbuf_put_zeros(msg, ROUND_UP(map_len + sizeof *oheh, 8)); + oheh->type = htons(OFPHET_VERSIONBITMAP); + oheh->length = htons(map_len + sizeof *oheh); + *(ovs_be32 *)(oheh + 1) = htonl(allowed_versions); + + ofpmsg_update_length(msg); + } + + return msg; +} + /* Returns an OpenFlow message that, sent on an OpenFlow connection whose * protocol is 'current', at least partly transitions the protocol to 'want'. * Stores in '*next' the protocol that will be in effect on the OpenFlow * connection if the switch processes the returned message correctly. (If * '*next != want' then the caller will have to iterate.) * - * If 'current == want', returns NULL and stores 'current' in '*next'. */ + * If 'current == want', or if it is not possible to transition from 'current' + * to 'want' (because, for example, 'current' and 'want' use different OpenFlow + * protocol versions), returns NULL and stores 'current' in '*next'. */ struct ofpbuf * ofputil_encode_set_protocol(enum ofputil_protocol current, enum ofputil_protocol want, enum ofputil_protocol *next) { + enum ofp_version cur_version, want_version; enum ofputil_protocol cur_base, want_base; bool cur_tid, want_tid; + cur_version = ofputil_protocol_to_ofp_version(current); + want_version = ofputil_protocol_to_ofp_version(want); + if (cur_version != want_version) { + *next = current; + return NULL; + } + cur_base = ofputil_protocol_to_base(current); want_base = ofputil_protocol_to_base(want); if (cur_base != want_base) { *next = ofputil_protocol_set_base(current, want_base); switch (want_base) { - case OFPUTIL_P_NXM: + case OFPUTIL_P_OF10_NXM: return ofputil_encode_nx_set_flow_format(NXFF_NXM); - case OFPUTIL_P_OF10: + case OFPUTIL_P_OF10_STD: return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10); - case OFPUTIL_P_OF12: - return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW12); + case OFPUTIL_P_OF12_OXM: + case OFPUTIL_P_OF13_OXM: + /* There are only one of each OpenFlow 1.2+ protocols and we already + * verified above that we're not trying to change versions. */ + NOT_REACHED(); - case OFPUTIL_P_OF10_TID: - case OFPUTIL_P_NXM_TID: + case OFPUTIL_P_OF10_STD_TID: + case OFPUTIL_P_OF10_NXM_TID: NOT_REACHED(); } } @@ -1014,7 +1367,7 @@ ofputil_encode_set_protocol(enum ofputil_protocol current, return ofputil_make_flow_mod_table_id(want_tid); } - assert(current == want); + ovs_assert(current == want); *next = current; return NULL; @@ -1028,7 +1381,7 @@ ofputil_encode_nx_set_flow_format(enum nx_flow_format nxff) struct nx_set_flow_format *sff; struct ofpbuf *msg; - assert(ofputil_nx_flow_format_is_valid(nxff)); + ovs_assert(ofputil_nx_flow_format_is_valid(nxff)); msg = ofpraw_alloc(OFPRAW_NXT_SET_FLOW_FORMAT, OFP10_VERSION, 0); sff = ofpbuf_put_zeros(msg, sizeof *sff); @@ -1044,13 +1397,10 @@ ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format) { switch (flow_format) { case NXFF_OPENFLOW10: - return OFPUTIL_P_OF10; + return OFPUTIL_P_OF10_STD; case NXFF_NXM: - return OFPUTIL_P_NXM; - - case NXFF_OPENFLOW12: - return OFPUTIL_P_OF12; + return OFPUTIL_P_OF10_NXM; default: return 0; @@ -1074,20 +1424,19 @@ 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(); } } struct ofpbuf * -ofputil_make_set_packet_in_format(enum nx_packet_in_format packet_in_format) +ofputil_make_set_packet_in_format(enum ofp_version ofp_version, + enum nx_packet_in_format packet_in_format) { struct nx_set_packet_in_format *spif; struct ofpbuf *msg; - msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT, OFP10_VERSION, 0); + msg = ofpraw_alloc(OFPRAW_NXT_SET_PACKET_IN_FORMAT, ofp_version, 0); spif = ofpbuf_put_zeros(msg, sizeof *spif); spif->format = htonl(packet_in_format); @@ -1137,7 +1486,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, ofm = ofpbuf_pull(&b, sizeof *ofm); - error = ofputil_pull_ofp11_match(&b, ntohs(ofm->priority), &fm->cr); + error = ofputil_pull_ofp11_match(&b, &fm->match, NULL); if (error) { return error; } @@ -1148,12 +1497,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, } /* Translate the message. */ + fm->priority = ntohs(ofm->priority); if (ofm->command == OFPFC_ADD) { fm->cookie = htonll(0); fm->cookie_mask = htonll(0); fm->new_cookie = ofm->cookie; } else { - /* XXX */ fm->cookie = ofm->cookie; fm->cookie_mask = ofm->cookie_mask; fm->new_cookie = htonll(UINT64_MAX); @@ -1167,32 +1516,24 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, if (error) { return error; } - if (ofm->out_group != htonl(OFPG_ANY)) { - return OFPERR_NXFMFC_GROUPS_NOT_SUPPORTED; + if ((ofm->command == OFPFC_DELETE + || ofm->command == OFPFC_DELETE_STRICT) + && ofm->out_group != htonl(OFPG_ANY)) { + return OFPERR_OFPFMFC_UNKNOWN; } fm->flags = ntohs(ofm->flags); } else { if (raw == OFPRAW_OFPT10_FLOW_MOD) { /* Standard OpenFlow 1.0 flow_mod. */ const struct ofp10_flow_mod *ofm; - uint16_t priority; enum ofperr error; /* Get the ofp10_flow_mod. */ ofm = ofpbuf_pull(&b, sizeof *ofm); - /* 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(OFPFW10_ALL))) { - priority = UINT16_MAX; - } - /* Translate the rule. */ - ofputil_cls_rule_from_ofp10_match(&ofm->match, priority, &fm->cr); - ofputil_normalize_rule(&fm->cr); + ofputil_match_from_ofp10_match(&ofm->match, &fm->match); + ofputil_normalize_match(&fm->match); /* Now get the actions. */ error = ofpacts_pull_openflow10(&b, b.size, ofpacts); @@ -1200,6 +1541,12 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, return error; } + /* OpenFlow 1.0 says that exact-match rules have to have the + * highest possible priority. */ + fm->priority = (ofm->match.wildcards & htonl(OFPFW10_ALL) + ? ntohs(ofm->priority) + : UINT16_MAX); + /* Translate the message. */ command = ntohs(ofm->command); fm->cookie = htonll(0); @@ -1217,8 +1564,8 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, /* Dissect the message. */ nfm = ofpbuf_pull(&b, sizeof *nfm); - error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority), - &fm->cr, &fm->cookie, &fm->cookie_mask); + error = nx_pull_match(&b, ntohs(nfm->match_len), + &fm->match, &fm->cookie, &fm->cookie_mask); if (error) { return error; } @@ -1234,6 +1581,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, * existing cookie. */ return OFPERR_NXBRC_NXM_INVALID; } + fm->priority = ntohs(nfm->priority); fm->new_cookie = nfm->cookie; fm->idle_timeout = ntohs(nfm->idle_timeout); fm->hard_timeout = ntohs(nfm->hard_timeout); @@ -1244,6 +1592,18 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm, NOT_REACHED(); } + if (fm->flags & OFPFF10_EMERG) { + /* We do not support the OpenFlow 1.0 emergency flow cache, which + * is not required in OpenFlow 1.0.1 and removed from OpenFlow 1.1. + * + * OpenFlow 1.0 specifies the error code to use when idle_timeout + * or hard_timeout is nonzero. Otherwise, there is no good error + * code, so just state that the flow table is full. */ + return (fm->hard_timeout || fm->idle_timeout + ? OFPERR_OFPFMFC_BAD_EMERG_TIMEOUT + : OFPERR_OFPFMFC_TABLE_FULL); + } + if (protocol & OFPUTIL_P_TID) { fm->command = command & 0xff; fm->table_id = command >> 8; @@ -1277,55 +1637,56 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, struct ofpbuf *msg; switch (protocol) { - case OFPUTIL_P_OF12: { + case OFPUTIL_P_OF12_OXM: + case OFPUTIL_P_OF13_OXM: { struct ofp11_flow_mod *ofm; - msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, OFP12_VERSION, + msg = ofpraw_alloc(OFPRAW_OFPT11_FLOW_MOD, + ofputil_protocol_to_ofp_version(protocol), NXM_TYPICAL_LEN + fm->ofpacts_len); ofm = ofpbuf_put_zeros(msg, sizeof *ofm); - ofm->cookie = fm->new_cookie; + if (fm->command == OFPFC_ADD) { + ofm->cookie = fm->new_cookie; + } else { + ofm->cookie = fm->cookie; + } ofm->cookie_mask = fm->cookie_mask; ofm->table_id = fm->table_id; ofm->command = fm->command; ofm->idle_timeout = htons(fm->idle_timeout); ofm->hard_timeout = htons(fm->hard_timeout); - ofm->priority = htons(fm->cr.priority); + ofm->priority = htons(fm->priority); ofm->buffer_id = htonl(fm->buffer_id); ofm->out_port = ofputil_port_to_ofp11(fm->out_port); ofm->out_group = htonl(OFPG11_ANY); ofm->flags = htons(fm->flags); - oxm_put_match(msg, &fm->cr); - if (fm->ofpacts) { - ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, - msg); - } + oxm_put_match(msg, &fm->match); + ofpacts_put_openflow11_instructions(fm->ofpacts, fm->ofpacts_len, msg); break; } - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: { + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: { struct ofp10_flow_mod *ofm; msg = ofpraw_alloc(OFPRAW_OFPT10_FLOW_MOD, OFP10_VERSION, fm->ofpacts_len); ofm = ofpbuf_put_zeros(msg, sizeof *ofm); - ofputil_cls_rule_to_ofp10_match(&fm->cr, &ofm->match); + ofputil_match_to_ofp10_match(&fm->match, &ofm->match); ofm->cookie = fm->new_cookie; ofm->command = ofputil_tid_command(fm, protocol); ofm->idle_timeout = htons(fm->idle_timeout); ofm->hard_timeout = htons(fm->hard_timeout); - ofm->priority = htons(fm->cr.priority); + ofm->priority = htons(fm->priority); ofm->buffer_id = htonl(fm->buffer_id); ofm->out_port = htons(fm->out_port); ofm->flags = htons(fm->flags); - if (fm->ofpacts) { - ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg); - } + ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg); break; } - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: { + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: { struct nx_flow_mod *nfm; int match_len; @@ -1334,18 +1695,16 @@ ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm, nfm = ofpbuf_put_zeros(msg, sizeof *nfm); nfm->command = ofputil_tid_command(fm, protocol); nfm->cookie = fm->new_cookie; - match_len = nx_put_match(msg, &fm->cr, fm->cookie, fm->cookie_mask); + match_len = nx_put_match(msg, &fm->match, fm->cookie, fm->cookie_mask); nfm = msg->l3; nfm->idle_timeout = htons(fm->idle_timeout); nfm->hard_timeout = htons(fm->hard_timeout); - nfm->priority = htons(fm->cr.priority); + nfm->priority = htons(fm->priority); nfm->buffer_id = htonl(fm->buffer_id); nfm->out_port = htons(fm->out_port); nfm->flags = htons(fm->flags); nfm->match_len = htons(match_len); - if (fm->ofpacts) { - ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg); - } + ofpacts_put_openflow10(fm->ofpacts, fm->ofpacts_len, msg); break; } @@ -1373,28 +1732,28 @@ ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms, for (i = 0; i < n_fms; i++) { const struct ofputil_flow_mod *fm = &fms[i]; - usable_protocols &= ofputil_usable_protocols(&fm->cr); + usable_protocols &= ofputil_usable_protocols(&fm->match); if (fm->table_id != 0xff) { usable_protocols &= OFPUTIL_P_TID; } - /* Matching of the cookie is only supported through NXM. */ + /* Matching of the cookie is only supported through NXM or OF1.1+. */ if (fm->cookie_mask != htonll(0)) { - usable_protocols &= OFPUTIL_P_NXM_ANY; + usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } } - assert(usable_protocols); return usable_protocols; } static enum ofperr -ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr, - const struct ofp10_flow_stats_request *ofsr, - bool aggregate) +ofputil_decode_ofpst10_flow_request(struct ofputil_flow_stats_request *fsr, + const struct ofp10_flow_stats_request *ofsr, + bool aggregate) { fsr->aggregate = aggregate; - ofputil_cls_rule_from_ofp10_match(&ofsr->match, 0, &fsr->match); + ofputil_match_from_ofp10_match(&ofsr->match, &fsr->match); fsr->out_port = ntohs(ofsr->out_port); fsr->table_id = ofsr->table_id; fsr->cookie = fsr->cookie_mask = htonll(0); @@ -1402,6 +1761,33 @@ ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr, return 0; } +static enum ofperr +ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr, + struct ofpbuf *b, bool aggregate) +{ + const struct ofp11_flow_stats_request *ofsr; + enum ofperr error; + + ofsr = ofpbuf_pull(b, sizeof *ofsr); + fsr->aggregate = aggregate; + fsr->table_id = ofsr->table_id; + error = ofputil_port_from_ofp11(ofsr->out_port, &fsr->out_port); + if (error) { + return error; + } + if (ofsr->out_group != htonl(OFPG11_ANY)) { + return OFPERR_OFPFMFC_UNKNOWN; + } + fsr->cookie = ofsr->cookie; + fsr->cookie_mask = ofsr->cookie_mask; + error = ofputil_pull_ofp11_match(b, &fsr->match, NULL); + if (error) { + return error; + } + + return 0; +} + static enum ofperr ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr, struct ofpbuf *b, bool aggregate) @@ -1410,7 +1796,7 @@ ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr, enum ofperr error; nfsr = ofpbuf_pull(b, sizeof *nfsr); - error = nx_pull_match(b, ntohs(nfsr->match_len), 0, &fsr->match, + error = nx_pull_match(b, ntohs(nfsr->match_len), &fsr->match, &fsr->cookie, &fsr->cookie_mask); if (error) { return error; @@ -1439,11 +1825,17 @@ ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr, ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); switch ((int) raw) { - case OFPRAW_OFPST_FLOW_REQUEST: - return ofputil_decode_ofpst_flow_request(fsr, b.data, false); + case OFPRAW_OFPST10_FLOW_REQUEST: + return ofputil_decode_ofpst10_flow_request(fsr, b.data, false); - case OFPRAW_OFPST_AGGREGATE_REQUEST: - return ofputil_decode_ofpst_flow_request(fsr, b.data, true); + case OFPRAW_OFPST10_AGGREGATE_REQUEST: + return ofputil_decode_ofpst10_flow_request(fsr, b.data, true); + + case OFPRAW_OFPST11_FLOW_REQUEST: + return ofputil_decode_ofpst11_flow_request(fsr, &b, false); + + case OFPRAW_OFPST11_AGGREGATE_REQUEST: + return ofputil_decode_ofpst11_flow_request(fsr, &b, true); case OFPRAW_NXST_FLOW_REQUEST: return ofputil_decode_nxst_flow_request(fsr, &b, false); @@ -1468,30 +1860,49 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, enum ofpraw raw; switch (protocol) { - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: { + case OFPUTIL_P_OF12_OXM: + case OFPUTIL_P_OF13_OXM: { + struct ofp11_flow_stats_request *ofsr; + + raw = (fsr->aggregate + ? OFPRAW_OFPST11_AGGREGATE_REQUEST + : OFPRAW_OFPST11_FLOW_REQUEST); + msg = ofpraw_alloc(raw, ofputil_protocol_to_ofp_version(protocol), + NXM_TYPICAL_LEN); + ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr); + ofsr->table_id = fsr->table_id; + ofsr->out_port = ofputil_port_to_ofp11(fsr->out_port); + ofsr->out_group = htonl(OFPG11_ANY); + ofsr->cookie = fsr->cookie; + ofsr->cookie_mask = fsr->cookie_mask; + oxm_put_match(msg, &fsr->match); + break; + } + + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: { struct ofp10_flow_stats_request *ofsr; raw = (fsr->aggregate - ? OFPRAW_OFPST_AGGREGATE_REQUEST - : OFPRAW_OFPST_FLOW_REQUEST); + ? OFPRAW_OFPST10_AGGREGATE_REQUEST + : OFPRAW_OFPST10_FLOW_REQUEST); msg = ofpraw_alloc(raw, OFP10_VERSION, 0); ofsr = ofpbuf_put_zeros(msg, sizeof *ofsr); - ofputil_cls_rule_to_ofp10_match(&fsr->match, &ofsr->match); + ofputil_match_to_ofp10_match(&fsr->match, &ofsr->match); ofsr->table_id = fsr->table_id; ofsr->out_port = htons(fsr->out_port); break; } - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: { + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: { struct nx_flow_stats_request *nfsr; int match_len; raw = (fsr->aggregate ? OFPRAW_NXST_AGGREGATE_REQUEST : OFPRAW_NXST_FLOW_REQUEST); - msg = ofpraw_alloc(raw, OFP10_VERSION, 0); + msg = ofpraw_alloc(raw, OFP10_VERSION, NXM_TYPICAL_LEN); ofpbuf_put_zeros(msg, sizeof *nfsr); match_len = nx_put_match(msg, &fsr->match, fsr->cookie, fsr->cookie_mask); @@ -1503,7 +1914,6 @@ ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr, break; } - case OFPUTIL_P_OF12: default: NOT_REACHED(); } @@ -1523,7 +1933,8 @@ ofputil_flow_stats_request_usable_protocols( usable_protocols = ofputil_usable_protocols(&fsr->match); if (fsr->cookie_mask != htonll(0)) { - usable_protocols &= OFPUTIL_P_NXM_ANY; + usable_protocols &= OFPUTIL_P_OF10_NXM_ANY | OFPUTIL_P_OF12_OXM + | OFPUTIL_P_OF13_OXM; } return usable_protocols; } @@ -1566,7 +1977,50 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, if (!msg->size) { return EOF; - } else if (raw == OFPRAW_OFPST_FLOW_REPLY) { + } else if (raw == OFPRAW_OFPST11_FLOW_REPLY + || raw == OFPRAW_OFPST13_FLOW_REPLY) { + const struct ofp11_flow_stats *ofs; + size_t length; + uint16_t padded_match_len; + + ofs = ofpbuf_try_pull(msg, sizeof *ofs); + if (!ofs) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply has %zu leftover " + "bytes at end", msg->size); + return EINVAL; + } + + length = ntohs(ofs->length); + if (length < sizeof *ofs) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply claims invalid " + "length %zu", length); + return EINVAL; + } + + if (ofputil_pull_ofp11_match(msg, &fs->match, &padded_match_len)) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match"); + return EINVAL; + } + + if (ofpacts_pull_openflow11_instructions(msg, length - sizeof *ofs - + padded_match_len, ofpacts)) { + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions"); + return EINVAL; + } + + fs->priority = ntohs(ofs->priority); + fs->table_id = ofs->table_id; + fs->duration_sec = ntohl(ofs->duration_sec); + fs->duration_nsec = ntohl(ofs->duration_nsec); + fs->idle_timeout = ntohs(ofs->idle_timeout); + fs->hard_timeout = ntohs(ofs->hard_timeout); + fs->flags = (raw == OFPRAW_OFPST13_FLOW_REPLY) ? ntohs(ofs->flags) : 0; + fs->idle_age = -1; + fs->hard_age = -1; + fs->cookie = ofs->cookie; + fs->packet_count = ntohll(ofs->packet_count); + fs->byte_count = ntohll(ofs->byte_count); + } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) { const struct ofp10_flow_stats *ofs; size_t length; @@ -1589,8 +2043,8 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, } fs->cookie = get_32aligned_be64(&ofs->cookie); - ofputil_cls_rule_from_ofp10_match(&ofs->match, ntohs(ofs->priority), - &fs->rule); + ofputil_match_from_ofp10_match(&ofs->match, &fs->match); + fs->priority = ntohs(ofs->priority); fs->table_id = ofs->table_id; fs->duration_sec = ntohl(ofs->duration_sec); fs->duration_nsec = ntohl(ofs->duration_nsec); @@ -1600,6 +2054,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, fs->hard_age = -1; fs->packet_count = ntohll(get_32aligned_be64(&ofs->packet_count)); fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count)); + fs->flags = 0; } else if (raw == OFPRAW_NXST_FLOW_REPLY) { const struct nx_flow_stats *nfs; size_t match_len, actions_len, length; @@ -1618,8 +2073,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, "claims invalid length %zu", match_len, length); return EINVAL; } - if (nx_pull_match(msg, match_len, ntohs(nfs->priority), &fs->rule, - NULL, NULL)) { + if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL)) { return EINVAL; } @@ -1632,6 +2086,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, fs->table_id = nfs->table_id; fs->duration_sec = ntohl(nfs->duration_sec); fs->duration_nsec = ntohl(nfs->duration_nsec); + fs->priority = ntohs(nfs->priority); fs->idle_timeout = ntohs(nfs->idle_timeout); fs->hard_timeout = ntohs(nfs->hard_timeout); fs->idle_age = -1; @@ -1646,6 +2101,7 @@ ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs, } fs->packet_count = ntohll(nfs->packet_count); fs->byte_count = ntohll(nfs->byte_count); + fs->flags = 0; } else { NOT_REACHED(); } @@ -1678,8 +2134,30 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, enum ofpraw raw; ofpraw_decode_partial(&raw, reply->data, reply->size); - if (raw == OFPRAW_OFPST_FLOW_REPLY) { - struct ofp10_flow_stats *ofs; + if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) { + struct ofp11_flow_stats *ofs; + + ofpbuf_put_uninit(reply, sizeof *ofs); + oxm_put_match(reply, &fs->match); + ofpacts_put_openflow11_instructions(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; + ofs->duration_sec = htonl(fs->duration_sec); + ofs->duration_nsec = htonl(fs->duration_nsec); + ofs->priority = htons(fs->priority); + ofs->idle_timeout = htons(fs->idle_timeout); + ofs->hard_timeout = htons(fs->hard_timeout); + ofs->flags = (raw == OFPRAW_OFPST13_FLOW_REPLY) ? htons(fs->flags) : 0; + memset(ofs->pad2, 0, sizeof ofs->pad2); + ofs->cookie = fs->cookie; + ofs->packet_count = htonll(unknown_to_zero(fs->packet_count)); + ofs->byte_count = htonll(unknown_to_zero(fs->byte_count)); + } else if (raw == OFPRAW_OFPST10_FLOW_REPLY) { + struct ofp10_flow_stats *ofs; ofpbuf_put_uninit(reply, sizeof *ofs); ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply); @@ -1688,10 +2166,10 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, ofs->length = htons(reply->size - start_ofs); ofs->table_id = fs->table_id; ofs->pad = 0; - ofputil_cls_rule_to_ofp10_match(&fs->rule, &ofs->match); + ofputil_match_to_ofp10_match(&fs->match, &ofs->match); ofs->duration_sec = htonl(fs->duration_sec); ofs->duration_nsec = htonl(fs->duration_nsec); - ofs->priority = htons(fs->rule.priority); + ofs->priority = htons(fs->priority); ofs->idle_timeout = htons(fs->idle_timeout); ofs->hard_timeout = htons(fs->hard_timeout); memset(ofs->pad2, 0, sizeof ofs->pad2); @@ -1705,7 +2183,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, int match_len; ofpbuf_put_uninit(reply, sizeof *nfs); - match_len = nx_put_match(reply, &fs->rule, 0, 0); + match_len = nx_put_match(reply, &fs->match, 0, 0); ofpacts_put_openflow10(fs->ofpacts, fs->ofpacts_len, reply); nfs = ofpbuf_at_assert(reply, start_ofs, sizeof *nfs); @@ -1714,7 +2192,7 @@ ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs, nfs->pad = 0; nfs->duration_sec = htonl(fs->duration_sec); nfs->duration_nsec = htonl(fs->duration_nsec); - nfs->priority = htons(fs->rule.priority); + nfs->priority = htons(fs->priority); nfs->idle_timeout = htons(fs->idle_timeout); nfs->hard_timeout = htons(fs->hard_timeout); nfs->idle_age = htons(fs->idle_age < 0 ? 0 @@ -1748,7 +2226,7 @@ ofputil_encode_aggregate_stats_reply( enum ofpraw raw; ofpraw_decode(&raw, request); - if (raw == OFPRAW_OFPST_AGGREGATE_REQUEST) { + if (raw == OFPRAW_OFPST10_AGGREGATE_REQUEST) { packet_count = unknown_to_zero(stats->packet_count); byte_count = unknown_to_zero(stats->byte_count); } else { @@ -1795,27 +2273,50 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); - if (raw == OFPRAW_OFPT10_FLOW_REMOVED) { - const struct ofp_flow_removed *ofr; + if (raw == OFPRAW_OFPT11_FLOW_REMOVED) { + const struct ofp12_flow_removed *ofr; + enum ofperr error; + + ofr = ofpbuf_pull(&b, sizeof *ofr); + + error = ofputil_pull_ofp11_match(&b, &fr->match, NULL); + if (error) { + return error; + } + + fr->priority = ntohs(ofr->priority); + fr->cookie = ofr->cookie; + fr->reason = ofr->reason; + fr->table_id = ofr->table_id; + fr->duration_sec = ntohl(ofr->duration_sec); + fr->duration_nsec = ntohl(ofr->duration_nsec); + fr->idle_timeout = ntohs(ofr->idle_timeout); + fr->hard_timeout = ntohs(ofr->hard_timeout); + fr->packet_count = ntohll(ofr->packet_count); + fr->byte_count = ntohll(ofr->byte_count); + } else if (raw == OFPRAW_OFPT10_FLOW_REMOVED) { + const struct ofp10_flow_removed *ofr; ofr = ofpbuf_pull(&b, sizeof *ofr); - ofputil_cls_rule_from_ofp10_match(&ofr->match, ntohs(ofr->priority), - &fr->rule); + ofputil_match_from_ofp10_match(&ofr->match, &fr->match); + fr->priority = ntohs(ofr->priority); fr->cookie = ofr->cookie; fr->reason = ofr->reason; + fr->table_id = 255; fr->duration_sec = ntohl(ofr->duration_sec); fr->duration_nsec = ntohl(ofr->duration_nsec); fr->idle_timeout = ntohs(ofr->idle_timeout); + fr->hard_timeout = 0; fr->packet_count = ntohll(ofr->packet_count); fr->byte_count = ntohll(ofr->byte_count); } else if (raw == OFPRAW_NXT_FLOW_REMOVED) { struct nx_flow_removed *nfr; - int error; + enum ofperr error; nfr = ofpbuf_pull(&b, sizeof *nfr); - error = nx_pull_match(&b, ntohs(nfr->match_len), ntohs(nfr->priority), - &fr->rule, NULL, NULL); + error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match, + NULL, NULL); if (error) { return error; } @@ -1823,11 +2324,14 @@ ofputil_decode_flow_removed(struct ofputil_flow_removed *fr, return OFPERR_OFPBRC_BAD_LEN; } + fr->priority = ntohs(nfr->priority); fr->cookie = nfr->cookie; fr->reason = nfr->reason; + fr->table_id = 255; fr->duration_sec = ntohl(nfr->duration_sec); fr->duration_nsec = ntohl(nfr->duration_nsec); fr->idle_timeout = ntohs(nfr->idle_timeout); + fr->hard_timeout = 0; fr->packet_count = ntohll(nfr->packet_count); fr->byte_count = ntohll(nfr->byte_count); } else { @@ -1847,16 +2351,38 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, struct ofpbuf *msg; switch (protocol) { - case OFPUTIL_P_OF10: - case OFPUTIL_P_OF10_TID: { - struct ofp_flow_removed *ofr; + case OFPUTIL_P_OF12_OXM: + case OFPUTIL_P_OF13_OXM: { + struct ofp12_flow_removed *ofr; + + msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED, + ofputil_protocol_to_ofp_version(protocol), + htonl(0), NXM_TYPICAL_LEN); + ofr = ofpbuf_put_zeros(msg, sizeof *ofr); + ofr->cookie = fr->cookie; + ofr->priority = htons(fr->priority); + ofr->reason = fr->reason; + ofr->table_id = fr->table_id; + ofr->duration_sec = htonl(fr->duration_sec); + ofr->duration_nsec = htonl(fr->duration_nsec); + ofr->idle_timeout = htons(fr->idle_timeout); + ofr->hard_timeout = htons(fr->hard_timeout); + ofr->packet_count = htonll(fr->packet_count); + ofr->byte_count = htonll(fr->byte_count); + oxm_put_match(msg, &fr->match); + break; + } + + case OFPUTIL_P_OF10_STD: + case OFPUTIL_P_OF10_STD_TID: { + struct ofp10_flow_removed *ofr; msg = ofpraw_alloc_xid(OFPRAW_OFPT10_FLOW_REMOVED, OFP10_VERSION, htonl(0), 0); ofr = ofpbuf_put_zeros(msg, sizeof *ofr); - ofputil_cls_rule_to_ofp10_match(&fr->rule, &ofr->match); + ofputil_match_to_ofp10_match(&fr->match, &ofr->match); ofr->cookie = fr->cookie; - ofr->priority = htons(fr->rule.priority); + ofr->priority = htons(fr->priority); ofr->reason = fr->reason; ofr->duration_sec = htonl(fr->duration_sec); ofr->duration_nsec = htonl(fr->duration_nsec); @@ -1866,19 +2392,19 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, break; } - case OFPUTIL_P_NXM: - case OFPUTIL_P_NXM_TID: { + case OFPUTIL_P_OF10_NXM: + case OFPUTIL_P_OF10_NXM_TID: { struct nx_flow_removed *nfr; int match_len; msg = ofpraw_alloc_xid(OFPRAW_NXT_FLOW_REMOVED, OFP10_VERSION, htonl(0), NXM_TYPICAL_LEN); nfr = ofpbuf_put_zeros(msg, sizeof *nfr); - match_len = nx_put_match(msg, &fr->rule, 0, 0); + match_len = nx_put_match(msg, &fr->match, 0, 0); nfr = msg->l3; nfr->cookie = fr->cookie; - nfr->priority = htons(fr->rule.priority); + nfr->priority = htons(fr->priority); nfr->reason = fr->reason; nfr->duration_sec = htonl(fr->duration_sec); nfr->duration_nsec = htonl(fr->duration_nsec); @@ -1889,7 +2415,6 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, break; } - case OFPUTIL_P_OF12: default: NOT_REACHED(); } @@ -1897,6 +2422,19 @@ ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr, return msg; } +static void +ofputil_decode_packet_in_finish(struct ofputil_packet_in *pin, + struct match *match, struct ofpbuf *b) +{ + pin->packet = b->data; + pin->packet_len = b->size; + + pin->fmd.in_port = match->flow.in_port; + pin->fmd.tun_id = match->flow.tunnel.tun_id; + pin->fmd.metadata = match->flow.metadata; + memcpy(pin->fmd.regs, match->flow.regs, sizeof pin->fmd.regs); +} + enum ofperr ofputil_decode_packet_in(struct ofputil_packet_in *pin, const struct ofp_header *oh) @@ -1908,10 +2446,42 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); - if (raw == OFPRAW_OFPT10_PACKET_IN) { - const struct ofp_packet_in *opi; + if (raw == OFPRAW_OFPT13_PACKET_IN || raw == OFPRAW_OFPT12_PACKET_IN) { + const struct ofp13_packet_in *opi; + struct match match; + int error; + size_t packet_in_size; + + if (raw == OFPRAW_OFPT12_PACKET_IN) { + packet_in_size = sizeof (struct ofp12_packet_in); + } else { + packet_in_size = sizeof (struct ofp13_packet_in); + } + + opi = ofpbuf_pull(&b, packet_in_size); + error = oxm_pull_match_loose(&b, &match); + if (error) { + return error; + } + + if (!ofpbuf_try_pull(&b, 2)) { + return OFPERR_OFPBRC_BAD_LEN; + } + + pin->reason = opi->pi.reason; + pin->table_id = opi->pi.table_id; + pin->buffer_id = ntohl(opi->pi.buffer_id); + pin->total_len = ntohs(opi->pi.total_len); + + if (raw == OFPRAW_OFPT13_PACKET_IN) { + pin->cookie = opi->cookie; + } + + ofputil_decode_packet_in_finish(pin, &match, &b); + } else if (raw == OFPRAW_OFPT10_PACKET_IN) { + const struct ofp10_packet_in *opi; - opi = ofpbuf_pull(&b, offsetof(struct ofp_packet_in, data)); + opi = ofpbuf_pull(&b, offsetof(struct ofp10_packet_in, data)); pin->packet = opi->data; pin->packet_len = b.size; @@ -1922,11 +2492,11 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, pin->total_len = ntohs(opi->total_len); } else if (raw == OFPRAW_NXT_PACKET_IN) { const struct nx_packet_in *npi; - struct cls_rule rule; + struct match match; int error; npi = ofpbuf_pull(&b, sizeof *npi); - error = nx_pull_match_loose(&b, ntohs(npi->match_len), 0, &rule, NULL, + error = nx_pull_match_loose(&b, ntohs(npi->match_len), &match, NULL, NULL); if (error) { return error; @@ -1936,26 +2506,14 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, return OFPERR_OFPBRC_BAD_LEN; } - pin->packet = b.data; - pin->packet_len = b.size; pin->reason = npi->reason; pin->table_id = npi->table_id; pin->cookie = npi->cookie; - pin->fmd.in_port = rule.flow.in_port; - - 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); - pin->buffer_id = ntohl(npi->buffer_id); pin->total_len = ntohs(npi->total_len); + + ofputil_decode_packet_in_finish(pin, &match, &b); } else { NOT_REACHED(); } @@ -1963,22 +2521,82 @@ ofputil_decode_packet_in(struct ofputil_packet_in *pin, return 0; } +static void +ofputil_packet_in_to_match(const struct ofputil_packet_in *pin, + struct match *match) +{ + int i; + + match_init_catchall(match); + if (pin->fmd.tun_id != htonll(0)) { + match_set_tun_id(match, pin->fmd.tun_id); + } + if (pin->fmd.metadata != htonll(0)) { + match_set_metadata(match, pin->fmd.metadata); + } + + for (i = 0; i < FLOW_N_REGS; i++) { + if (pin->fmd.regs[i]) { + match_set_reg(match, i, pin->fmd.regs[i]); + } + } + + match_set_in_port(match, pin->fmd.in_port); +} + /* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message * in the format specified by 'packet_in_format'. */ struct ofpbuf * ofputil_encode_packet_in(const struct ofputil_packet_in *pin, + enum ofputil_protocol protocol, enum nx_packet_in_format packet_in_format) { size_t send_len = MIN(pin->send_len, pin->packet_len); struct ofpbuf *packet; /* Add OFPT_PACKET_IN. */ - if (packet_in_format == NXPIF_OPENFLOW10) { - struct ofp_packet_in *opi; + if (protocol == OFPUTIL_P_OF13_OXM || protocol == OFPUTIL_P_OF12_OXM) { + struct ofp13_packet_in *opi; + struct match match; + enum ofpraw packet_in_raw; + enum ofp_version packet_in_version; + size_t packet_in_size; + + if (protocol == OFPUTIL_P_OF12_OXM) { + packet_in_raw = OFPRAW_OFPT12_PACKET_IN; + packet_in_version = OFP12_VERSION; + packet_in_size = sizeof (struct ofp12_packet_in); + } else { + packet_in_raw = OFPRAW_OFPT13_PACKET_IN; + packet_in_version = OFP13_VERSION; + packet_in_size = sizeof (struct ofp13_packet_in); + } + + ofputil_packet_in_to_match(pin, &match); + + /* The final argument is just an estimate of the space required. */ + packet = ofpraw_alloc_xid(packet_in_raw, packet_in_version, + htonl(0), (sizeof(struct flow_metadata) * 2 + + 2 + send_len)); + ofpbuf_put_zeros(packet, packet_in_size); + oxm_put_match(packet, &match); + ofpbuf_put_zeros(packet, 2); + ofpbuf_put(packet, pin->packet, send_len); + + opi = packet->l3; + opi->pi.buffer_id = htonl(pin->buffer_id); + opi->pi.total_len = htons(pin->total_len); + opi->pi.reason = pin->reason; + opi->pi.table_id = pin->table_id; + if (protocol == OFPUTIL_P_OF13_OXM) { + opi->cookie = pin->cookie; + } + } else if (packet_in_format == NXPIF_OPENFLOW10) { + struct ofp10_packet_in *opi; packet = ofpraw_alloc_xid(OFPRAW_OFPT10_PACKET_IN, OFP10_VERSION, htonl(0), send_len); - opi = ofpbuf_put_zeros(packet, offsetof(struct ofp_packet_in, data)); + opi = ofpbuf_put_zeros(packet, offsetof(struct ofp10_packet_in, data)); opi->total_len = htons(pin->total_len); opi->in_port = htons(pin->fmd.in_port); opi->reason = pin->reason; @@ -1987,30 +2605,17 @@ ofputil_encode_packet_in(const struct ofputil_packet_in *pin, ofpbuf_put(packet, pin->packet, send_len); } else if (packet_in_format == NXPIF_NXM) { struct nx_packet_in *npi; - struct cls_rule rule; + struct match match; size_t match_len; - size_t i; - - 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], - pin->fmd.reg_masks[i]); - } - cls_rule_set_in_port(&rule, pin->fmd.in_port); + ofputil_packet_in_to_match(pin, &match); /* The final argument is just an estimate of the space required. */ packet = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN, OFP10_VERSION, htonl(0), (sizeof(struct flow_metadata) * 2 + 2 + send_len)); ofpbuf_put_zeros(packet, sizeof *npi); - match_len = nx_put_match(packet, &rule, 0, 0); + match_len = nx_put_match(packet, &match, 0, 0); ofpbuf_put_zeros(packet, 2); ofpbuf_put(packet, pin->packet, send_len); @@ -2077,29 +2682,49 @@ ofputil_decode_packet_out(struct ofputil_packet_out *po, const struct ofp_header *oh, struct ofpbuf *ofpacts) { - const struct ofp_packet_out *opo; - enum ofperr error; enum ofpraw raw; struct ofpbuf b; ofpbuf_use_const(&b, oh, ntohs(oh->length)); raw = ofpraw_pull_assert(&b); - assert(raw == OFPRAW_OFPT10_PACKET_OUT); - opo = ofpbuf_pull(&b, sizeof *opo); - po->buffer_id = ntohl(opo->buffer_id); - po->in_port = ntohs(opo->in_port); + if (raw == OFPRAW_OFPT11_PACKET_OUT) { + enum ofperr error; + const struct ofp11_packet_out *opo = ofpbuf_pull(&b, sizeof *opo); + + po->buffer_id = ntohl(opo->buffer_id); + error = ofputil_port_from_ofp11(opo->in_port, &po->in_port); + if (error) { + return error; + } + + error = ofpacts_pull_openflow11_actions(&b, ntohs(opo->actions_len), + ofpacts); + if (error) { + return error; + } + } else if (raw == OFPRAW_OFPT10_PACKET_OUT) { + enum ofperr error; + const struct ofp10_packet_out *opo = ofpbuf_pull(&b, sizeof *opo); + + po->buffer_id = ntohl(opo->buffer_id); + po->in_port = ntohs(opo->in_port); + + error = ofpacts_pull_openflow10(&b, ntohs(opo->actions_len), ofpacts); + if (error) { + return error; + } + } else { + NOT_REACHED(); + } + if (po->in_port >= OFPP_MAX && po->in_port != OFPP_LOCAL && 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 OFPERR_OFPBRC_BAD_PORT; } - error = ofpacts_pull_openflow10(&b, ntohs(opo->actions_len), ofpacts); - if (error) { - return error; - } po->ofpacts = ofpacts->data; po->ofpacts_len = ofpacts->size; @@ -2192,8 +2817,8 @@ ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp, pp->supported = netdev_port_features_from_ofp10(opp->supported); pp->peer = netdev_port_features_from_ofp10(opp->peer); - pp->curr_speed = netdev_features_to_bps(pp->curr) / 1000; - pp->max_speed = netdev_features_to_bps(pp->supported) / 1000; + pp->curr_speed = netdev_features_to_bps(pp->curr, 0) / 1000; + pp->max_speed = netdev_features_to_bps(pp->supported, 0) / 1000; return 0; } @@ -2235,6 +2860,7 @@ ofputil_get_phy_port_size(enum ofp_version ofp_version) return sizeof(struct ofp10_phy_port); case OFP11_VERSION: case OFP12_VERSION: + case OFP13_VERSION: return sizeof(struct ofp11_port); default: NOT_REACHED(); @@ -2297,7 +2923,8 @@ ofputil_put_phy_port(enum ofp_version ofp_version, } case OFP11_VERSION: - case OFP12_VERSION: { + case OFP12_VERSION: + case OFP13_VERSION: { struct ofp11_port *op; if (b->size + sizeof *op <= UINT16_MAX) { op = ofpbuf_put_uninit(b, sizeof *op); @@ -2326,7 +2953,8 @@ ofputil_append_port_desc_stats_reply(enum ofp_version ofp_version, } case OFP11_VERSION: - case OFP12_VERSION: { + case OFP12_VERSION: + case OFP13_VERSION: { struct ofp11_port *op; op = ofpmp_append(replies, sizeof *op); @@ -2395,6 +3023,7 @@ ofputil_capabilities_mask(enum ofp_version ofp_version) case OFP11_VERSION: return OFPC_COMMON | OFPC_ARP_MATCH_IP; case OFP12_VERSION: + case OFP13_VERSION: return OFPC_COMMON | OFPC12_PORT_BLOCKED; default: /* Caller needs to check osf->header.version itself */ @@ -2422,6 +3051,7 @@ ofputil_decode_switch_features(const struct ofp_header *oh, features->datapath_id = ntohll(osf->datapath_id); features->n_buffers = ntohl(osf->n_buffers); features->n_tables = osf->n_tables; + features->auxiliary_id = 0; features->capabilities = ntohl(osf->capabilities) & ofputil_capabilities_mask(oh->version); @@ -2435,11 +3065,15 @@ ofputil_decode_switch_features(const struct ofp_header *oh, features->capabilities |= OFPUTIL_C_STP; } features->actions = decode_action_bits(osf->actions, of10_action_bits); - } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY) { + } else if (raw == OFPRAW_OFPT11_FEATURES_REPLY + || raw == OFPRAW_OFPT13_FEATURES_REPLY) { if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) { features->capabilities |= OFPUTIL_C_GROUP_STATS; } features->actions = 0; + if (raw == OFPRAW_OFPT13_FEATURES_REPLY) { + features->auxiliary_id = osf->auxiliary_id; + } } else { return OFPERR_OFPBRC_BAD_VERSION; } @@ -2516,6 +3150,9 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features, case OFP12_VERSION: raw = OFPRAW_OFPT11_FEATURES_REPLY; break; + case OFP13_VERSION: + raw = OFPRAW_OFPT13_FEATURES_REPLY; + break; default: NOT_REACHED(); } @@ -2535,6 +3172,9 @@ ofputil_encode_switch_features(const struct ofputil_switch_features *features, } osf->actions = encode_action_bits(features->actions, of10_action_bits); break; + case OFP13_VERSION: + osf->auxiliary_id = features->auxiliary_id; + /* fall through */ case OFP11_VERSION: case OFP12_VERSION: if (features->capabilities & OFPUTIL_C_GROUP_STATS) { @@ -2557,7 +3197,9 @@ ofputil_put_switch_features_port(const struct ofputil_phy_port *pp, { const struct ofp_header *oh = b->data; - ofputil_put_phy_port(oh->version, pp, b); + if (oh->version < OFP13_VERSION) { + ofputil_put_phy_port(oh->version, pp, b); + } } /* ofputil_port_status */ @@ -2584,7 +3226,7 @@ ofputil_decode_port_status(const struct ofp_header *oh, ps->reason = ops->reason; retval = ofputil_pull_phy_port(oh->version, &b, &ps->desc); - assert(retval != EOF); + ovs_assert(retval != EOF); return retval; } @@ -2608,6 +3250,7 @@ ofputil_encode_port_status(const struct ofputil_port_status *ps, case OFP11_VERSION: case OFP12_VERSION: + case OFP13_VERSION: raw = OFPRAW_OFPT11_PORT_STATUS; break; @@ -2690,12 +3333,14 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm, break; } - case OFP11_VERSION: { + case OFP11_VERSION: + case OFP12_VERSION: + case OFP13_VERSION: { struct ofp11_port_mod *opm; b = ofpraw_alloc(OFPRAW_OFPT11_PORT_MOD, ofp_version, 0); opm = ofpbuf_put_zeros(b, sizeof *opm); - opm->port_no = htonl(pm->port_no); + opm->port_no = ofputil_port_to_ofp11(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); @@ -2703,7 +3348,6 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm, break; } - case OFP12_VERSION: default: NOT_REACHED(); } @@ -2711,6 +3355,263 @@ ofputil_encode_port_mod(const struct ofputil_port_mod *pm, return b; } +/* ofputil_role_request */ + +/* Decodes the OpenFlow "role request" or "role reply" message in '*oh' into + * an abstract form in '*rr'. Returns 0 if successful, otherwise an + * OFPERR_* value. */ +enum ofperr +ofputil_decode_role_message(const struct ofp_header *oh, + struct ofputil_role_request *rr) +{ + const struct ofp12_role_request *orr = ofpmsg_body(oh); + uint32_t role = ntohl(orr->role); + struct ofpbuf b; + enum ofpraw raw; + + memset(rr, 0, sizeof *rr); + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + raw = ofpraw_pull_assert(&b); + + if (raw == OFPRAW_OFPT12_ROLE_REQUEST + || raw == OFPRAW_OFPT12_ROLE_REPLY) { + + if (raw == OFPRAW_OFPT12_ROLE_REQUEST) { + if (role == OFPCR12_ROLE_NOCHANGE) { + rr->request_current_role_only = true; + return 0; + } + if (role == OFPCR12_ROLE_MASTER || role == OFPCR12_ROLE_SLAVE) { + rr->generation_id = ntohll(orr->generation_id); + rr->have_generation_id = true; + } + } + + /* Map to enum nx_role */ + role -= 1; /* OFPCR12_ROLE_MASTER -> NX_ROLE_MASTER etc. */ + } else if (raw != OFPRAW_NXT_ROLE_REQUEST + && raw != OFPRAW_NXT_ROLE_REPLY) { + return OFPERR_OFPBRC_BAD_TYPE; + } + + if (role != NX_ROLE_OTHER && role != NX_ROLE_MASTER + && role != NX_ROLE_SLAVE) { + return OFPERR_OFPRRFC_BAD_ROLE; + } + + rr->role = role; + return 0; +} + +/* Returns an encoded form of a role reply suitable for the "request" in a + * buffer owned by the caller. */ +struct ofpbuf * +ofputil_encode_role_reply(const struct ofp_header *request, + enum nx_role role) +{ + struct ofp12_role_request *reply; + struct ofpbuf *buf; + size_t reply_size; + + struct ofpbuf b; + enum ofpraw raw; + + ofpbuf_use_const(&b, request, ntohs(request->length)); + raw = ofpraw_pull_assert(&b); + if (raw == OFPRAW_OFPT12_ROLE_REQUEST) { + reply_size = sizeof (struct ofp12_role_request); + raw = OFPRAW_OFPT12_ROLE_REPLY; + } + else if (raw == OFPRAW_NXT_ROLE_REQUEST) { + reply_size = sizeof (struct nx_role_request); + raw = OFPRAW_NXT_ROLE_REPLY; + } else { + NOT_REACHED(); + } + + buf = ofpraw_alloc_reply(raw, request, 0); + reply = ofpbuf_put_zeros(buf, reply_size); + + if (raw == OFPRAW_OFPT12_ROLE_REPLY) { + /* Map to OpenFlow enum ofp12_controller_role */ + role += 1; /* NX_ROLE_MASTER -> OFPCR12_ROLE_MASTER etc. */ + /* + * OpenFlow specification does not specify use of generation_id field + * on reply messages. Intuitively, it would seem a good idea to return + * the current value. However, the current value is undefined + * initially, and there is no way to insert an undefined value in the + * message. Therefore we leave the generation_id zeroed on reply + * messages. + * + * A request for clarification has been filed with the Open Networking + * Foundation as EXT-272. + */ + } + reply->role = htonl(role); + + return buf; +} + +/* Table stats. */ + +static void +ofputil_put_ofp10_table_stats(const struct ofp12_table_stats *in, + struct ofpbuf *buf) +{ + struct wc_map { + enum ofp10_flow_wildcards wc10; + enum oxm12_ofb_match_fields mf12; + }; + + static const struct wc_map wc_map[] = { + { OFPFW10_IN_PORT, OFPXMT12_OFB_IN_PORT }, + { OFPFW10_DL_VLAN, OFPXMT12_OFB_VLAN_VID }, + { OFPFW10_DL_SRC, OFPXMT12_OFB_ETH_SRC }, + { OFPFW10_DL_DST, OFPXMT12_OFB_ETH_DST}, + { OFPFW10_DL_TYPE, OFPXMT12_OFB_ETH_TYPE }, + { OFPFW10_NW_PROTO, OFPXMT12_OFB_IP_PROTO }, + { OFPFW10_TP_SRC, OFPXMT12_OFB_TCP_SRC }, + { OFPFW10_TP_DST, OFPXMT12_OFB_TCP_DST }, + { OFPFW10_NW_SRC_MASK, OFPXMT12_OFB_IPV4_SRC }, + { OFPFW10_NW_DST_MASK, OFPXMT12_OFB_IPV4_DST }, + { OFPFW10_DL_VLAN_PCP, OFPXMT12_OFB_VLAN_PCP }, + { OFPFW10_NW_TOS, OFPXMT12_OFB_IP_DSCP }, + }; + + struct ofp10_table_stats *out; + const struct wc_map *p; + + out = ofpbuf_put_zeros(buf, sizeof *out); + out->table_id = in->table_id; + ovs_strlcpy(out->name, in->name, sizeof out->name); + out->wildcards = 0; + for (p = wc_map; p < &wc_map[ARRAY_SIZE(wc_map)]; p++) { + if (in->wildcards & htonll(1ULL << p->mf12)) { + out->wildcards |= htonl(p->wc10); + } + } + out->max_entries = in->max_entries; + out->active_count = in->active_count; + put_32aligned_be64(&out->lookup_count, in->lookup_count); + put_32aligned_be64(&out->matched_count, in->matched_count); +} + +static ovs_be32 +oxm12_to_ofp11_flow_match_fields(ovs_be64 oxm12) +{ + struct map { + enum ofp11_flow_match_fields fmf11; + enum oxm12_ofb_match_fields mf12; + }; + + static const struct map map[] = { + { OFPFMF11_IN_PORT, OFPXMT12_OFB_IN_PORT }, + { OFPFMF11_DL_VLAN, OFPXMT12_OFB_VLAN_VID }, + { OFPFMF11_DL_VLAN_PCP, OFPXMT12_OFB_VLAN_PCP }, + { OFPFMF11_DL_TYPE, OFPXMT12_OFB_ETH_TYPE }, + { OFPFMF11_NW_TOS, OFPXMT12_OFB_IP_DSCP }, + { OFPFMF11_NW_PROTO, OFPXMT12_OFB_IP_PROTO }, + { OFPFMF11_TP_SRC, OFPXMT12_OFB_TCP_SRC }, + { OFPFMF11_TP_DST, OFPXMT12_OFB_TCP_DST }, + { OFPFMF11_MPLS_LABEL, OFPXMT12_OFB_MPLS_LABEL }, + { OFPFMF11_MPLS_TC, OFPXMT12_OFB_MPLS_TC }, + /* I don't know what OFPFMF11_TYPE means. */ + { OFPFMF11_DL_SRC, OFPXMT12_OFB_ETH_SRC }, + { OFPFMF11_DL_DST, OFPXMT12_OFB_ETH_DST }, + { OFPFMF11_NW_SRC, OFPXMT12_OFB_IPV4_SRC }, + { OFPFMF11_NW_DST, OFPXMT12_OFB_IPV4_DST }, + { OFPFMF11_METADATA, OFPXMT12_OFB_METADATA }, + }; + + const struct map *p; + uint32_t fmf11; + + fmf11 = 0; + for (p = map; p < &map[ARRAY_SIZE(map)]; p++) { + if (oxm12 & htonll(1ULL << p->mf12)) { + fmf11 |= p->fmf11; + } + } + return htonl(fmf11); +} + +static void +ofputil_put_ofp11_table_stats(const struct ofp12_table_stats *in, + struct ofpbuf *buf) +{ + struct ofp11_table_stats *out; + + out = ofpbuf_put_zeros(buf, sizeof *out); + out->table_id = in->table_id; + ovs_strlcpy(out->name, in->name, sizeof out->name); + out->wildcards = oxm12_to_ofp11_flow_match_fields(in->wildcards); + out->match = oxm12_to_ofp11_flow_match_fields(in->match); + out->instructions = in->instructions; + out->write_actions = in->write_actions; + out->apply_actions = in->apply_actions; + out->config = in->config; + out->max_entries = in->max_entries; + out->active_count = in->active_count; + out->lookup_count = in->lookup_count; + out->matched_count = in->matched_count; +} + +static void +ofputil_put_ofp13_table_stats(const struct ofp12_table_stats *in, + struct ofpbuf *buf) +{ + struct ofp13_table_stats *out; + + /* OF 1.3 splits table features off the ofp_table_stats, + * so there is not much here. */ + + out = ofpbuf_put_uninit(buf, sizeof *out); + out->table_id = in->table_id; + out->active_count = in->active_count; + out->lookup_count = in->lookup_count; + out->matched_count = in->matched_count; +} + +struct ofpbuf * +ofputil_encode_table_stats_reply(const struct ofp12_table_stats stats[], int n, + const struct ofp_header *request) +{ + struct ofpbuf *reply; + int i; + + reply = ofpraw_alloc_stats_reply(request, n * sizeof *stats); + + switch ((enum ofp_version) request->version) { + case OFP10_VERSION: + for (i = 0; i < n; i++) { + ofputil_put_ofp10_table_stats(&stats[i], reply); + } + break; + + case OFP11_VERSION: + for (i = 0; i < n; i++) { + ofputil_put_ofp11_table_stats(&stats[i], reply); + } + break; + + case OFP12_VERSION: + ofpbuf_put(reply, stats, n * sizeof *stats); + break; + + case OFP13_VERSION: + for (i = 0; i < n; i++) { + ofputil_put_ofp13_table_stats(&stats[i], reply); + } + break; + + default: + NOT_REACHED(); + } + + return reply; +} + /* ofputil_flow_monitor_request */ /* Converts an NXST_FLOW_MONITOR request in 'msg' into an abstract @@ -2764,8 +3665,7 @@ ofputil_decode_flow_monitor_request(struct ofputil_flow_monitor_request *rq, 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); + return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL, NULL); } void @@ -2794,7 +3694,7 @@ ofputil_append_flow_monitor_request( /* 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. + * initialized update->match to point to space allocated for a match. * * Uses 'ofpacts' to store the abstract OFPACT_* version of the update's * actions (except for NXFME_ABBREV, which never includes actions). The caller @@ -2868,9 +3768,9 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update, update->hard_timeout = ntohs(nfuf->hard_timeout); update->table_id = nfuf->table_id; update->cookie = nfuf->cookie; + update->priority = ntohs(nfuf->priority); - error = nx_pull_match(msg, match_len, ntohs(nfuf->priority), - update->match, NULL, NULL); + error = nx_pull_match(msg, match_len, update->match, NULL, NULL); if (error) { return error; } @@ -2888,7 +3788,7 @@ ofputil_decode_flow_update(struct ofputil_flow_update *update, VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW_MONITOR reply has bad event %"PRIu16, ntohs(nfuh->event)); - return OFPERR_OFPET_BAD_REQUEST; + return OFPERR_NXBRC_FM_BAD_EVENT; } bad_len: @@ -2955,7 +3855,7 @@ ofputil_append_flow_update(const struct ofputil_flow_update *update, nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf); nfuf->reason = htons(update->reason); - nfuf->priority = htons(update->match->priority); + nfuf->priority = htons(update->priority); nfuf->idle_timeout = htons(update->idle_timeout); nfuf->hard_timeout = htons(update->hard_timeout); nfuf->match_len = htons(match_len); @@ -2971,10 +3871,10 @@ ofputil_append_flow_update(const struct ofputil_flow_update *update, } struct ofpbuf * -ofputil_encode_packet_out(const struct ofputil_packet_out *po) +ofputil_encode_packet_out(const struct ofputil_packet_out *po, + enum ofputil_protocol protocol) { - struct ofp_packet_out *opo; - size_t actions_ofs; + enum ofp_version ofp_version = ofputil_protocol_to_ofp_version(protocol); struct ofpbuf *msg; size_t size; @@ -2983,15 +3883,43 @@ ofputil_encode_packet_out(const struct ofputil_packet_out *po) size += po->packet_len; } - msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size); - ofpbuf_put_zeros(msg, sizeof *opo); - actions_ofs = msg->size; - ofpacts_put_openflow10(po->ofpacts, po->ofpacts_len, msg); + switch (ofp_version) { + case OFP10_VERSION: { + struct ofp10_packet_out *opo; + size_t actions_ofs; + + msg = ofpraw_alloc(OFPRAW_OFPT10_PACKET_OUT, OFP10_VERSION, size); + ofpbuf_put_zeros(msg, sizeof *opo); + actions_ofs = msg->size; + ofpacts_put_openflow10(po->ofpacts, po->ofpacts_len, msg); + + opo = msg->l3; + opo->buffer_id = htonl(po->buffer_id); + opo->in_port = htons(po->in_port); + opo->actions_len = htons(msg->size - actions_ofs); + break; + } + + case OFP11_VERSION: + case OFP12_VERSION: + case OFP13_VERSION: { + struct ofp11_packet_out *opo; + size_t len; + + msg = ofpraw_alloc(OFPRAW_OFPT11_PACKET_OUT, ofp_version, size); + ofpbuf_put_zeros(msg, sizeof *opo); + len = ofpacts_put_openflow11_actions(po->ofpacts, po->ofpacts_len, msg); + + opo = msg->l3; + opo->buffer_id = htonl(po->buffer_id); + opo->in_port = ofputil_port_to_ofp11(po->in_port); + opo->actions_len = htons(len); + break; + } - opo = msg->l3; - opo->buffer_id = htonl(po->buffer_id); - opo->in_port = htons(po->in_port); - opo->actions_len = htons(msg->size - actions_ofs); + default: + NOT_REACHED(); + } if (po->buffer_id == UINT32_MAX) { ofpbuf_put(msg, po->packet, po->packet_len); @@ -3032,6 +3960,7 @@ ofputil_encode_barrier_request(enum ofp_version ofp_version) enum ofpraw type; switch (ofp_version) { + case OFP13_VERSION: case OFP12_VERSION: case OFP11_VERSION: type = OFPRAW_OFPT11_BARRIER_REQUEST; @@ -3149,38 +4078,83 @@ ofputil_check_output_port(uint16_t port, int max_ports) OFPUTIL_NAMED_PORT(ALL) \ OFPUTIL_NAMED_PORT(CONTROLLER) \ OFPUTIL_NAMED_PORT(LOCAL) \ + OFPUTIL_NAMED_PORT(ANY) + +/* For backwards compatibility, so that "none" is recognized as OFPP_ANY */ +#define OFPUTIL_NAMED_PORTS_WITH_NONE \ + OFPUTIL_NAMED_PORTS \ OFPUTIL_NAMED_PORT(NONE) -/* Checks whether 's' is the string representation of an OpenFlow port number, - * either as an integer or a string name (e.g. "LOCAL"). If it is, stores the - * number in '*port' and returns true. Otherwise, returns false. */ +/* Stores the port number represented by 's' into '*portp'. 's' may be an + * integer or, for reserved ports, the standard OpenFlow name for the port + * (e.g. "LOCAL"). + * + * Returns true if successful, false if 's' is not a valid OpenFlow port number + * or name. The caller should issue an error message in this case, because + * this function usually does not. (This gives the caller an opportunity to + * look up the port name another way, e.g. by contacting the switch and listing + * the names of all its ports). + * + * This function accepts OpenFlow 1.0 port numbers. It also accepts a subset + * of OpenFlow 1.1+ port numbers, mapping those port numbers into the 16-bit + * range as described in include/openflow/openflow-1.1.h. */ bool -ofputil_port_from_string(const char *name, uint16_t *port) +ofputil_port_from_string(const char *s, uint16_t *portp) { - struct pair { - const char *name; - uint16_t value; - }; - static const struct pair pairs[] = { -#define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME}, - OFPUTIL_NAMED_PORTS -#undef OFPUTIL_NAMED_PORT - }; - static const int n_pairs = ARRAY_SIZE(pairs); - int i; + unsigned int port32; - if (str_to_int(name, 0, &i) && i >= 0 && i < UINT16_MAX) { - *port = i; - return true; - } + *portp = 0; + if (str_to_uint(s, 10, &port32)) { + if (port32 < OFPP_MAX) { + *portp = port32; + return true; + } else if (port32 < OFPP_FIRST_RESV) { + VLOG_WARN("port %u is a reserved OF1.0 port number that will " + "be translated to %u when talking to an OF1.1 or " + "later controller", port32, port32 + OFPP11_OFFSET); + *portp = port32; + return true; + } else if (port32 <= OFPP_LAST_RESV) { + struct ds s; + + ds_init(&s); + ofputil_format_port(port32, &s); + VLOG_WARN_ONCE("referring to port %s as %u is deprecated for " + "compatibility with future versions of OpenFlow", + ds_cstr(&s), port32); + ds_destroy(&s); - for (i = 0; i < n_pairs; i++) { - if (!strcasecmp(name, pairs[i].name)) { - *port = pairs[i].value; + *portp = port32; return true; + } else if (port32 < OFPP11_MAX) { + VLOG_WARN("port %u is outside the supported range 0 through " + "%"PRIx16" or 0x%x through 0x%"PRIx32, port32, + UINT16_MAX, (unsigned int) OFPP11_MAX, UINT32_MAX); + return false; + } else { + *portp = port32 - OFPP11_OFFSET; + return true; + } + } else { + struct pair { + const char *name; + uint16_t value; + }; + static const struct pair pairs[] = { +#define OFPUTIL_NAMED_PORT(NAME) {#NAME, OFPP_##NAME}, + OFPUTIL_NAMED_PORTS_WITH_NONE +#undef OFPUTIL_NAMED_PORT + }; + const struct pair *p; + + for (p = pairs; p < &pairs[ARRAY_SIZE(pairs)]; p++) { + if (!strcasecmp(s, p->name)) { + *portp = p->value; + return true; + } } + return false; } - return false; } /* Appends to 's' a string representation of the OpenFlow port number 'port'. @@ -3218,7 +4192,8 @@ ofputil_pull_phy_port(enum ofp_version ofp_version, struct ofpbuf *b, return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF; } case OFP11_VERSION: - case OFP12_VERSION: { + case OFP12_VERSION: + case OFP13_VERSION: { const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op); return op ? ofputil_decode_ofp11_port(pp, op) : EOF; } @@ -3244,9 +4219,9 @@ ofputil_action_code_from_name(const char *name) { static const char *names[OFPUTIL_N_ACTIONS] = { NULL, -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME, -#define OFPAT11_ACTION(ENUM, STRUCT, NAME) NAME, -#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, +#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME, +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, +#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME, #include "ofp-util.def" }; @@ -3272,9 +4247,10 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) case OFPUTIL_ACTION_INVALID: NOT_REACHED(); -#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ +#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \ + case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); -#define OFPAT11_ACTION OFPAT10_ACTION #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf); #include "ofp-util.def" @@ -3298,7 +4274,8 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) ofputil_init_##ENUM(s); \ return s; \ } -#define OFPAT11_ACTION OFPAT10_ACTION +#define OFPAT11_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ + OFPAT10_ACTION(ENUM, STRUCT, NAME) #define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \ void \ ofputil_init_##ENUM(struct STRUCT *s) \ @@ -3319,23 +4296,8 @@ ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf) } #include "ofp-util.def" -/* "Normalizes" the wildcards in 'rule'. That means: - * - * 1. If the type of level N is known, then only the valid fields for that - * level may be specified. For example, ARP does not have a TOS field, - * so nw_tos must be wildcarded if 'rule' specifies an ARP flow. - * Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and - * ipv6_dst (and other fields) must be wildcarded if 'rule' specifies an - * IPv4 flow. - * - * 2. If the type of level N is not known (or not understood by Open - * vSwitch), then no fields at all for that level may be specified. For - * example, Open vSwitch does not understand SCTP, an L4 protocol, so the - * L4 fields tp_src and tp_dst must be wildcarded if 'rule' specifies an - * SCTP flow. - */ -void -ofputil_normalize_rule(struct cls_rule *rule) +static void +ofputil_normalize_match__(struct match *match, bool may_log) { enum { MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */ @@ -3351,72 +4313,72 @@ ofputil_normalize_rule(struct cls_rule *rule) struct flow_wildcards wc; /* Figure out what fields may be matched. */ - if (rule->flow.dl_type == htons(ETH_TYPE_IP)) { + if (match->flow.dl_type == htons(ETH_TYPE_IP)) { may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR; - if (rule->flow.nw_proto == IPPROTO_TCP || - rule->flow.nw_proto == IPPROTO_UDP || - rule->flow.nw_proto == IPPROTO_ICMP) { + if (match->flow.nw_proto == IPPROTO_TCP || + match->flow.nw_proto == IPPROTO_UDP || + match->flow.nw_proto == IPPROTO_ICMP) { may_match |= MAY_TP_ADDR; } - } else if (rule->flow.dl_type == htons(ETH_TYPE_IPV6)) { + } else if (match->flow.dl_type == htons(ETH_TYPE_IPV6)) { may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6; - if (rule->flow.nw_proto == IPPROTO_TCP || - rule->flow.nw_proto == IPPROTO_UDP) { + if (match->flow.nw_proto == IPPROTO_TCP || + match->flow.nw_proto == IPPROTO_UDP) { may_match |= MAY_TP_ADDR; - } else if (rule->flow.nw_proto == IPPROTO_ICMPV6) { + } else if (match->flow.nw_proto == IPPROTO_ICMPV6) { may_match |= MAY_TP_ADDR; - if (rule->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) { + if (match->flow.tp_src == htons(ND_NEIGHBOR_SOLICIT)) { may_match |= MAY_ND_TARGET | MAY_ARP_SHA; - } else if (rule->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) { + } else if (match->flow.tp_src == htons(ND_NEIGHBOR_ADVERT)) { may_match |= MAY_ND_TARGET | MAY_ARP_THA; } } - } else if (rule->flow.dl_type == htons(ETH_TYPE_ARP)) { + } else if (match->flow.dl_type == htons(ETH_TYPE_ARP) || + match->flow.dl_type == htons(ETH_TYPE_RARP)) { may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA; } else { may_match = 0; } /* Clear the fields that may not be matched. */ - wc = rule->wc; + wc = match->wc; if (!(may_match & MAY_NW_ADDR)) { - wc.nw_src_mask = wc.nw_dst_mask = htonl(0); + wc.masks.nw_src = wc.masks.nw_dst = htonl(0); } if (!(may_match & MAY_TP_ADDR)) { - wc.tp_src_mask = wc.tp_dst_mask = htons(0); + wc.masks.tp_src = wc.masks.tp_dst = htons(0); } if (!(may_match & MAY_NW_PROTO)) { - wc.wildcards |= FWW_NW_PROTO; + wc.masks.nw_proto = 0; } if (!(may_match & MAY_IPVx)) { - wc.wildcards |= FWW_NW_DSCP; - wc.wildcards |= FWW_NW_ECN; - wc.wildcards |= FWW_NW_TTL; + wc.masks.nw_tos = 0; + wc.masks.nw_ttl = 0; } if (!(may_match & MAY_ARP_SHA)) { - memset(wc.arp_sha_mask, 0, ETH_ADDR_LEN); + memset(wc.masks.arp_sha, 0, ETH_ADDR_LEN); } if (!(may_match & MAY_ARP_THA)) { - memset(wc.arp_tha_mask, 0, ETH_ADDR_LEN); + memset(wc.masks.arp_tha, 0, ETH_ADDR_LEN); } if (!(may_match & MAY_IPV6)) { - wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any; - wc.ipv6_label_mask = htonl(0); + wc.masks.ipv6_src = wc.masks.ipv6_dst = in6addr_any; + wc.masks.ipv6_label = htonl(0); } if (!(may_match & MAY_ND_TARGET)) { - wc.nd_target_mask = in6addr_any; + wc.masks.nd_target = in6addr_any; } /* Log any changes. */ - if (!flow_wildcards_equal(&wc, &rule->wc)) { - bool log = !VLOG_DROP_INFO(&bad_ofmsg_rl); - char *pre = log ? cls_rule_to_string(rule) : NULL; + if (!flow_wildcards_equal(&wc, &match->wc)) { + bool log = may_log && !VLOG_DROP_INFO(&bad_ofmsg_rl); + char *pre = log ? match_to_string(match, OFP_DEFAULT_PRIORITY) : NULL; - rule->wc = wc; - cls_rule_zero_wildcarded_fields(rule); + match->wc = wc; + match_zero_wildcarded_fields(match); if (log) { - char *post = cls_rule_to_string(rule); + char *post = match_to_string(match, OFP_DEFAULT_PRIORITY); VLOG_INFO("normalization changed ofp_match, details:"); VLOG_INFO(" pre: %s", pre); VLOG_INFO("post: %s", post); @@ -3426,6 +4388,39 @@ ofputil_normalize_rule(struct cls_rule *rule) } } +/* "Normalizes" the wildcards in 'match'. That means: + * + * 1. If the type of level N is known, then only the valid fields for that + * level may be specified. For example, ARP does not have a TOS field, + * so nw_tos must be wildcarded if 'match' specifies an ARP flow. + * Similarly, IPv4 does not have any IPv6 addresses, so ipv6_src and + * ipv6_dst (and other fields) must be wildcarded if 'match' specifies an + * IPv4 flow. + * + * 2. If the type of level N is not known (or not understood by Open + * vSwitch), then no fields at all for that level may be specified. For + * example, Open vSwitch does not understand SCTP, an L4 protocol, so the + * L4 fields tp_src and tp_dst must be wildcarded if 'match' specifies an + * SCTP flow. + * + * If this function changes 'match', it logs a rate-limited informational + * message. */ +void +ofputil_normalize_match(struct match *match) +{ + ofputil_normalize_match__(match, true); +} + +/* Same as ofputil_normalize_match() without the logging. Thus, this function + * is suitable for a program's internal use, whereas ofputil_normalize_match() + * sense for use on flows received from elsewhere (so that a bug in the program + * that sent them can be reported and corrected). */ +void +ofputil_normalize_match_quiet(struct match *match) +{ + ofputil_normalize_match__(match, false); +} + /* Parses a key or a key-value pair from '*stringp'. * * On success: Stores the key into '*keyp'. Stores the value, if present, into @@ -3495,3 +4490,542 @@ ofputil_parse_key_value(char **stringp, char **keyp, char **valuep) *valuep = value; return true; } + +/* Encode a dump ports request for 'port', the encoded message + * will be for Open Flow version 'ofp_version'. Returns message + * as a struct ofpbuf. Returns encoded message on success, NULL on error */ +struct ofpbuf * +ofputil_encode_dump_ports_request(enum ofp_version ofp_version, int16_t port) +{ + struct ofpbuf *request; + + switch (ofp_version) { + case OFP10_VERSION: { + struct ofp10_port_stats_request *req; + request = ofpraw_alloc(OFPRAW_OFPST10_PORT_REQUEST, ofp_version, 0); + req = ofpbuf_put_zeros(request, sizeof *req); + req->port_no = htons(port); + break; + } + case OFP11_VERSION: + case OFP12_VERSION: + case OFP13_VERSION: { + struct ofp11_port_stats_request *req; + request = ofpraw_alloc(OFPRAW_OFPST11_PORT_REQUEST, ofp_version, 0); + req = ofpbuf_put_zeros(request, sizeof *req); + req->port_no = ofputil_port_to_ofp11(port); + break; + } + default: + NOT_REACHED(); + } + + return request; +} + +static void +ofputil_port_stats_to_ofp10(const struct ofputil_port_stats *ops, + struct ofp10_port_stats *ps10) +{ + ps10->port_no = htons(ops->port_no); + memset(ps10->pad, 0, sizeof ps10->pad); + put_32aligned_be64(&ps10->rx_packets, htonll(ops->stats.rx_packets)); + put_32aligned_be64(&ps10->tx_packets, htonll(ops->stats.tx_packets)); + put_32aligned_be64(&ps10->rx_bytes, htonll(ops->stats.rx_bytes)); + put_32aligned_be64(&ps10->tx_bytes, htonll(ops->stats.tx_bytes)); + put_32aligned_be64(&ps10->rx_dropped, htonll(ops->stats.rx_dropped)); + put_32aligned_be64(&ps10->tx_dropped, htonll(ops->stats.tx_dropped)); + put_32aligned_be64(&ps10->rx_errors, htonll(ops->stats.rx_errors)); + put_32aligned_be64(&ps10->tx_errors, htonll(ops->stats.tx_errors)); + put_32aligned_be64(&ps10->rx_frame_err, htonll(ops->stats.rx_frame_errors)); + put_32aligned_be64(&ps10->rx_over_err, htonll(ops->stats.rx_over_errors)); + put_32aligned_be64(&ps10->rx_crc_err, htonll(ops->stats.rx_crc_errors)); + put_32aligned_be64(&ps10->collisions, htonll(ops->stats.collisions)); +} + +static void +ofputil_port_stats_to_ofp11(const struct ofputil_port_stats *ops, + struct ofp11_port_stats *ps11) +{ + ps11->port_no = ofputil_port_to_ofp11(ops->port_no); + memset(ps11->pad, 0, sizeof ps11->pad); + ps11->rx_packets = htonll(ops->stats.rx_packets); + ps11->tx_packets = htonll(ops->stats.tx_packets); + ps11->rx_bytes = htonll(ops->stats.rx_bytes); + ps11->tx_bytes = htonll(ops->stats.tx_bytes); + ps11->rx_dropped = htonll(ops->stats.rx_dropped); + ps11->tx_dropped = htonll(ops->stats.tx_dropped); + ps11->rx_errors = htonll(ops->stats.rx_errors); + ps11->tx_errors = htonll(ops->stats.tx_errors); + ps11->rx_frame_err = htonll(ops->stats.rx_frame_errors); + ps11->rx_over_err = htonll(ops->stats.rx_over_errors); + ps11->rx_crc_err = htonll(ops->stats.rx_crc_errors); + ps11->collisions = htonll(ops->stats.collisions); +} + +static void +ofputil_port_stats_to_ofp13(const struct ofputil_port_stats *ops, + struct ofp13_port_stats *ps13) +{ + ofputil_port_stats_to_ofp11(ops, &ps13->ps); + + /* OF 1.3 adds duration fields */ + /* FIXME: Need to implement port alive duration (sec + nsec) */ + ps13->duration_sec = htonl(~0); + ps13->duration_nsec = htonl(~0); +} + + +/* Encode a ports stat for 'ops' and append it to 'replies'. */ +void +ofputil_append_port_stat(struct list *replies, + const struct ofputil_port_stats *ops) +{ + struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); + struct ofp_header *oh = msg->data; + + switch ((enum ofp_version)oh->version) { + case OFP13_VERSION: { + struct ofp13_port_stats *reply = ofpmp_append(replies, sizeof *reply); + ofputil_port_stats_to_ofp13(ops, reply); + break; + } + case OFP12_VERSION: + case OFP11_VERSION: { + struct ofp11_port_stats *reply = ofpmp_append(replies, sizeof *reply); + ofputil_port_stats_to_ofp11(ops, reply); + break; + } + + case OFP10_VERSION: { + struct ofp10_port_stats *reply = ofpmp_append(replies, sizeof *reply); + ofputil_port_stats_to_ofp10(ops, reply); + break; + } + + default: + NOT_REACHED(); + } +} + +static enum ofperr +ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops, + const struct ofp10_port_stats *ps10) +{ + memset(ops, 0, sizeof *ops); + + ops->port_no = ntohs(ps10->port_no); + ops->stats.rx_packets = ntohll(get_32aligned_be64(&ps10->rx_packets)); + ops->stats.tx_packets = ntohll(get_32aligned_be64(&ps10->tx_packets)); + ops->stats.rx_bytes = ntohll(get_32aligned_be64(&ps10->rx_bytes)); + ops->stats.tx_bytes = ntohll(get_32aligned_be64(&ps10->tx_bytes)); + ops->stats.rx_dropped = ntohll(get_32aligned_be64(&ps10->rx_dropped)); + ops->stats.tx_dropped = ntohll(get_32aligned_be64(&ps10->tx_dropped)); + ops->stats.rx_errors = ntohll(get_32aligned_be64(&ps10->rx_errors)); + ops->stats.tx_errors = ntohll(get_32aligned_be64(&ps10->tx_errors)); + ops->stats.rx_frame_errors = + ntohll(get_32aligned_be64(&ps10->rx_frame_err)); + ops->stats.rx_over_errors = ntohll(get_32aligned_be64(&ps10->rx_over_err)); + ops->stats.rx_crc_errors = ntohll(get_32aligned_be64(&ps10->rx_crc_err)); + ops->stats.collisions = ntohll(get_32aligned_be64(&ps10->collisions)); + + return 0; +} + +static enum ofperr +ofputil_port_stats_from_ofp11(struct ofputil_port_stats *ops, + const struct ofp11_port_stats *ps11) +{ + enum ofperr error; + + memset(ops, 0, sizeof *ops); + error = ofputil_port_from_ofp11(ps11->port_no, &ops->port_no); + if (error) { + return error; + } + + ops->stats.rx_packets = ntohll(ps11->rx_packets); + ops->stats.tx_packets = ntohll(ps11->tx_packets); + ops->stats.rx_bytes = ntohll(ps11->rx_bytes); + ops->stats.tx_bytes = ntohll(ps11->tx_bytes); + ops->stats.rx_dropped = ntohll(ps11->rx_dropped); + ops->stats.tx_dropped = ntohll(ps11->tx_dropped); + ops->stats.rx_errors = ntohll(ps11->rx_errors); + ops->stats.tx_errors = ntohll(ps11->tx_errors); + ops->stats.rx_frame_errors = ntohll(ps11->rx_frame_err); + ops->stats.rx_over_errors = ntohll(ps11->rx_over_err); + ops->stats.rx_crc_errors = ntohll(ps11->rx_crc_err); + ops->stats.collisions = ntohll(ps11->collisions); + + return 0; +} + +static enum ofperr +ofputil_port_stats_from_ofp13(struct ofputil_port_stats *ops, + const struct ofp13_port_stats *ps13) +{ + enum ofperr error = + ofputil_port_stats_from_ofp11(ops, &ps13->ps); + if (!error) { + /* FIXME: Get ps13->duration_sec and ps13->duration_nsec, + * Add to netdev_stats? */ + } + + return error; +} + + +/* Returns the number of port stats elements in OFPTYPE_PORT_STATS_REPLY + * message 'oh'. */ +size_t +ofputil_count_port_stats(const struct ofp_header *oh) +{ + struct ofpbuf b; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + ofpraw_pull_assert(&b); + + BUILD_ASSERT(sizeof(struct ofp10_port_stats) == + sizeof(struct ofp11_port_stats)); + return b.size / sizeof(struct ofp10_port_stats); +} + +/* Converts an OFPST_PORT_STATS reply in 'msg' into an abstract + * ofputil_port_stats in 'ps'. + * + * Multiple OFPST_PORT_STATS replies can be packed into a single OpenFlow + * message. Calling this function multiple times for a single 'msg' iterates + * through the replies. The caller must initially leave 'msg''s layer pointers + * null and not modify them between calls. + * + * Returns 0 if successful, EOF if no replies were left in this 'msg', + * otherwise a positive errno value. */ +int +ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg) +{ + enum ofperr error; + enum ofpraw raw; + + error = (msg->l2 + ? ofpraw_decode(&raw, msg->l2) + : ofpraw_pull(&raw, msg)); + if (error) { + return error; + } + + if (!msg->size) { + return EOF; + } else if (raw == OFPRAW_OFPST13_PORT_REPLY) { + const struct ofp13_port_stats *ps13; + + ps13 = ofpbuf_try_pull(msg, sizeof *ps13); + if (!ps13) { + goto bad_len; + } + return ofputil_port_stats_from_ofp13(ps, ps13); + } else if (raw == OFPRAW_OFPST11_PORT_REPLY) { + const struct ofp11_port_stats *ps11; + + ps11 = ofpbuf_try_pull(msg, sizeof *ps11); + if (!ps11) { + goto bad_len; + } + return ofputil_port_stats_from_ofp11(ps, ps11); + } else if (raw == OFPRAW_OFPST10_PORT_REPLY) { + const struct ofp10_port_stats *ps10; + + ps10 = ofpbuf_try_pull(msg, sizeof *ps10); + if (!ps10) { + goto bad_len; + } + return ofputil_port_stats_from_ofp10(ps, ps10); + } else { + NOT_REACHED(); + } + + bad_len: + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_PORT reply has %zu leftover " + "bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; +} + +/* Parse a port status request message into a 16 bit OpenFlow 1.0 + * port number and stores the latter in '*ofp10_port'. + * Returns 0 if successful, otherwise an OFPERR_* number. */ +enum ofperr +ofputil_decode_port_stats_request(const struct ofp_header *request, + uint16_t *ofp10_port) +{ + switch ((enum ofp_version)request->version) { + case OFP13_VERSION: + case OFP12_VERSION: + case OFP11_VERSION: { + const struct ofp11_port_stats_request *psr11 = ofpmsg_body(request); + return ofputil_port_from_ofp11(psr11->port_no, ofp10_port); + } + + case OFP10_VERSION: { + const struct ofp10_port_stats_request *psr10 = ofpmsg_body(request); + *ofp10_port = ntohs(psr10->port_no); + return 0; + } + + default: + NOT_REACHED(); + } +} + +/* Parse a queue status request message into 'oqsr'. + * Returns 0 if successful, otherwise an OFPERR_* number. */ +enum ofperr +ofputil_decode_queue_stats_request(const struct ofp_header *request, + struct ofputil_queue_stats_request *oqsr) +{ + switch ((enum ofp_version)request->version) { + case OFP13_VERSION: + case OFP12_VERSION: + case OFP11_VERSION: { + const struct ofp11_queue_stats_request *qsr11 = ofpmsg_body(request); + oqsr->queue_id = ntohl(qsr11->queue_id); + return ofputil_port_from_ofp11(qsr11->port_no, &oqsr->port_no); + } + + case OFP10_VERSION: { + const struct ofp10_queue_stats_request *qsr10 = ofpmsg_body(request); + oqsr->queue_id = ntohl(qsr10->queue_id); + oqsr->port_no = ntohs(qsr10->port_no); + /* OF 1.0 uses OFPP_ALL for OFPP_ANY */ + if (oqsr->port_no == OFPP_ALL) { + oqsr->port_no = OFPP_ANY; + } + return 0; + } + + default: + NOT_REACHED(); + } +} + +/* Encode a queue statsrequest for 'oqsr', the encoded message + * will be fore Open Flow version 'ofp_version'. Returns message + * as a struct ofpbuf. Returns encoded message on success, NULL on error */ +struct ofpbuf * +ofputil_encode_queue_stats_request(enum ofp_version ofp_version, + const struct ofputil_queue_stats_request *oqsr) +{ + struct ofpbuf *request; + + switch (ofp_version) { + case OFP11_VERSION: + case OFP12_VERSION: + case OFP13_VERSION: { + struct ofp11_queue_stats_request *req; + request = ofpraw_alloc(OFPRAW_OFPST11_QUEUE_REQUEST, ofp_version, 0); + req = ofpbuf_put_zeros(request, sizeof *req); + req->port_no = ofputil_port_to_ofp11(oqsr->port_no); + req->queue_id = htonl(oqsr->queue_id); + break; + } + case OFP10_VERSION: { + struct ofp10_queue_stats_request *req; + request = ofpraw_alloc(OFPRAW_OFPST10_QUEUE_REQUEST, ofp_version, 0); + req = ofpbuf_put_zeros(request, sizeof *req); + /* OpenFlow 1.0 needs OFPP_ALL instead of OFPP_ANY */ + req->port_no = htons(oqsr->port_no == OFPP_ANY + ? OFPP_ALL : oqsr->port_no); + req->queue_id = htonl(oqsr->queue_id); + break; + } + default: + NOT_REACHED(); + } + + return request; +} + +/* Returns the number of queue stats elements in OFPTYPE_QUEUE_STATS_REPLY + * message 'oh'. */ +size_t +ofputil_count_queue_stats(const struct ofp_header *oh) +{ + struct ofpbuf b; + + ofpbuf_use_const(&b, oh, ntohs(oh->length)); + ofpraw_pull_assert(&b); + + BUILD_ASSERT(sizeof(struct ofp10_queue_stats) == + sizeof(struct ofp11_queue_stats)); + return b.size / sizeof(struct ofp10_queue_stats); +} + +static enum ofperr +ofputil_queue_stats_from_ofp10(struct ofputil_queue_stats *oqs, + const struct ofp10_queue_stats *qs10) +{ + oqs->port_no = ntohs(qs10->port_no); + oqs->queue_id = ntohl(qs10->queue_id); + oqs->stats.tx_bytes = ntohll(get_32aligned_be64(&qs10->tx_bytes)); + oqs->stats.tx_packets = ntohll(get_32aligned_be64(&qs10->tx_packets)); + oqs->stats.tx_errors = ntohll(get_32aligned_be64(&qs10->tx_errors)); + + return 0; +} + +static enum ofperr +ofputil_queue_stats_from_ofp11(struct ofputil_queue_stats *oqs, + const struct ofp11_queue_stats *qs11) +{ + enum ofperr error; + + error = ofputil_port_from_ofp11(qs11->port_no, &oqs->port_no); + if (error) { + return error; + } + + oqs->queue_id = ntohl(qs11->queue_id); + oqs->stats.tx_bytes = ntohll(qs11->tx_bytes); + oqs->stats.tx_packets = ntohll(qs11->tx_packets); + oqs->stats.tx_errors = ntohll(qs11->tx_errors); + + return 0; +} + +static enum ofperr +ofputil_queue_stats_from_ofp13(struct ofputil_queue_stats *oqs, + const struct ofp13_queue_stats *qs13) +{ + enum ofperr error + = ofputil_queue_stats_from_ofp11(oqs, &qs13->qs); + if (!error) { + /* FIXME: Get qs13->duration_sec and qs13->duration_nsec, + * Add to netdev_queue_stats? */ + } + + return error; +} + +/* Converts an OFPST_QUEUE_STATS reply in 'msg' into an abstract + * ofputil_queue_stats in 'qs'. + * + * Multiple OFPST_QUEUE_STATS replies can be packed into a single OpenFlow + * message. Calling this function multiple times for a single 'msg' iterates + * through the replies. The caller must initially leave 'msg''s layer pointers + * null and not modify them between calls. + * + * Returns 0 if successful, EOF if no replies were left in this 'msg', + * otherwise a positive errno value. */ +int +ofputil_decode_queue_stats(struct ofputil_queue_stats *qs, struct ofpbuf *msg) +{ + enum ofperr error; + enum ofpraw raw; + + error = (msg->l2 + ? ofpraw_decode(&raw, msg->l2) + : ofpraw_pull(&raw, msg)); + if (error) { + return error; + } + + if (!msg->size) { + return EOF; + } else if (raw == OFPRAW_OFPST13_QUEUE_REPLY) { + const struct ofp13_queue_stats *qs13; + + qs13 = ofpbuf_try_pull(msg, sizeof *qs13); + if (!qs13) { + goto bad_len; + } + return ofputil_queue_stats_from_ofp13(qs, qs13); + } else if (raw == OFPRAW_OFPST11_QUEUE_REPLY) { + const struct ofp11_queue_stats *qs11; + + qs11 = ofpbuf_try_pull(msg, sizeof *qs11); + if (!qs11) { + goto bad_len; + } + return ofputil_queue_stats_from_ofp11(qs, qs11); + } else if (raw == OFPRAW_OFPST10_QUEUE_REPLY) { + const struct ofp10_queue_stats *qs10; + + qs10 = ofpbuf_try_pull(msg, sizeof *qs10); + if (!qs10) { + goto bad_len; + } + return ofputil_queue_stats_from_ofp10(qs, qs10); + } else { + NOT_REACHED(); + } + + bad_len: + VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_QUEUE reply has %zu leftover " + "bytes at end", msg->size); + return OFPERR_OFPBRC_BAD_LEN; +} + +static void +ofputil_queue_stats_to_ofp10(const struct ofputil_queue_stats *oqs, + struct ofp10_queue_stats *qs10) +{ + qs10->port_no = htons(oqs->port_no); + memset(qs10->pad, 0, sizeof qs10->pad); + qs10->queue_id = htonl(oqs->queue_id); + put_32aligned_be64(&qs10->tx_bytes, htonll(oqs->stats.tx_bytes)); + put_32aligned_be64(&qs10->tx_packets, htonll(oqs->stats.tx_packets)); + put_32aligned_be64(&qs10->tx_errors, htonll(oqs->stats.tx_errors)); +} + +static void +ofputil_queue_stats_to_ofp11(const struct ofputil_queue_stats *oqs, + struct ofp11_queue_stats *qs11) +{ + qs11->port_no = ofputil_port_to_ofp11(oqs->port_no); + qs11->queue_id = htonl(oqs->queue_id); + qs11->tx_bytes = htonll(oqs->stats.tx_bytes); + qs11->tx_packets = htonll(oqs->stats.tx_packets); + qs11->tx_errors = htonll(oqs->stats.tx_errors); +} + +static void +ofputil_queue_stats_to_ofp13(const struct ofputil_queue_stats *oqs, + struct ofp13_queue_stats *qs13) +{ + ofputil_queue_stats_to_ofp11(oqs, &qs13->qs); + /* OF 1.3 adds duration fields */ + /* FIXME: Need to implement queue alive duration (sec + nsec) */ + qs13->duration_sec = htonl(~0); + qs13->duration_nsec = htonl(~0); +} + +/* Encode a queue stat for 'oqs' and append it to 'replies'. */ +void +ofputil_append_queue_stat(struct list *replies, + const struct ofputil_queue_stats *oqs) +{ + struct ofpbuf *msg = ofpbuf_from_list(list_back(replies)); + struct ofp_header *oh = msg->data; + + switch ((enum ofp_version)oh->version) { + case OFP13_VERSION: { + struct ofp13_queue_stats *reply = ofpmp_append(replies, sizeof *reply); + ofputil_queue_stats_to_ofp13(oqs, reply); + break; + } + + case OFP12_VERSION: + case OFP11_VERSION: { + struct ofp11_queue_stats *reply = ofpmp_append(replies, sizeof *reply); + ofputil_queue_stats_to_ofp11(oqs, reply); + break; + } + + case OFP10_VERSION: { + struct ofp10_queue_stats *reply = ofpmp_append(replies, sizeof *reply); + ofputil_queue_stats_to_ofp10(oqs, reply); + break; + } + + default: + NOT_REACHED(); + } +}