/*
- * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (c) 2008-2017 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
#include <config.h>
-#include "ofp-print.h"
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <netinet/in.h>
#include <netinet/icmp6.h>
#include <stdlib.h>
+#include "bitmap.h"
#include "bundle.h"
#include "byte-order.h"
#include "classifier.h"
#include "netdev.h"
#include "nx-match.h"
#include "id-pool.h"
-#include "ofp-actions.h"
-#include "ofp-prop.h"
#include "openflow/netronome-ext.h"
#include "openvswitch/dynamic-string.h"
+#include "openvswitch/json.h"
#include "openvswitch/meta-flow.h"
+#include "openvswitch/ofp-actions.h"
#include "openvswitch/ofp-errors.h"
#include "openvswitch/ofp-msgs.h"
+#include "openvswitch/ofp-print.h"
+#include "openvswitch/ofp-prop.h"
#include "openvswitch/ofp-util.h"
#include "openvswitch/ofpbuf.h"
#include "openvswitch/type-props.h"
#include "openvswitch/vlog.h"
+#include "openflow/intel-ext.h"
#include "packets.h"
-#include "pktbuf.h"
#include "random.h"
#include "tun-metadata.h"
#include "unaligned.h"
-#include "bitmap.h"
+#include "util.h"
#include "uuid.h"
VLOG_DEFINE_THIS_MODULE(ofp_util);
void
ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
{
- BUILD_ASSERT_DECL(FLOW_WC_SEQ == 35);
+ BUILD_ASSERT_DECL(FLOW_WC_SEQ == 39);
/* Initialize most of wc. */
flow_wildcards_init_catchall(wc);
/* VLAN TCI mask. */
if (!(ofpfw & OFPFW10_DL_VLAN_PCP)) {
- wc->masks.vlan_tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
+ wc->masks.vlans[0].tci |= htons(VLAN_PCP_MASK | VLAN_CFI);
}
if (!(ofpfw & OFPFW10_DL_VLAN)) {
- wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
+ wc->masks.vlans[0].tci |= htons(VLAN_VID_MASK | VLAN_CFI);
}
}
/* Initialize match->wc. */
memset(&match->flow, 0, sizeof match->flow);
ofputil_wildcard_from_ofpfw10(ofpfw, &match->wc);
+ memset(&match->tun_md, 0, sizeof match->tun_md);
/* Initialize most of match->flow. */
match->flow.nw_src = ofmatch->nw_src;
* 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. */
- match->flow.vlan_tci = htons(0);
- match->wc.masks.vlan_tci = htons(0xffff);
+ match->flow.vlans[0].tci = htons(0);
+ match->wc.masks.vlans[0].tci = htons(0xffff);
} else {
ovs_be16 vid, pcp, tci;
uint16_t hpcp;
hpcp = (ofmatch->dl_vlan_pcp << VLAN_PCP_SHIFT) & VLAN_PCP_MASK;
pcp = htons(hpcp);
tci = vid | pcp | htons(VLAN_CFI);
- match->flow.vlan_tci = tci & match->wc.masks.vlan_tci;
+ match->flow.vlans[0].tci = tci & match->wc.masks.vlans[0].tci;
}
/* Clean up. */
/* Translate VLANs. */
ofmatch->dl_vlan = htons(0);
ofmatch->dl_vlan_pcp = 0;
- if (match->wc.masks.vlan_tci == htons(0)) {
+ if (match->wc.masks.vlans[0].tci == htons(0)) {
ofpfw |= OFPFW10_DL_VLAN | OFPFW10_DL_VLAN_PCP;
- } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI)
- && !(match->flow.vlan_tci & htons(VLAN_CFI))) {
+ } else if (match->wc.masks.vlans[0].tci & htons(VLAN_CFI)
+ && !(match->flow.vlans[0].tci & htons(VLAN_CFI))) {
ofmatch->dl_vlan = htons(OFP10_VLAN_NONE);
} else {
- if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) {
+ if (!(match->wc.masks.vlans[0].tci & htons(VLAN_VID_MASK))) {
ofpfw |= OFPFW10_DL_VLAN;
} else {
- ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci));
+ ofmatch->dl_vlan =
+ htons(vlan_tci_to_vid(match->flow.vlans[0].tci));
}
- if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) {
+ if (!(match->wc.masks.vlans[0].tci & htons(VLAN_PCP_MASK))) {
ofpfw |= OFPFW10_DL_VLAN_PCP;
} else {
- ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci);
+ ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlans[0].tci);
}
}
}
enum ofperr
-ofputil_pull_ofp11_match(struct ofpbuf *buf, struct match *match,
- uint16_t *padded_match_len)
+ofputil_pull_ofp11_match(struct ofpbuf *buf, const struct tun_table *tun_table,
+ const struct vl_mff_map *vl_mff_map,
+ struct match *match, uint16_t *padded_match_len)
{
struct ofp11_match_header *omh = buf->data;
uint16_t match_len;
if (padded_match_len) {
*padded_match_len = ROUND_UP(match_len, 8);
}
- return oxm_pull_match(buf, match);
+ return oxm_pull_match(buf, false, tun_table, vl_mff_map, match);
default:
return OFPERR_OFPBMC_BAD_TYPE;
if (!(wc & OFPFW11_DL_VLAN)) {
if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) {
/* Match only packets without a VLAN tag. */
- match->flow.vlan_tci = htons(0);
- match->wc.masks.vlan_tci = OVS_BE16_MAX;
+ match->flow.vlans[0].tci = htons(0);
+ match->wc.masks.vlans[0].tci = OVS_BE16_MAX;
} else {
if (ofmatch->dl_vlan == htons(OFPVID11_ANY)) {
/* Match any packet with a VLAN tag regardless of VID. */
- match->flow.vlan_tci = htons(VLAN_CFI);
- match->wc.masks.vlan_tci = htons(VLAN_CFI);
+ match->flow.vlans[0].tci = htons(VLAN_CFI);
+ match->wc.masks.vlans[0].tci = htons(VLAN_CFI);
} else if (ntohs(ofmatch->dl_vlan) < 4096) {
/* Match only packets with the specified VLAN VID. */
- match->flow.vlan_tci = htons(VLAN_CFI) | ofmatch->dl_vlan;
- match->wc.masks.vlan_tci = htons(VLAN_CFI | VLAN_VID_MASK);
+ match->flow.vlans[0].tci = htons(VLAN_CFI) | ofmatch->dl_vlan;
+ match->wc.masks.vlans[0].tci = htons(VLAN_CFI | VLAN_VID_MASK);
} else {
/* Invalid VID. */
return OFPERR_OFPBMC_BAD_VALUE;
if (!(wc & OFPFW11_DL_VLAN_PCP)) {
if (ofmatch->dl_vlan_pcp <= 7) {
- match->flow.vlan_tci |= htons(ofmatch->dl_vlan_pcp
+ match->flow.vlans[0].tci |= htons(ofmatch->dl_vlan_pcp
<< VLAN_PCP_SHIFT);
- match->wc.masks.vlan_tci |= htons(VLAN_PCP_MASK);
+ match->wc.masks.vlans[0].tci |= htons(VLAN_PCP_MASK);
} else {
/* Invalid PCP. */
return OFPERR_OFPBMC_BAD_VALUE;
ofmatch->dl_dst = match->flow.dl_dst;
ofmatch->dl_dst_mask = eth_addr_invert(match->wc.masks.dl_dst);
- if (match->wc.masks.vlan_tci == htons(0)) {
+ if (match->wc.masks.vlans[0].tci == htons(0)) {
wc |= OFPFW11_DL_VLAN | OFPFW11_DL_VLAN_PCP;
- } else if (match->wc.masks.vlan_tci & htons(VLAN_CFI)
- && !(match->flow.vlan_tci & htons(VLAN_CFI))) {
+ } else if (match->wc.masks.vlans[0].tci & htons(VLAN_CFI)
+ && !(match->flow.vlans[0].tci & htons(VLAN_CFI))) {
ofmatch->dl_vlan = htons(OFPVID11_NONE);
wc |= OFPFW11_DL_VLAN_PCP;
} else {
- if (!(match->wc.masks.vlan_tci & htons(VLAN_VID_MASK))) {
+ if (!(match->wc.masks.vlans[0].tci & htons(VLAN_VID_MASK))) {
ofmatch->dl_vlan = htons(OFPVID11_ANY);
} else {
- ofmatch->dl_vlan = htons(vlan_tci_to_vid(match->flow.vlan_tci));
+ ofmatch->dl_vlan =
+ htons(vlan_tci_to_vid(match->flow.vlans[0].tci));
}
- if (!(match->wc.masks.vlan_tci & htons(VLAN_PCP_MASK))) {
+ if (!(match->wc.masks.vlans[0].tci & htons(VLAN_PCP_MASK))) {
wc |= OFPFW11_DL_VLAN_PCP;
} else {
- ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlan_tci);
+ ofmatch->dl_vlan_pcp = vlan_tci_to_pcp(match->flow.vlans[0].tci);
}
}
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
case OFPUTIL_P_OF15_OXM:
+ case OFPUTIL_P_OF16_OXM:
return NXM_TYPICAL_LEN;
default:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
case OFPUTIL_P_OF15_OXM:
+ case OFPUTIL_P_OF16_OXM:
return oxm_put_match(b, match,
ofputil_protocol_to_ofp_version(protocol));
}
#define N_PROTO_ABBREVS ARRAY_SIZE(proto_abbrevs)
enum ofputil_protocol ofputil_flow_dump_protocols[] = {
+ OFPUTIL_P_OF16_OXM,
OFPUTIL_P_OF15_OXM,
OFPUTIL_P_OF14_OXM,
OFPUTIL_P_OF13_OXM,
return OFPUTIL_P_OF14_OXM;
case OFP15_VERSION:
return OFPUTIL_P_OF15_OXM;
+ case OFP16_VERSION:
+ return OFPUTIL_P_OF16_OXM;
default:
return 0;
}
return OFP14_VERSION;
case OFPUTIL_P_OF15_OXM:
return OFP15_VERSION;
+ case OFPUTIL_P_OF16_OXM:
+ return OFP16_VERSION;
}
OVS_NOT_REACHED();
case OFPUTIL_P_OF15_OXM:
return OFPUTIL_P_OF15_OXM;
+ case OFPUTIL_P_OF16_OXM:
+ return OFPUTIL_P_OF16_OXM;
+
default:
OVS_NOT_REACHED();
}
case OFPUTIL_P_OF15_OXM:
return ofputil_protocol_set_tid(OFPUTIL_P_OF15_OXM, tid);
+ case OFPUTIL_P_OF16_OXM:
+ return ofputil_protocol_set_tid(OFPUTIL_P_OF16_OXM, tid);
+
default:
OVS_NOT_REACHED();
}
case OFPUTIL_P_OF15_OXM:
return "OXM-OpenFlow15";
+
+ case OFPUTIL_P_OF16_OXM:
+ return "OXM-OpenFlow16";
}
/* Check abbreviations. */
if (!strcasecmp(s, "OpenFlow15")) {
return OFP15_VERSION;
}
+ if (!strcasecmp(s, "OpenFlow16")) {
+ return OFP16_VERSION;
+ }
return 0;
}
return "OpenFlow14";
case OFP15_VERSION:
return "OpenFlow15";
+ case OFP16_VERSION:
+ return "OpenFlow16";
default:
OVS_NOT_REACHED();
}
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
case OFPUTIL_P_OF15_OXM:
+ case OFPUTIL_P_OF16_OXM:
/* There is only one variant of each OpenFlow 1.1+ protocol, and we
* verified above that we're not trying to change versions. */
OVS_NOT_REACHED();
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
const struct ofp_header *oh,
enum ofputil_protocol protocol,
+ const struct tun_table *tun_table,
+ const struct vl_mff_map *vl_mff_map,
struct ofpbuf *ofpacts,
ofp_port_t max_port, uint8_t max_table)
{
ovs_be16 raw_flags;
enum ofperr error;
-
- /* Ignored for non-delete actions */
- fm->delete_reason = OFPRR_DELETE;
-
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
enum ofpraw raw = ofpraw_pull_assert(&b);
if (raw == OFPRAW_OFPT11_FLOW_MOD) {
ofm = ofpbuf_pull(&b, sizeof *ofm);
- error = ofputil_pull_ofp11_match(&b, &fm->match, NULL);
+ error = ofputil_pull_ofp11_match(&b, tun_table, vl_mff_map, &fm->match,
+ NULL);
if (error) {
return error;
}
/* Dissect the message. */
nfm = ofpbuf_pull(&b, sizeof *nfm);
error = nx_pull_match(&b, ntohs(nfm->match_len),
- &fm->match, &fm->cookie, &fm->cookie_mask);
+ &fm->match, &fm->cookie, &fm->cookie_mask,
+ false, tun_table, vl_mff_map);
if (error) {
return error;
}
}
}
+ /* Check for mismatched conntrack original direction tuple address fields
+ * w.r.t. the IP version of the match. */
+ if (((fm->match.wc.masks.ct_nw_src || fm->match.wc.masks.ct_nw_dst)
+ && fm->match.flow.dl_type != htons(ETH_TYPE_IP))
+ || ((ipv6_addr_is_set(&fm->match.wc.masks.ct_ipv6_src)
+ || ipv6_addr_is_set(&fm->match.wc.masks.ct_ipv6_dst))
+ && fm->match.flow.dl_type != htons(ETH_TYPE_IPV6))) {
+ return OFPERR_OFPBAC_MATCH_INCONSISTENT;
+ }
+
if (fm->command > OFPFC_DELETE_STRICT) {
return OFPERR_OFPFMFC_BAD_COMMAND;
}
- error = ofpacts_pull_openflow_instructions(&b, b.size,
- oh->version, ofpacts);
+ fm->ofpacts_tlv_bitmap = 0;
+ error = ofpacts_pull_openflow_instructions(&b, b.size, oh->version,
+ vl_mff_map,
+ &fm->ofpacts_tlv_bitmap,
+ ofpacts);
if (error) {
return error;
}
}
return ofpacts_check_consistency(fm->ofpacts, fm->ofpacts_len,
- &fm->match.flow, max_port,
+ &fm->match, max_port,
fm->table_id, max_table, protocol);
}
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
- case OFPUTIL_P_OF15_OXM: {
+ case OFPUTIL_P_OF15_OXM:
+ case OFPUTIL_P_OF16_OXM: {
struct ofp11_flow_mod *ofm;
int tailroom;
static enum ofperr
ofputil_decode_ofpst11_flow_request(struct ofputil_flow_stats_request *fsr,
- struct ofpbuf *b, bool aggregate)
+ struct ofpbuf *b, bool aggregate,
+ const struct tun_table *tun_table,
+ const struct vl_mff_map *vl_mff_map)
{
const struct ofp11_flow_stats_request *ofsr;
enum ofperr error;
fsr->out_group = ntohl(ofsr->out_group);
fsr->cookie = ofsr->cookie;
fsr->cookie_mask = ofsr->cookie_mask;
- error = ofputil_pull_ofp11_match(b, &fsr->match, NULL);
+ error = ofputil_pull_ofp11_match(b, tun_table, vl_mff_map, &fsr->match,
+ NULL);
if (error) {
return error;
}
static enum ofperr
ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
- struct ofpbuf *b, bool aggregate)
+ struct ofpbuf *b, bool aggregate,
+ const struct tun_table *tun_table,
+ const struct vl_mff_map *vl_mff_map)
{
const struct nx_flow_stats_request *nfsr;
enum ofperr error;
nfsr = ofpbuf_pull(b, sizeof *nfsr);
error = nx_pull_match(b, ntohs(nfsr->match_len), &fsr->match,
- &fsr->cookie, &fsr->cookie_mask);
+ &fsr->cookie, &fsr->cookie_mask, false, tun_table,
+ vl_mff_map);
if (error) {
return error;
}
hdr = ofpbuf_at_assert(msg, 0, sizeof *hdr);
prop_len = ntohs(hdr->len);
- if (prop_len < sizeof *hdr || prop_len > msg->size || prop_len % 8) {
+ if (prop_len < sizeof *hdr || prop_len > len || prop_len % 8) {
return OFPERR_OFPBRC_BAD_LEN;
}
/* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
* request 'oh', into an abstract flow_stats_request in 'fsr'. Returns 0 if
- * successful, otherwise an OpenFlow error code. */
+ * successful, otherwise an OpenFlow error code.
+ *
+ * 'vl_mff_map' is an optional parameter that is used to validate the length
+ * of variable length mf_fields in 'match'. If it is not provided, the
+ * default mf_fields with maximum length will be used. */
enum ofperr
ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
- const struct ofp_header *oh)
+ const struct ofp_header *oh,
+ const struct tun_table *tun_table,
+ const struct vl_mff_map *vl_mff_map)
{
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
enum ofpraw raw = ofpraw_pull_assert(&b);
return ofputil_decode_ofpst10_flow_request(fsr, b.data, true);
case OFPRAW_OFPST11_FLOW_REQUEST:
- return ofputil_decode_ofpst11_flow_request(fsr, &b, false);
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, false, tun_table,
+ vl_mff_map);
case OFPRAW_OFPST11_AGGREGATE_REQUEST:
- return ofputil_decode_ofpst11_flow_request(fsr, &b, true);
+ return ofputil_decode_ofpst11_flow_request(fsr, &b, true, tun_table,
+ vl_mff_map);
case OFPRAW_NXST_FLOW_REQUEST:
- return ofputil_decode_nxst_flow_request(fsr, &b, false);
+ return ofputil_decode_nxst_flow_request(fsr, &b, false, tun_table,
+ vl_mff_map);
case OFPRAW_NXST_AGGREGATE_REQUEST:
- return ofputil_decode_nxst_flow_request(fsr, &b, true);
+ return ofputil_decode_nxst_flow_request(fsr, &b, true, tun_table,
+ vl_mff_map);
default:
/* Hey, the caller lied. */
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
- case OFPUTIL_P_OF15_OXM: {
+ case OFPUTIL_P_OF15_OXM:
+ case OFPUTIL_P_OF16_OXM: {
struct ofp11_flow_stats_request *ofsr;
raw = (fsr->aggregate
return EINVAL;
}
- if (ofputil_pull_ofp11_match(msg, &fs->match, &padded_match_len)) {
+ if (ofputil_pull_ofp11_match(msg, NULL, NULL, &fs->match,
+ &padded_match_len)) {
VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad match");
return EINVAL;
}
"claims invalid length %"PRIuSIZE, match_len, length);
return EINVAL;
}
- if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL)) {
+ if (nx_pull_match(msg, match_len, &fs->match, NULL, NULL, false, NULL,
+ NULL)) {
return EINVAL;
}
instructions_len = length - sizeof *nfs - ROUND_UP(match_len, 8);
}
if (ofpacts_pull_openflow_instructions(msg, instructions_len, oh->version,
- ofpacts)) {
+ NULL, NULL, ofpacts)) {
VLOG_WARN_RL(&bad_ofmsg_rl, "OFPST_FLOW reply bad instructions");
return EINVAL;
}
* have been initialized with ofpmp_init(). */
void
ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
- struct ovs_list *replies)
+ struct ovs_list *replies,
+ const struct tun_table *tun_table)
{
+ struct ofputil_flow_stats *fs_ = CONST_CAST(struct ofputil_flow_stats *,
+ fs);
+ const struct tun_table *orig_tun_table;
struct ofpbuf *reply = ofpbuf_from_list(ovs_list_back(replies));
size_t start_ofs = reply->size;
enum ofp_version version = ofpmp_version(replies);
enum ofpraw raw = ofpmp_decode_raw(replies);
+ orig_tun_table = fs->match.flow.tunnel.metadata.tab;
+ fs_->match.flow.tunnel.metadata.tab = tun_table;
+
if (raw == OFPRAW_OFPST11_FLOW_REPLY || raw == OFPRAW_OFPST13_FLOW_REPLY) {
struct ofp11_flow_stats *ofs;
}
ofpmp_postappend(replies, start_ofs);
+ fs_->match.flow.tunnel.metadata.tab = orig_tun_table;
}
/* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
ofr = ofpbuf_pull(&b, sizeof *ofr);
- error = ofputil_pull_ofp11_match(&b, &fr->match, NULL);
+ error = ofputil_pull_ofp11_match(&b, NULL, NULL, &fr->match, NULL);
if (error) {
return error;
}
enum ofperr error;
nfr = ofpbuf_pull(&b, sizeof *nfr);
- error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match,
- NULL, NULL);
+ error = nx_pull_match(&b, ntohs(nfr->match_len), &fr->match, NULL,
+ NULL, false, NULL, NULL);
if (error) {
return error;
}
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
- case OFPUTIL_P_OF15_OXM: {
+ case OFPUTIL_P_OF15_OXM:
+ case OFPUTIL_P_OF16_OXM: {
struct ofp12_flow_removed *ofr;
msg = ofpraw_alloc_xid(OFPRAW_OFPT11_FLOW_REMOVED,
* arguments needs to be initialized. */
static enum ofperr
decode_nx_packet_in2(const struct ofp_header *oh, bool loose,
+ const struct tun_table *tun_table,
+ const struct vl_mff_map *vl_mff_map,
struct ofputil_packet_in *pin,
size_t *total_len, uint32_t *buffer_id,
struct ofpbuf *continuation)
case NXPINT_METADATA:
error = oxm_decode_match(payload.msg, ofpbuf_msgsize(&payload),
+ loose, tun_table, vl_mff_map,
&pin->flow_metadata);
break;
* it separately from the original OpenFlow message. This is also true for
* 'pin->userdata' (which could also end up NULL if there is no userdata).
*
+ * 'vl_mff_map' is an optional parameter that is used to validate the length
+ * of variable length mf_fields in 'match'. If it is not provided, the
+ * default mf_fields with maximum length will be used.
+ *
* Returns 0 if successful, otherwise an OpenFlow error code. */
enum ofperr
ofputil_decode_packet_in(const struct ofp_header *oh, bool loose,
+ const struct tun_table *tun_table,
+ const struct vl_mff_map *vl_mff_map,
struct ofputil_packet_in *pin,
size_t *total_lenp, uint32_t *buffer_idp,
struct ofpbuf *continuation)
const ovs_be64 *cookie = (raw == OFPRAW_OFPT13_PACKET_IN
? ofpbuf_pull(&b, sizeof *cookie)
: NULL);
- enum ofperr error = oxm_pull_match_loose(&b, &pin->flow_metadata);
+ enum ofperr error = oxm_pull_match_loose(&b, false, tun_table,
+ &pin->flow_metadata);
if (error) {
return error;
}
npi = ofpbuf_pull(&b, sizeof *npi);
error = nx_pull_match_loose(&b, ntohs(npi->match_len),
- &pin->flow_metadata, NULL, NULL);
+ &pin->flow_metadata, NULL, NULL, false,
+ NULL);
if (error) {
return error;
}
pin->packet = b.data;
pin->packet_len = b.size;
} else if (raw == OFPRAW_NXT_PACKET_IN2 || raw == OFPRAW_NXT_RESUME) {
- enum ofperr error = decode_nx_packet_in2(oh, loose, pin, &total_len,
+ enum ofperr error = decode_nx_packet_in2(oh, loose, tun_table,
+ vl_mff_map, pin, &total_len,
&buffer_id, continuation);
if (error) {
return error;
* function omits it. The caller can add it itself if desired. */
static void
ofputil_put_packet_in(const struct ofputil_packet_in *pin,
- enum ofp_version version, uint32_t buffer_id,
- size_t include_bytes, struct ofpbuf *msg)
+ enum ofp_version version, size_t include_bytes,
+ struct ofpbuf *msg)
{
/* Add packet properties. */
ofpprop_put(msg, NXPINT_PACKET, pin->packet, include_bytes);
if (include_bytes != pin->packet_len) {
ofpprop_put_u32(msg, NXPINT_FULL_LEN, pin->packet_len);
}
- if (buffer_id != UINT32_MAX) {
- ofpprop_put_u32(msg, NXPINT_BUFFER_ID, buffer_id);
- }
/* Add flow properties. */
ofpprop_put_u8(msg, NXPINT_TABLE_ID, pin->table_id);
* function omits it. The caller can add it itself if desired. */
static void
ofputil_put_packet_in_private(const struct ofputil_packet_in_private *pin,
- enum ofp_version version, uint32_t buffer_id,
- size_t include_bytes, struct ofpbuf *msg)
+ enum ofp_version version, size_t include_bytes,
+ struct ofpbuf *msg)
{
- ofputil_put_packet_in(&pin->public, version, buffer_id,
- include_bytes, msg);
+ ofputil_put_packet_in(&pin->public, version, include_bytes, msg);
size_t continuation_ofs = ofpprop_start_nested(msg, NXPINT_CONTINUATION);
size_t inner_ofs = msg->size;
ofpprop_put_uuid(msg, NXCPT_BRIDGE, &pin->bridge);
}
- for (size_t i = 0; i < pin->n_stack; i++) {
- const union mf_subvalue *s = &pin->stack[i];
- size_t ofs;
- for (ofs = 0; ofs < sizeof *s; ofs++) {
- if (s->u8[ofs]) {
- break;
- }
- }
+ struct ofpbuf pin_stack;
+ ofpbuf_use_const(&pin_stack, pin->stack, pin->stack_size);
- ofpprop_put(msg, NXCPT_STACK, &s->u8[ofs], sizeof *s - ofs);
+ while (pin_stack.size) {
+ uint8_t len;
+ uint8_t *val = nx_stack_pop(&pin_stack, &len);
+ ofpprop_put(msg, NXCPT_STACK, val, len);
}
if (pin->mirrors) {
}
static struct ofpbuf *
-ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin,
- uint32_t buffer_id)
+ofputil_encode_ofp10_packet_in(const struct ofputil_packet_in *pin)
{
struct ofp10_packet_in *opi;
struct ofpbuf *msg;
opi->total_len = htons(pin->packet_len);
opi->in_port = htons(ofp_to_u16(pin->flow_metadata.flow.in_port.ofp_port));
opi->reason = encode_packet_in_reason(pin->reason, OFP10_VERSION);
- opi->buffer_id = htonl(buffer_id);
+ opi->buffer_id = htonl(UINT32_MAX);
return msg;
}
static struct ofpbuf *
ofputil_encode_nx_packet_in(const struct ofputil_packet_in *pin,
- enum ofp_version version, uint32_t buffer_id)
+ enum ofp_version version)
{
struct nx_packet_in *npi;
struct ofpbuf *msg;
ofpbuf_put_zeros(msg, 2);
npi = msg->msg;
- npi->buffer_id = htonl(buffer_id);
+ npi->buffer_id = htonl(UINT32_MAX);
npi->total_len = htons(pin->packet_len);
npi->reason = encode_packet_in_reason(pin->reason, version);
npi->table_id = pin->table_id;
static struct ofpbuf *
ofputil_encode_nx_packet_in2(const struct ofputil_packet_in_private *pin,
- enum ofp_version version, uint32_t buffer_id,
- size_t include_bytes)
+ enum ofp_version version, size_t include_bytes)
{
/* 'extra' is just an estimate of the space required. */
size_t extra = (pin->public.packet_len
+ NXM_TYPICAL_LEN /* flow_metadata */
- + pin->n_stack * 16
+ + pin->stack_size * 4
+ pin->actions_len
+ pin->action_set_len
+ 256); /* fudge factor */
struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_PACKET_IN2, version,
htonl(0), extra);
- ofputil_put_packet_in_private(pin, version, buffer_id, include_bytes, msg);
+ ofputil_put_packet_in_private(pin, version, include_bytes, msg);
if (pin->public.userdata_len) {
ofpprop_put(msg, NXPINT_USERDATA, pin->public.userdata,
pin->public.userdata_len);
}
static struct ofpbuf *
-ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin,
- uint32_t buffer_id)
+ofputil_encode_ofp11_packet_in(const struct ofputil_packet_in *pin)
{
struct ofp11_packet_in *opi;
struct ofpbuf *msg;
msg = ofpraw_alloc_xid(OFPRAW_OFPT11_PACKET_IN, OFP11_VERSION,
htonl(0), pin->packet_len);
opi = ofpbuf_put_zeros(msg, sizeof *opi);
- opi->buffer_id = htonl(buffer_id);
+ opi->buffer_id = htonl(UINT32_MAX);
opi->in_port = ofputil_port_to_ofp11(
pin->flow_metadata.flow.in_port.ofp_port);
opi->in_phy_port = opi->in_port;
static struct ofpbuf *
ofputil_encode_ofp12_packet_in(const struct ofputil_packet_in *pin,
- enum ofp_version version,
- uint32_t buffer_id)
+ enum ofp_version version)
{
enum ofpraw raw = (version >= OFP13_VERSION
? OFPRAW_OFPT13_PACKET_IN
htonl(0), NXM_TYPICAL_LEN + 2 + pin->packet_len);
struct ofp12_packet_in *opi = ofpbuf_put_zeros(msg, sizeof *opi);
- opi->buffer_id = htonl(buffer_id);
+ opi->buffer_id = htonl(UINT32_MAX);
opi->total_len = htons(pin->packet_len);
opi->reason = encode_packet_in_reason(pin->reason, version);
opi->table_id = pin->table_id;
/* Converts abstract ofputil_packet_in_private 'pin' into a PACKET_IN message
* for 'protocol', using the packet-in format specified by 'packet_in_format'.
*
- * If 'pkt_buf' is nonnull and 'max_len' allows the packet to be buffered, this
- * function will attempt to obtain a buffer ID from 'pktbuf' and truncate the
- * packet to 'max_len' bytes. Otherwise, or if 'pktbuf' doesn't have a free
- * buffer, it will send the whole packet without buffering.
- *
* This function is really meant only for use by ovs-vswitchd. To any other
* code, the "continuation" data, i.e. the data that is in struct
* ofputil_packet_in_private but not in struct ofputil_packet_in, is supposed
struct ofpbuf *
ofputil_encode_packet_in_private(const struct ofputil_packet_in_private *pin,
enum ofputil_protocol protocol,
- enum nx_packet_in_format packet_in_format,
- uint16_t max_len, struct pktbuf *pktbuf)
+ enum nx_packet_in_format packet_in_format)
{
enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
- /* Get buffer ID. */
- ofp_port_t in_port = pin->public.flow_metadata.flow.in_port.ofp_port;
- uint32_t buffer_id = (max_len != OFPCML12_NO_BUFFER && pktbuf
- ? pktbuf_save(pktbuf, pin->public.packet,
- pin->public.packet_len, in_port)
- : UINT32_MAX);
-
- /* Calculate the number of bytes of the packet to include in the
- * packet-in:
- *
- * - If not buffered, the whole thing.
- *
- * - Otherwise, no more than 'max_len' bytes. */
- size_t include_bytes = (buffer_id == UINT32_MAX
- ? pin->public.packet_len
- : MIN(max_len, pin->public.packet_len));
-
struct ofpbuf *msg;
switch (packet_in_format) {
case NXPIF_STANDARD:
case OFPUTIL_P_OF10_STD_TID:
case OFPUTIL_P_OF10_NXM:
case OFPUTIL_P_OF10_NXM_TID:
- msg = ofputil_encode_ofp10_packet_in(&pin->public, buffer_id);
+ msg = ofputil_encode_ofp10_packet_in(&pin->public);
break;
case OFPUTIL_P_OF11_STD:
- msg = ofputil_encode_ofp11_packet_in(&pin->public, buffer_id);
+ msg = ofputil_encode_ofp11_packet_in(&pin->public);
break;
case OFPUTIL_P_OF12_OXM:
case OFPUTIL_P_OF13_OXM:
case OFPUTIL_P_OF14_OXM:
case OFPUTIL_P_OF15_OXM:
- msg = ofputil_encode_ofp12_packet_in(&pin->public, version, buffer_id);
+ case OFPUTIL_P_OF16_OXM:
+ msg = ofputil_encode_ofp12_packet_in(&pin->public, version);
break;
default:
break;
case NXPIF_NXT_PACKET_IN:
- msg = ofputil_encode_nx_packet_in(&pin->public, version, buffer_id);
+ msg = ofputil_encode_nx_packet_in(&pin->public, version);
break;
case NXPIF_NXT_PACKET_IN2:
- return ofputil_encode_nx_packet_in2(pin, version, buffer_id,
- include_bytes);
+ return ofputil_encode_nx_packet_in2(pin, version,
+ pin->public.packet_len);
default:
OVS_NOT_REACHED();
}
- ofpbuf_put(msg, pin->public.packet, include_bytes);
+ ofpbuf_put(msg, pin->public.packet, pin->public.packet_len);
ofpmsg_update_length(msg);
return msg;
}
size_t extra = pin->packet_len + NXM_TYPICAL_LEN + continuation->size;
struct ofpbuf *msg = ofpraw_alloc_xid(OFPRAW_NXT_RESUME, version,
0, extra);
- ofputil_put_packet_in(pin, version, UINT32_MAX, pin->packet_len, msg);
+ ofputil_put_packet_in(pin, version, pin->packet_len, msg);
ofpprop_put_nested(msg, NXPINT_CONTINUATION, continuation);
ofpmsg_update_length(msg);
return msg;
}
static enum ofperr
-parse_subvalue_prop(const struct ofpbuf *property, union mf_subvalue *sv)
+parse_stack_prop(const struct ofpbuf *property, struct ofpbuf *stack)
{
unsigned int len = ofpbuf_msgsize(property);
- if (len > sizeof *sv) {
+ if (len > sizeof(union mf_subvalue)) {
VLOG_WARN_RL(&bad_ofmsg_rl, "NXCPT_STACK property has bad length %u",
len);
return OFPERR_OFPBPC_BAD_LEN;
}
- memset(sv, 0, sizeof *sv);
- memcpy(&sv->u8[sizeof *sv - len], property->msg, len);
+ nx_stack_push_bottom(stack, property->msg, len);
return 0;
}
}
return ofpacts_pull_openflow_actions(property, property->size,
- version, ofpacts);
+ version, NULL, NULL, ofpacts);
}
/* This is like ofputil_decode_packet_in(), except that it decodes the
* opaque to any process other than ovs-vswitchd, so this function should not
* be used outside ovs-vswitchd.
*
+ * 'vl_mff_map' is an optional parameter that is used to validate the length
+ * of variable length mf_fields in 'match'. If it is not provided, the
+ * default mf_fields with maximum length will be used.
+ *
* When successful, 'pin' contains some dynamically allocated data. Call
* ofputil_packet_in_private_destroy() to free this data. */
enum ofperr
ofputil_decode_packet_in_private(const struct ofp_header *oh, bool loose,
+ const struct tun_table *tun_table,
+ const struct vl_mff_map *vl_mff_map,
struct ofputil_packet_in_private *pin,
size_t *total_len, uint32_t *buffer_id)
{
struct ofpbuf continuation;
enum ofperr error;
- error = ofputil_decode_packet_in(oh, loose, &pin->public, total_len,
- buffer_id, &continuation);
+ error = ofputil_decode_packet_in(oh, loose, tun_table, vl_mff_map,
+ &pin->public, total_len, buffer_id,
+ &continuation);
if (error) {
return error;
}
uint8_t table_id = 0;
ovs_be64 cookie = 0;
- size_t allocated_stack = 0;
+ struct ofpbuf stack;
+ ofpbuf_init(&stack, 0);
while (continuation.size > 0) {
struct ofpbuf payload;
uint64_t type;
error = ofpprop_pull(&continuation, &payload, &type);
- ovs_assert(!error);
+ if (error) {
+ break;
+ }
switch (type) {
case NXCPT_BRIDGE:
break;
case NXCPT_STACK:
- if (pin->n_stack >= allocated_stack) {
- pin->stack = x2nrealloc(pin->stack, &allocated_stack,
- sizeof *pin->stack);
- }
- error = parse_subvalue_prop(&payload,
- &pin->stack[pin->n_stack++]);
+ error = parse_stack_prop(&payload, &stack);
break;
case NXCPT_MIRRORS:
pin->actions = ofpbuf_steal_data(&actions);
pin->action_set_len = action_set.size;
pin->action_set = ofpbuf_steal_data(&action_set);
+ pin->stack_size = stack.size;
+ pin->stack = ofpbuf_steal_data(&stack);
if (error) {
ofputil_packet_in_private_destroy(pin);
}
- return 0;
+ return error;
}
/* Frees data in 'pin' that is dynamically allocated by
* message's actions. The caller must initialize 'ofpacts' and retains
* ownership of it. 'po->ofpacts' will point into the 'ofpacts' buffer.
*
+ * 'po->packet' refers to the packet data in 'oh', so the buffer containing
+ * 'oh' must not be destroyed while 'po' is being used.
+ *
* Returns 0 if successful, otherwise an OFPERR_* value. */
enum ofperr
ofputil_decode_packet_out(struct ofputil_packet_out *po,
const struct ofp_header *oh,
+ const struct tun_table *tun_table,
struct ofpbuf *ofpacts)
{
struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
enum ofpraw raw = ofpraw_pull_assert(&b);
ofpbuf_clear(ofpacts);
- if (raw == OFPRAW_OFPT11_PACKET_OUT) {
+ match_init_catchall(&po->flow_metadata);
+ if (raw == OFPRAW_OFPT15_PACKET_OUT) {
+ enum ofperr error;
+ const struct ofp15_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
+
+ po->buffer_id = ntohl(opo->buffer_id);
+ error = oxm_pull_match_loose(&b, true, tun_table, &po->flow_metadata);
+ if (error) {
+ return error;
+ }
+
+ if (!po->flow_metadata.wc.masks.in_port.ofp_port) {
+ return OFPERR_OFPBRC_BAD_PORT;
+ }
+
+ error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
+ oh->version, NULL, NULL,
+ ofpacts);
+ if (error) {
+ return error;
+ }
+ } else if (raw == OFPRAW_OFPT11_PACKET_OUT) {
enum ofperr error;
+ ofp_port_t in_port;
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);
+ error = ofputil_port_from_ofp11(opo->in_port, &in_port);
if (error) {
return error;
}
+ match_set_in_port(&po->flow_metadata, in_port);
error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
- oh->version, ofpacts);
+ oh->version, NULL, NULL,
+ ofpacts);
if (error) {
return error;
}
const struct ofp10_packet_out *opo = ofpbuf_pull(&b, sizeof *opo);
po->buffer_id = ntohl(opo->buffer_id);
- po->in_port = u16_to_ofp(ntohs(opo->in_port));
+ match_set_in_port(&po->flow_metadata, u16_to_ofp(ntohs(opo->in_port)));
error = ofpacts_pull_openflow_actions(&b, ntohs(opo->actions_len),
- oh->version, ofpacts);
+ oh->version, NULL, NULL,
+ ofpacts);
if (error) {
return error;
}
OVS_NOT_REACHED();
}
- if (ofp_to_u16(po->in_port) >= ofp_to_u16(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);
+ ofp_port_t in_port = po->flow_metadata.flow.in_port.ofp_port;
+ if (ofp_to_u16(in_port) >= ofp_to_u16(OFPP_MAX)
+ && in_port != OFPP_LOCAL
+ && in_port != OFPP_NONE
+ && in_port != OFPP_CONTROLLER) {
+ VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx32,
+ po->flow_metadata.flow.in_port.ofp_port);
return OFPERR_OFPBRC_BAD_PORT;
}
{
pp->port_no = u16_to_ofp(ntohs(opp->port_no));
pp->hw_addr = opp->hw_addr;
- ovs_strlcpy(pp->name, opp->name, OFP_MAX_PORT_NAME_LEN);
+ ovs_strlcpy_arrays(pp->name, opp->name);
pp->config = ntohl(opp->config) & OFPPC10_ALL;
pp->state = ntohl(opp->state) & OFPPS10_ALL;
return error;
}
pp->hw_addr = op->hw_addr;
- ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
+ ovs_strlcpy_arrays(pp->name, op->name);
pp->config = ntohl(op->config) & OFPPC11_ALL;
pp->state = ntohl(op->state) & OFPPS11_ALL;
}
static enum ofperr
-ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+ofputil_pull_ofp14_port_properties(const void *props, size_t len,
+ struct ofputil_phy_port *pp)
{
- struct ofp14_port *op = ofpbuf_try_pull(msg, sizeof *op);
- if (!op) {
- return OFPERR_OFPBRC_BAD_LEN;
- }
-
- size_t len = ntohs(op->length);
- if (len < sizeof *op || len - sizeof *op > msg->size) {
- return OFPERR_OFPBRC_BAD_LEN;
- }
- len -= sizeof *op;
-
- enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
- if (error) {
- return error;
- }
- pp->hw_addr = op->hw_addr;
- ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
-
- pp->config = ntohl(op->config) & OFPPC11_ALL;
- pp->state = ntohl(op->state) & OFPPS11_ALL;
-
- struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
- len);
+ struct ofpbuf properties = ofpbuf_const_initializer(props, len);
while (properties.size > 0) {
struct ofpbuf payload;
enum ofperr error;
return 0;
}
+static enum ofperr
+ofputil_pull_ofp14_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+{
+ const struct ofp14_port *op = ofpbuf_try_pull(msg, sizeof *op);
+ if (!op) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ size_t len = ntohs(op->length);
+ if (len < sizeof *op || len - sizeof *op > msg->size) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ len -= sizeof *op;
+
+ enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
+ if (error) {
+ return error;
+ }
+ pp->hw_addr = op->hw_addr;
+ ovs_strlcpy_arrays(pp->name, op->name);
+
+ pp->config = ntohl(op->config) & OFPPC11_ALL;
+ pp->state = ntohl(op->state) & OFPPS11_ALL;
+
+ return ofputil_pull_ofp14_port_properties(ofpbuf_pull(msg, len), len, pp);
+}
+
+static enum ofperr
+ofputil_pull_ofp16_port(struct ofputil_phy_port *pp, struct ofpbuf *msg)
+{
+ const struct ofp16_port *op = ofpbuf_try_pull(msg, sizeof *op);
+ if (!op) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+
+ size_t len = ntohs(op->length);
+ if (len < sizeof *op || len - sizeof *op > msg->size) {
+ return OFPERR_OFPBRC_BAD_LEN;
+ }
+ len -= sizeof *op;
+
+ enum ofperr error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
+ if (error) {
+ return error;
+ }
+ if (op->hw_addr_type & htons(OFPPHAT16_EUI48)) {
+ pp->hw_addr = op->hw_addr;
+ }
+ if (op->hw_addr_type & htons(OFPPHAT16_EUI64)) {
+ pp->hw_addr64 = op->hw_addr64;
+ }
+ ovs_strlcpy_arrays(pp->name, op->name);
+
+ pp->config = ntohl(op->config) & OFPPC11_ALL;
+ pp->state = ntohl(op->state) & OFPPS11_ALL;
+
+ return ofputil_pull_ofp14_port_properties(ofpbuf_pull(msg, len), len, pp);
+}
+
static void
ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp,
struct ofp10_phy_port *opp)
opp->port_no = htons(ofp_to_u16(pp->port_no));
opp->hw_addr = pp->hw_addr;
- ovs_strlcpy(opp->name, pp->name, OFP_MAX_PORT_NAME_LEN);
+ ovs_strlcpy_arrays(opp->name, pp->name);
opp->config = htonl(pp->config & OFPPC10_ALL);
opp->state = htonl(pp->state & OFPPS10_ALL);
op->port_no = ofputil_port_to_ofp11(pp->port_no);
op->hw_addr = pp->hw_addr;
- ovs_strlcpy(op->name, pp->name, OFP_MAX_PORT_NAME_LEN);
+ ovs_strlcpy_arrays(op->name, pp->name);
op->config = htonl(pp->config & OFPPC11_ALL);
op->state = htonl(pp->state & OFPPS11_ALL);
}
static void
-ofputil_put_ofp14_port(const struct ofputil_phy_port *pp,
- struct ofpbuf *b)
+ofputil_encode_ofp14_port_ethernet_prop(
+ const struct ofputil_phy_port *pp,
+ struct ofp14_port_desc_prop_ethernet *eth)
+{
+ eth->curr = netdev_port_features_to_ofp11(pp->curr);
+ eth->advertised = netdev_port_features_to_ofp11(pp->advertised);
+ eth->supported = netdev_port_features_to_ofp11(pp->supported);
+ eth->peer = netdev_port_features_to_ofp11(pp->peer);
+ eth->curr_speed = htonl(pp->curr_speed);
+ eth->max_speed = htonl(pp->max_speed);
+}
+
+static void
+ofputil_put_ofp14_port(const struct ofputil_phy_port *pp, struct ofpbuf *b)
{
struct ofp14_port *op;
struct ofp14_port_desc_prop_ethernet *eth;
op->port_no = ofputil_port_to_ofp11(pp->port_no);
op->length = htons(sizeof *op + sizeof *eth);
op->hw_addr = pp->hw_addr;
- ovs_strlcpy(op->name, pp->name, sizeof op->name);
+ ovs_strlcpy_arrays(op->name, pp->name);
op->config = htonl(pp->config & OFPPC11_ALL);
op->state = htonl(pp->state & OFPPS11_ALL);
eth = ofpprop_put_zeros(b, OFPPDPT14_ETHERNET, sizeof *eth);
- eth->curr = netdev_port_features_to_ofp11(pp->curr);
- eth->advertised = netdev_port_features_to_ofp11(pp->advertised);
- eth->supported = netdev_port_features_to_ofp11(pp->supported);
- eth->peer = netdev_port_features_to_ofp11(pp->peer);
- eth->curr_speed = htonl(pp->curr_speed);
- eth->max_speed = htonl(pp->max_speed);
+ ofputil_encode_ofp14_port_ethernet_prop(pp, eth);
+}
+
+static void
+ofputil_put_ofp16_port(const struct ofputil_phy_port *pp, struct ofpbuf *b)
+{
+ struct ofp16_port *op;
+ struct ofp14_port_desc_prop_ethernet *eth;
+
+ ofpbuf_prealloc_tailroom(b, sizeof *op + sizeof *eth);
+
+ op = ofpbuf_put_zeros(b, sizeof *op);
+ op->port_no = ofputil_port_to_ofp11(pp->port_no);
+ op->length = htons(sizeof *op + sizeof *eth);
+ if (!eth_addr_is_zero(pp->hw_addr)) {
+ op->hw_addr_type |= htons(OFPPHAT16_EUI48);
+ op->hw_addr = pp->hw_addr;
+ }
+ if (!eth_addr64_is_zero(pp->hw_addr64)) {
+ op->hw_addr_type |= htons(OFPPHAT16_EUI64);
+ op->hw_addr64 = pp->hw_addr64;
+ }
+ ovs_strlcpy_arrays(op->name, pp->name);
+ op->config = htonl(pp->config & OFPPC11_ALL);
+ op->state = htonl(pp->state & OFPPS11_ALL);
+
+ eth = ofpprop_put_zeros(b, OFPPDPT14_ETHERNET, sizeof *eth);
+ ofputil_encode_ofp14_port_ethernet_prop(pp, eth);
}
static void
case OFP15_VERSION:
ofputil_put_ofp14_port(pp, b);
break;
+ case OFP16_VERSION:
+ ofputil_put_ofp16_port(pp, b);
+ break;
default:
OVS_NOT_REACHED();
request = ofpraw_alloc(OFPRAW_OFPST10_PORT_DESC_REQUEST,
ofp_version, 0);
break;
- case OFP15_VERSION:{
+ case OFP15_VERSION:
+ case OFP16_VERSION:{
struct ofp15_port_desc_request *req;
request = ofpraw_alloc(OFPRAW_OFPST15_PORT_DESC_REQUEST,
ofp_version, 0);
BUILD_ASSERT_DECL((int) OFPUTIL_C_IP_REASM == OFPC_IP_REASM);
BUILD_ASSERT_DECL((int) OFPUTIL_C_QUEUE_STATS == OFPC_QUEUE_STATS);
BUILD_ASSERT_DECL((int) OFPUTIL_C_ARP_MATCH_IP == OFPC_ARP_MATCH_IP);
+BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_BLOCKED == OFPC12_PORT_BLOCKED);
+BUILD_ASSERT_DECL((int) OFPUTIL_C_BUNDLES == OFPC14_BUNDLES);
+BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_MONITORING == OFPC14_FLOW_MONITORING);
static uint32_t
ofputil_capabilities_mask(enum ofp_version ofp_version)
return OFPC_COMMON | OFPC_ARP_MATCH_IP;
case OFP12_VERSION:
case OFP13_VERSION:
+ return OFPC_COMMON | OFPC12_PORT_BLOCKED;
case OFP14_VERSION:
case OFP15_VERSION:
- return OFPC_COMMON | OFPC12_PORT_BLOCKED;
+ case OFP16_VERSION:
+ return OFPC_COMMON | OFPC12_PORT_BLOCKED | OFPC14_BUNDLES
+ | OFPC14_FLOW_MONITORING;
default:
/* Caller needs to check osf->header.version itself */
return 0;
case OFP13_VERSION:
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
raw = OFPRAW_OFPT13_FEATURES_REPLY;
break;
default:
osf->n_buffers = htonl(features->n_buffers);
osf->n_tables = features->n_tables;
- osf->capabilities = htonl(features->capabilities & OFPC_COMMON);
osf->capabilities = htonl(features->capabilities &
ofputil_capabilities_mask(version));
switch (version) {
case OFP13_VERSION:
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
osf->auxiliary_id = features->auxiliary_id;
/* fall through */
case OFP11_VERSION:
raw = OFPRAW_OFPT14_PORT_STATUS;
break;
+ case OFP16_VERSION:
+ raw = OFPRAW_OFPT16_PORT_STATUS;
+ break;
+
default:
OVS_NOT_REACHED();
}
return error;
}
-/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
- * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */
-enum ofperr
-ofputil_decode_port_mod(const struct ofp_header *oh,
- struct ofputil_port_mod *pm, bool loose)
+static enum ofperr
+ofputil_decode_ofp10_port_mod(const struct ofp10_port_mod *opm,
+ struct ofputil_port_mod *pm)
+{
+ pm->port_no = u16_to_ofp(ntohs(opm->port_no));
+ pm->hw_addr = opm->hw_addr;
+ pm->config = ntohl(opm->config) & OFPPC10_ALL;
+ pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
+ pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
+ return 0;
+}
+
+static enum ofperr
+ofputil_decode_ofp11_port_mod(const struct ofp11_port_mod *opm,
+ struct ofputil_port_mod *pm)
{
- struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
- enum ofpraw raw = ofpraw_pull_assert(&b);
- if (raw == OFPRAW_OFPT10_PORT_MOD) {
- const struct ofp10_port_mod *opm = b.data;
+ enum ofperr error;
- pm->port_no = u16_to_ofp(ntohs(opm->port_no));
- pm->hw_addr = opm->hw_addr;
- pm->config = ntohl(opm->config) & OFPPC10_ALL;
- pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
- pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
- } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
- const struct ofp11_port_mod *opm = b.data;
+ error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+ if (error) {
+ return error;
+ }
+
+ pm->hw_addr = opm->hw_addr;
+ pm->config = ntohl(opm->config) & OFPPC11_ALL;
+ pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
+ pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
+
+ return 0;
+}
+
+static enum ofperr
+ofputil_decode_ofp14_port_mod_properties(struct ofpbuf *b, bool loose,
+ struct ofputil_port_mod *pm)
+{
+ while (b->size > 0) {
+ struct ofpbuf property;
enum ofperr error;
+ uint64_t type;
- error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+ error = ofpprop_pull(b, &property, &type);
if (error) {
return error;
}
- pm->hw_addr = opm->hw_addr;
- pm->config = ntohl(opm->config) & OFPPC11_ALL;
- pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
- pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
- } else if (raw == OFPRAW_OFPT14_PORT_MOD) {
- const struct ofp14_port_mod *opm = ofpbuf_pull(&b, sizeof *opm);
- enum ofperr error;
+ switch (type) {
+ case OFPPMPT14_ETHERNET:
+ error = parse_port_mod_ethernet_property(&property, pm);
+ break;
- memset(pm, 0, sizeof *pm);
+ default:
+ error = OFPPROP_UNKNOWN(loose, "port_mod", type);
+ break;
+ }
- error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
if (error) {
return error;
}
+ }
+ return 0;
+}
- pm->hw_addr = opm->hw_addr;
- pm->config = ntohl(opm->config) & OFPPC11_ALL;
- pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
+static enum ofperr
+ofputil_decode_ofp14_port_mod(struct ofpbuf *b, bool loose,
+ struct ofputil_port_mod *pm)
+{
+ const struct ofp14_port_mod *opm = ofpbuf_pull(b, sizeof *opm);
+ enum ofperr error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+ if (error) {
+ return error;
+ }
- while (b.size > 0) {
- struct ofpbuf property;
- enum ofperr error;
- uint64_t type;
+ pm->hw_addr = opm->hw_addr;
+ pm->config = ntohl(opm->config) & OFPPC11_ALL;
+ pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
- error = ofpprop_pull(&b, &property, &type);
- if (error) {
- return error;
- }
+ return ofputil_decode_ofp14_port_mod_properties(b, loose, pm);
+}
- switch (type) {
- case OFPPMPT14_ETHERNET:
- error = parse_port_mod_ethernet_property(&property, pm);
- break;
+static enum ofperr
+ofputil_decode_ofp16_port_mod(struct ofpbuf *b, bool loose,
+ struct ofputil_port_mod *pm)
+{
+ const struct ofp16_port_mod *opm = ofpbuf_pull(b, sizeof *opm);
+ enum ofperr error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
+ if (error) {
+ return error;
+ }
- default:
- error = OFPPROP_UNKNOWN(loose, "port_mod", type);
- break;
- }
+ if (opm->hw_addr_type & htons(OFPPHAT16_EUI48)) {
+ pm->hw_addr = opm->hw_addr;
+ }
+ if (opm->hw_addr_type & htons(OFPPHAT16_EUI64)) {
+ pm->hw_addr64 = opm->hw_addr64;
+ }
+ pm->hw_addr = opm->hw_addr;
+ pm->config = ntohl(opm->config) & OFPPC11_ALL;
+ pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
- if (error) {
- return error;
- }
- }
+ return ofputil_decode_ofp14_port_mod_properties(b, loose, pm);
+}
+
+/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
+ * '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */
+enum ofperr
+ofputil_decode_port_mod(const struct ofp_header *oh,
+ struct ofputil_port_mod *pm, bool loose)
+{
+ memset(pm, 0, sizeof *pm);
+
+ struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+ enum ofpraw raw = ofpraw_pull_assert(&b);
+
+ enum ofperr error;
+ if (raw == OFPRAW_OFPT10_PORT_MOD) {
+ error = ofputil_decode_ofp10_port_mod(b.data, pm);
+ } else if (raw == OFPRAW_OFPT11_PORT_MOD) {
+ error = ofputil_decode_ofp11_port_mod(b.data, pm);
+ } else if (raw == OFPRAW_OFPT14_PORT_MOD) {
+ error = ofputil_decode_ofp14_port_mod(&b, loose, pm);
+ } else if (raw == OFPRAW_OFPT16_PORT_MOD) {
+ error = ofputil_decode_ofp16_port_mod(&b, loose, pm);
} else {
- return OFPERR_OFPBRC_BAD_TYPE;
+ error = OFPERR_OFPBRC_BAD_TYPE;
}
pm->config &= pm->mask;
- return 0;
+ return error;
}
/* Converts the abstract form of a "port mod" message in '*pm' into an OpenFlow
}
break;
}
- default:
- OVS_NOT_REACHED();
+ case OFP16_VERSION: {
+ struct ofp16_port_mod *opm;
+
+ b = ofpraw_alloc(OFPRAW_OFPT16_PORT_MOD, ofp_version, 0);
+ opm = ofpbuf_put_zeros(b, sizeof *opm);
+ opm->port_no = ofputil_port_to_ofp11(pm->port_no);
+ if (!eth_addr_is_zero(pm->hw_addr)) {
+ opm->hw_addr_type |= htons(OFPPHAT16_EUI48);
+ opm->hw_addr = pm->hw_addr;
+ }
+ if (!eth_addr64_is_zero(pm->hw_addr64)) {
+ opm->hw_addr_type |= htons(OFPPHAT16_EUI64);
+ opm->hw_addr64 = pm->hw_addr64;
+ }
+ opm->config = htonl(pm->config & OFPPC11_ALL);
+ opm->mask = htonl(pm->mask & OFPPC11_ALL);
+
+ if (pm->advertise) {
+ ofpprop_put_be32(b, OFPPMPT14_ETHERNET,
+ netdev_port_features_to_ofp11(pm->advertise));
+ }
+ break;
+ }
+ default:
+ OVS_NOT_REACHED();
}
return b;
enum ofperr error;
bool hasmask;
- error = nx_pull_header(payload, &field, &hasmask);
+ error = nx_pull_header(payload, NULL, &field, &hasmask);
if (!error) {
bitmap_set1(hasmask ? masked.bm : exact.bm, field->id);
} else if (error != OFPERR_OFPBMC_BAD_FIELD || !loose) {
return OFPERR_OFPTFFC_BAD_TABLE;
}
- ovs_strlcpy(tf->name, otf->name, OFP_MAX_TABLE_NAME_LEN);
+ ovs_strlcpy_arrays(tf->name, otf->name);
tf->metadata_match = otf->metadata_match;
tf->metadata_write = otf->metadata_write;
tf->miss_config = OFPUTIL_TABLE_MISS_DEFAULT;
case OFP13_VERSION:
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
request = ofpraw_alloc(OFPRAW_OFPST13_TABLE_FEATURES_REQUEST,
ofp_version, 0);
break;
otf = ofpbuf_put_zeros(reply, sizeof *otf);
otf->table_id = tf->table_id;
- ovs_strlcpy(otf->name, tf->name, sizeof otf->name);
+ ovs_strlcpy_arrays(otf->name, tf->name);
otf->metadata_match = tf->metadata_match;
otf->metadata_write = tf->metadata_write;
if (version >= OFP14_VERSION) {
enum ofp_version version)
{
uint32_t config = 0;
- /* See the section "OFPTC_* Table Configuration" in DESIGN.md for more
+ /* Search for "OFPTC_* Table Configuration" in the documentation for more
* information on the crazy evolution of this field. */
switch (version) {
case OFP10_VERSION:
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
/* OpenFlow 1.4 introduced OFPTC14_EVICTION and
* OFPTC14_VACANCY_EVENTS. */
if (eviction == OFPUTIL_TABLE_EVICTION_ON) {
break;
}
case OFP14_VERSION:
- case OFP15_VERSION: {
+ case OFP15_VERSION:
+ case OFP16_VERSION: {
struct ofp14_table_mod *otm;
b = ofpraw_alloc(OFPRAW_OFPT14_TABLE_MOD, ofp_version, 0);
out = ofpbuf_put_zeros(buf, sizeof *out);
out->table_id = features->table_id;
- ovs_strlcpy(out->name, features->name, sizeof out->name);
+ ovs_strlcpy_arrays(out->name, features->name);
out->wildcards = mf_bitmap_to_of10(&wc);
out->max_entries = htonl(features->max_entries);
out->active_count = htonl(stats->active_count);
out = ofpbuf_put_zeros(buf, sizeof *out);
out->table_id = features->table_id;
- ovs_strlcpy(out->name, features->name, sizeof out->name);
+ ovs_strlcpy_arrays(out->name, features->name);
out->wildcards = mf_bitmap_to_of11(&wc);
out->match = mf_bitmap_to_of11(&features->match);
out->instructions = ovsinst_bitmap_to_openflow(
out = ofpbuf_put_zeros(buf, sizeof *out);
out->table_id = features->table_id;
- ovs_strlcpy(out->name, features->name, sizeof out->name);
+ ovs_strlcpy_arrays(out->name, features->name);
out->match = oxm_bitmap_from_mf_bitmap(&features->match, OFP12_VERSION);
out->wildcards = oxm_bitmap_from_mf_bitmap(&features->wildcard,
OFP12_VERSION);
case OFP13_VERSION:
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
ofputil_put_ofp13_table_stats(stats, reply);
break;
}
features->table_id = ots->table_id;
- ovs_strlcpy(features->name, ots->name, sizeof features->name);
+ ovs_strlcpy_arrays(features->name, ots->name);
features->max_entries = ntohl(ots->max_entries);
features->match = features->wildcard = mf_bitmap_from_of10(ots->wildcards);
}
features->table_id = ots->table_id;
- ovs_strlcpy(features->name, ots->name, sizeof features->name);
+ ovs_strlcpy_arrays(features->name, ots->name);
features->max_entries = ntohl(ots->max_entries);
features->nonmiss.instructions = ovsinst_bitmap_from_openflow(
ots->instructions, OFP11_VERSION);
}
features->table_id = ots->table_id;
- ovs_strlcpy(features->name, ots->name, sizeof features->name);
+ ovs_strlcpy_arrays(features->name, ots->name);
features->metadata_match = ots->metadata_match;
features->metadata_write = ots->metadata_write;
features->miss_config = ofputil_decode_table_miss(ots->config,
case OFP13_VERSION:
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
return ofputil_decode_ofp13_table_stats(msg, stats, features);
default:
rq->out_port = u16_to_ofp(ntohs(nfmr->out_port));
rq->table_id = nfmr->table_id;
- return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL, NULL);
+ return nx_pull_match(msg, ntohs(nfmr->match_len), &rq->match, NULL,
+ NULL, false, NULL, NULL);
}
void
update->cookie = nfuf->cookie;
update->priority = ntohs(nfuf->priority);
- error = nx_pull_match(msg, match_len, update->match, NULL, NULL);
+ error = nx_pull_match(msg, match_len, &update->match, NULL, NULL,
+ false, NULL, NULL);
if (error) {
return error;
}
actions_len = length - sizeof *nfuf - ROUND_UP(match_len, 8);
error = ofpacts_pull_openflow_actions(msg, actions_len, oh->version,
- ofpacts);
+ NULL, NULL, ofpacts);
if (error) {
return error;
}
void
ofputil_append_flow_update(const struct ofputil_flow_update *update,
- struct ovs_list *replies)
+ struct ovs_list *replies,
+ const struct tun_table *tun_table)
{
+ struct ofputil_flow_update *update_ =
+ CONST_CAST(struct ofputil_flow_update *, update);
+ const struct tun_table *orig_tun_table;
enum ofp_version version = ofpmp_version(replies);
struct nx_flow_update_header *nfuh;
struct ofpbuf *msg;
size_t start_ofs;
+ orig_tun_table = update->match.flow.tunnel.metadata.tab;
+ update_->match.flow.tunnel.metadata.tab = tun_table;
+
msg = ofpbuf_from_list(ovs_list_back(replies));
start_ofs = msg->size;
int match_len;
ofpbuf_put_zeros(msg, sizeof *nfuf);
- match_len = nx_put_match(msg, update->match, htonll(0), htonll(0));
+ match_len = nx_put_match(msg, &update->match, htonll(0), htonll(0));
ofpacts_put_openflow_actions(update->ofpacts, update->ofpacts_len, msg,
version);
nfuf = ofpbuf_at_assert(msg, start_ofs, sizeof *nfuf);
nfuh->event = htons(update->event);
ofpmp_postappend(replies, start_ofs);
+ update_->match.flow.tunnel.metadata.tab = orig_tun_table;
}
\f
struct ofpbuf *
opo = msg->msg;
opo->buffer_id = htonl(po->buffer_id);
- opo->in_port = htons(ofp_to_u16(po->in_port));
+ opo->in_port =htons(ofp_to_u16(
+ po->flow_metadata.flow.in_port.ofp_port));
opo->actions_len = htons(msg->size - actions_ofs);
break;
}
case OFP11_VERSION:
case OFP12_VERSION:
case OFP13_VERSION:
- case OFP14_VERSION:
- case OFP15_VERSION: {
+ case OFP14_VERSION: {
struct ofp11_packet_out *opo;
size_t len;
ofp_version);
opo = msg->msg;
opo->buffer_id = htonl(po->buffer_id);
- opo->in_port = ofputil_port_to_ofp11(po->in_port);
+ opo->in_port =
+ ofputil_port_to_ofp11(po->flow_metadata.flow.in_port.ofp_port);
+ opo->actions_len = htons(len);
+ break;
+ }
+
+ case OFP15_VERSION:
+ case OFP16_VERSION: {
+ struct ofp15_packet_out *opo;
+ size_t len;
+
+ /* The final argument is just an estimate of the space required. */
+ msg = ofpraw_alloc(OFPRAW_OFPT15_PACKET_OUT, ofp_version,
+ size + NXM_TYPICAL_LEN);
+ ofpbuf_put_zeros(msg, sizeof *opo);
+ oxm_put_match(msg, &po->flow_metadata, ofp_version);
+ len = ofpacts_put_openflow_actions(po->ofpacts, po->ofpacts_len, msg,
+ ofp_version);
+ opo = msg->msg;
+ opo->buffer_id = htonl(po->buffer_id);
opo->actions_len = htons(len);
break;
}
enum ofpraw type;
switch (ofp_version) {
+ case OFP16_VERSION:
case OFP15_VERSION:
case OFP14_VERSION:
case OFP13_VERSION:
/* 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").
+ * (e.g. "LOCAL"). If 'port_map' is nonnull, also accepts names in it (quoted
+ * or unquoted).
*
* 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
* 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 *s, ofp_port_t *portp)
+ofputil_port_from_string(const char *s,
+ const struct ofputil_port_map *port_map,
+ ofp_port_t *portp)
{
unsigned int port32; /* int is at least 32 bits wide. */
"be translated to %u when talking to an OF1.1 or "
"later controller", port32, port32 + OFPP11_OFFSET);
} else if (port32 <= ofp_to_u16(OFPP_LAST_RESV)) {
- char name[OFP_MAX_PORT_NAME_LEN];
+ char name[OFP10_MAX_PORT_NAME_LEN];
- ofputil_port_to_string(u16_to_ofp(port32), name, sizeof name);
+ ofputil_port_to_string(u16_to_ofp(port32), NULL,
+ name, sizeof name);
VLOG_WARN_ONCE("referring to port %s as %"PRIu32" is deprecated "
"for compatibility with OpenFlow 1.1 and later",
name, port32);
} else if (port32 < ofp11_to_u32(OFPP11_MAX)) {
VLOG_WARN("port %u is outside the supported range 0 through "
- "%"PRIx16" or 0x%x through 0x%"PRIx32, port32,
+ "%x or 0x%x through 0x%"PRIx32, port32,
UINT16_MAX, ofp11_to_u32(OFPP11_MAX), UINT32_MAX);
return false;
} else {
return true;
}
}
+
+ ofp_port_t ofp_port = OFPP_NONE;
+ if (s[0] != '"') {
+ ofp_port = ofputil_port_map_get_number(port_map, s);
+ } else {
+ size_t length = strlen(s);
+ char *name = NULL;
+ if (length > 1
+ && s[length - 1] == '"'
+ && json_string_unescape(s + 1, length - 2, &name)) {
+ ofp_port = ofputil_port_map_get_number(port_map, name);
+ }
+ free(name);
+ }
+ if (ofp_port != OFPP_NONE) {
+ *portp = ofp_port;
+ return true;
+ }
+
return false;
}
}
+const char *
+ofputil_port_get_reserved_name(ofp_port_t port)
+{
+ switch (port) {
+#define OFPUTIL_NAMED_PORT(NAME) case OFPP_##NAME: return #NAME;
+ OFPUTIL_NAMED_PORTS
+#undef OFPUTIL_NAMED_PORT
+
+ default:
+ return NULL;
+ }
+}
+
+/* A port name doesn't need to be quoted if it is alphanumeric and starts with
+ * a letter. */
+static bool
+port_name_needs_quotes(const char *port_name)
+{
+ if (!isalpha((unsigned char) port_name[0])) {
+ return true;
+ }
+
+ for (const char *p = port_name + 1; *p; p++) {
+ if (!isalnum((unsigned char) *p)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void
+put_port_name(const char *port_name, struct ds *s)
+{
+ if (port_name_needs_quotes(port_name)) {
+ json_string_escape(port_name, s);
+ } else {
+ ds_put_cstr(s, port_name);
+ }
+}
+
/* Appends to 's' a string representation of the OpenFlow port number 'port'.
* Most ports' string representation is just the port number, but for special
* ports, e.g. OFPP_LOCAL, it is the name, e.g. "LOCAL". */
void
-ofputil_format_port(ofp_port_t port, struct ds *s)
+ofputil_format_port(ofp_port_t port, const struct ofputil_port_map *port_map,
+ struct ds *s)
{
- char name[OFP_MAX_PORT_NAME_LEN];
+ const char *reserved_name = ofputil_port_get_reserved_name(port);
+ if (reserved_name) {
+ ds_put_cstr(s, reserved_name);
+ return;
+ }
- ofputil_port_to_string(port, name, sizeof name);
- ds_put_cstr(s, name);
+ const char *port_name = ofputil_port_map_get_name(port_map, port);
+ if (port_name) {
+ put_port_name(port_name, s);
+ return;
+ }
+
+ ds_put_format(s, "%"PRIu32, port);
}
/* Puts in the 'bufsize' byte in 'namebuf' a null-terminated string
* by name, e.g. "LOCAL". */
void
ofputil_port_to_string(ofp_port_t port,
- char namebuf[OFP_MAX_PORT_NAME_LEN], size_t bufsize)
+ const struct ofputil_port_map *port_map,
+ char *namebuf, size_t bufsize)
{
- switch (port) {
-#define OFPUTIL_NAMED_PORT(NAME) \
- case OFPP_##NAME: \
- ovs_strlcpy(namebuf, #NAME, bufsize); \
- break;
- OFPUTIL_NAMED_PORTS
-#undef OFPUTIL_NAMED_PORT
+ const char *reserved_name = ofputil_port_get_reserved_name(port);
+ if (reserved_name) {
+ ovs_strlcpy(namebuf, reserved_name, bufsize);
+ return;
+ }
- default:
- snprintf(namebuf, bufsize, "%"PRIu16, port);
- break;
+ const char *port_name = ofputil_port_map_get_name(port_map, port);
+ if (port_name) {
+ struct ds s = DS_EMPTY_INITIALIZER;
+ put_port_name(port_name, &s);
+ ovs_strlcpy(namebuf, ds_cstr(&s), bufsize);
+ ds_destroy(&s);
+ return;
+ }
+
+ snprintf(namebuf, bufsize, "%"PRIu32, port);
+}
+\f
+/* ofputil_port_map. */
+struct ofputil_port_map_node {
+ struct hmap_node name_node;
+ struct hmap_node number_node;
+ ofp_port_t ofp_port; /* Port number. */
+ char *name; /* Port name. */
+
+ /* OpenFlow doesn't require port names to be unique, although that's the
+ * only sensible way. However, even in Open vSwitch it's possible for two
+ * ports to appear to have the same name if their names are longer than the
+ * maximum length supported by a given version of OpenFlow. So, we guard
+ * against duplicate names to avoid giving unexpected results in this
+ * corner case.
+ *
+ * OpenFlow does require port numbers to be unique. We check for duplicate
+ * ports numbers just in case a switch has a bug. */
+ bool duplicate;
+};
+
+void
+ofputil_port_map_init(struct ofputil_port_map *map)
+{
+ hmap_init(&map->by_name);
+ hmap_init(&map->by_number);
+}
+
+static struct ofputil_port_map_node *
+ofputil_port_map_find_by_name(const struct ofputil_port_map *map,
+ const char *name)
+{
+ struct ofputil_port_map_node *node;
+
+ HMAP_FOR_EACH_WITH_HASH (node, name_node, hash_string(name, 0),
+ &map->by_name) {
+ if (!strcmp(name, node->name)) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+static struct ofputil_port_map_node *
+ofputil_port_map_find_by_number(const struct ofputil_port_map *map,
+ ofp_port_t ofp_port)
+{
+ struct ofputil_port_map_node *node;
+
+ HMAP_FOR_EACH_IN_BUCKET (node, number_node, hash_ofp_port(ofp_port),
+ &map->by_number) {
+ if (node->ofp_port == ofp_port) {
+ return node;
+ }
}
+ return NULL;
}
+void
+ofputil_port_map_put(struct ofputil_port_map *map,
+ ofp_port_t ofp_port, const char *name)
+{
+ struct ofputil_port_map_node *node;
+
+ /* Look for duplicate name. */
+ node = ofputil_port_map_find_by_name(map, name);
+ if (node) {
+ if (node->ofp_port != ofp_port) {
+ node->duplicate = true;
+ }
+ return;
+ }
+
+ /* Look for duplicate number. */
+ node = ofputil_port_map_find_by_number(map, ofp_port);
+ if (node) {
+ node->duplicate = true;
+ return;
+ }
+
+ /* Add new node. */
+ node = xmalloc(sizeof *node);
+ hmap_insert(&map->by_number, &node->number_node, hash_ofp_port(ofp_port));
+ hmap_insert(&map->by_name, &node->name_node, hash_string(name, 0));
+ node->ofp_port = ofp_port;
+ node->name = xstrdup(name);
+ node->duplicate = false;
+}
+
+const char *
+ofputil_port_map_get_name(const struct ofputil_port_map *map,
+ ofp_port_t ofp_port)
+{
+ struct ofputil_port_map_node *node
+ = map ? ofputil_port_map_find_by_number(map, ofp_port) : NULL;
+ return node && !node->duplicate ? node->name : NULL;
+}
+
+ofp_port_t
+ofputil_port_map_get_number(const struct ofputil_port_map *map,
+ const char *name)
+{
+ struct ofputil_port_map_node *node
+ = map ? ofputil_port_map_find_by_name(map, name) : NULL;
+ return node && !node->duplicate ? node->ofp_port : OFPP_NONE;
+}
+
+void
+ofputil_port_map_destroy(struct ofputil_port_map *map)
+{
+ if (map) {
+ struct ofputil_port_map_node *node, *next;
+
+ HMAP_FOR_EACH_SAFE (node, next, name_node, &map->by_name) {
+ hmap_remove(&map->by_name, &node->name_node);
+ hmap_remove(&map->by_number, &node->number_node);
+ free(node->name);
+ free(node);
+ }
+ hmap_destroy(&map->by_name);
+ hmap_destroy(&map->by_number);
+ }
+}
+\f
/* Stores the group id represented by 's' into '*group_idp'. 's' may be an
* integer or, for reserved group IDs, the standard OpenFlow name for the group
* (either "ANY" or "ALL").
case OFP14_VERSION:
case OFP15_VERSION:
return b->size ? ofputil_pull_ofp14_port(pp, b) : EOF;
+ case OFP16_VERSION:
+ return b->size ? ofputil_pull_ofp16_port(pp, b) : EOF;
default:
OVS_NOT_REACHED();
}
/* Log any changes. */
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;
+ char *pre = (log
+ ? match_to_string(match, NULL, OFP_DEFAULT_PRIORITY)
+ : NULL);
match->wc = wc;
match_zero_wildcarded_fields(match);
if (log) {
- char *post = match_to_string(match, OFP_DEFAULT_PRIORITY);
+ char *post = match_to_string(match, NULL, OFP_DEFAULT_PRIORITY);
VLOG_INFO("normalization changed ofp_match, details:");
VLOG_INFO(" pre: %s", pre);
VLOG_INFO("post: %s", post);
ofputil_normalize_match__(match, false);
}
+static size_t
+parse_value(const char *s, const char *delimiters)
+{
+ size_t n = 0;
+
+ /* Iterate until we reach a delimiter.
+ *
+ * strchr(s, '\0') returns s+strlen(s), so this test handles the null
+ * terminator at the end of 's'. */
+ while (!strchr(delimiters, s[n])) {
+ if (s[n] == '(') {
+ int level = 0;
+ do {
+ switch (s[n]) {
+ case '\0':
+ return n;
+ case '(':
+ level++;
+ break;
+ case ')':
+ level--;
+ break;
+ }
+ n++;
+ } while (level > 0);
+ } else {
+ n++;
+ }
+ }
+ return n;
+}
+
/* Parses a key or a key-value pair from '*stringp'.
*
* On success: Stores the key into '*keyp'. Stores the value, if present, into
bool
ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
{
- char *pos, *key, *value;
- size_t key_len;
-
- pos = *stringp;
- pos += strspn(pos, ", \t\r\n");
- if (*pos == '\0') {
+ /* Skip white space and delimiters. If that brings us to the end of the
+ * input string, we are done and there are no more key-value pairs. */
+ *stringp += strspn(*stringp, ", \t\r\n");
+ if (**stringp == '\0') {
*keyp = *valuep = NULL;
return false;
}
- key = pos;
- key_len = strcspn(pos, ":=(, \t\r\n");
- if (key[key_len] == ':' || key[key_len] == '=') {
- /* The value can be separated by a colon. */
- size_t value_len;
-
- value = key + key_len + 1;
- value_len = strcspn(value, ", \t\r\n");
- pos = value + value_len + (value[value_len] != '\0');
- value[value_len] = '\0';
- } else if (key[key_len] == '(') {
- /* The value can be surrounded by balanced parentheses. The outermost
- * set of parentheses is removed. */
- int level = 1;
- size_t value_len;
-
- value = key + key_len + 1;
- for (value_len = 0; level > 0; value_len++) {
- switch (value[value_len]) {
- case '\0':
- level = 0;
- break;
-
- case '(':
- level++;
- break;
+ /* Extract the key and the delimiter that ends the key-value pair or begins
+ * the value. Advance the input position past the key and delimiter. */
+ char *key = *stringp;
+ size_t key_len = strcspn(key, ":=(, \t\r\n");
+ char key_delim = key[key_len];
+ key[key_len] = '\0';
+ *stringp += key_len + (key_delim != '\0');
- case ')':
- level--;
- break;
- }
- }
- value[value_len - 1] = '\0';
- pos = value + value_len;
+ /* Figure out what delimiter ends the value:
+ *
+ * - If key_delim is ":" or "=", the value extends until white space
+ * or a comma.
+ *
+ * - If key_delim is "(", the value extends until ")".
+ *
+ * If there is no value, we are done. */
+ const char *value_delims;
+ if (key_delim == ':' || key_delim == '=') {
+ value_delims = ", \t\r\n";
+ } else if (key_delim == '(') {
+ value_delims = ")";
} else {
- /* There might be no value at all. */
- value = key + key_len; /* Will become the empty string below. */
- pos = key + key_len + (key[key_len] != '\0');
+ *keyp = key;
+ *valuep = key + key_len; /* Empty string. */
+ return true;
}
- key[key_len] = '\0';
- *stringp = pos;
+ /* Extract the value. Advance the input position past the value and
+ * delimiter. */
+ char *value = *stringp;
+ size_t value_len = parse_value(value, value_delims);
+ char value_delim = value[value_len];
+ value[value_len] = '\0';
+ *stringp += value_len + (value_delim != '\0');
+
*keyp = key;
*valuep = value;
return true;
case OFP12_VERSION:
case OFP13_VERSION:
case OFP14_VERSION:
- case OFP15_VERSION: {
+ case OFP15_VERSION:
+ case OFP16_VERSION: {
struct ofp11_port_stats_request *req;
request = ofpraw_alloc(OFPRAW_OFPST11_PORT_REQUEST, ofp_version, 0);
req = ofpbuf_put_zeros(request, sizeof *req);
struct ovs_list *replies)
{
struct ofp14_port_stats_prop_ethernet *eth;
+ struct intel_port_stats_rfc2819 *stats_rfc2819;
struct ofp14_port_stats *ps14;
struct ofpbuf *reply;
- reply = ofpmp_reserve(replies, sizeof *ps14 + sizeof *eth);
+ reply = ofpmp_reserve(replies, sizeof *ps14 + sizeof *eth +
+ sizeof *stats_rfc2819);
ps14 = ofpbuf_put_uninit(reply, sizeof *ps14);
- ps14->length = htons(sizeof *ps14 + sizeof *eth);
+ ps14->length = htons(sizeof *ps14 + sizeof *eth +
+ sizeof *stats_rfc2819);
memset(ps14->pad, 0, sizeof ps14->pad);
ps14->port_no = ofputil_port_to_ofp11(ops->port_no);
ps14->duration_sec = htonl(ops->duration_sec);
eth->rx_over_err = htonll(ops->stats.rx_over_errors);
eth->rx_crc_err = htonll(ops->stats.rx_crc_errors);
eth->collisions = htonll(ops->stats.collisions);
+
+ uint64_t prop_type = OFPPROP_EXP(INTEL_VENDOR_ID,
+ INTEL_PORT_STATS_RFC2819);
+
+ stats_rfc2819 = ofpprop_put_zeros(reply, prop_type,
+ sizeof *stats_rfc2819);
+
+ memset(stats_rfc2819->pad, 0, sizeof stats_rfc2819->pad);
+ stats_rfc2819->rx_1_to_64_packets = htonll(ops->stats.rx_1_to_64_packets);
+ stats_rfc2819->rx_65_to_127_packets =
+ htonll(ops->stats.rx_65_to_127_packets);
+ stats_rfc2819->rx_128_to_255_packets =
+ htonll(ops->stats.rx_128_to_255_packets);
+ stats_rfc2819->rx_256_to_511_packets =
+ htonll(ops->stats.rx_256_to_511_packets);
+ stats_rfc2819->rx_512_to_1023_packets =
+ htonll(ops->stats.rx_512_to_1023_packets);
+ stats_rfc2819->rx_1024_to_1522_packets =
+ htonll(ops->stats.rx_1024_to_1522_packets);
+ stats_rfc2819->rx_1523_to_max_packets =
+ htonll(ops->stats.rx_1523_to_max_packets);
+
+ stats_rfc2819->tx_1_to_64_packets = htonll(ops->stats.tx_1_to_64_packets);
+ stats_rfc2819->tx_65_to_127_packets =
+ htonll(ops->stats.tx_65_to_127_packets);
+ stats_rfc2819->tx_128_to_255_packets =
+ htonll(ops->stats.tx_128_to_255_packets);
+ stats_rfc2819->tx_256_to_511_packets =
+ htonll(ops->stats.tx_256_to_511_packets);
+ stats_rfc2819->tx_512_to_1023_packets =
+ htonll(ops->stats.tx_512_to_1023_packets);
+ stats_rfc2819->tx_1024_to_1522_packets =
+ htonll(ops->stats.tx_1024_to_1522_packets);
+ stats_rfc2819->tx_1523_to_max_packets =
+ htonll(ops->stats.tx_1523_to_max_packets);
+
+ stats_rfc2819->tx_multicast_packets =
+ htonll(ops->stats.tx_multicast_packets);
+ stats_rfc2819->rx_broadcast_packets =
+ htonll(ops->stats.rx_broadcast_packets);
+ stats_rfc2819->tx_broadcast_packets =
+ htonll(ops->stats.tx_broadcast_packets);
+ stats_rfc2819->rx_undersized_errors =
+ htonll(ops->stats.rx_undersized_errors);
+ stats_rfc2819->rx_oversize_errors =
+ htonll(ops->stats.rx_oversize_errors);
+ stats_rfc2819->rx_fragmented_errors =
+ htonll(ops->stats.rx_fragmented_errors);
+ stats_rfc2819->rx_jabber_errors =
+ htonll(ops->stats.rx_jabber_errors);
}
/* Encode a ports stat for 'ops' and append it to 'replies'. */
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
ofputil_append_ofp14_port_stats(ops, replies);
break;
ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops,
const struct ofp10_port_stats *ps10)
{
- memset(ops, 0, sizeof *ops);
ops->port_no = u16_to_ofp(ntohs(ps10->port_no));
ops->stats.rx_packets = ntohll(get_32aligned_be64(&ps10->rx_packets));
{
enum ofperr error;
- memset(ops, 0, sizeof *ops);
error = ofputil_port_from_ofp11(ps11->port_no, &ops->port_no);
if (error) {
return error;
return 0;
}
+static enum ofperr
+parse_intel_port_stats_rfc2819_property(const struct ofpbuf *payload,
+ struct ofputil_port_stats *ops)
+{
+ const struct intel_port_stats_rfc2819 *rfc2819 = payload->data;
+
+ if (payload->size != sizeof *rfc2819) {
+ return OFPERR_OFPBPC_BAD_LEN;
+ }
+ ops->stats.rx_1_to_64_packets = ntohll(rfc2819->rx_1_to_64_packets);
+ ops->stats.rx_65_to_127_packets = ntohll(rfc2819->rx_65_to_127_packets);
+ ops->stats.rx_128_to_255_packets = ntohll(rfc2819->rx_128_to_255_packets);
+ ops->stats.rx_256_to_511_packets = ntohll(rfc2819->rx_256_to_511_packets);
+ ops->stats.rx_512_to_1023_packets =
+ ntohll(rfc2819->rx_512_to_1023_packets);
+ ops->stats.rx_1024_to_1522_packets =
+ ntohll(rfc2819->rx_1024_to_1522_packets);
+ ops->stats.rx_1523_to_max_packets =
+ ntohll(rfc2819->rx_1523_to_max_packets);
+
+ ops->stats.tx_1_to_64_packets = ntohll(rfc2819->tx_1_to_64_packets);
+ ops->stats.tx_65_to_127_packets = ntohll(rfc2819->tx_65_to_127_packets);
+ ops->stats.tx_128_to_255_packets = ntohll(rfc2819->tx_128_to_255_packets);
+ ops->stats.tx_256_to_511_packets = ntohll(rfc2819->tx_256_to_511_packets);
+ ops->stats.tx_512_to_1023_packets =
+ ntohll(rfc2819->tx_512_to_1023_packets);
+ ops->stats.tx_1024_to_1522_packets =
+ ntohll(rfc2819->tx_1024_to_1522_packets);
+ ops->stats.tx_1523_to_max_packets =
+ ntohll(rfc2819->tx_1523_to_max_packets);
+
+ ops->stats.tx_multicast_packets = ntohll(rfc2819->tx_multicast_packets);
+ ops->stats.rx_broadcast_packets = ntohll(rfc2819->rx_broadcast_packets);
+ ops->stats.tx_broadcast_packets = ntohll(rfc2819->tx_broadcast_packets);
+ ops->stats.rx_undersized_errors = ntohll(rfc2819->rx_undersized_errors);
+
+ ops->stats.rx_oversize_errors = ntohll(rfc2819->rx_oversize_errors);
+ ops->stats.rx_fragmented_errors = ntohll(rfc2819->rx_fragmented_errors);
+ ops->stats.rx_jabber_errors = ntohll(rfc2819->rx_jabber_errors);
+
+ return 0;
+}
+
+static enum ofperr
+parse_intel_port_stats_property(const struct ofpbuf *payload,
+ uint32_t exp_type,
+ struct ofputil_port_stats *ops)
+{
+ enum ofperr error;
+
+ switch (exp_type) {
+ case INTEL_PORT_STATS_RFC2819:
+ error = parse_intel_port_stats_rfc2819_property(payload, ops);
+ break;
+ default:
+ error = OFPERR_OFPBPC_BAD_EXP_TYPE;
+ break;
+ }
+
+ return error;
+}
+
static enum ofperr
ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
struct ofpbuf *msg)
ops->stats.tx_dropped = ntohll(ps14->tx_dropped);
ops->stats.rx_errors = ntohll(ps14->rx_errors);
ops->stats.tx_errors = ntohll(ps14->tx_errors);
- ops->stats.rx_frame_errors = UINT64_MAX;
- ops->stats.rx_over_errors = UINT64_MAX;
- ops->stats.rx_crc_errors = UINT64_MAX;
- ops->stats.collisions = UINT64_MAX;
+
struct ofpbuf properties = ofpbuf_const_initializer(ofpbuf_pull(msg, len),
len);
while (properties.size > 0) {
struct ofpbuf payload;
enum ofperr error;
- uint64_t type;
+ uint64_t type = 0;
error = ofpprop_pull(&properties, &payload, &type);
if (error) {
return error;
}
-
switch (type) {
case OFPPSPT14_ETHERNET:
error = parse_ofp14_port_stats_ethernet_property(&payload, ops);
break;
-
+ case OFPPROP_EXP(INTEL_VENDOR_ID, INTEL_PORT_STATS_RFC2819):
+ error = parse_intel_port_stats_property(&payload,
+ INTEL_PORT_STATS_RFC2819,
+ ops);
+ break;
default:
error = OFPPROP_UNKNOWN(true, "port stats", type);
break;
enum ofperr error;
enum ofpraw raw;
+ memset(&(ps->stats), 0xFF, sizeof (ps->stats));
+
error = (msg->header ? ofpraw_decode(&raw, msg->header)
: ofpraw_pull(&raw, msg));
if (error) {
return ofputil_pull_ofp14_port_stats(ps, msg);
} 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;
ofp_port_t *ofp10_port)
{
switch ((enum ofp_version)request->version) {
+ case OFP16_VERSION:
case OFP15_VERSION:
case OFP14_VERSION:
case OFP13_VERSION:
}
}
+static void
+ofputil_ipfix_stats_to_reply(const struct ofputil_ipfix_stats *ois,
+ struct nx_ipfix_stats_reply *reply)
+{
+ reply->collector_set_id = htonl(ois->collector_set_id);
+ reply->total_flows = htonll(ois->total_flows);
+ reply->current_flows = htonll(ois->current_flows);
+ reply->pkts = htonll(ois->pkts);
+ reply->ipv4_pkts = htonll(ois->ipv4_pkts);
+ reply->ipv6_pkts = htonll(ois->ipv6_pkts);
+ reply->error_pkts = htonll(ois->error_pkts);
+ reply->ipv4_error_pkts = htonll(ois->ipv4_error_pkts);
+ reply->ipv6_error_pkts = htonll(ois->ipv6_error_pkts);
+ reply->tx_pkts = htonll(ois->tx_pkts);
+ reply->tx_errors = htonll(ois->tx_errors);
+ memset(reply->pad, 0, sizeof reply->pad);
+}
+
+/* Encode a ipfix stat for 'ois' and append it to 'replies'. */
+void
+ofputil_append_ipfix_stat(struct ovs_list *replies,
+ const struct ofputil_ipfix_stats *ois)
+{
+ struct nx_ipfix_stats_reply *reply = ofpmp_append(replies, sizeof *reply);
+ ofputil_ipfix_stats_to_reply(ois, reply);
+}
+
+static enum ofperr
+ofputil_ipfix_stats_from_nx(struct ofputil_ipfix_stats *is,
+ const struct nx_ipfix_stats_reply *reply)
+{
+ is->collector_set_id = ntohl(reply->collector_set_id);
+ is->total_flows = ntohll(reply->total_flows);
+ is->current_flows = ntohll(reply->current_flows);
+ is->pkts = ntohll(reply->pkts);
+ is->ipv4_pkts = ntohll(reply->ipv4_pkts);
+ is->ipv6_pkts = ntohll(reply->ipv6_pkts);
+ is->error_pkts = ntohll(reply->error_pkts);
+ is->ipv4_error_pkts = ntohll(reply->ipv4_error_pkts);
+ is->ipv6_error_pkts = ntohll(reply->ipv6_error_pkts);
+ is->tx_pkts = ntohll(reply->tx_pkts);
+ is->tx_errors = ntohll(reply->tx_errors);
+
+ return 0;
+}
+
+int
+ofputil_pull_ipfix_stats(struct ofputil_ipfix_stats *is, struct ofpbuf *msg)
+{
+ enum ofperr error;
+ enum ofpraw raw;
+
+ memset(is, 0xFF, sizeof (*is));
+
+ error = (msg->header ? ofpraw_decode(&raw, msg->header)
+ : ofpraw_pull(&raw, msg));
+ if (error) {
+ return error;
+ }
+
+ if (!msg->size) {
+ return EOF;
+ } else if (raw == OFPRAW_NXST_IPFIX_BRIDGE_REPLY ||
+ raw == OFPRAW_NXST_IPFIX_FLOW_REPLY) {
+ struct nx_ipfix_stats_reply *reply;
+
+ reply = ofpbuf_try_pull(msg, sizeof *reply);
+ return ofputil_ipfix_stats_from_nx(is, reply);
+ } else {
+ OVS_NOT_REACHED();
+ }
+}
+
+
+/* Returns the number of ipfix stats elements in
+ * OFPTYPE_IPFIX_BRIDGE_STATS_REPLY or OFPTYPE_IPFIX_FLOW_STATS_REPLY
+ * message 'oh'. */
+size_t
+ofputil_count_ipfix_stats(const struct ofp_header *oh)
+{
+ struct ofpbuf b = ofpbuf_const_initializer(oh, ntohs(oh->length));
+ ofpraw_pull_assert(&b);
+
+ return b.size / sizeof(struct ofputil_ipfix_stats);
+}
+
/* Frees all of the "struct ofputil_bucket"s in the 'buckets' list. */
void
ofputil_bucket_list_destroy(struct ovs_list *buckets)
case OFP12_VERSION:
case OFP13_VERSION:
case OFP14_VERSION:
- case OFP15_VERSION: {
+ case OFP15_VERSION:
+ case OFP16_VERSION: {
struct ofp11_group_stats_request *req;
request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_REQUEST, ofp_version, 0);
req = ofpbuf_put_zeros(request, sizeof *req);
ofputil_uninit_group_desc(struct ofputil_group_desc *gd)
{
ofputil_bucket_list_destroy(&gd->buckets);
- free(&gd->props.fields);
+ ofputil_group_properties_destroy(&gd->props);
}
/* Decodes the OpenFlow group description request in 'oh', returning the group
request = ofpraw_alloc(OFPRAW_OFPST11_GROUP_DESC_REQUEST,
ofp_version, 0);
break;
- case OFP15_VERSION:{
+ case OFP15_VERSION:
+ case OFP16_VERSION: {
struct ofp15_group_desc_request *req;
request = ofpraw_alloc(OFPRAW_OFPST15_GROUP_DESC_REQUEST,
ofp_version, 0);
case OFP13_VERSION:
case OFP14_VERSION:
- case OFP15_VERSION: {
+ case OFP15_VERSION:
+ case OFP16_VERSION: {
struct ofp13_group_stats *gs13;
length = sizeof *gs13 + bucket_counter_size;
case OFP13_VERSION:
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
request = ofpraw_alloc(OFPRAW_OFPST12_GROUP_FEATURES_REQUEST,
ofp_version, 0);
break;
break;
case OFP15_VERSION:
+ case OFP16_VERSION:
ofputil_append_ofp15_group_desc_reply(gds, buckets, replies, version);
break;
ofpbuf_init(&ofpacts, 0);
error = ofpacts_pull_openflow_actions(msg, ob_len - sizeof *ob,
- version, &ofpacts);
+ version, NULL, NULL, &ofpacts);
if (error) {
ofpbuf_uninit(&ofpacts);
ofputil_bucket_list_destroy(buckets);
buckets_length -= ob_len;
err = ofpacts_pull_openflow_actions(msg, actions_len, version,
- &ofpacts);
+ NULL, NULL, &ofpacts);
if (err) {
goto err;
}
memset(gp, 0, sizeof *gp);
}
+void
+ofputil_group_properties_copy(struct ofputil_group_props *to,
+ const struct ofputil_group_props *from)
+{
+ *to = *from;
+ to->fields.values = xmemdup(from->fields.values, from->fields.values_size);
+}
+
+void
+ofputil_group_properties_destroy(struct ofputil_group_props *gp)
+{
+ free(gp->fields.values);
+}
+
static enum ofperr
parse_group_prop_ntr_selection_method(struct ofpbuf *payload,
enum ofp11_group_type group_type,
switch (group_cmd) {
case OFPGC15_ADD:
case OFPGC15_MODIFY:
+ case OFPGC15_ADD_OR_MOD:
break;
case OFPGC15_DELETE:
case OFPGC15_INSERT_BUCKET:
return OFPERR_OFPBPC_BAD_VALUE;
}
- if (strcmp("hash", prop->selection_method)) {
+ if (strcmp("hash", prop->selection_method)
+ && strcmp("dp_hash", prop->selection_method)) {
OFPPROP_LOG(&bad_ofmsg_rl, false,
"ntr selection method '%s' is not supported",
prop->selection_method);
return OFPERR_OFPBPC_BAD_VALUE;
}
+ /* 'method_len' is now non-zero. */
strcpy(gp->selection_method, prop->selection_method);
gp->selection_method_param = ntohll(prop->selection_method_param);
- if (!method_len && gp->selection_method_param) {
- OFPPROP_LOG(&bad_ofmsg_rl, false, "ntr selection method parameter is "
- "non-zero but selection method is empty");
- return OFPERR_OFPBPC_BAD_VALUE;
- }
-
ofpbuf_pull(payload, sizeof *prop);
fields_len = ntohs(prop->length) - sizeof *prop;
- if (!method_len && fields_len) {
- OFPPROP_LOG(&bad_ofmsg_rl, false, "ntr selection method parameter is "
- "zero but fields are provided");
+ if (fields_len && strcmp("hash", gp->selection_method)) {
+ OFPPROP_LOG(&bad_ofmsg_rl, false, "ntr selection method %s "
+ "does not support fields", gp->selection_method);
return OFPERR_OFPBPC_BAD_VALUE;
}
return ofputil_decode_ofp11_group_desc_reply(gd, msg, version);
case OFP15_VERSION:
+ case OFP16_VERSION:
return ofputil_decode_ofp15_group_desc_reply(gd, msg, version);
case OFP10_VERSION:
ofputil_uninit_group_mod(struct ofputil_group_mod *gm)
{
ofputil_bucket_list_destroy(&gm->buckets);
+ ofputil_group_properties_destroy(&gm->props);
}
static struct ofpbuf *
switch (cmd) {
case OFPGC15_ADD:
case OFPGC15_MODIFY:
+ case OFPGC15_ADD_OR_MOD:
case OFPGC15_DELETE:
version = "1.1";
opt_version = "11";
break;
case OFPGC15_MODIFY:
+ case OFPGC15_ADD_OR_MOD:
cmd_str = "mod-group";
break;
case OFP12_VERSION:
case OFP13_VERSION:
case OFP14_VERSION:
- if (gm->command > OFPGC11_DELETE) {
+ if (gm->command > OFPGC11_DELETE && gm->command != OFPGC11_ADD_OR_MOD) {
bad_group_cmd(gm->command);
}
return ofputil_encode_ofp11_group_mod(ofp_version, gm);
case OFP15_VERSION:
+ case OFP16_VERSION:
return ofputil_encode_ofp15_group_mod(ofp_version, gm);
default:
case OFPGC11_ADD:
case OFPGC11_MODIFY:
+ case OFPGC11_ADD_OR_MOD:
case OFPGC11_DELETE:
default:
if (gm->command_bucket_id == OFPG15_BUCKET_ALL) {
break;
case OFP15_VERSION:
+ case OFP16_VERSION:
err = ofputil_pull_ofp15_group_mod(&msg, ofp_version, gm);
break;
switch (gm->command) {
case OFPGC11_ADD:
case OFPGC11_MODIFY:
+ case OFPGC11_ADD_OR_MOD:
case OFPGC11_DELETE:
case OFPGC15_INSERT_BUCKET:
break;
return 0;
}
+/* Destroys 'bms'. */
+void
+ofputil_free_bundle_msgs(struct ofputil_bundle_msg *bms, size_t n_bms)
+{
+ for (size_t i = 0; i < n_bms; i++) {
+ switch ((int)bms[i].type) {
+ case OFPTYPE_FLOW_MOD:
+ free(CONST_CAST(struct ofpact *, bms[i].fm.ofpacts));
+ break;
+ case OFPTYPE_GROUP_MOD:
+ ofputil_uninit_group_mod(&bms[i].gm);
+ break;
+ case OFPTYPE_PACKET_OUT:
+ free(bms[i].po.ofpacts);
+ free(CONST_CAST(void *, bms[i].po.packet));
+ break;
+ default:
+ break;
+ }
+ }
+ free(bms);
+}
+
+void
+ofputil_encode_bundle_msgs(const struct ofputil_bundle_msg *bms,
+ size_t n_bms, struct ovs_list *requests,
+ enum ofputil_protocol protocol)
+{
+ enum ofp_version version = ofputil_protocol_to_ofp_version(protocol);
+
+ for (size_t i = 0; i < n_bms; i++) {
+ struct ofpbuf *request = NULL;
+
+ switch ((int)bms[i].type) {
+ case OFPTYPE_FLOW_MOD:
+ request = ofputil_encode_flow_mod(&bms[i].fm, protocol);
+ break;
+ case OFPTYPE_GROUP_MOD:
+ request = ofputil_encode_group_mod(version, &bms[i].gm);
+ break;
+ case OFPTYPE_PACKET_OUT:
+ request = ofputil_encode_packet_out(&bms[i].po, protocol);
+ break;
+ default:
+ break;
+ }
+ if (request) {
+ ovs_list_push_back(requests, &request->list_node);
+ }
+ }
+}
+
/* Parse a queue status request message into 'oqsr'.
* Returns 0 if successful, otherwise an OFPERR_* number. */
enum ofperr
struct ofputil_queue_stats_request *oqsr)
{
switch ((enum ofp_version)request->version) {
+ case OFP16_VERSION:
case OFP15_VERSION:
case OFP14_VERSION:
case OFP13_VERSION:
case OFP12_VERSION:
case OFP13_VERSION:
case OFP14_VERSION:
- case OFP15_VERSION: {
+ case OFP15_VERSION:
+ case OFP16_VERSION: {
struct ofp11_queue_stats_request *req;
request = ofpraw_alloc(OFPRAW_OFPST11_QUEUE_REQUEST, ofp_version, 0);
req = ofpbuf_put_zeros(request, sizeof *req);
}
case OFP14_VERSION:
- case OFP15_VERSION: {
+ case OFP15_VERSION:
+ case OFP16_VERSION: {
struct ofp14_queue_stats *reply = ofpmp_append(replies, sizeof *reply);
ofputil_queue_stats_to_ofp14(oqs, reply);
break;
case OFP13_VERSION:
case OFP14_VERSION:
case OFP15_VERSION:
+ case OFP16_VERSION:
request = ofpraw_alloc(ofp_version == OFP13_VERSION
? OFPRAW_ONFT13_BUNDLE_CONTROL
: OFPRAW_OFPT14_BUNDLE_CONTROL, ofp_version, 0);
/* Minimum required by OpenFlow 1.4. */
case OFPTYPE_PORT_MOD:
case OFPTYPE_FLOW_MOD:
+ /* Other supported types. */
+ case OFPTYPE_GROUP_MOD:
+ case OFPTYPE_PACKET_OUT:
return true;
/* Nice to have later. */
case OFPTYPE_FLOW_MOD_TABLE_ID:
- case OFPTYPE_GROUP_MOD:
case OFPTYPE_TABLE_MOD:
case OFPTYPE_METER_MOD:
- case OFPTYPE_PACKET_OUT:
case OFPTYPE_NXT_TLV_TABLE_MOD:
/* Not to be bundlable. */
case OFPTYPE_NXT_TLV_TABLE_REQUEST:
case OFPTYPE_NXT_TLV_TABLE_REPLY:
case OFPTYPE_NXT_RESUME:
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REQUEST:
+ case OFPTYPE_IPFIX_BRIDGE_STATS_REPLY:
+ case OFPTYPE_IPFIX_FLOW_STATS_REQUEST:
+ case OFPTYPE_IPFIX_FLOW_STATS_REPLY:
+ case OFPTYPE_CT_FLUSH_ZONE:
break;
}
msg->msg = b.data;
if (msg->msg->version != oh->version) {
- return OFPERR_NXBFC_BAD_VERSION;
+ return OFPERR_OFPBFC_BAD_VERSION;
}
size_t inner_len = ntohs(msg->msg->length);
if (inner_len < sizeof(struct ofp_header) || inner_len > b.size) {
request = ofpraw_alloc_xid(ofp_version == OFP13_VERSION
? OFPRAW_ONFT13_BUNDLE_ADD_MESSAGE
: OFPRAW_OFPT14_BUNDLE_ADD_MESSAGE, ofp_version,
- msg->msg->xid, 0);
+ msg->msg->xid, ntohs(msg->msg->length));
m = ofpbuf_put_zeros(request, sizeof *m);
m->bundle_id = htonl(msg->bundle_id);
m->flags = htons(msg->flags);
ofpbuf_put(request, msg->msg, ntohs(msg->msg->length));
+ ofpmsg_update_length(request);
return request;
}
ofputil_encode_get_async_reply(const struct ofp_header *oh,
const struct ofputil_async_cfg *ac)
{
- struct ofpbuf *buf;
-
enum ofpraw raw = (oh->version < OFP14_VERSION
? OFPRAW_OFPT13_GET_ASYNC_REPLY
: OFPRAW_OFPT14_GET_ASYNC_REPLY);
raw == OFPRAW_OFPT14_GET_ASYNC_REPLY,
oh->version, UINT32_MAX);
return reply;
-
- return buf;
}
/* Encodes and returns a message, in a format appropriate for OpenFlow version