2010-05-27 13:14:05 -07:00
|
|
|
|
/*
|
2012-05-02 15:21:36 -07:00
|
|
|
|
* Copyright (c) 2008, 2009, 2010, 2011, 2012 Nicira, Inc.
|
2010-05-27 13:14:05 -07:00
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at:
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include "ofp-print.h"
|
2011-01-12 13:42:50 -08:00
|
|
|
|
#include <errno.h>
|
2010-05-27 13:14:05 -07:00
|
|
|
|
#include <inttypes.h>
|
2011-11-01 13:25:49 +01:00
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <netinet/in.h>
|
2011-05-09 16:25:48 -07:00
|
|
|
|
#include <netinet/icmp6.h>
|
2010-05-27 13:14:05 -07:00
|
|
|
|
#include <stdlib.h>
|
2011-04-05 12:37:52 -07:00
|
|
|
|
#include "autopath.h"
|
2011-06-10 17:45:45 -07:00
|
|
|
|
#include "bundle.h"
|
2010-10-28 17:13:18 -07:00
|
|
|
|
#include "byte-order.h"
|
2010-11-10 14:39:54 -08:00
|
|
|
|
#include "classifier.h"
|
2011-01-12 13:42:50 -08:00
|
|
|
|
#include "dynamic-string.h"
|
2011-09-12 16:19:57 -07:00
|
|
|
|
#include "learn.h"
|
2011-12-28 12:42:14 -08:00
|
|
|
|
#include "meta-flow.h"
|
2012-02-15 16:33:04 -08:00
|
|
|
|
#include "multipath.h"
|
2012-02-15 14:23:38 -08:00
|
|
|
|
#include "netdev.h"
|
2010-11-11 10:41:33 -08:00
|
|
|
|
#include "nx-match.h"
|
2011-01-12 13:42:50 -08:00
|
|
|
|
#include "ofp-errors.h"
|
2010-05-27 13:14:05 -07:00
|
|
|
|
#include "ofp-util.h"
|
|
|
|
|
#include "ofpbuf.h"
|
|
|
|
|
#include "packets.h"
|
|
|
|
|
#include "random.h"
|
2011-03-10 15:02:05 -08:00
|
|
|
|
#include "unaligned.h"
|
2010-12-09 10:41:32 -08:00
|
|
|
|
#include "type-props.h"
|
2010-07-16 11:02:49 -07:00
|
|
|
|
#include "vlog.h"
|
2010-05-27 13:14:05 -07:00
|
|
|
|
|
2010-10-19 14:47:01 -07:00
|
|
|
|
VLOG_DEFINE_THIS_MODULE(ofp_util);
|
2010-05-27 13:14:05 -07:00
|
|
|
|
|
|
|
|
|
/* Rate limit for OpenFlow message parse errors. These always indicate a bug
|
|
|
|
|
* in the peer and so there's not much point in showing a lot of them. */
|
|
|
|
|
static struct vlog_rate_limit bad_ofmsg_rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
|
|
2010-11-22 10:09:18 -08:00
|
|
|
|
/* Given the wildcard bit count in the least-significant 6 of 'wcbits', returns
|
|
|
|
|
* an IP netmask with a 1 in each bit that must match and a 0 in each bit that
|
|
|
|
|
* is wildcarded.
|
|
|
|
|
*
|
|
|
|
|
* The bits in 'wcbits' are in the format used in enum ofp_flow_wildcards: 0
|
|
|
|
|
* is exact match, 1 ignores the LSB, 2 ignores the 2 least-significant bits,
|
|
|
|
|
* ..., 32 and higher wildcard the entire field. This is the *opposite* of the
|
|
|
|
|
* usual convention where e.g. /24 indicates that 8 bits (not 24 bits) are
|
|
|
|
|
* wildcarded. */
|
|
|
|
|
ovs_be32
|
|
|
|
|
ofputil_wcbits_to_netmask(int wcbits)
|
|
|
|
|
{
|
|
|
|
|
wcbits &= 0x3f;
|
|
|
|
|
return wcbits < 32 ? htonl(~((1u << wcbits) - 1)) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given the IP netmask 'netmask', returns the number of bits of the IP address
|
2011-08-17 10:55:15 -07:00
|
|
|
|
* that it wildcards, that is, the number of 0-bits in 'netmask'. 'netmask'
|
|
|
|
|
* must be a CIDR netmask (see ip_is_cidr()). */
|
2010-11-22 10:09:18 -08:00
|
|
|
|
int
|
|
|
|
|
ofputil_netmask_to_wcbits(ovs_be32 netmask)
|
|
|
|
|
{
|
2011-08-17 10:55:15 -07:00
|
|
|
|
return 32 - ip_count_cidr_bits(netmask);
|
2010-11-22 10:09:18 -08:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-10 14:39:54 -08:00
|
|
|
|
/* A list of the FWW_* and OFPFW_ bits that have the same value, meaning, and
|
|
|
|
|
* name. */
|
|
|
|
|
#define WC_INVARIANT_LIST \
|
|
|
|
|
WC_INVARIANT_BIT(IN_PORT) \
|
|
|
|
|
WC_INVARIANT_BIT(DL_TYPE) \
|
2012-01-27 17:16:05 -08:00
|
|
|
|
WC_INVARIANT_BIT(NW_PROTO)
|
2010-11-10 14:39:54 -08:00
|
|
|
|
|
|
|
|
|
/* Verify that all of the invariant bits (as defined on WC_INVARIANT_LIST)
|
|
|
|
|
* actually have the same names and values. */
|
|
|
|
|
#define WC_INVARIANT_BIT(NAME) BUILD_ASSERT_DECL(FWW_##NAME == OFPFW_##NAME);
|
|
|
|
|
WC_INVARIANT_LIST
|
|
|
|
|
#undef WC_INVARIANT_BIT
|
|
|
|
|
|
|
|
|
|
/* WC_INVARIANTS is the invariant bits (as defined on WC_INVARIANT_LIST) all
|
|
|
|
|
* OR'd together. */
|
2011-05-06 12:38:20 -07:00
|
|
|
|
static const flow_wildcards_t WC_INVARIANTS = 0
|
2010-11-10 14:39:54 -08:00
|
|
|
|
#define WC_INVARIANT_BIT(NAME) | FWW_##NAME
|
|
|
|
|
WC_INVARIANT_LIST
|
|
|
|
|
#undef WC_INVARIANT_BIT
|
2011-05-06 12:38:20 -07:00
|
|
|
|
;
|
2010-11-10 14:39:54 -08:00
|
|
|
|
|
2011-05-31 16:15:45 -07:00
|
|
|
|
/* Converts the wildcard in 'ofpfw' into a flow_wildcards in 'wc' for use in
|
|
|
|
|
* struct cls_rule. It is the caller's responsibility to handle the special
|
|
|
|
|
* case where the flow match's dl_vlan is set to OFP_VLAN_NONE. */
|
2011-06-01 09:09:20 -07:00
|
|
|
|
void
|
2011-05-31 16:15:45 -07:00
|
|
|
|
ofputil_wildcard_from_openflow(uint32_t ofpfw, struct flow_wildcards *wc)
|
2010-11-10 14:39:54 -08:00
|
|
|
|
{
|
2012-05-29 00:38:21 +12:00
|
|
|
|
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11);
|
2011-07-29 13:15:09 -07:00
|
|
|
|
|
2010-11-10 14:39:54 -08:00
|
|
|
|
/* Initialize most of rule->wc. */
|
2010-12-06 16:11:55 -08:00
|
|
|
|
flow_wildcards_init_catchall(wc);
|
2011-05-06 12:38:20 -07:00
|
|
|
|
wc->wildcards = (OVS_FORCE flow_wildcards_t) ofpfw & WC_INVARIANTS;
|
2010-12-07 14:02:17 -08:00
|
|
|
|
|
|
|
|
|
/* Wildcard fields that aren't defined by ofp_match or tun_id. */
|
2011-11-09 23:39:16 -08:00
|
|
|
|
wc->wildcards |= (FWW_ARP_SHA | FWW_ARP_THA | FWW_NW_ECN | FWW_NW_TTL
|
2012-04-25 15:48:40 -07:00
|
|
|
|
| FWW_IPV6_LABEL);
|
2010-12-07 14:02:17 -08:00
|
|
|
|
|
2011-11-09 23:39:16 -08:00
|
|
|
|
if (ofpfw & OFPFW_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;
|
2010-11-10 14:39:54 -08:00
|
|
|
|
}
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
|
2010-11-10 14:39:54 -08:00
|
|
|
|
wc->nw_src_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_SRC_SHIFT);
|
|
|
|
|
wc->nw_dst_mask = ofputil_wcbits_to_netmask(ofpfw >> OFPFW_NW_DST_SHIFT);
|
|
|
|
|
|
2012-01-27 17:16:05 -08:00
|
|
|
|
if (!(ofpfw & OFPFW_TP_SRC)) {
|
|
|
|
|
wc->tp_src_mask = htons(UINT16_MAX);
|
|
|
|
|
}
|
|
|
|
|
if (!(ofpfw & OFPFW_TP_DST)) {
|
|
|
|
|
wc->tp_dst_mask = htons(UINT16_MAX);
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-29 00:38:21 +12:00
|
|
|
|
if (!(ofpfw & OFPFW_DL_SRC)) {
|
|
|
|
|
memset(wc->dl_src_mask, 0xff, ETH_ADDR_LEN);
|
|
|
|
|
}
|
|
|
|
|
if (!(ofpfw & OFPFW_DL_DST)) {
|
|
|
|
|
memset(wc->dl_dst_mask, 0xff, ETH_ADDR_LEN);
|
2010-11-10 14:39:54 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-05-31 16:15:45 -07:00
|
|
|
|
/* VLAN TCI mask. */
|
|
|
|
|
if (!(ofpfw & OFPFW_DL_VLAN_PCP)) {
|
|
|
|
|
wc->vlan_tci_mask |= htons(VLAN_PCP_MASK | VLAN_CFI);
|
|
|
|
|
}
|
|
|
|
|
if (!(ofpfw & OFPFW_DL_VLAN)) {
|
|
|
|
|
wc->vlan_tci_mask |= htons(VLAN_VID_MASK | VLAN_CFI);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts the ofp_match in 'match' into a cls_rule in 'rule', with the given
|
|
|
|
|
* 'priority'. */
|
|
|
|
|
void
|
|
|
|
|
ofputil_cls_rule_from_match(const struct ofp_match *match,
|
|
|
|
|
unsigned int priority, struct cls_rule *rule)
|
|
|
|
|
{
|
|
|
|
|
uint32_t ofpfw = ntohl(match->wildcards) & OFPFW_ALL;
|
|
|
|
|
|
|
|
|
|
/* Initialize rule->priority, rule->wc. */
|
|
|
|
|
rule->priority = !ofpfw ? UINT16_MAX : priority;
|
|
|
|
|
ofputil_wildcard_from_openflow(ofpfw, &rule->wc);
|
|
|
|
|
|
2010-11-23 10:06:28 -08:00
|
|
|
|
/* Initialize most of rule->flow. */
|
2010-11-10 14:39:54 -08:00
|
|
|
|
rule->flow.nw_src = match->nw_src;
|
|
|
|
|
rule->flow.nw_dst = match->nw_dst;
|
2011-05-11 12:13:10 -07:00
|
|
|
|
rule->flow.in_port = ntohs(match->in_port);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
rule->flow.dl_type = ofputil_dl_type_from_openflow(match->dl_type);
|
2010-11-10 14:39:54 -08:00
|
|
|
|
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);
|
2011-11-09 17:10:27 -08:00
|
|
|
|
rule->flow.nw_tos = match->nw_tos & IP_DSCP_MASK;
|
2010-11-10 14:39:54 -08:00
|
|
|
|
rule->flow.nw_proto = match->nw_proto;
|
|
|
|
|
|
2010-11-23 10:06:28 -08:00
|
|
|
|
/* Translate VLANs. */
|
2011-05-31 16:15:44 -07:00
|
|
|
|
if (!(ofpfw & OFPFW_DL_VLAN) && match->dl_vlan == htons(OFP_VLAN_NONE)) {
|
|
|
|
|
/* Match only packets without 802.1Q header.
|
|
|
|
|
*
|
|
|
|
|
* When OFPFW_DL_VLAN_PCP is wildcarded, this is obviously correct.
|
|
|
|
|
*
|
|
|
|
|
* If OFPFW_DL_VLAN_PCP is matched, the flow match is contradictory,
|
|
|
|
|
* because we can't have a specific PCP without an 802.1Q header.
|
|
|
|
|
* However, older versions of OVS treated this as matching packets
|
|
|
|
|
* withut an 802.1Q header, so we do here too. */
|
2010-11-23 10:06:28 -08:00
|
|
|
|
rule->flow.vlan_tci = htons(0);
|
2011-05-31 16:15:45 -07:00
|
|
|
|
rule->wc.vlan_tci_mask = htons(0xffff);
|
2011-05-31 16:15:44 -07:00
|
|
|
|
} 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);
|
|
|
|
|
tci = vid | pcp | htons(VLAN_CFI);
|
2011-05-31 16:15:45 -07:00
|
|
|
|
rule->flow.vlan_tci = tci & rule->wc.vlan_tci_mask;
|
2010-11-23 10:06:28 -08:00
|
|
|
|
}
|
|
|
|
|
|
2010-11-10 14:39:54 -08:00
|
|
|
|
/* Clean up. */
|
|
|
|
|
cls_rule_zero_wildcarded_fields(rule);
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-26 09:42:18 -07:00
|
|
|
|
/* Convert 'rule' into the OpenFlow match structure 'match'. */
|
2010-11-10 14:39:54 -08:00
|
|
|
|
void
|
2011-04-26 09:42:18 -07:00
|
|
|
|
ofputil_cls_rule_to_match(const struct cls_rule *rule, struct ofp_match *match)
|
2010-11-10 14:39:54 -08:00
|
|
|
|
{
|
|
|
|
|
const struct flow_wildcards *wc = &rule->wc;
|
2011-05-06 12:38:20 -07:00
|
|
|
|
uint32_t ofpfw;
|
2010-11-10 14:39:54 -08:00
|
|
|
|
|
2010-11-23 10:06:28 -08:00
|
|
|
|
/* Figure out most OpenFlow wildcards. */
|
2011-05-06 12:38:20 -07:00
|
|
|
|
ofpfw = (OVS_FORCE uint32_t) (wc->wildcards & WC_INVARIANTS);
|
2010-11-10 14:39:54 -08:00
|
|
|
|
ofpfw |= ofputil_netmask_to_wcbits(wc->nw_src_mask) << OFPFW_NW_SRC_SHIFT;
|
|
|
|
|
ofpfw |= ofputil_netmask_to_wcbits(wc->nw_dst_mask) << OFPFW_NW_DST_SHIFT;
|
2011-11-09 23:39:16 -08:00
|
|
|
|
if (wc->wildcards & FWW_NW_DSCP) {
|
2010-11-10 14:39:54 -08:00
|
|
|
|
ofpfw |= OFPFW_NW_TOS;
|
|
|
|
|
}
|
2012-01-27 17:16:05 -08:00
|
|
|
|
if (!wc->tp_src_mask) {
|
|
|
|
|
ofpfw |= OFPFW_TP_SRC;
|
|
|
|
|
}
|
|
|
|
|
if (!wc->tp_dst_mask) {
|
|
|
|
|
ofpfw |= OFPFW_TP_DST;
|
|
|
|
|
}
|
2012-05-29 00:38:21 +12:00
|
|
|
|
if (eth_addr_is_zero(wc->dl_src_mask)) {
|
|
|
|
|
ofpfw |= OFPFW_DL_SRC;
|
|
|
|
|
}
|
|
|
|
|
if (eth_addr_is_zero(wc->dl_dst_mask)) {
|
|
|
|
|
ofpfw |= OFPFW_DL_DST;
|
|
|
|
|
}
|
2010-12-09 14:19:51 -08:00
|
|
|
|
|
2010-11-23 10:06:28 -08:00
|
|
|
|
/* Translate VLANs. */
|
|
|
|
|
match->dl_vlan = htons(0);
|
|
|
|
|
match->dl_vlan_pcp = 0;
|
|
|
|
|
if (rule->wc.vlan_tci_mask == htons(0)) {
|
|
|
|
|
ofpfw |= OFPFW_DL_VLAN | OFPFW_DL_VLAN_PCP;
|
|
|
|
|
} else if (rule->wc.vlan_tci_mask & htons(VLAN_CFI)
|
|
|
|
|
&& !(rule->flow.vlan_tci & htons(VLAN_CFI))) {
|
|
|
|
|
match->dl_vlan = htons(OFP_VLAN_NONE);
|
|
|
|
|
} else {
|
|
|
|
|
if (!(rule->wc.vlan_tci_mask & htons(VLAN_VID_MASK))) {
|
|
|
|
|
ofpfw |= OFPFW_DL_VLAN;
|
|
|
|
|
} else {
|
|
|
|
|
match->dl_vlan = htons(vlan_tci_to_vid(rule->flow.vlan_tci));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(rule->wc.vlan_tci_mask & htons(VLAN_PCP_MASK))) {
|
|
|
|
|
ofpfw |= OFPFW_DL_VLAN_PCP;
|
|
|
|
|
} else {
|
|
|
|
|
match->dl_vlan_pcp = vlan_tci_to_pcp(rule->flow.vlan_tci);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Compose most of the match structure. */
|
2010-11-10 14:39:54 -08:00
|
|
|
|
match->wildcards = htonl(ofpfw);
|
2011-05-11 12:13:10 -07:00
|
|
|
|
match->in_port = htons(rule->flow.in_port);
|
2010-11-10 14:39:54 -08:00
|
|
|
|
memcpy(match->dl_src, rule->flow.dl_src, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(match->dl_dst, rule->flow.dl_dst, ETH_ADDR_LEN);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
match->dl_type = ofputil_dl_type_to_openflow(rule->flow.dl_type);
|
2010-11-10 14:39:54 -08:00
|
|
|
|
match->nw_src = rule->flow.nw_src;
|
|
|
|
|
match->nw_dst = rule->flow.nw_dst;
|
2011-11-09 17:10:27 -08:00
|
|
|
|
match->nw_tos = rule->flow.nw_tos & IP_DSCP_MASK;
|
2010-11-10 14:39:54 -08:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
/* Given a 'dl_type' value in the format used in struct flow, returns the
|
|
|
|
|
* corresponding 'dl_type' value for use in an OpenFlow ofp_match structure. */
|
|
|
|
|
ovs_be16
|
|
|
|
|
ofputil_dl_type_to_openflow(ovs_be16 flow_dl_type)
|
|
|
|
|
{
|
|
|
|
|
return (flow_dl_type == htons(FLOW_DL_TYPE_NONE)
|
|
|
|
|
? htons(OFP_DL_TYPE_NOT_ETH_TYPE)
|
|
|
|
|
: flow_dl_type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given a 'dl_type' value in the format used in an OpenFlow ofp_match
|
|
|
|
|
* structure, returns the corresponding 'dl_type' value for use in struct
|
|
|
|
|
* flow. */
|
|
|
|
|
ovs_be16
|
|
|
|
|
ofputil_dl_type_from_openflow(ovs_be16 ofp_dl_type)
|
|
|
|
|
{
|
|
|
|
|
return (ofp_dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)
|
|
|
|
|
? htons(FLOW_DL_TYPE_NONE)
|
|
|
|
|
: ofp_dl_type);
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-15 11:04:40 -08:00
|
|
|
|
/* Returns a transaction ID to use for an outgoing OpenFlow message. */
|
2010-11-16 11:00:25 -08:00
|
|
|
|
static ovs_be32
|
2010-05-27 13:14:05 -07:00
|
|
|
|
alloc_xid(void)
|
|
|
|
|
{
|
2010-11-15 11:04:40 -08:00
|
|
|
|
static uint32_t next_xid = 1;
|
2010-11-16 11:00:25 -08:00
|
|
|
|
return htonl(next_xid++);
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
|
|
|
|
/* Basic parsing of OpenFlow messages. */
|
2010-05-27 13:14:05 -07:00
|
|
|
|
|
2010-12-06 10:20:20 -08:00
|
|
|
|
struct ofputil_msg_type {
|
|
|
|
|
enum ofputil_msg_code code; /* OFPUTIL_*. */
|
2012-01-12 11:35:57 -08:00
|
|
|
|
uint8_t ofp_version; /* An OpenFlow version or 0 for "any". */
|
2010-12-06 10:20:20 -08:00
|
|
|
|
uint32_t value; /* OFPT_*, OFPST_*, NXT_*, or NXST_*. */
|
|
|
|
|
const char *name; /* e.g. "OFPT_FLOW_REMOVED". */
|
|
|
|
|
unsigned int min_size; /* Minimum total message size in bytes. */
|
|
|
|
|
/* 0 if 'min_size' is the exact size that the message must be. Otherwise,
|
|
|
|
|
* the message may exceed 'min_size' by an even multiple of this value. */
|
|
|
|
|
unsigned int extra_multiple;
|
|
|
|
|
};
|
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
/* Represents a malformed OpenFlow message. */
|
|
|
|
|
static const struct ofputil_msg_type ofputil_invalid_type = {
|
2012-01-12 11:35:57 -08:00
|
|
|
|
OFPUTIL_MSG_INVALID, 0, 0, "OFPUTIL_MSG_INVALID", 0, 0
|
2011-10-27 12:54:44 -07:00
|
|
|
|
};
|
|
|
|
|
|
2010-12-06 10:20:20 -08:00
|
|
|
|
struct ofputil_msg_category {
|
|
|
|
|
const char *name; /* e.g. "OpenFlow message" */
|
|
|
|
|
const struct ofputil_msg_type *types;
|
|
|
|
|
size_t n_types;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr missing_error; /* Error value for missing type. */
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ofputil_check_length(const struct ofputil_msg_type *type, unsigned int size)
|
2010-12-06 10:20:20 -08:00
|
|
|
|
{
|
|
|
|
|
switch (type->extra_multiple) {
|
|
|
|
|
case 0:
|
|
|
|
|
if (size != type->min_size) {
|
2011-10-27 12:54:44 -07:00
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
|
2010-12-06 10:20:20 -08:00
|
|
|
|
"length %u (expected length %u)",
|
2011-10-27 12:54:44 -07:00
|
|
|
|
type->name, size, type->min_size);
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
2011-10-27 12:54:44 -07:00
|
|
|
|
return 0;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
|
if (size < type->min_size) {
|
2011-10-27 12:54:44 -07:00
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
|
2010-12-06 10:20:20 -08:00
|
|
|
|
"length %u (expected length at least %u bytes)",
|
2011-10-27 12:54:44 -07:00
|
|
|
|
type->name, size, type->min_size);
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
2011-10-27 12:54:44 -07:00
|
|
|
|
return 0;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if (size < type->min_size
|
|
|
|
|
|| (size - type->min_size) % type->extra_multiple) {
|
2011-10-27 12:54:44 -07:00
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "received %s with incorrect "
|
2010-12-06 10:20:20 -08:00
|
|
|
|
"length %u (must be exactly %u bytes or longer "
|
|
|
|
|
"by an integer multiple of %u bytes)",
|
2011-10-27 12:54:44 -07:00
|
|
|
|
type->name, size,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
type->min_size, type->extra_multiple);
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
2011-10-27 12:54:44 -07:00
|
|
|
|
return 0;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ofputil_lookup_openflow_message(const struct ofputil_msg_category *cat,
|
2012-01-12 11:35:57 -08:00
|
|
|
|
uint8_t version, uint32_t value,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
const struct ofputil_msg_type **typep)
|
|
|
|
|
{
|
|
|
|
|
const struct ofputil_msg_type *type;
|
|
|
|
|
|
|
|
|
|
for (type = cat->types; type < &cat->types[cat->n_types]; type++) {
|
2012-01-12 11:35:57 -08:00
|
|
|
|
if (type->value == value
|
|
|
|
|
&& (!type->ofp_version || version == type->ofp_version)) {
|
2010-12-06 10:20:20 -08:00
|
|
|
|
*typep = type;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-13 16:20:06 -08:00
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "received %s of unknown type %"PRIu32,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
cat->name, value);
|
|
|
|
|
return cat->missing_error;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ofputil_decode_vendor(const struct ofp_header *oh, size_t length,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
const struct ofputil_msg_type **typep)
|
|
|
|
|
{
|
|
|
|
|
static const struct ofputil_msg_type nxt_messages[] = {
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXT_ROLE_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
NXT_ROLE_REQUEST, "NXT_ROLE_REQUEST",
|
|
|
|
|
sizeof(struct nx_role_request), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXT_ROLE_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
NXT_ROLE_REPLY, "NXT_ROLE_REPLY",
|
|
|
|
|
sizeof(struct nx_role_request), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXT_SET_FLOW_FORMAT, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
NXT_SET_FLOW_FORMAT, "NXT_SET_FLOW_FORMAT",
|
2012-01-12 11:35:50 -08:00
|
|
|
|
sizeof(struct nx_set_flow_format), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXT_SET_PACKET_IN_FORMAT, OFP10_VERSION,
|
2011-12-09 15:48:26 -08:00
|
|
|
|
NXT_SET_PACKET_IN_FORMAT, "NXT_SET_PACKET_IN_FORMAT",
|
2012-01-12 11:35:50 -08:00
|
|
|
|
sizeof(struct nx_set_packet_in_format), 0 },
|
2011-12-09 15:48:26 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXT_PACKET_IN, OFP10_VERSION,
|
2011-12-09 15:48:26 -08:00
|
|
|
|
NXT_PACKET_IN, "NXT_PACKET_IN",
|
2012-01-12 11:35:50 -08:00
|
|
|
|
sizeof(struct nx_packet_in), 1 },
|
2011-12-09 15:48:26 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXT_FLOW_MOD, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
NXT_FLOW_MOD, "NXT_FLOW_MOD",
|
|
|
|
|
sizeof(struct nx_flow_mod), 8 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXT_FLOW_REMOVED, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
NXT_FLOW_REMOVED, "NXT_FLOW_REMOVED",
|
|
|
|
|
sizeof(struct nx_flow_removed), 8 },
|
2011-06-09 15:18:21 -07:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXT_FLOW_MOD_TABLE_ID, OFP10_VERSION,
|
2011-06-09 15:18:21 -07:00
|
|
|
|
NXT_FLOW_MOD_TABLE_ID, "NXT_FLOW_MOD_TABLE_ID",
|
2012-01-12 11:35:50 -08:00
|
|
|
|
sizeof(struct nx_flow_mod_table_id), 0 },
|
2012-02-07 10:13:52 -08:00
|
|
|
|
|
|
|
|
|
{ OFPUTIL_NXT_FLOW_AGE, OFP10_VERSION,
|
|
|
|
|
NXT_FLOW_AGE, "NXT_FLOW_AGE",
|
|
|
|
|
sizeof(struct nicira_header), 0 },
|
2012-02-09 14:06:35 -08:00
|
|
|
|
|
|
|
|
|
{ OFPUTIL_NXT_SET_ASYNC_CONFIG, OFP10_VERSION,
|
|
|
|
|
NXT_SET_ASYNC_CONFIG, "NXT_SET_ASYNC_CONFIG",
|
|
|
|
|
sizeof(struct nx_async_config), 0 },
|
2012-02-09 14:17:33 -08:00
|
|
|
|
|
|
|
|
|
{ OFPUTIL_NXT_SET_CONTROLLER_ID, OFP10_VERSION,
|
|
|
|
|
NXT_SET_CONTROLLER_ID, "NXT_SET_CONTROLLER_ID",
|
|
|
|
|
sizeof(struct nx_controller_id), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct ofputil_msg_category nxt_category = {
|
|
|
|
|
"Nicira extension message",
|
|
|
|
|
nxt_messages, ARRAY_SIZE(nxt_messages),
|
2012-01-12 15:48:19 -08:00
|
|
|
|
OFPERR_OFPBRC_BAD_SUBTYPE
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const struct ofp_vendor_header *ovh;
|
|
|
|
|
const struct nicira_header *nh;
|
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
if (length < sizeof(struct ofp_vendor_header)) {
|
|
|
|
|
if (length == ntohs(oh->length)) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor message");
|
|
|
|
|
}
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2011-10-27 12:54:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ovh = (const struct ofp_vendor_header *) oh;
|
|
|
|
|
if (ovh->vendor != htonl(NX_VENDOR_ID)) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor message for unknown "
|
|
|
|
|
"vendor %"PRIx32, ntohl(ovh->vendor));
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_VENDOR;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
if (length < sizeof(struct nicira_header)) {
|
|
|
|
|
if (length == ntohs(oh->length)) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "received Nicira vendor message of "
|
|
|
|
|
"length %u (expected at least %zu)",
|
|
|
|
|
ntohs(ovh->header.length),
|
|
|
|
|
sizeof(struct nicira_header));
|
|
|
|
|
}
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nh = (const struct nicira_header *) oh;
|
2012-01-12 11:35:57 -08:00
|
|
|
|
return ofputil_lookup_openflow_message(&nxt_category, oh->version,
|
|
|
|
|
ntohl(nh->subtype), typep);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
check_nxstats_msg(const struct ofp_header *oh, size_t length)
|
2010-12-06 10:20:20 -08:00
|
|
|
|
{
|
2011-05-26 09:30:25 -07:00
|
|
|
|
const struct ofp_stats_msg *osm = (const struct ofp_stats_msg *) oh;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
ovs_be32 vendor;
|
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
if (length < sizeof(struct ofp_vendor_stats_msg)) {
|
|
|
|
|
if (length == ntohs(oh->length)) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "truncated vendor stats message");
|
|
|
|
|
}
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2011-10-27 12:54:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-05-26 09:34:05 -07:00
|
|
|
|
memcpy(&vendor, osm + 1, sizeof vendor);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
if (vendor != htonl(NX_VENDOR_ID)) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "received vendor stats message for "
|
|
|
|
|
"unknown vendor %"PRIx32, ntohl(vendor));
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_VENDOR;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
if (length < sizeof(struct nicira_stats_msg)) {
|
|
|
|
|
if (length == ntohs(osm->header.length)) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "truncated Nicira stats message");
|
|
|
|
|
}
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ofputil_decode_nxst_request(const struct ofp_header *oh, size_t length,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
const struct ofputil_msg_type **typep)
|
|
|
|
|
{
|
|
|
|
|
static const struct ofputil_msg_type nxst_requests[] = {
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXST_FLOW_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
NXST_FLOW, "NXST_FLOW request",
|
|
|
|
|
sizeof(struct nx_flow_stats_request), 8 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXST_AGGREGATE_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
NXST_AGGREGATE, "NXST_AGGREGATE request",
|
|
|
|
|
sizeof(struct nx_aggregate_stats_request), 8 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct ofputil_msg_category nxst_request_category = {
|
2010-12-13 16:20:24 -08:00
|
|
|
|
"Nicira extension statistics request",
|
2010-12-06 10:20:20 -08:00
|
|
|
|
nxst_requests, ARRAY_SIZE(nxst_requests),
|
2012-01-12 15:48:19 -08:00
|
|
|
|
OFPERR_OFPBRC_BAD_SUBTYPE
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const struct nicira_stats_msg *nsm;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
error = check_nxstats_msg(oh, length);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsm = (struct nicira_stats_msg *) oh;
|
2012-01-12 11:35:57 -08:00
|
|
|
|
return ofputil_lookup_openflow_message(&nxst_request_category, oh->version,
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ntohl(nsm->subtype), typep);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ofputil_decode_nxst_reply(const struct ofp_header *oh, size_t length,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
const struct ofputil_msg_type **typep)
|
|
|
|
|
{
|
|
|
|
|
static const struct ofputil_msg_type nxst_replies[] = {
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXST_FLOW_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
NXST_FLOW, "NXST_FLOW reply",
|
|
|
|
|
sizeof(struct nicira_stats_msg), 8 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_NXST_AGGREGATE_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
NXST_AGGREGATE, "NXST_AGGREGATE reply",
|
|
|
|
|
sizeof(struct nx_aggregate_stats_reply), 0 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct ofputil_msg_category nxst_reply_category = {
|
2010-12-13 16:20:24 -08:00
|
|
|
|
"Nicira extension statistics reply",
|
2010-12-06 10:20:20 -08:00
|
|
|
|
nxst_replies, ARRAY_SIZE(nxst_replies),
|
2012-01-12 15:48:19 -08:00
|
|
|
|
OFPERR_OFPBRC_BAD_SUBTYPE
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const struct nicira_stats_msg *nsm;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
error = check_nxstats_msg(oh, length);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nsm = (struct nicira_stats_msg *) oh;
|
2012-01-12 11:35:57 -08:00
|
|
|
|
return ofputil_lookup_openflow_message(&nxst_reply_category, oh->version,
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ntohl(nsm->subtype), typep);
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
check_stats_msg(const struct ofp_header *oh, size_t length)
|
|
|
|
|
{
|
|
|
|
|
if (length < sizeof(struct ofp_stats_msg)) {
|
|
|
|
|
if (length == ntohs(oh->length)) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "truncated stats message");
|
|
|
|
|
}
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2011-10-27 12:54:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ofputil_decode_ofpst_request(const struct ofp_header *oh, size_t length,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
const struct ofputil_msg_type **typep)
|
|
|
|
|
{
|
|
|
|
|
static const struct ofputil_msg_type ofpst_requests[] = {
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_DESC_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_DESC, "OFPST_DESC request",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_stats_msg), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_FLOW_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_FLOW, "OFPST_FLOW request",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_flow_stats_request), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_AGGREGATE_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_AGGREGATE, "OFPST_AGGREGATE request",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_flow_stats_request), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_TABLE_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_TABLE, "OFPST_TABLE request",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_stats_msg), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_PORT_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_PORT, "OFPST_PORT request",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_port_stats_request), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_QUEUE_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_QUEUE, "OFPST_QUEUE request",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_queue_stats_request), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-05-04 14:42:04 -07:00
|
|
|
|
{ OFPUTIL_OFPST_PORT_DESC_REQUEST, OFP10_VERSION,
|
|
|
|
|
OFPST_PORT_DESC, "OFPST_PORT_DESC request",
|
|
|
|
|
sizeof(struct ofp_stats_msg), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ 0, 0,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_VENDOR, "OFPST_VENDOR request",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_vendor_stats_msg), 1 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct ofputil_msg_category ofpst_request_category = {
|
|
|
|
|
"OpenFlow statistics",
|
|
|
|
|
ofpst_requests, ARRAY_SIZE(ofpst_requests),
|
2012-01-12 15:48:19 -08:00
|
|
|
|
OFPERR_OFPBRC_BAD_STAT
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
2011-05-26 09:30:25 -07:00
|
|
|
|
const struct ofp_stats_msg *request = (const struct ofp_stats_msg *) oh;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
error = check_stats_msg(oh, length);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-06 10:20:20 -08:00
|
|
|
|
error = ofputil_lookup_openflow_message(&ofpst_request_category,
|
2012-01-12 11:35:57 -08:00
|
|
|
|
oh->version, ntohs(request->type),
|
|
|
|
|
typep);
|
2011-05-26 09:30:25 -07:00
|
|
|
|
if (!error && request->type == htons(OFPST_VENDOR)) {
|
2011-10-27 12:54:44 -07:00
|
|
|
|
error = ofputil_decode_nxst_request(oh, length, typep);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ofputil_decode_ofpst_reply(const struct ofp_header *oh, size_t length,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
const struct ofputil_msg_type **typep)
|
|
|
|
|
{
|
|
|
|
|
static const struct ofputil_msg_type ofpst_replies[] = {
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_DESC_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_DESC, "OFPST_DESC reply",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_desc_stats), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_FLOW_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_FLOW, "OFPST_FLOW reply",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_stats_msg), 1 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_AGGREGATE_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_AGGREGATE, "OFPST_AGGREGATE reply",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_aggregate_stats_reply), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_TABLE_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_TABLE, "OFPST_TABLE reply",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_stats_msg), sizeof(struct ofp_table_stats) },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_PORT_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_PORT, "OFPST_PORT reply",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_stats_msg), sizeof(struct ofp_port_stats) },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPST_QUEUE_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_QUEUE, "OFPST_QUEUE reply",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_stats_msg), sizeof(struct ofp_queue_stats) },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-05-04 14:42:04 -07:00
|
|
|
|
{ OFPUTIL_OFPST_PORT_DESC_REPLY, OFP10_VERSION,
|
|
|
|
|
OFPST_PORT_DESC, "OFPST_PORT_DESC reply",
|
|
|
|
|
sizeof(struct ofp_stats_msg), sizeof(struct ofp10_phy_port) },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ 0, 0,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPST_VENDOR, "OFPST_VENDOR reply",
|
2011-05-31 16:49:06 -07:00
|
|
|
|
sizeof(struct ofp_vendor_stats_msg), 1 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct ofputil_msg_category ofpst_reply_category = {
|
|
|
|
|
"OpenFlow statistics",
|
|
|
|
|
ofpst_replies, ARRAY_SIZE(ofpst_replies),
|
2012-01-12 15:48:19 -08:00
|
|
|
|
OFPERR_OFPBRC_BAD_STAT
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
2011-05-26 09:30:25 -07:00
|
|
|
|
const struct ofp_stats_msg *reply = (const struct ofp_stats_msg *) oh;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
error = check_stats_msg(oh, length);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
error = ofputil_lookup_openflow_message(&ofpst_reply_category, oh->version,
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ntohs(reply->type), typep);
|
2011-05-26 09:30:25 -07:00
|
|
|
|
if (!error && reply->type == htons(OFPST_VENDOR)) {
|
2011-10-27 12:54:44 -07:00
|
|
|
|
error = ofputil_decode_nxst_reply(oh, length, typep);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ofputil_decode_msg_type__(const struct ofp_header *oh, size_t length,
|
|
|
|
|
const struct ofputil_msg_type **typep)
|
2010-12-06 10:20:20 -08:00
|
|
|
|
{
|
|
|
|
|
static const struct ofputil_msg_type ofpt_messages[] = {
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_HELLO, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_HELLO, "OFPT_HELLO",
|
|
|
|
|
sizeof(struct ofp_hello), 1 },
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
{ OFPUTIL_OFPT_ERROR, 0,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_ERROR, "OFPT_ERROR",
|
|
|
|
|
sizeof(struct ofp_error_msg), 1 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_ECHO_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_ECHO_REQUEST, "OFPT_ECHO_REQUEST",
|
|
|
|
|
sizeof(struct ofp_header), 1 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_ECHO_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_ECHO_REPLY, "OFPT_ECHO_REPLY",
|
|
|
|
|
sizeof(struct ofp_header), 1 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_FEATURES_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_FEATURES_REQUEST, "OFPT_FEATURES_REQUEST",
|
|
|
|
|
sizeof(struct ofp_header), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_FEATURES_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY",
|
2012-02-15 16:33:04 -08:00
|
|
|
|
sizeof(struct ofp_switch_features), sizeof(struct ofp10_phy_port) },
|
|
|
|
|
{ OFPUTIL_OFPT_FEATURES_REPLY, OFP11_VERSION,
|
|
|
|
|
OFPT_FEATURES_REPLY, "OFPT_FEATURES_REPLY",
|
|
|
|
|
sizeof(struct ofp_switch_features), sizeof(struct ofp11_port) },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_GET_CONFIG_REQUEST, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_GET_CONFIG_REQUEST, "OFPT_GET_CONFIG_REQUEST",
|
|
|
|
|
sizeof(struct ofp_header), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_GET_CONFIG_REPLY, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_GET_CONFIG_REPLY, "OFPT_GET_CONFIG_REPLY",
|
|
|
|
|
sizeof(struct ofp_switch_config), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_SET_CONFIG, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_SET_CONFIG, "OFPT_SET_CONFIG",
|
|
|
|
|
sizeof(struct ofp_switch_config), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_PACKET_IN, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_PACKET_IN, "OFPT_PACKET_IN",
|
|
|
|
|
offsetof(struct ofp_packet_in, data), 1 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_FLOW_REMOVED, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_FLOW_REMOVED, "OFPT_FLOW_REMOVED",
|
|
|
|
|
sizeof(struct ofp_flow_removed), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_PORT_STATUS, OFP10_VERSION,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_PORT_STATUS, "OFPT_PORT_STATUS",
|
2012-02-15 16:33:04 -08:00
|
|
|
|
sizeof(struct ofp_port_status) + sizeof(struct ofp10_phy_port), 0 },
|
|
|
|
|
{ OFPUTIL_OFPT_PORT_STATUS, OFP11_VERSION,
|
|
|
|
|
OFPT_PORT_STATUS, "OFPT_PORT_STATUS",
|
|
|
|
|
sizeof(struct ofp_port_status) + sizeof(struct ofp11_port), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_PACKET_OUT, OFP10_VERSION,
|
2012-02-14 16:58:39 -08:00
|
|
|
|
OFPT10_PACKET_OUT, "OFPT_PACKET_OUT",
|
2010-12-06 10:20:20 -08:00
|
|
|
|
sizeof(struct ofp_packet_out), 1 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_FLOW_MOD, OFP10_VERSION,
|
2012-02-14 16:58:39 -08:00
|
|
|
|
OFPT10_FLOW_MOD, "OFPT_FLOW_MOD",
|
2010-12-06 10:20:20 -08:00
|
|
|
|
sizeof(struct ofp_flow_mod), 1 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_PORT_MOD, OFP10_VERSION,
|
2012-02-14 16:58:39 -08:00
|
|
|
|
OFPT10_PORT_MOD, "OFPT_PORT_MOD",
|
2012-02-15 16:33:04 -08:00
|
|
|
|
sizeof(struct ofp10_port_mod), 0 },
|
|
|
|
|
{ OFPUTIL_OFPT_PORT_MOD, OFP11_VERSION,
|
|
|
|
|
OFPT11_PORT_MOD, "OFPT_PORT_MOD",
|
|
|
|
|
sizeof(struct ofp11_port_mod), 0 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ 0, OFP10_VERSION,
|
2012-02-14 16:58:39 -08:00
|
|
|
|
OFPT10_STATS_REQUEST, "OFPT_STATS_REQUEST",
|
2011-05-26 09:30:25 -07:00
|
|
|
|
sizeof(struct ofp_stats_msg), 1 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ 0, OFP10_VERSION,
|
2012-02-14 16:58:39 -08:00
|
|
|
|
OFPT10_STATS_REPLY, "OFPT_STATS_REPLY",
|
2011-05-26 09:30:25 -07:00
|
|
|
|
sizeof(struct ofp_stats_msg), 1 },
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_BARRIER_REQUEST, OFP10_VERSION,
|
2012-02-14 16:58:39 -08:00
|
|
|
|
OFPT10_BARRIER_REQUEST, "OFPT_BARRIER_REQUEST",
|
2010-12-06 10:20:20 -08:00
|
|
|
|
sizeof(struct ofp_header), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ OFPUTIL_OFPT_BARRIER_REPLY, OFP10_VERSION,
|
2012-02-14 16:58:39 -08:00
|
|
|
|
OFPT10_BARRIER_REPLY, "OFPT_BARRIER_REPLY",
|
2010-12-06 10:20:20 -08:00
|
|
|
|
sizeof(struct ofp_header), 0 },
|
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
{ 0, 0,
|
2010-12-06 10:20:20 -08:00
|
|
|
|
OFPT_VENDOR, "OFPT_VENDOR",
|
|
|
|
|
sizeof(struct ofp_vendor_header), 1 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct ofputil_msg_category ofpt_category = {
|
|
|
|
|
"OpenFlow message",
|
|
|
|
|
ofpt_messages, ARRAY_SIZE(ofpt_messages),
|
2012-01-12 15:48:19 -08:00
|
|
|
|
OFPERR_OFPBRC_BAD_TYPE
|
2010-12-06 10:20:20 -08:00
|
|
|
|
};
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-01-12 11:35:57 -08:00
|
|
|
|
error = ofputil_lookup_openflow_message(&ofpt_category, oh->version,
|
|
|
|
|
oh->type, typep);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
if (!error) {
|
2012-02-15 15:39:41 -08:00
|
|
|
|
switch ((oh->version << 8) | oh->type) {
|
|
|
|
|
case (OFP10_VERSION << 8) | OFPT_VENDOR:
|
|
|
|
|
case (OFP11_VERSION << 8) | OFPT_VENDOR:
|
2011-10-27 12:54:44 -07:00
|
|
|
|
error = ofputil_decode_vendor(oh, length, typep);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-02-15 15:39:41 -08:00
|
|
|
|
case (OFP10_VERSION << 8) | OFPT10_STATS_REQUEST:
|
|
|
|
|
case (OFP11_VERSION << 8) | OFPT11_STATS_REQUEST:
|
2011-10-27 12:54:44 -07:00
|
|
|
|
error = ofputil_decode_ofpst_request(oh, length, typep);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2012-02-15 15:39:41 -08:00
|
|
|
|
case (OFP10_VERSION << 8) | OFPT10_STATS_REPLY:
|
|
|
|
|
case (OFP11_VERSION << 8) | OFPT11_STATS_REPLY:
|
2011-10-27 12:54:44 -07:00
|
|
|
|
error = ofputil_decode_ofpst_reply(oh, length, typep);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-10-27 12:54:44 -07:00
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
/* Decodes the message type represented by 'oh'. Returns 0 if successful or an
|
|
|
|
|
* OpenFlow error code on failure. Either way, stores in '*typep' a type
|
|
|
|
|
* structure that can be inspected with the ofputil_msg_type_*() functions.
|
2011-10-27 12:54:44 -07:00
|
|
|
|
*
|
|
|
|
|
* oh->length must indicate the correct length of the message (and must be at
|
|
|
|
|
* least sizeof(struct ofp_header)).
|
|
|
|
|
*
|
|
|
|
|
* Success indicates that 'oh' is at least as long as the minimum-length
|
|
|
|
|
* message of its type. */
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ofputil_decode_msg_type(const struct ofp_header *oh,
|
|
|
|
|
const struct ofputil_msg_type **typep)
|
|
|
|
|
{
|
|
|
|
|
size_t length = ntohs(oh->length);
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2011-10-27 12:54:44 -07:00
|
|
|
|
|
|
|
|
|
error = ofputil_decode_msg_type__(oh, length, typep);
|
|
|
|
|
if (!error) {
|
|
|
|
|
error = ofputil_check_length(*typep, length);
|
|
|
|
|
}
|
2010-12-06 10:20:20 -08:00
|
|
|
|
if (error) {
|
2011-10-27 12:54:44 -07:00
|
|
|
|
*typep = &ofputil_invalid_type;
|
|
|
|
|
}
|
|
|
|
|
return error;
|
|
|
|
|
}
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2011-10-27 12:54:44 -07:00
|
|
|
|
/* Decodes the message type represented by 'oh', of which only the first
|
|
|
|
|
* 'length' bytes are available. Returns 0 if successful or an OpenFlow error
|
2012-01-12 15:48:19 -08:00
|
|
|
|
* code on failure. Either way, stores in '*typep' a type structure that can
|
|
|
|
|
* be inspected with the ofputil_msg_type_*() functions. */
|
|
|
|
|
enum ofperr
|
2011-10-27 12:54:44 -07:00
|
|
|
|
ofputil_decode_msg_type_partial(const struct ofp_header *oh, size_t length,
|
|
|
|
|
const struct ofputil_msg_type **typep)
|
|
|
|
|
{
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2011-10-27 12:54:44 -07:00
|
|
|
|
|
|
|
|
|
error = (length >= sizeof *oh
|
|
|
|
|
? ofputil_decode_msg_type__(oh, length, typep)
|
2012-01-12 15:48:19 -08:00
|
|
|
|
: OFPERR_OFPBRC_BAD_LEN);
|
2011-10-27 12:54:44 -07:00
|
|
|
|
if (error) {
|
2010-12-06 10:20:20 -08:00
|
|
|
|
*typep = &ofputil_invalid_type;
|
|
|
|
|
}
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns an OFPUTIL_* message type code for 'type'. */
|
|
|
|
|
enum ofputil_msg_code
|
|
|
|
|
ofputil_msg_type_code(const struct ofputil_msg_type *type)
|
|
|
|
|
{
|
|
|
|
|
return type->code;
|
|
|
|
|
}
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Protocols. */
|
2010-12-07 13:22:46 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
struct proto_abbrev {
|
|
|
|
|
enum ofputil_protocol protocol;
|
|
|
|
|
const char *name;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* 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" },
|
|
|
|
|
};
|
|
|
|
|
#define N_PROTO_ABBREVS ARRAY_SIZE(proto_abbrevs)
|
|
|
|
|
|
|
|
|
|
enum ofputil_protocol ofputil_flow_dump_protocols[] = {
|
|
|
|
|
OFPUTIL_P_NXM,
|
|
|
|
|
OFPUTIL_P_OF10,
|
|
|
|
|
};
|
|
|
|
|
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. */
|
|
|
|
|
enum ofputil_protocol
|
|
|
|
|
ofputil_protocol_from_ofp_version(int version)
|
|
|
|
|
{
|
|
|
|
|
switch (version) {
|
2011-12-06 22:33:49 -08:00
|
|
|
|
case OFP10_VERSION: return OFPUTIL_P_OF10;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
default: return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
/* Returns the OpenFlow protocol version number (e.g. OFP10_VERSION or
|
|
|
|
|
* OFP11_VERSION) that corresponds to 'protocol'. */
|
|
|
|
|
uint8_t
|
|
|
|
|
ofputil_protocol_to_ofp_version(enum ofputil_protocol protocol)
|
|
|
|
|
{
|
|
|
|
|
switch (protocol) {
|
|
|
|
|
case OFPUTIL_P_OF10:
|
|
|
|
|
case OFPUTIL_P_OF10_TID:
|
|
|
|
|
case OFPUTIL_P_NXM:
|
|
|
|
|
case OFPUTIL_P_NXM_TID:
|
|
|
|
|
return OFP10_VERSION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Returns true if 'protocol' is a single OFPUTIL_P_* value, false
|
|
|
|
|
* otherwise. */
|
2010-12-07 13:22:46 -08:00
|
|
|
|
bool
|
2012-02-10 13:30:23 -08:00
|
|
|
|
ofputil_protocol_is_valid(enum ofputil_protocol protocol)
|
2010-12-07 13:22:46 -08:00
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return protocol & OFPUTIL_P_ANY && is_pow2(protocol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns the equivalent of 'protocol' with the Nicira flow_mod_table_id
|
|
|
|
|
* extension turned on or off if 'enable' is true or false, respectively.
|
|
|
|
|
*
|
|
|
|
|
* This extension is only useful for protocols whose "standard" version does
|
|
|
|
|
* not allow specific tables to be modified. In particular, this is true of
|
|
|
|
|
* OpenFlow 1.0. In later versions of OpenFlow, a flow_mod request always
|
|
|
|
|
* specifies a table ID and so there is no need for such an extension. When
|
|
|
|
|
* 'protocol' is such a protocol that doesn't need a flow_mod_table_id
|
|
|
|
|
* extension, this function just returns its 'protocol' argument unchanged
|
|
|
|
|
* regardless of the value of 'enable'. */
|
|
|
|
|
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_NXM:
|
|
|
|
|
case OFPUTIL_P_NXM_TID:
|
|
|
|
|
return enable ? OFPUTIL_P_NXM_TID : OFPUTIL_P_NXM;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
NOT_REACHED();
|
2010-12-07 13:22:46 -08:00
|
|
|
|
}
|
2012-02-10 13:30:23 -08:00
|
|
|
|
}
|
2010-12-07 13:22:46 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Returns the "base" version of 'protocol'. That is, if 'protocol' includes
|
|
|
|
|
* some extension to a standard protocol version, the return value is the
|
|
|
|
|
* standard version of that protocol without any extension. If 'protocol' is a
|
|
|
|
|
* standard protocol version, returns 'protocol' unchanged. */
|
|
|
|
|
enum ofputil_protocol
|
|
|
|
|
ofputil_protocol_to_base(enum ofputil_protocol protocol)
|
|
|
|
|
{
|
|
|
|
|
return ofputil_protocol_set_tid(protocol, false);
|
2010-12-07 13:22:46 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Returns 'new_base' with any extensions taken from 'cur'. */
|
|
|
|
|
enum ofputil_protocol
|
|
|
|
|
ofputil_protocol_set_base(enum ofputil_protocol cur,
|
|
|
|
|
enum ofputil_protocol new_base)
|
2010-12-07 13:22:46 -08:00
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
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_NXM:
|
|
|
|
|
case OFPUTIL_P_NXM_TID:
|
|
|
|
|
return ofputil_protocol_set_tid(OFPUTIL_P_NXM, tid);
|
|
|
|
|
|
2010-12-07 13:22:46 -08:00
|
|
|
|
default:
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Returns a string form of 'protocol', if a simple form exists (that is, if
|
|
|
|
|
* 'protocol' is either a single protocol or it is a combination of protocols
|
|
|
|
|
* that have a single abbreviation). Otherwise, returns NULL. */
|
|
|
|
|
const char *
|
|
|
|
|
ofputil_protocol_to_string(enum ofputil_protocol protocol)
|
2010-12-07 13:32:01 -08:00
|
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
|
const struct proto_abbrev *p;
|
|
|
|
|
|
|
|
|
|
/* 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:
|
|
|
|
|
return "NXM-table_id";
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_P_NXM_TID:
|
|
|
|
|
return "NXM+table_id";
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_P_OF10:
|
|
|
|
|
return "OpenFlow10-table_id";
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_P_OF10_TID:
|
|
|
|
|
return "OpenFlow10+table_id";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check abbreviations. */
|
|
|
|
|
for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) {
|
|
|
|
|
if (protocol == p->protocol) {
|
|
|
|
|
return p->name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns a string that represents 'protocols'. The return value might be a
|
|
|
|
|
* comma-separated list if 'protocols' doesn't have a simple name. The return
|
|
|
|
|
* value is "none" if 'protocols' is 0.
|
|
|
|
|
*
|
|
|
|
|
* The caller must free the returned string (with free()). */
|
|
|
|
|
char *
|
|
|
|
|
ofputil_protocols_to_string(enum ofputil_protocol protocols)
|
|
|
|
|
{
|
|
|
|
|
struct ds s;
|
|
|
|
|
|
|
|
|
|
assert(!(protocols & ~OFPUTIL_P_ANY));
|
|
|
|
|
if (protocols == 0) {
|
|
|
|
|
return xstrdup("none");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_init(&s);
|
|
|
|
|
while (protocols) {
|
|
|
|
|
const struct proto_abbrev *p;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
if (s.length) {
|
|
|
|
|
ds_put_char(&s, ',');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) {
|
|
|
|
|
if ((protocols & p->protocol) == p->protocol) {
|
|
|
|
|
ds_put_cstr(&s, p->name);
|
|
|
|
|
protocols &= ~p->protocol;
|
|
|
|
|
goto match;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < CHAR_BIT * sizeof(enum ofputil_protocol); i++) {
|
|
|
|
|
enum ofputil_protocol bit = 1u << i;
|
|
|
|
|
|
|
|
|
|
if (protocols & bit) {
|
|
|
|
|
ds_put_cstr(&s, ofputil_protocol_to_string(bit));
|
|
|
|
|
protocols &= ~bit;
|
|
|
|
|
goto match;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
|
|
|
|
|
match: ;
|
|
|
|
|
}
|
|
|
|
|
return ds_steal_cstr(&s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum ofputil_protocol
|
|
|
|
|
ofputil_protocol_from_string__(const char *s, size_t n)
|
|
|
|
|
{
|
|
|
|
|
const struct proto_abbrev *p;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < CHAR_BIT * sizeof(enum ofputil_protocol); i++) {
|
|
|
|
|
enum ofputil_protocol bit = 1u << i;
|
|
|
|
|
const char *name = ofputil_protocol_to_string(bit);
|
|
|
|
|
|
|
|
|
|
if (name && n == strlen(name) && !strncasecmp(s, name, n)) {
|
|
|
|
|
return bit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (p = proto_abbrevs; p < &proto_abbrevs[N_PROTO_ABBREVS]; p++) {
|
|
|
|
|
if (n == strlen(p->name) && !strncasecmp(s, p->name, n)) {
|
|
|
|
|
return p->protocol;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns the nonempty set of protocols represented by 's', which can be a
|
|
|
|
|
* single protocol name or abbreviation or a comma-separated list of them.
|
|
|
|
|
*
|
|
|
|
|
* Aborts the program with an error message if 's' is invalid. */
|
|
|
|
|
enum ofputil_protocol
|
|
|
|
|
ofputil_protocols_from_string(const char *s)
|
|
|
|
|
{
|
|
|
|
|
const char *orig_s = s;
|
|
|
|
|
enum ofputil_protocol protocols;
|
|
|
|
|
|
|
|
|
|
protocols = 0;
|
|
|
|
|
while (*s) {
|
|
|
|
|
enum ofputil_protocol p;
|
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
|
|
n = strcspn(s, ",");
|
|
|
|
|
if (n == 0) {
|
|
|
|
|
s++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p = ofputil_protocol_from_string__(s, n);
|
|
|
|
|
if (!p) {
|
|
|
|
|
ovs_fatal(0, "%.*s: unknown flow protocol", (int) n, s);
|
|
|
|
|
}
|
|
|
|
|
protocols |= p;
|
|
|
|
|
|
|
|
|
|
s += n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!protocols) {
|
|
|
|
|
ovs_fatal(0, "%s: no flow protocol specified", orig_s);
|
|
|
|
|
}
|
|
|
|
|
return protocols;
|
2010-12-07 13:32:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-12-09 15:48:26 -08:00
|
|
|
|
bool
|
|
|
|
|
ofputil_packet_in_format_is_valid(enum nx_packet_in_format packet_in_format)
|
|
|
|
|
{
|
|
|
|
|
switch (packet_in_format) {
|
|
|
|
|
case NXPIF_OPENFLOW10:
|
|
|
|
|
case NXPIF_NXM:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char *
|
|
|
|
|
ofputil_packet_in_format_to_string(enum nx_packet_in_format packet_in_format)
|
|
|
|
|
{
|
|
|
|
|
switch (packet_in_format) {
|
|
|
|
|
case NXPIF_OPENFLOW10:
|
|
|
|
|
return "openflow10";
|
|
|
|
|
case NXPIF_NXM:
|
|
|
|
|
return "nxm";
|
|
|
|
|
default:
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ofputil_packet_in_format_from_string(const char *s)
|
|
|
|
|
{
|
|
|
|
|
return (!strcmp(s, "openflow10") ? NXPIF_OPENFLOW10
|
|
|
|
|
: !strcmp(s, "nxm") ? NXPIF_NXM
|
|
|
|
|
: -1);
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:32:01 -08:00
|
|
|
|
static bool
|
|
|
|
|
regs_fully_wildcarded(const struct flow_wildcards *wc)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < FLOW_N_REGS; i++) {
|
|
|
|
|
if (wc->reg_masks[i] != 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Returns a bit-mask of ofputil_protocols that can be used for sending 'rule'
|
|
|
|
|
* 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)
|
2011-01-20 15:29:00 -08:00
|
|
|
|
{
|
|
|
|
|
const struct flow_wildcards *wc = &rule->wc;
|
|
|
|
|
|
2012-05-29 00:38:21 +12:00
|
|
|
|
BUILD_ASSERT_DECL(FLOW_WC_SEQ == 11);
|
2011-07-29 13:15:09 -07:00
|
|
|
|
|
2012-05-29 00:38:21 +12:00
|
|
|
|
/* NXM and OF1.1+ supports bitwise matching on ethernet addresses. */
|
|
|
|
|
if (!eth_mask_is_exact(wc->dl_src_mask)
|
|
|
|
|
&& !eth_addr_is_zero(wc->dl_src_mask)) {
|
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
|
|
|
|
}
|
|
|
|
|
if (!eth_mask_is_exact(wc->dl_dst_mask)
|
|
|
|
|
&& !eth_addr_is_zero(wc->dl_dst_mask)) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
2011-01-20 15:29:00 -08:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 14:02:17 -08:00
|
|
|
|
/* Only NXM supports matching ARP hardware addresses. */
|
|
|
|
|
if (!(wc->wildcards & FWW_ARP_SHA) || !(wc->wildcards & FWW_ARP_THA)) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
2010-12-07 14:02:17 -08:00
|
|
|
|
}
|
|
|
|
|
|
2010-12-29 19:03:46 -08:00
|
|
|
|
/* Only NXM supports matching IPv6 traffic. */
|
|
|
|
|
if (!(wc->wildcards & FWW_DL_TYPE)
|
|
|
|
|
&& (rule->flow.dl_type == htons(ETH_TYPE_IPV6))) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-20 15:29:00 -08:00
|
|
|
|
/* Only NXM supports matching registers. */
|
|
|
|
|
if (!regs_fully_wildcarded(wc)) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
2011-01-20 15:29:00 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-26 09:42:18 -07:00
|
|
|
|
/* Only NXM supports matching tun_id. */
|
|
|
|
|
if (wc->tun_id_mask != htonll(0)) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
2011-01-20 15:29:00 -08:00
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
/* Only NXM supports matching fragments. */
|
2011-11-09 17:10:27 -08:00
|
|
|
|
if (wc->nw_frag_mask) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-01 15:57:56 -07:00
|
|
|
|
/* Only NXM supports matching IPv6 flow label. */
|
|
|
|
|
if (!(wc->wildcards & FWW_IPV6_LABEL)) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
2011-11-01 15:57:56 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-02 23:34:15 -07:00
|
|
|
|
/* Only NXM supports matching IP ECN bits. */
|
2011-11-09 23:39:16 -08:00
|
|
|
|
if (!(wc->wildcards & FWW_NW_ECN)) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
2011-11-02 23:34:15 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-05 15:48:12 -07:00
|
|
|
|
/* Only NXM supports matching IP TTL/hop limit. */
|
|
|
|
|
if (!(wc->wildcards & FWW_NW_TTL)) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
2011-11-05 15:48:12 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-27 17:16:05 -08:00
|
|
|
|
/* 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))) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_NXM_ANY;
|
2012-01-27 17:16:05 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-20 15:29:00 -08:00
|
|
|
|
/* Other formats can express this rule. */
|
2012-02-10 13:30:23 -08:00
|
|
|
|
return OFPUTIL_P_ANY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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'. */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_set_protocol(enum ofputil_protocol current,
|
|
|
|
|
enum ofputil_protocol want,
|
|
|
|
|
enum ofputil_protocol *next)
|
|
|
|
|
{
|
|
|
|
|
enum ofputil_protocol cur_base, want_base;
|
|
|
|
|
bool cur_tid, want_tid;
|
|
|
|
|
|
|
|
|
|
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:
|
|
|
|
|
return ofputil_encode_nx_set_flow_format(NXFF_NXM);
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_P_OF10:
|
|
|
|
|
return ofputil_encode_nx_set_flow_format(NXFF_OPENFLOW10);
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_P_OF10_TID:
|
|
|
|
|
case OFPUTIL_P_NXM_TID:
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cur_tid = (current & OFPUTIL_P_TID) != 0;
|
|
|
|
|
want_tid = (want & OFPUTIL_P_TID) != 0;
|
|
|
|
|
if (cur_tid != want_tid) {
|
|
|
|
|
*next = ofputil_protocol_set_tid(current, want_tid);
|
|
|
|
|
return ofputil_make_flow_mod_table_id(want_tid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(current == want);
|
|
|
|
|
|
|
|
|
|
*next = current;
|
|
|
|
|
return NULL;
|
2010-12-07 13:32:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Returns an NXT_SET_FLOW_FORMAT message that can be used to set the flow
|
|
|
|
|
* format to 'nxff'. */
|
2010-12-07 13:32:01 -08:00
|
|
|
|
struct ofpbuf *
|
2012-02-10 13:30:23 -08:00
|
|
|
|
ofputil_encode_nx_set_flow_format(enum nx_flow_format nxff)
|
2010-12-07 13:32:01 -08:00
|
|
|
|
{
|
2012-01-12 11:35:50 -08:00
|
|
|
|
struct nx_set_flow_format *sff;
|
2010-12-07 13:32:01 -08:00
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
assert(ofputil_nx_flow_format_is_valid(nxff));
|
|
|
|
|
|
2011-04-26 09:42:18 -07:00
|
|
|
|
sff = make_nxmsg(sizeof *sff, NXT_SET_FLOW_FORMAT, &msg);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
sff->format = htonl(nxff);
|
2010-12-07 13:32:01 -08:00
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Returns the base protocol if 'flow_format' is a valid NXFF_* value, false
|
|
|
|
|
* otherwise. */
|
|
|
|
|
enum ofputil_protocol
|
|
|
|
|
ofputil_nx_flow_format_to_protocol(enum nx_flow_format flow_format)
|
|
|
|
|
{
|
|
|
|
|
switch (flow_format) {
|
|
|
|
|
case NXFF_OPENFLOW10:
|
|
|
|
|
return OFPUTIL_P_OF10;
|
|
|
|
|
|
|
|
|
|
case NXFF_NXM:
|
|
|
|
|
return OFPUTIL_P_NXM;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns true if 'flow_format' is a valid NXFF_* value, false otherwise. */
|
|
|
|
|
bool
|
|
|
|
|
ofputil_nx_flow_format_is_valid(enum nx_flow_format flow_format)
|
|
|
|
|
{
|
|
|
|
|
return ofputil_nx_flow_format_to_protocol(flow_format) != 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns a string version of 'flow_format', which must be a valid NXFF_*
|
|
|
|
|
* value. */
|
|
|
|
|
const char *
|
|
|
|
|
ofputil_nx_flow_format_to_string(enum nx_flow_format flow_format)
|
|
|
|
|
{
|
|
|
|
|
switch (flow_format) {
|
|
|
|
|
case NXFF_OPENFLOW10:
|
|
|
|
|
return "openflow10";
|
|
|
|
|
case NXFF_NXM:
|
|
|
|
|
return "nxm";
|
|
|
|
|
default:
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-09 15:48:26 -08:00
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_make_set_packet_in_format(enum nx_packet_in_format packet_in_format)
|
|
|
|
|
{
|
2012-01-12 11:35:50 -08:00
|
|
|
|
struct nx_set_packet_in_format *spif;
|
2011-12-09 15:48:26 -08:00
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
spif = make_nxmsg(sizeof *spif, NXT_SET_PACKET_IN_FORMAT, &msg);
|
|
|
|
|
spif->format = htonl(packet_in_format);
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-12 09:58:01 -07:00
|
|
|
|
/* Returns an OpenFlow message that can be used to turn the flow_mod_table_id
|
|
|
|
|
* extension on or off (according to 'flow_mod_table_id'). */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_make_flow_mod_table_id(bool flow_mod_table_id)
|
|
|
|
|
{
|
2012-01-12 11:35:50 -08:00
|
|
|
|
struct nx_flow_mod_table_id *nfmti;
|
2011-05-12 09:58:01 -07:00
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
nfmti = make_nxmsg(sizeof *nfmti, NXT_FLOW_MOD_TABLE_ID, &msg);
|
|
|
|
|
nfmti->set = flow_mod_table_id;
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 13:22:46 -08:00
|
|
|
|
/* Converts an OFPT_FLOW_MOD or NXT_FLOW_MOD message 'oh' into an abstract
|
|
|
|
|
* flow_mod in 'fm'. Returns 0 if successful, otherwise an OpenFlow error
|
|
|
|
|
* code.
|
|
|
|
|
*
|
2010-12-07 12:45:24 -08:00
|
|
|
|
* Does not validate the flow_mod actions. */
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr
|
2011-08-08 14:46:38 -07:00
|
|
|
|
ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
|
2012-02-10 13:30:23 -08:00
|
|
|
|
const struct ofp_header *oh,
|
|
|
|
|
enum ofputil_protocol protocol)
|
2010-12-07 12:45:24 -08:00
|
|
|
|
{
|
|
|
|
|
const struct ofputil_msg_type *type;
|
2011-05-12 09:58:01 -07:00
|
|
|
|
uint16_t command;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
|
2010-12-07 15:47:19 -08:00
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
ofputil_decode_msg_type(oh, &type);
|
|
|
|
|
if (ofputil_msg_type_code(type) == OFPUTIL_OFPT_FLOW_MOD) {
|
|
|
|
|
/* Standard OpenFlow flow_mod. */
|
|
|
|
|
const struct ofp_flow_mod *ofm;
|
ofp-util: Revise OpenFlow 1.0 ofp_match normalization.
For a long time, Open vSwitch has "normalized" OpenFlow 1.0 flows in a
funny way: it tries to change fields that are wildcarded into fields
that are exact-match. For example, the normalize_match() function
knows that if dl_type is wildcarded, then all of the L3 and L4 fields
will always be extracted as 0, so it sets those fields to exact-match
and their values to 0.
The reason for this was originally that exact-match flows were much
cheaper for Open vSwitch to implement, because they could be implemented
with a hash table, whereas other kinds of flows had to be implemented
with an expensive linear search. But these days Open vSwitch has a
smarter classifier in which wildcarded flows have minimal cost. Also,
it is no longer possible for OpenFlow 1.0 to specify truly exact-match
flows, because Open vSwitch supports fields for which OpenFlow 1.0
cannot specify values and therefore will always be wildcarded.
Now, it no longer makes sense to do this transformation, so this commit
removes it. Presumably, this will be less surprising for users.
Reviewed-by: Simon Horman <horms@verge.net.au>
2011-05-02 11:04:33 -07:00
|
|
|
|
uint16_t priority;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
/* Dissect the message. */
|
2010-12-07 15:49:36 -08:00
|
|
|
|
ofm = ofpbuf_pull(&b, sizeof *ofm);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
ofp-util: Revise OpenFlow 1.0 ofp_match normalization.
For a long time, Open vSwitch has "normalized" OpenFlow 1.0 flows in a
funny way: it tries to change fields that are wildcarded into fields
that are exact-match. For example, the normalize_match() function
knows that if dl_type is wildcarded, then all of the L3 and L4 fields
will always be extracted as 0, so it sets those fields to exact-match
and their values to 0.
The reason for this was originally that exact-match flows were much
cheaper for Open vSwitch to implement, because they could be implemented
with a hash table, whereas other kinds of flows had to be implemented
with an expensive linear search. But these days Open vSwitch has a
smarter classifier in which wildcarded flows have minimal cost. Also,
it is no longer possible for OpenFlow 1.0 to specify truly exact-match
flows, because Open vSwitch supports fields for which OpenFlow 1.0
cannot specify values and therefore will always be wildcarded.
Now, it no longer makes sense to do this transformation, so this commit
removes it. Presumably, this will be less surprising for users.
Reviewed-by: Simon Horman <horms@verge.net.au>
2011-05-02 11:04:33 -07:00
|
|
|
|
/* Set priority based on original wildcards. Normally we'd allow
|
|
|
|
|
* ofputil_cls_rule_from_match() to do this for us, but
|
2011-05-09 16:25:48 -07:00
|
|
|
|
* ofputil_normalize_rule() can put wildcards where the original flow
|
ofp-util: Revise OpenFlow 1.0 ofp_match normalization.
For a long time, Open vSwitch has "normalized" OpenFlow 1.0 flows in a
funny way: it tries to change fields that are wildcarded into fields
that are exact-match. For example, the normalize_match() function
knows that if dl_type is wildcarded, then all of the L3 and L4 fields
will always be extracted as 0, so it sets those fields to exact-match
and their values to 0.
The reason for this was originally that exact-match flows were much
cheaper for Open vSwitch to implement, because they could be implemented
with a hash table, whereas other kinds of flows had to be implemented
with an expensive linear search. But these days Open vSwitch has a
smarter classifier in which wildcarded flows have minimal cost. Also,
it is no longer possible for OpenFlow 1.0 to specify truly exact-match
flows, because Open vSwitch supports fields for which OpenFlow 1.0
cannot specify values and therefore will always be wildcarded.
Now, it no longer makes sense to do this transformation, so this commit
removes it. Presumably, this will be less surprising for users.
Reviewed-by: Simon Horman <horms@verge.net.au>
2011-05-02 11:04:33 -07:00
|
|
|
|
* didn't have them. */
|
|
|
|
|
priority = ntohs(ofm->priority);
|
|
|
|
|
if (!(ofm->match.wildcards & htonl(OFPFW_ALL))) {
|
|
|
|
|
priority = UINT16_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-09 16:25:48 -07:00
|
|
|
|
/* Translate the rule. */
|
|
|
|
|
ofputil_cls_rule_from_match(&ofm->match, priority, &fm->cr);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
ofputil_normalize_rule(&fm->cr);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
/* Translate the message. */
|
2011-05-12 09:58:01 -07:00
|
|
|
|
command = ntohs(ofm->command);
|
2012-03-24 01:02:26 -07:00
|
|
|
|
fm->cookie = htonll(0);
|
|
|
|
|
fm->cookie_mask = htonll(0);
|
|
|
|
|
fm->new_cookie = ofm->cookie;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
fm->idle_timeout = ntohs(ofm->idle_timeout);
|
|
|
|
|
fm->hard_timeout = ntohs(ofm->hard_timeout);
|
|
|
|
|
fm->buffer_id = ntohl(ofm->buffer_id);
|
|
|
|
|
fm->out_port = ntohs(ofm->out_port);
|
|
|
|
|
fm->flags = ntohs(ofm->flags);
|
|
|
|
|
} else if (ofputil_msg_type_code(type) == OFPUTIL_NXT_FLOW_MOD) {
|
|
|
|
|
/* Nicira extended flow_mod. */
|
|
|
|
|
const struct nx_flow_mod *nfm;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
/* Dissect the message. */
|
2010-12-07 15:49:36 -08:00
|
|
|
|
nfm = ofpbuf_pull(&b, sizeof *nfm);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
error = nx_pull_match(&b, ntohs(nfm->match_len), ntohs(nfm->priority),
|
2011-12-23 12:23:24 -08:00
|
|
|
|
&fm->cr, &fm->cookie, &fm->cookie_mask);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
error = ofputil_pull_actions(&b, b.size, &fm->actions, &fm->n_actions);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Translate the message. */
|
2011-05-12 09:58:01 -07:00
|
|
|
|
command = ntohs(nfm->command);
|
2012-03-24 01:02:26 -07:00
|
|
|
|
if ((command & 0xff) == OFPFC_ADD && fm->cookie_mask) {
|
|
|
|
|
/* Flow additions may only set a new cookie, not match an
|
|
|
|
|
* existing cookie. */
|
|
|
|
|
return OFPERR_NXBRC_NXM_INVALID;
|
2011-12-23 12:23:24 -08:00
|
|
|
|
}
|
2012-03-24 01:02:26 -07:00
|
|
|
|
fm->new_cookie = nfm->cookie;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
fm->idle_timeout = ntohs(nfm->idle_timeout);
|
|
|
|
|
fm->hard_timeout = ntohs(nfm->hard_timeout);
|
|
|
|
|
fm->buffer_id = ntohl(nfm->buffer_id);
|
|
|
|
|
fm->out_port = ntohs(nfm->out_port);
|
|
|
|
|
fm->flags = ntohs(nfm->flags);
|
|
|
|
|
} else {
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
if (protocol & OFPUTIL_P_TID) {
|
2011-05-12 09:58:01 -07:00
|
|
|
|
fm->command = command & 0xff;
|
|
|
|
|
fm->table_id = command >> 8;
|
|
|
|
|
} else {
|
|
|
|
|
fm->command = command;
|
|
|
|
|
fm->table_id = 0xff;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-07 12:45:24 -08:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts 'fm' into an OFPT_FLOW_MOD or NXT_FLOW_MOD message according to
|
2012-05-16 08:22:17 -07:00
|
|
|
|
* 'protocol' and returns the message. */
|
2010-12-07 12:45:24 -08:00
|
|
|
|
struct ofpbuf *
|
2011-08-08 14:46:38 -07:00
|
|
|
|
ofputil_encode_flow_mod(const struct ofputil_flow_mod *fm,
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol protocol)
|
2010-12-07 12:45:24 -08:00
|
|
|
|
{
|
|
|
|
|
size_t actions_len = fm->n_actions * sizeof *fm->actions;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
struct ofp_flow_mod *ofm;
|
|
|
|
|
struct nx_flow_mod *nfm;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
struct ofpbuf *msg;
|
2011-05-12 09:58:01 -07:00
|
|
|
|
uint16_t command;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
int match_len;
|
2011-05-12 09:58:01 -07:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
command = (protocol & OFPUTIL_P_TID
|
2011-05-12 09:58:01 -07:00
|
|
|
|
? (fm->command & 0xff) | (fm->table_id << 8)
|
|
|
|
|
: fm->command);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
switch (protocol) {
|
|
|
|
|
case OFPUTIL_P_OF10:
|
|
|
|
|
case OFPUTIL_P_OF10_TID:
|
2010-12-07 12:45:24 -08:00
|
|
|
|
msg = ofpbuf_new(sizeof *ofm + actions_len);
|
2012-02-14 16:58:39 -08:00
|
|
|
|
ofm = put_openflow(sizeof *ofm, OFPT10_FLOW_MOD, msg);
|
2011-04-26 09:42:18 -07:00
|
|
|
|
ofputil_cls_rule_to_match(&fm->cr, &ofm->match);
|
2012-03-24 01:02:26 -07:00
|
|
|
|
ofm->cookie = fm->new_cookie;
|
2011-06-15 09:43:03 -07:00
|
|
|
|
ofm->command = htons(command);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
ofm->idle_timeout = htons(fm->idle_timeout);
|
|
|
|
|
ofm->hard_timeout = htons(fm->hard_timeout);
|
|
|
|
|
ofm->priority = htons(fm->cr.priority);
|
|
|
|
|
ofm->buffer_id = htonl(fm->buffer_id);
|
|
|
|
|
ofm->out_port = htons(fm->out_port);
|
|
|
|
|
ofm->flags = htons(fm->flags);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
break;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
case OFPUTIL_P_NXM:
|
|
|
|
|
case OFPUTIL_P_NXM_TID:
|
2010-12-07 12:45:24 -08:00
|
|
|
|
msg = ofpbuf_new(sizeof *nfm + NXM_TYPICAL_LEN + actions_len);
|
|
|
|
|
put_nxmsg(sizeof *nfm, NXT_FLOW_MOD, msg);
|
|
|
|
|
nfm = msg->data;
|
2011-05-12 09:58:01 -07:00
|
|
|
|
nfm->command = htons(command);
|
2012-03-24 01:02:26 -07:00
|
|
|
|
nfm->cookie = fm->new_cookie;
|
|
|
|
|
match_len = nx_put_match(msg, &fm->cr, fm->cookie, fm->cookie_mask);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
nfm->idle_timeout = htons(fm->idle_timeout);
|
|
|
|
|
nfm->hard_timeout = htons(fm->hard_timeout);
|
|
|
|
|
nfm->priority = htons(fm->cr.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);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
2010-12-07 12:45:24 -08:00
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpbuf_put(msg, fm->actions, actions_len);
|
|
|
|
|
update_openflow_length(msg);
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Returns a bitmask with a 1-bit for each protocol that could be used to
|
|
|
|
|
* send all of the 'n_fm's flow table modification requests in 'fms', and a
|
|
|
|
|
* 0-bit for each protocol that is inadequate.
|
|
|
|
|
*
|
|
|
|
|
* (The return value will have at least one 1-bit.) */
|
|
|
|
|
enum ofputil_protocol
|
|
|
|
|
ofputil_flow_mod_usable_protocols(const struct ofputil_flow_mod *fms,
|
|
|
|
|
size_t n_fms)
|
|
|
|
|
{
|
|
|
|
|
enum ofputil_protocol usable_protocols;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
usable_protocols = OFPUTIL_P_ANY;
|
|
|
|
|
for (i = 0; i < n_fms; i++) {
|
|
|
|
|
const struct ofputil_flow_mod *fm = &fms[i];
|
|
|
|
|
|
|
|
|
|
usable_protocols &= ofputil_usable_protocols(&fm->cr);
|
|
|
|
|
if (fm->table_id != 0xff) {
|
|
|
|
|
usable_protocols &= OFPUTIL_P_TID;
|
|
|
|
|
}
|
2012-03-24 01:02:26 -07:00
|
|
|
|
|
|
|
|
|
/* Matching of the cookie is only supported through NXM. */
|
|
|
|
|
if (fm->cookie_mask != htonll(0)) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
usable_protocols &= OFPUTIL_P_NXM_ANY;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert(usable_protocols);
|
|
|
|
|
|
|
|
|
|
return usable_protocols;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-08-08 14:48:48 -07:00
|
|
|
|
ofputil_decode_ofpst_flow_request(struct ofputil_flow_stats_request *fsr,
|
2010-12-07 12:45:24 -08:00
|
|
|
|
const struct ofp_header *oh,
|
|
|
|
|
bool aggregate)
|
|
|
|
|
{
|
2011-05-31 16:49:06 -07:00
|
|
|
|
const struct ofp_flow_stats_request *ofsr =
|
|
|
|
|
(const struct ofp_flow_stats_request *) oh;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
fsr->aggregate = aggregate;
|
2011-04-26 09:42:18 -07:00
|
|
|
|
ofputil_cls_rule_from_match(&ofsr->match, 0, &fsr->match);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
fsr->out_port = ntohs(ofsr->out_port);
|
|
|
|
|
fsr->table_id = ofsr->table_id;
|
2011-12-23 12:23:24 -08:00
|
|
|
|
fsr->cookie = fsr->cookie_mask = htonll(0);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-08-08 14:48:48 -07:00
|
|
|
|
ofputil_decode_nxst_flow_request(struct ofputil_flow_stats_request *fsr,
|
2010-12-07 12:45:24 -08:00
|
|
|
|
const struct ofp_header *oh,
|
|
|
|
|
bool aggregate)
|
|
|
|
|
{
|
|
|
|
|
const struct nx_flow_stats_request *nfsr;
|
|
|
|
|
struct ofpbuf b;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
2010-12-07 15:47:19 -08:00
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
2010-12-07 15:49:36 -08:00
|
|
|
|
nfsr = ofpbuf_pull(&b, sizeof *nfsr);
|
2011-12-23 12:23:24 -08:00
|
|
|
|
error = nx_pull_match(&b, ntohs(nfsr->match_len), 0, &fsr->match,
|
|
|
|
|
&fsr->cookie, &fsr->cookie_mask);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
if (b.size) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fsr->aggregate = aggregate;
|
|
|
|
|
fsr->out_port = ntohs(nfsr->out_port);
|
|
|
|
|
fsr->table_id = nfsr->table_id;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts an OFPST_FLOW, OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE
|
2011-04-26 09:42:18 -07:00
|
|
|
|
* request 'oh', into an abstract flow_stats_request in 'fsr'. Returns 0 if
|
|
|
|
|
* successful, otherwise an OpenFlow error code. */
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr
|
2011-08-08 14:48:48 -07:00
|
|
|
|
ofputil_decode_flow_stats_request(struct ofputil_flow_stats_request *fsr,
|
2011-04-26 09:42:18 -07:00
|
|
|
|
const struct ofp_header *oh)
|
2010-12-07 12:45:24 -08:00
|
|
|
|
{
|
|
|
|
|
const struct ofputil_msg_type *type;
|
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
int code;
|
|
|
|
|
|
2010-12-07 15:47:19 -08:00
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
ofputil_decode_msg_type(oh, &type);
|
|
|
|
|
code = ofputil_msg_type_code(type);
|
|
|
|
|
switch (code) {
|
|
|
|
|
case OFPUTIL_OFPST_FLOW_REQUEST:
|
2011-04-26 09:42:18 -07:00
|
|
|
|
return ofputil_decode_ofpst_flow_request(fsr, oh, false);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
case OFPUTIL_OFPST_AGGREGATE_REQUEST:
|
2011-04-26 09:42:18 -07:00
|
|
|
|
return ofputil_decode_ofpst_flow_request(fsr, oh, true);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
case OFPUTIL_NXST_FLOW_REQUEST:
|
|
|
|
|
return ofputil_decode_nxst_flow_request(fsr, oh, false);
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_NXST_AGGREGATE_REQUEST:
|
|
|
|
|
return ofputil_decode_nxst_flow_request(fsr, oh, true);
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
/* Hey, the caller lied. */
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts abstract flow_stats_request 'fsr' into an OFPST_FLOW,
|
2011-03-10 15:02:05 -08:00
|
|
|
|
* OFPST_AGGREGATE, NXST_FLOW, or NXST_AGGREGATE request 'oh' according to
|
2012-02-10 13:30:23 -08:00
|
|
|
|
* 'protocol', and returns the message. */
|
2010-12-07 12:45:24 -08:00
|
|
|
|
struct ofpbuf *
|
2011-08-08 14:48:48 -07:00
|
|
|
|
ofputil_encode_flow_stats_request(const struct ofputil_flow_stats_request *fsr,
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol protocol)
|
2010-12-07 12:45:24 -08:00
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
switch (protocol) {
|
|
|
|
|
case OFPUTIL_P_OF10:
|
|
|
|
|
case OFPUTIL_P_OF10_TID: {
|
2010-12-07 12:45:24 -08:00
|
|
|
|
struct ofp_flow_stats_request *ofsr;
|
|
|
|
|
int type;
|
|
|
|
|
|
|
|
|
|
type = fsr->aggregate ? OFPST_AGGREGATE : OFPST_FLOW;
|
2011-05-31 16:49:06 -07:00
|
|
|
|
ofsr = ofputil_make_stats_request(sizeof *ofsr, type, 0, &msg);
|
2011-04-26 09:42:18 -07:00
|
|
|
|
ofputil_cls_rule_to_match(&fsr->match, &ofsr->match);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
ofsr->table_id = fsr->table_id;
|
|
|
|
|
ofsr->out_port = htons(fsr->out_port);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_P_NXM:
|
|
|
|
|
case OFPUTIL_P_NXM_TID: {
|
2010-12-07 12:45:24 -08:00
|
|
|
|
struct nx_flow_stats_request *nfsr;
|
|
|
|
|
int match_len;
|
2010-12-13 16:20:54 -08:00
|
|
|
|
int subtype;
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
2010-12-13 16:20:54 -08:00
|
|
|
|
subtype = fsr->aggregate ? NXST_AGGREGATE : NXST_FLOW;
|
2011-05-31 16:49:06 -07:00
|
|
|
|
ofputil_make_stats_request(sizeof *nfsr, OFPST_VENDOR, subtype, &msg);
|
2011-12-23 12:23:24 -08:00
|
|
|
|
match_len = nx_put_match(msg, &fsr->match,
|
|
|
|
|
fsr->cookie, fsr->cookie_mask);
|
2010-12-07 12:45:24 -08:00
|
|
|
|
|
|
|
|
|
nfsr = msg->data;
|
|
|
|
|
nfsr->out_port = htons(fsr->out_port);
|
|
|
|
|
nfsr->match_len = htons(match_len);
|
|
|
|
|
nfsr->table_id = fsr->table_id;
|
2012-02-10 13:30:23 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
2010-12-07 12:45:24 -08:00
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
2010-12-06 10:20:20 -08:00
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
/* Returns a bitmask with a 1-bit for each protocol that could be used to
|
|
|
|
|
* accurately encode 'fsr', and a 0-bit for each protocol that is inadequate.
|
|
|
|
|
*
|
|
|
|
|
* (The return value will have at least one 1-bit.) */
|
|
|
|
|
enum ofputil_protocol
|
|
|
|
|
ofputil_flow_stats_request_usable_protocols(
|
|
|
|
|
const struct ofputil_flow_stats_request *fsr)
|
|
|
|
|
{
|
|
|
|
|
enum ofputil_protocol usable_protocols;
|
|
|
|
|
|
|
|
|
|
usable_protocols = ofputil_usable_protocols(&fsr->match);
|
|
|
|
|
if (fsr->cookie_mask != htonll(0)) {
|
|
|
|
|
usable_protocols &= OFPUTIL_P_NXM_ANY;
|
|
|
|
|
}
|
|
|
|
|
return usable_protocols;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-10 15:02:05 -08:00
|
|
|
|
/* Converts an OFPST_FLOW or NXST_FLOW reply in 'msg' into an abstract
|
2011-04-26 09:42:18 -07:00
|
|
|
|
* ofputil_flow_stats in 'fs'.
|
2011-03-10 15:02:05 -08:00
|
|
|
|
*
|
|
|
|
|
* Multiple OFPST_FLOW or NXST_FLOW 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.
|
|
|
|
|
*
|
2012-02-07 10:13:52 -08:00
|
|
|
|
* Most switches don't send the values needed to populate fs->idle_age and
|
|
|
|
|
* fs->hard_age, so those members will usually be set to 0. If the switch from
|
|
|
|
|
* which 'msg' originated is known to implement NXT_FLOW_AGE, then pass
|
|
|
|
|
* 'flow_age_extension' as true so that the contents of 'msg' determine the
|
|
|
|
|
* 'idle_age' and 'hard_age' members in 'fs'.
|
|
|
|
|
*
|
2011-03-10 15:02:05 -08:00
|
|
|
|
* Returns 0 if successful, EOF if no replies were left in this 'msg',
|
|
|
|
|
* otherwise a positive errno value. */
|
|
|
|
|
int
|
|
|
|
|
ofputil_decode_flow_stats_reply(struct ofputil_flow_stats *fs,
|
2012-02-07 10:13:52 -08:00
|
|
|
|
struct ofpbuf *msg,
|
|
|
|
|
bool flow_age_extension)
|
2011-03-10 15:02:05 -08:00
|
|
|
|
{
|
|
|
|
|
const struct ofputil_msg_type *type;
|
|
|
|
|
int code;
|
|
|
|
|
|
|
|
|
|
ofputil_decode_msg_type(msg->l2 ? msg->l2 : msg->data, &type);
|
|
|
|
|
code = ofputil_msg_type_code(type);
|
|
|
|
|
if (!msg->l2) {
|
|
|
|
|
msg->l2 = msg->data;
|
|
|
|
|
if (code == OFPUTIL_OFPST_FLOW_REPLY) {
|
2011-05-26 09:30:25 -07:00
|
|
|
|
ofpbuf_pull(msg, sizeof(struct ofp_stats_msg));
|
2011-03-10 15:02:05 -08:00
|
|
|
|
} else if (code == OFPUTIL_NXST_FLOW_REPLY) {
|
|
|
|
|
ofpbuf_pull(msg, sizeof(struct nicira_stats_msg));
|
|
|
|
|
} else {
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!msg->size) {
|
|
|
|
|
return EOF;
|
|
|
|
|
} else if (code == OFPUTIL_OFPST_FLOW_REPLY) {
|
|
|
|
|
const struct ofp_flow_stats *ofs;
|
|
|
|
|
size_t length;
|
|
|
|
|
|
|
|
|
|
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_actions(msg, length - sizeof *ofs,
|
|
|
|
|
&fs->actions, &fs->n_actions)) {
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fs->cookie = get_32aligned_be64(&ofs->cookie);
|
|
|
|
|
ofputil_cls_rule_from_match(&ofs->match, ntohs(ofs->priority),
|
2011-04-26 09:42:18 -07:00
|
|
|
|
&fs->rule);
|
2011-03-10 15:02:05 -08:00
|
|
|
|
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);
|
2012-02-07 10:13:52 -08:00
|
|
|
|
fs->idle_age = -1;
|
|
|
|
|
fs->hard_age = -1;
|
2011-03-10 15:02:05 -08:00
|
|
|
|
fs->packet_count = ntohll(get_32aligned_be64(&ofs->packet_count));
|
|
|
|
|
fs->byte_count = ntohll(get_32aligned_be64(&ofs->byte_count));
|
|
|
|
|
} else if (code == OFPUTIL_NXST_FLOW_REPLY) {
|
|
|
|
|
const struct nx_flow_stats *nfs;
|
|
|
|
|
size_t match_len, length;
|
|
|
|
|
|
|
|
|
|
nfs = ofpbuf_try_pull(msg, sizeof *nfs);
|
|
|
|
|
if (!nfs) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply has %zu leftover "
|
|
|
|
|
"bytes at end", msg->size);
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
length = ntohs(nfs->length);
|
|
|
|
|
match_len = ntohs(nfs->match_len);
|
|
|
|
|
if (length < sizeof *nfs + ROUND_UP(match_len, 8)) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "NXST_FLOW reply with match_len=%zu "
|
|
|
|
|
"claims invalid length %zu", match_len, length);
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
2011-12-23 12:23:24 -08:00
|
|
|
|
if (nx_pull_match(msg, match_len, ntohs(nfs->priority), &fs->rule,
|
|
|
|
|
NULL, NULL)) {
|
2011-03-10 15:02:05 -08:00
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ofputil_pull_actions(msg,
|
|
|
|
|
length - sizeof *nfs - ROUND_UP(match_len, 8),
|
|
|
|
|
&fs->actions, &fs->n_actions)) {
|
|
|
|
|
return EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fs->cookie = nfs->cookie;
|
|
|
|
|
fs->table_id = nfs->table_id;
|
|
|
|
|
fs->duration_sec = ntohl(nfs->duration_sec);
|
|
|
|
|
fs->duration_nsec = ntohl(nfs->duration_nsec);
|
|
|
|
|
fs->idle_timeout = ntohs(nfs->idle_timeout);
|
|
|
|
|
fs->hard_timeout = ntohs(nfs->hard_timeout);
|
2012-02-07 10:13:52 -08:00
|
|
|
|
fs->idle_age = -1;
|
|
|
|
|
fs->hard_age = -1;
|
|
|
|
|
if (flow_age_extension) {
|
|
|
|
|
if (nfs->idle_age) {
|
|
|
|
|
fs->idle_age = ntohs(nfs->idle_age) - 1;
|
|
|
|
|
}
|
|
|
|
|
if (nfs->hard_age) {
|
|
|
|
|
fs->hard_age = ntohs(nfs->hard_age) - 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-03-10 15:02:05 -08:00
|
|
|
|
fs->packet_count = ntohll(nfs->packet_count);
|
|
|
|
|
fs->byte_count = ntohll(nfs->byte_count);
|
|
|
|
|
} else {
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-27 15:16:08 -07:00
|
|
|
|
/* Returns 'count' unchanged except that UINT64_MAX becomes 0.
|
|
|
|
|
*
|
|
|
|
|
* We use this in situations where OVS internally uses UINT64_MAX to mean
|
|
|
|
|
* "value unknown" but OpenFlow 1.0 does not define any unknown value. */
|
|
|
|
|
static uint64_t
|
|
|
|
|
unknown_to_zero(uint64_t count)
|
|
|
|
|
{
|
|
|
|
|
return count != UINT64_MAX ? count : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-31 16:55:02 -07:00
|
|
|
|
/* Appends an OFPST_FLOW or NXST_FLOW reply that contains the data in 'fs' to
|
|
|
|
|
* those already present in the list of ofpbufs in 'replies'. 'replies' should
|
|
|
|
|
* have been initialized with ofputil_start_stats_reply(). */
|
|
|
|
|
void
|
|
|
|
|
ofputil_append_flow_stats_reply(const struct ofputil_flow_stats *fs,
|
|
|
|
|
struct list *replies)
|
|
|
|
|
{
|
|
|
|
|
size_t act_len = fs->n_actions * sizeof *fs->actions;
|
|
|
|
|
const struct ofp_stats_msg *osm;
|
|
|
|
|
|
|
|
|
|
osm = ofpbuf_from_list(list_back(replies))->data;
|
|
|
|
|
if (osm->type == htons(OFPST_FLOW)) {
|
|
|
|
|
size_t len = offsetof(struct ofp_flow_stats, actions) + act_len;
|
|
|
|
|
struct ofp_flow_stats *ofs;
|
|
|
|
|
|
|
|
|
|
ofs = ofputil_append_stats_reply(len, replies);
|
|
|
|
|
ofs->length = htons(len);
|
|
|
|
|
ofs->table_id = fs->table_id;
|
|
|
|
|
ofs->pad = 0;
|
|
|
|
|
ofputil_cls_rule_to_match(&fs->rule, &ofs->match);
|
|
|
|
|
ofs->duration_sec = htonl(fs->duration_sec);
|
|
|
|
|
ofs->duration_nsec = htonl(fs->duration_nsec);
|
|
|
|
|
ofs->priority = htons(fs->rule.priority);
|
|
|
|
|
ofs->idle_timeout = htons(fs->idle_timeout);
|
|
|
|
|
ofs->hard_timeout = htons(fs->hard_timeout);
|
|
|
|
|
memset(ofs->pad2, 0, sizeof ofs->pad2);
|
|
|
|
|
put_32aligned_be64(&ofs->cookie, fs->cookie);
|
2011-05-27 15:16:08 -07:00
|
|
|
|
put_32aligned_be64(&ofs->packet_count,
|
|
|
|
|
htonll(unknown_to_zero(fs->packet_count)));
|
|
|
|
|
put_32aligned_be64(&ofs->byte_count,
|
|
|
|
|
htonll(unknown_to_zero(fs->byte_count)));
|
2011-05-31 16:55:02 -07:00
|
|
|
|
memcpy(ofs->actions, fs->actions, act_len);
|
|
|
|
|
} else if (osm->type == htons(OFPST_VENDOR)) {
|
|
|
|
|
struct nx_flow_stats *nfs;
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
size_t start_len;
|
|
|
|
|
|
|
|
|
|
msg = ofputil_reserve_stats_reply(
|
|
|
|
|
sizeof *nfs + NXM_MAX_LEN + act_len, replies);
|
|
|
|
|
start_len = msg->size;
|
|
|
|
|
|
|
|
|
|
nfs = ofpbuf_put_uninit(msg, sizeof *nfs);
|
|
|
|
|
nfs->table_id = fs->table_id;
|
|
|
|
|
nfs->pad = 0;
|
|
|
|
|
nfs->duration_sec = htonl(fs->duration_sec);
|
|
|
|
|
nfs->duration_nsec = htonl(fs->duration_nsec);
|
|
|
|
|
nfs->priority = htons(fs->rule.priority);
|
|
|
|
|
nfs->idle_timeout = htons(fs->idle_timeout);
|
|
|
|
|
nfs->hard_timeout = htons(fs->hard_timeout);
|
2012-02-07 10:13:52 -08:00
|
|
|
|
nfs->idle_age = htons(fs->idle_age < 0 ? 0
|
|
|
|
|
: fs->idle_age < UINT16_MAX ? fs->idle_age + 1
|
|
|
|
|
: UINT16_MAX);
|
|
|
|
|
nfs->hard_age = htons(fs->hard_age < 0 ? 0
|
|
|
|
|
: fs->hard_age < UINT16_MAX ? fs->hard_age + 1
|
|
|
|
|
: UINT16_MAX);
|
2011-12-23 12:23:24 -08:00
|
|
|
|
nfs->match_len = htons(nx_put_match(msg, &fs->rule, 0, 0));
|
2011-05-31 16:55:02 -07:00
|
|
|
|
nfs->cookie = fs->cookie;
|
|
|
|
|
nfs->packet_count = htonll(fs->packet_count);
|
|
|
|
|
nfs->byte_count = htonll(fs->byte_count);
|
|
|
|
|
ofpbuf_put(msg, fs->actions, act_len);
|
|
|
|
|
nfs->length = htons(msg->size - start_len);
|
|
|
|
|
} else {
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-26 16:02:56 -07:00
|
|
|
|
/* Converts abstract ofputil_aggregate_stats 'stats' into an OFPST_AGGREGATE or
|
2012-02-10 13:30:23 -08:00
|
|
|
|
* NXST_AGGREGATE reply according to 'protocol', and returns the message. */
|
2011-05-26 16:02:56 -07:00
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_aggregate_stats_reply(
|
|
|
|
|
const struct ofputil_aggregate_stats *stats,
|
|
|
|
|
const struct ofp_stats_msg *request)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
if (request->type == htons(OFPST_AGGREGATE)) {
|
|
|
|
|
struct ofp_aggregate_stats_reply *asr;
|
|
|
|
|
|
|
|
|
|
asr = ofputil_make_stats_reply(sizeof *asr, request, &msg);
|
2011-05-27 15:16:08 -07:00
|
|
|
|
put_32aligned_be64(&asr->packet_count,
|
|
|
|
|
htonll(unknown_to_zero(stats->packet_count)));
|
|
|
|
|
put_32aligned_be64(&asr->byte_count,
|
|
|
|
|
htonll(unknown_to_zero(stats->byte_count)));
|
2011-05-26 16:02:56 -07:00
|
|
|
|
asr->flow_count = htonl(stats->flow_count);
|
|
|
|
|
} else if (request->type == htons(OFPST_VENDOR)) {
|
|
|
|
|
struct nx_aggregate_stats_reply *nasr;
|
|
|
|
|
|
|
|
|
|
nasr = ofputil_make_stats_reply(sizeof *nasr, request, &msg);
|
|
|
|
|
assert(nasr->nsm.subtype == htonl(NXST_AGGREGATE));
|
|
|
|
|
nasr->packet_count = htonll(stats->packet_count);
|
|
|
|
|
nasr->byte_count = htonll(stats->byte_count);
|
|
|
|
|
nasr->flow_count = htonl(stats->flow_count);
|
|
|
|
|
} else {
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-26 09:42:18 -07:00
|
|
|
|
/* Converts an OFPT_FLOW_REMOVED or NXT_FLOW_REMOVED message 'oh' into an
|
|
|
|
|
* abstract ofputil_flow_removed in 'fr'. Returns 0 if successful, otherwise
|
|
|
|
|
* an OpenFlow error code. */
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr
|
2010-12-09 10:31:49 -08:00
|
|
|
|
ofputil_decode_flow_removed(struct ofputil_flow_removed *fr,
|
2011-04-26 09:42:18 -07:00
|
|
|
|
const struct ofp_header *oh)
|
2010-12-09 10:31:49 -08:00
|
|
|
|
{
|
|
|
|
|
const struct ofputil_msg_type *type;
|
|
|
|
|
enum ofputil_msg_code code;
|
|
|
|
|
|
|
|
|
|
ofputil_decode_msg_type(oh, &type);
|
|
|
|
|
code = ofputil_msg_type_code(type);
|
|
|
|
|
if (code == OFPUTIL_OFPT_FLOW_REMOVED) {
|
|
|
|
|
const struct ofp_flow_removed *ofr;
|
|
|
|
|
|
|
|
|
|
ofr = (const struct ofp_flow_removed *) oh;
|
|
|
|
|
ofputil_cls_rule_from_match(&ofr->match, ntohs(ofr->priority),
|
2011-04-26 09:42:18 -07:00
|
|
|
|
&fr->rule);
|
2010-12-09 10:31:49 -08:00
|
|
|
|
fr->cookie = ofr->cookie;
|
|
|
|
|
fr->reason = ofr->reason;
|
|
|
|
|
fr->duration_sec = ntohl(ofr->duration_sec);
|
|
|
|
|
fr->duration_nsec = ntohl(ofr->duration_nsec);
|
|
|
|
|
fr->idle_timeout = ntohs(ofr->idle_timeout);
|
|
|
|
|
fr->packet_count = ntohll(ofr->packet_count);
|
|
|
|
|
fr->byte_count = ntohll(ofr->byte_count);
|
|
|
|
|
} else if (code == OFPUTIL_NXT_FLOW_REMOVED) {
|
|
|
|
|
struct nx_flow_removed *nfr;
|
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
|
|
|
|
|
|
|
|
|
nfr = ofpbuf_pull(&b, sizeof *nfr);
|
|
|
|
|
error = nx_pull_match(&b, ntohs(nfr->match_len), ntohs(nfr->priority),
|
2011-12-23 12:23:24 -08:00
|
|
|
|
&fr->rule, NULL, NULL);
|
2010-12-09 10:31:49 -08:00
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
if (b.size) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2010-12-09 10:31:49 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fr->cookie = nfr->cookie;
|
|
|
|
|
fr->reason = nfr->reason;
|
|
|
|
|
fr->duration_sec = ntohl(nfr->duration_sec);
|
|
|
|
|
fr->duration_nsec = ntohl(nfr->duration_nsec);
|
|
|
|
|
fr->idle_timeout = ntohs(nfr->idle_timeout);
|
|
|
|
|
fr->packet_count = ntohll(nfr->packet_count);
|
|
|
|
|
fr->byte_count = ntohll(nfr->byte_count);
|
|
|
|
|
} else {
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-15 09:49:14 -07:00
|
|
|
|
/* Converts abstract ofputil_flow_removed 'fr' into an OFPT_FLOW_REMOVED or
|
2012-02-10 13:30:23 -08:00
|
|
|
|
* NXT_FLOW_REMOVED message 'oh' according to 'protocol', and returns the
|
2011-03-15 09:49:14 -07:00
|
|
|
|
* message. */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_flow_removed(const struct ofputil_flow_removed *fr,
|
2012-02-10 13:30:23 -08:00
|
|
|
|
enum ofputil_protocol protocol)
|
2011-03-15 09:49:14 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
|
switch (protocol) {
|
|
|
|
|
case OFPUTIL_P_OF10:
|
|
|
|
|
case OFPUTIL_P_OF10_TID: {
|
2011-03-15 09:49:14 -07:00
|
|
|
|
struct ofp_flow_removed *ofr;
|
|
|
|
|
|
|
|
|
|
ofr = make_openflow_xid(sizeof *ofr, OFPT_FLOW_REMOVED, htonl(0),
|
|
|
|
|
&msg);
|
2011-04-26 09:42:18 -07:00
|
|
|
|
ofputil_cls_rule_to_match(&fr->rule, &ofr->match);
|
2011-05-20 16:42:40 -07:00
|
|
|
|
ofr->cookie = fr->cookie;
|
2011-03-15 09:49:14 -07:00
|
|
|
|
ofr->priority = htons(fr->rule.priority);
|
|
|
|
|
ofr->reason = fr->reason;
|
|
|
|
|
ofr->duration_sec = htonl(fr->duration_sec);
|
|
|
|
|
ofr->duration_nsec = htonl(fr->duration_nsec);
|
|
|
|
|
ofr->idle_timeout = htons(fr->idle_timeout);
|
2011-05-27 15:16:08 -07:00
|
|
|
|
ofr->packet_count = htonll(unknown_to_zero(fr->packet_count));
|
|
|
|
|
ofr->byte_count = htonll(unknown_to_zero(fr->byte_count));
|
2012-02-10 13:30:23 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_P_NXM:
|
|
|
|
|
case OFPUTIL_P_NXM_TID: {
|
2011-03-15 09:49:14 -07:00
|
|
|
|
struct nx_flow_removed *nfr;
|
|
|
|
|
int match_len;
|
|
|
|
|
|
|
|
|
|
make_nxmsg_xid(sizeof *nfr, NXT_FLOW_REMOVED, htonl(0), &msg);
|
2011-12-23 12:23:24 -08:00
|
|
|
|
match_len = nx_put_match(msg, &fr->rule, 0, 0);
|
2011-03-15 09:49:14 -07:00
|
|
|
|
|
|
|
|
|
nfr = msg->data;
|
|
|
|
|
nfr->cookie = fr->cookie;
|
|
|
|
|
nfr->priority = htons(fr->rule.priority);
|
|
|
|
|
nfr->reason = fr->reason;
|
|
|
|
|
nfr->duration_sec = htonl(fr->duration_sec);
|
|
|
|
|
nfr->duration_nsec = htonl(fr->duration_nsec);
|
|
|
|
|
nfr->idle_timeout = htons(fr->idle_timeout);
|
|
|
|
|
nfr->match_len = htons(match_len);
|
|
|
|
|
nfr->packet_count = htonll(fr->packet_count);
|
|
|
|
|
nfr->byte_count = htonll(fr->byte_count);
|
2012-02-10 13:30:23 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
default:
|
2011-03-15 09:49:14 -07:00
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-22 16:35:23 -08:00
|
|
|
|
int
|
|
|
|
|
ofputil_decode_packet_in(struct ofputil_packet_in *pin,
|
|
|
|
|
const struct ofp_header *oh)
|
|
|
|
|
{
|
|
|
|
|
const struct ofputil_msg_type *type;
|
|
|
|
|
enum ofputil_msg_code code;
|
|
|
|
|
|
|
|
|
|
ofputil_decode_msg_type(oh, &type);
|
|
|
|
|
code = ofputil_msg_type_code(type);
|
|
|
|
|
memset(pin, 0, sizeof *pin);
|
|
|
|
|
|
|
|
|
|
if (code == OFPUTIL_OFPT_PACKET_IN) {
|
|
|
|
|
const struct ofp_packet_in *opi = (const struct ofp_packet_in *) oh;
|
|
|
|
|
|
|
|
|
|
pin->packet = opi->data;
|
|
|
|
|
pin->packet_len = ntohs(opi->header.length)
|
|
|
|
|
- offsetof(struct ofp_packet_in, data);
|
|
|
|
|
|
2012-01-04 16:40:13 -08:00
|
|
|
|
pin->fmd.in_port = ntohs(opi->in_port);
|
2011-12-22 16:35:23 -08:00
|
|
|
|
pin->reason = opi->reason;
|
|
|
|
|
pin->buffer_id = ntohl(opi->buffer_id);
|
|
|
|
|
pin->total_len = ntohs(opi->total_len);
|
2011-12-09 15:48:26 -08:00
|
|
|
|
} else if (code == OFPUTIL_NXT_PACKET_IN) {
|
2012-01-12 11:35:50 -08:00
|
|
|
|
const struct nx_packet_in *npi;
|
2011-12-09 15:48:26 -08:00
|
|
|
|
struct cls_rule rule;
|
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, oh, ntohs(oh->length));
|
|
|
|
|
|
|
|
|
|
npi = ofpbuf_pull(&b, sizeof *npi);
|
|
|
|
|
error = nx_pull_match_loose(&b, ntohs(npi->match_len), 0, &rule, NULL,
|
|
|
|
|
NULL);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ofpbuf_try_pull(&b, 2)) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2011-12-09 15:48:26 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
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);
|
2011-12-22 16:35:23 -08:00
|
|
|
|
} else {
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-09 15:48:26 -08:00
|
|
|
|
/* Converts abstract ofputil_packet_in 'pin' into a PACKET_IN message
|
|
|
|
|
* in the format specified by 'packet_in_format'. */
|
2011-03-22 09:13:02 -07:00
|
|
|
|
struct ofpbuf *
|
2011-12-09 15:48:26 -08:00
|
|
|
|
ofputil_encode_packet_in(const struct ofputil_packet_in *pin,
|
|
|
|
|
enum nx_packet_in_format packet_in_format)
|
2011-03-22 09:13:02 -07:00
|
|
|
|
{
|
2011-12-09 15:48:26 -08:00
|
|
|
|
size_t send_len = MIN(pin->send_len, pin->packet_len);
|
|
|
|
|
struct ofpbuf *packet;
|
2011-03-22 09:13:02 -07:00
|
|
|
|
|
|
|
|
|
/* Add OFPT_PACKET_IN. */
|
2011-12-09 15:48:26 -08:00
|
|
|
|
if (packet_in_format == NXPIF_OPENFLOW10) {
|
|
|
|
|
size_t header_len = offsetof(struct ofp_packet_in, data);
|
|
|
|
|
struct ofp_packet_in *opi;
|
|
|
|
|
|
|
|
|
|
packet = ofpbuf_new(send_len + header_len);
|
|
|
|
|
opi = ofpbuf_put_zeros(packet, header_len);
|
2011-12-06 22:33:49 -08:00
|
|
|
|
opi->header.version = OFP10_VERSION;
|
2011-12-09 15:48:26 -08:00
|
|
|
|
opi->header.type = OFPT_PACKET_IN;
|
|
|
|
|
opi->total_len = htons(pin->total_len);
|
|
|
|
|
opi->in_port = htons(pin->fmd.in_port);
|
|
|
|
|
opi->reason = pin->reason;
|
|
|
|
|
opi->buffer_id = htonl(pin->buffer_id);
|
|
|
|
|
|
|
|
|
|
ofpbuf_put(packet, pin->packet, send_len);
|
|
|
|
|
} else if (packet_in_format == NXPIF_NXM) {
|
2012-01-12 11:35:50 -08:00
|
|
|
|
struct nx_packet_in *npi;
|
2011-12-09 15:48:26 -08:00
|
|
|
|
struct cls_rule rule;
|
|
|
|
|
size_t match_len;
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
/* Estimate of required PACKET_IN length includes the NPI header, space
|
|
|
|
|
* for the match (2 times sizeof the metadata seems like enough), 2
|
|
|
|
|
* bytes for padding, and the packet length. */
|
|
|
|
|
packet = ofpbuf_new(sizeof *npi + sizeof(struct flow_metadata) * 2
|
|
|
|
|
+ 2 + send_len);
|
|
|
|
|
|
|
|
|
|
cls_rule_init_catchall(&rule, 0);
|
|
|
|
|
cls_rule_set_tun_id_masked(&rule, pin->fmd.tun_id,
|
|
|
|
|
pin->fmd.tun_id_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);
|
|
|
|
|
|
|
|
|
|
ofpbuf_put_zeros(packet, sizeof *npi);
|
|
|
|
|
match_len = nx_put_match(packet, &rule, 0, 0);
|
|
|
|
|
ofpbuf_put_zeros(packet, 2);
|
|
|
|
|
ofpbuf_put(packet, pin->packet, send_len);
|
|
|
|
|
|
|
|
|
|
npi = packet->data;
|
2011-12-06 22:33:49 -08:00
|
|
|
|
npi->nxh.header.version = OFP10_VERSION;
|
2011-12-09 15:48:26 -08:00
|
|
|
|
npi->nxh.header.type = OFPT_VENDOR;
|
|
|
|
|
npi->nxh.vendor = htonl(NX_VENDOR_ID);
|
|
|
|
|
npi->nxh.subtype = htonl(NXT_PACKET_IN);
|
|
|
|
|
|
|
|
|
|
npi->buffer_id = htonl(pin->buffer_id);
|
|
|
|
|
npi->total_len = htons(pin->total_len);
|
|
|
|
|
npi->reason = pin->reason;
|
|
|
|
|
npi->table_id = pin->table_id;
|
|
|
|
|
npi->cookie = pin->cookie;
|
|
|
|
|
npi->match_len = htons(match_len);
|
|
|
|
|
} else {
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
update_openflow_length(packet);
|
|
|
|
|
|
|
|
|
|
return packet;
|
2011-03-22 09:13:02 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-02-07 14:46:34 -08:00
|
|
|
|
const char *
|
|
|
|
|
ofputil_packet_in_reason_to_string(enum ofp_packet_in_reason reason)
|
|
|
|
|
{
|
|
|
|
|
static char s[INT_STRLEN(int) + 1];
|
|
|
|
|
|
|
|
|
|
switch (reason) {
|
|
|
|
|
case OFPR_NO_MATCH:
|
|
|
|
|
return "no_match";
|
|
|
|
|
case OFPR_ACTION:
|
|
|
|
|
return "action";
|
|
|
|
|
case OFPR_INVALID_TTL:
|
|
|
|
|
return "invalid_ttl";
|
|
|
|
|
|
|
|
|
|
case OFPR_N_REASONS:
|
|
|
|
|
default:
|
|
|
|
|
sprintf(s, "%d", (int) reason);
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ofputil_packet_in_reason_from_string(const char *s,
|
|
|
|
|
enum ofp_packet_in_reason *reason)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < OFPR_N_REASONS; i++) {
|
|
|
|
|
if (!strcasecmp(s, ofputil_packet_in_reason_to_string(i))) {
|
|
|
|
|
*reason = i;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-06 14:17:49 -08:00
|
|
|
|
enum ofperr
|
|
|
|
|
ofputil_decode_packet_out(struct ofputil_packet_out *po,
|
|
|
|
|
const struct ofp_packet_out *opo)
|
|
|
|
|
{
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
|
|
|
|
|
po->buffer_id = ntohl(opo->buffer_id);
|
|
|
|
|
po->in_port = ntohs(opo->in_port);
|
|
|
|
|
if (po->in_port >= OFPP_MAX && po->in_port != OFPP_LOCAL
|
2012-05-07 12:30:54 -07:00
|
|
|
|
&& po->in_port != OFPP_NONE && po->in_port != OFPP_CONTROLLER) {
|
2012-02-06 14:17:49 -08:00
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "packet-out has bad input port %#"PRIx16,
|
|
|
|
|
po->in_port);
|
|
|
|
|
return OFPERR_NXBRC_BAD_IN_PORT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, opo, ntohs(opo->header.length));
|
|
|
|
|
ofpbuf_pull(&b, sizeof *opo);
|
|
|
|
|
|
|
|
|
|
error = ofputil_pull_actions(&b, ntohs(opo->actions_len),
|
|
|
|
|
&po->actions, &po->n_actions);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (po->buffer_id == UINT32_MAX) {
|
|
|
|
|
po->packet = b.data;
|
|
|
|
|
po->packet_len = b.size;
|
|
|
|
|
} else {
|
|
|
|
|
po->packet = NULL;
|
|
|
|
|
po->packet_len = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-02-15 14:23:38 -08:00
|
|
|
|
|
|
|
|
|
/* ofputil_phy_port */
|
|
|
|
|
|
|
|
|
|
/* NETDEV_F_* to and from OFPPF_* and OFPPF10_*. */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_10MB_HD == OFPPF_10MB_HD); /* bit 0 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_10MB_FD == OFPPF_10MB_FD); /* bit 1 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_100MB_HD == OFPPF_100MB_HD); /* bit 2 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_100MB_FD == OFPPF_100MB_FD); /* bit 3 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_1GB_HD == OFPPF_1GB_HD); /* bit 4 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */
|
|
|
|
|
|
|
|
|
|
/* NETDEV_F_ bits 11...15 are OFPPF10_ bits 7...11: */
|
2012-02-15 16:33:04 -08:00
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == (OFPPF10_COPPER << 4));
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == (OFPPF10_FIBER << 4));
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == (OFPPF10_AUTONEG << 4));
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == (OFPPF10_PAUSE << 4));
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == (OFPPF10_PAUSE_ASYM << 4));
|
2012-02-15 14:23:38 -08:00
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
static enum netdev_features
|
|
|
|
|
netdev_port_features_from_ofp10(ovs_be32 ofp10_)
|
2012-02-15 14:23:38 -08:00
|
|
|
|
{
|
|
|
|
|
uint32_t ofp10 = ntohl(ofp10_);
|
|
|
|
|
return (ofp10 & 0x7f) | ((ofp10 & 0xf80) << 4);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
static ovs_be32
|
|
|
|
|
netdev_port_features_to_ofp10(enum netdev_features features)
|
2012-02-15 14:23:38 -08:00
|
|
|
|
{
|
|
|
|
|
return htonl((features & 0x7f) | ((features & 0xf800) >> 4));
|
|
|
|
|
}
|
2012-02-06 14:17:49 -08:00
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_10MB_HD == OFPPF_10MB_HD); /* bit 0 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_10MB_FD == OFPPF_10MB_FD); /* bit 1 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_100MB_HD == OFPPF_100MB_HD); /* bit 2 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_100MB_FD == OFPPF_100MB_FD); /* bit 3 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_1GB_HD == OFPPF_1GB_HD); /* bit 4 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_1GB_FD == OFPPF_1GB_FD); /* bit 5 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_10GB_FD == OFPPF_10GB_FD); /* bit 6 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_40GB_FD == OFPPF11_40GB_FD); /* bit 7 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_100GB_FD == OFPPF11_100GB_FD); /* bit 8 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_1TB_FD == OFPPF11_1TB_FD); /* bit 9 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_OTHER == OFPPF11_OTHER); /* bit 10 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_COPPER == OFPPF11_COPPER); /* bit 11 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_FIBER == OFPPF11_FIBER); /* bit 12 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_AUTONEG == OFPPF11_AUTONEG); /* bit 13 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE == OFPPF11_PAUSE); /* bit 14 */
|
|
|
|
|
BUILD_ASSERT_DECL((int) NETDEV_F_PAUSE_ASYM == OFPPF11_PAUSE_ASYM);/* bit 15 */
|
|
|
|
|
|
|
|
|
|
static enum netdev_features
|
|
|
|
|
netdev_port_features_from_ofp11(ovs_be32 ofp11)
|
|
|
|
|
{
|
|
|
|
|
return ntohl(ofp11) & 0xffff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ovs_be32
|
|
|
|
|
netdev_port_features_to_ofp11(enum netdev_features features)
|
|
|
|
|
{
|
|
|
|
|
return htonl(features & 0xffff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum ofperr
|
|
|
|
|
ofputil_decode_ofp10_phy_port(struct ofputil_phy_port *pp,
|
|
|
|
|
const struct ofp10_phy_port *opp)
|
|
|
|
|
{
|
|
|
|
|
memset(pp, 0, sizeof *pp);
|
|
|
|
|
|
|
|
|
|
pp->port_no = ntohs(opp->port_no);
|
|
|
|
|
memcpy(pp->hw_addr, opp->hw_addr, OFP_ETH_ALEN);
|
|
|
|
|
ovs_strlcpy(pp->name, opp->name, OFP_MAX_PORT_NAME_LEN);
|
|
|
|
|
|
|
|
|
|
pp->config = ntohl(opp->config) & OFPPC10_ALL;
|
|
|
|
|
pp->state = ntohl(opp->state) & OFPPS10_ALL;
|
|
|
|
|
|
|
|
|
|
pp->curr = netdev_port_features_from_ofp10(opp->curr);
|
|
|
|
|
pp->advertised = netdev_port_features_from_ofp10(opp->advertised);
|
|
|
|
|
pp->supported = netdev_port_features_from_ofp10(opp->supported);
|
|
|
|
|
pp->peer = netdev_port_features_from_ofp10(opp->peer);
|
|
|
|
|
|
|
|
|
|
pp->curr_speed = netdev_features_to_bps(pp->curr) / 1000;
|
|
|
|
|
pp->max_speed = netdev_features_to_bps(pp->supported) / 1000;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum ofperr
|
|
|
|
|
ofputil_decode_ofp11_port(struct ofputil_phy_port *pp,
|
|
|
|
|
const struct ofp11_port *op)
|
|
|
|
|
{
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
|
|
|
|
|
memset(pp, 0, sizeof *pp);
|
|
|
|
|
|
|
|
|
|
error = ofputil_port_from_ofp11(op->port_no, &pp->port_no);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
memcpy(pp->hw_addr, op->hw_addr, OFP_ETH_ALEN);
|
|
|
|
|
ovs_strlcpy(pp->name, op->name, OFP_MAX_PORT_NAME_LEN);
|
|
|
|
|
|
|
|
|
|
pp->config = ntohl(op->config) & OFPPC11_ALL;
|
|
|
|
|
pp->state = ntohl(op->state) & OFPPC11_ALL;
|
|
|
|
|
|
|
|
|
|
pp->curr = netdev_port_features_from_ofp11(op->curr);
|
|
|
|
|
pp->advertised = netdev_port_features_from_ofp11(op->advertised);
|
|
|
|
|
pp->supported = netdev_port_features_from_ofp11(op->supported);
|
|
|
|
|
pp->peer = netdev_port_features_from_ofp11(op->peer);
|
|
|
|
|
|
|
|
|
|
pp->curr_speed = ntohl(op->curr_speed);
|
|
|
|
|
pp->max_speed = ntohl(op->max_speed);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-08 00:01:11 -07:00
|
|
|
|
static size_t
|
|
|
|
|
ofputil_get_phy_port_size(uint8_t ofp_version)
|
|
|
|
|
{
|
|
|
|
|
return ofp_version == OFP10_VERSION ? sizeof(struct ofp10_phy_port)
|
|
|
|
|
: sizeof(struct ofp11_port);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
static void
|
|
|
|
|
ofputil_encode_ofp10_phy_port(const struct ofputil_phy_port *pp,
|
|
|
|
|
struct ofp10_phy_port *opp)
|
|
|
|
|
{
|
|
|
|
|
memset(opp, 0, sizeof *opp);
|
|
|
|
|
|
|
|
|
|
opp->port_no = htons(pp->port_no);
|
|
|
|
|
memcpy(opp->hw_addr, pp->hw_addr, ETH_ADDR_LEN);
|
|
|
|
|
ovs_strlcpy(opp->name, pp->name, OFP_MAX_PORT_NAME_LEN);
|
|
|
|
|
|
|
|
|
|
opp->config = htonl(pp->config & OFPPC10_ALL);
|
|
|
|
|
opp->state = htonl(pp->state & OFPPS10_ALL);
|
|
|
|
|
|
|
|
|
|
opp->curr = netdev_port_features_to_ofp10(pp->curr);
|
|
|
|
|
opp->advertised = netdev_port_features_to_ofp10(pp->advertised);
|
|
|
|
|
opp->supported = netdev_port_features_to_ofp10(pp->supported);
|
|
|
|
|
opp->peer = netdev_port_features_to_ofp10(pp->peer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofputil_encode_ofp11_port(const struct ofputil_phy_port *pp,
|
|
|
|
|
struct ofp11_port *op)
|
|
|
|
|
{
|
|
|
|
|
memset(op, 0, sizeof *op);
|
|
|
|
|
|
|
|
|
|
op->port_no = ofputil_port_to_ofp11(pp->port_no);
|
|
|
|
|
memcpy(op->hw_addr, pp->hw_addr, ETH_ADDR_LEN);
|
|
|
|
|
ovs_strlcpy(op->name, pp->name, OFP_MAX_PORT_NAME_LEN);
|
|
|
|
|
|
|
|
|
|
op->config = htonl(pp->config & OFPPC11_ALL);
|
|
|
|
|
op->state = htonl(pp->state & OFPPS11_ALL);
|
|
|
|
|
|
|
|
|
|
op->curr = netdev_port_features_to_ofp11(pp->curr);
|
|
|
|
|
op->advertised = netdev_port_features_to_ofp11(pp->advertised);
|
|
|
|
|
op->supported = netdev_port_features_to_ofp11(pp->supported);
|
|
|
|
|
op->peer = netdev_port_features_to_ofp11(pp->peer);
|
|
|
|
|
|
|
|
|
|
op->curr_speed = htonl(pp->curr_speed);
|
|
|
|
|
op->max_speed = htonl(pp->max_speed);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ofputil_put_phy_port(uint8_t ofp_version, const struct ofputil_phy_port *pp,
|
|
|
|
|
struct ofpbuf *b)
|
|
|
|
|
{
|
|
|
|
|
if (ofp_version == OFP10_VERSION) {
|
|
|
|
|
struct ofp10_phy_port *opp;
|
|
|
|
|
if (b->size + sizeof *opp <= UINT16_MAX) {
|
|
|
|
|
opp = ofpbuf_put_uninit(b, sizeof *opp);
|
|
|
|
|
ofputil_encode_ofp10_phy_port(pp, opp);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
struct ofp11_port *op;
|
|
|
|
|
if (b->size + sizeof *op <= UINT16_MAX) {
|
|
|
|
|
op = ofpbuf_put_uninit(b, sizeof *op);
|
|
|
|
|
ofputil_encode_ofp11_port(pp, op);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-05-04 14:42:04 -07:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ofputil_append_port_desc_stats_reply(uint8_t ofp_version,
|
|
|
|
|
const struct ofputil_phy_port *pp,
|
|
|
|
|
struct list *replies)
|
|
|
|
|
{
|
|
|
|
|
if (ofp_version == OFP10_VERSION) {
|
|
|
|
|
struct ofp10_phy_port *opp;
|
|
|
|
|
|
|
|
|
|
opp = ofputil_append_stats_reply(sizeof *opp, replies);
|
|
|
|
|
ofputil_encode_ofp10_phy_port(pp, opp);
|
|
|
|
|
} else {
|
|
|
|
|
struct ofp11_port *op;
|
|
|
|
|
|
|
|
|
|
op = ofputil_append_stats_reply(sizeof *op, replies);
|
|
|
|
|
ofputil_encode_ofp11_port(pp, op);
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-02-15 16:33:04 -08:00
|
|
|
|
|
|
|
|
|
/* ofputil_switch_features */
|
|
|
|
|
|
|
|
|
|
#define OFPC_COMMON (OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \
|
|
|
|
|
OFPC_IP_REASM | OFPC_QUEUE_STATS | OFPC_ARP_MATCH_IP)
|
|
|
|
|
BUILD_ASSERT_DECL((int) OFPUTIL_C_FLOW_STATS == OFPC_FLOW_STATS);
|
|
|
|
|
BUILD_ASSERT_DECL((int) OFPUTIL_C_TABLE_STATS == OFPC_TABLE_STATS);
|
|
|
|
|
BUILD_ASSERT_DECL((int) OFPUTIL_C_PORT_STATS == OFPC_PORT_STATS);
|
|
|
|
|
BUILD_ASSERT_DECL((int) OFPUTIL_C_IP_REASM == OFPC_IP_REASM);
|
|
|
|
|
BUILD_ASSERT_DECL((int) OFPUTIL_C_QUEUE_STATS == OFPC_QUEUE_STATS);
|
|
|
|
|
BUILD_ASSERT_DECL((int) OFPUTIL_C_ARP_MATCH_IP == OFPC_ARP_MATCH_IP);
|
|
|
|
|
|
|
|
|
|
struct ofputil_action_bit_translation {
|
|
|
|
|
enum ofputil_action_bitmap ofputil_bit;
|
|
|
|
|
int of_bit;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct ofputil_action_bit_translation of10_action_bits[] = {
|
|
|
|
|
{ OFPUTIL_A_OUTPUT, OFPAT10_OUTPUT },
|
|
|
|
|
{ OFPUTIL_A_SET_VLAN_VID, OFPAT10_SET_VLAN_VID },
|
|
|
|
|
{ OFPUTIL_A_SET_VLAN_PCP, OFPAT10_SET_VLAN_PCP },
|
|
|
|
|
{ OFPUTIL_A_STRIP_VLAN, OFPAT10_STRIP_VLAN },
|
|
|
|
|
{ OFPUTIL_A_SET_DL_SRC, OFPAT10_SET_DL_SRC },
|
|
|
|
|
{ OFPUTIL_A_SET_DL_DST, OFPAT10_SET_DL_DST },
|
|
|
|
|
{ OFPUTIL_A_SET_NW_SRC, OFPAT10_SET_NW_SRC },
|
|
|
|
|
{ OFPUTIL_A_SET_NW_DST, OFPAT10_SET_NW_DST },
|
|
|
|
|
{ OFPUTIL_A_SET_NW_TOS, OFPAT10_SET_NW_TOS },
|
|
|
|
|
{ OFPUTIL_A_SET_TP_SRC, OFPAT10_SET_TP_SRC },
|
|
|
|
|
{ OFPUTIL_A_SET_TP_DST, OFPAT10_SET_TP_DST },
|
|
|
|
|
{ OFPUTIL_A_ENQUEUE, OFPAT10_ENQUEUE },
|
|
|
|
|
{ 0, 0 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const struct ofputil_action_bit_translation of11_action_bits[] = {
|
|
|
|
|
{ OFPUTIL_A_OUTPUT, OFPAT11_OUTPUT },
|
|
|
|
|
{ OFPUTIL_A_SET_VLAN_VID, OFPAT11_SET_VLAN_VID },
|
|
|
|
|
{ OFPUTIL_A_SET_VLAN_PCP, OFPAT11_SET_VLAN_PCP },
|
|
|
|
|
{ OFPUTIL_A_SET_DL_SRC, OFPAT11_SET_DL_SRC },
|
|
|
|
|
{ OFPUTIL_A_SET_DL_DST, OFPAT11_SET_DL_DST },
|
|
|
|
|
{ OFPUTIL_A_SET_NW_SRC, OFPAT11_SET_NW_SRC },
|
|
|
|
|
{ OFPUTIL_A_SET_NW_DST, OFPAT11_SET_NW_DST },
|
|
|
|
|
{ OFPUTIL_A_SET_NW_TOS, OFPAT11_SET_NW_TOS },
|
|
|
|
|
{ OFPUTIL_A_SET_NW_ECN, OFPAT11_SET_NW_ECN },
|
|
|
|
|
{ OFPUTIL_A_SET_TP_SRC, OFPAT11_SET_TP_SRC },
|
|
|
|
|
{ OFPUTIL_A_SET_TP_DST, OFPAT11_SET_TP_DST },
|
|
|
|
|
{ OFPUTIL_A_COPY_TTL_OUT, OFPAT11_COPY_TTL_OUT },
|
|
|
|
|
{ OFPUTIL_A_COPY_TTL_IN, OFPAT11_COPY_TTL_IN },
|
|
|
|
|
{ OFPUTIL_A_SET_MPLS_LABEL, OFPAT11_SET_MPLS_LABEL },
|
|
|
|
|
{ OFPUTIL_A_SET_MPLS_TC, OFPAT11_SET_MPLS_TC },
|
|
|
|
|
{ OFPUTIL_A_SET_MPLS_TTL, OFPAT11_SET_MPLS_TTL },
|
|
|
|
|
{ OFPUTIL_A_DEC_MPLS_TTL, OFPAT11_DEC_MPLS_TTL },
|
|
|
|
|
{ OFPUTIL_A_PUSH_VLAN, OFPAT11_PUSH_VLAN },
|
|
|
|
|
{ OFPUTIL_A_POP_VLAN, OFPAT11_POP_VLAN },
|
|
|
|
|
{ OFPUTIL_A_PUSH_MPLS, OFPAT11_PUSH_MPLS },
|
|
|
|
|
{ OFPUTIL_A_POP_MPLS, OFPAT11_POP_MPLS },
|
|
|
|
|
{ OFPUTIL_A_SET_QUEUE, OFPAT11_SET_QUEUE },
|
|
|
|
|
{ OFPUTIL_A_GROUP, OFPAT11_GROUP },
|
|
|
|
|
{ OFPUTIL_A_SET_NW_TTL, OFPAT11_SET_NW_TTL },
|
|
|
|
|
{ OFPUTIL_A_DEC_NW_TTL, OFPAT11_DEC_NW_TTL },
|
|
|
|
|
{ 0, 0 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static enum ofputil_action_bitmap
|
|
|
|
|
decode_action_bits(ovs_be32 of_actions,
|
|
|
|
|
const struct ofputil_action_bit_translation *x)
|
|
|
|
|
{
|
|
|
|
|
enum ofputil_action_bitmap ofputil_actions;
|
|
|
|
|
|
|
|
|
|
ofputil_actions = 0;
|
|
|
|
|
for (; x->ofputil_bit; x++) {
|
|
|
|
|
if (of_actions & htonl(1u << x->of_bit)) {
|
|
|
|
|
ofputil_actions |= x->ofputil_bit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ofputil_actions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Decodes an OpenFlow 1.0 or 1.1 "switch_features" structure 'osf' into an
|
|
|
|
|
* abstract representation in '*features'. Initializes '*b' to iterate over
|
|
|
|
|
* the OpenFlow port structures following 'osf' with later calls to
|
2012-05-04 14:42:04 -07:00
|
|
|
|
* ofputil_pull_phy_port(). Returns 0 if successful, otherwise an
|
2012-02-15 16:33:04 -08:00
|
|
|
|
* OFPERR_* value. */
|
|
|
|
|
enum ofperr
|
|
|
|
|
ofputil_decode_switch_features(const struct ofp_switch_features *osf,
|
|
|
|
|
struct ofputil_switch_features *features,
|
|
|
|
|
struct ofpbuf *b)
|
|
|
|
|
{
|
|
|
|
|
ofpbuf_use_const(b, osf, ntohs(osf->header.length));
|
|
|
|
|
ofpbuf_pull(b, sizeof *osf);
|
|
|
|
|
|
|
|
|
|
features->datapath_id = ntohll(osf->datapath_id);
|
|
|
|
|
features->n_buffers = ntohl(osf->n_buffers);
|
|
|
|
|
features->n_tables = osf->n_tables;
|
|
|
|
|
|
|
|
|
|
features->capabilities = ntohl(osf->capabilities) & OFPC_COMMON;
|
|
|
|
|
|
2012-05-08 00:01:11 -07:00
|
|
|
|
if (b->size % ofputil_get_phy_port_size(osf->header.version)) {
|
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (osf->header.version == OFP10_VERSION) {
|
2012-02-15 16:33:04 -08:00
|
|
|
|
if (osf->capabilities & htonl(OFPC10_STP)) {
|
|
|
|
|
features->capabilities |= OFPUTIL_C_STP;
|
|
|
|
|
}
|
|
|
|
|
features->actions = decode_action_bits(osf->actions, of10_action_bits);
|
|
|
|
|
} else if (osf->header.version == OFP11_VERSION) {
|
|
|
|
|
if (osf->capabilities & htonl(OFPC11_GROUP_STATS)) {
|
|
|
|
|
features->capabilities |= OFPUTIL_C_GROUP_STATS;
|
|
|
|
|
}
|
|
|
|
|
features->actions = decode_action_bits(osf->actions, of11_action_bits);
|
|
|
|
|
} else {
|
|
|
|
|
return OFPERR_OFPBRC_BAD_VERSION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-04 17:19:26 -07:00
|
|
|
|
/* Returns true if the maximum number of ports are in 'osf'. */
|
|
|
|
|
static bool
|
|
|
|
|
max_ports_in_features(const struct ofp_switch_features *osf)
|
|
|
|
|
{
|
2012-05-08 00:01:11 -07:00
|
|
|
|
size_t pp_size = ofputil_get_phy_port_size(osf->header.version);
|
2012-05-04 17:19:26 -07:00
|
|
|
|
return ntohs(osf->header.length) + pp_size > UINT16_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given a buffer 'b' that contains a Features Reply message, checks if
|
|
|
|
|
* it contains the maximum number of ports that will fit. If so, it
|
|
|
|
|
* returns true and removes the ports from the message. The caller
|
|
|
|
|
* should then send an OFPST_PORT_DESC stats request to get the ports,
|
|
|
|
|
* since the switch may have more ports than could be represented in the
|
|
|
|
|
* Features Reply. Otherwise, returns false.
|
|
|
|
|
*/
|
|
|
|
|
bool
|
|
|
|
|
ofputil_switch_features_ports_trunc(struct ofpbuf *b)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_switch_features *osf = b->data;
|
|
|
|
|
|
|
|
|
|
if (max_ports_in_features(osf)) {
|
|
|
|
|
/* Remove all the ports. */
|
|
|
|
|
b->size = sizeof(*osf);
|
|
|
|
|
update_openflow_length(b);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:33:04 -08:00
|
|
|
|
static ovs_be32
|
|
|
|
|
encode_action_bits(enum ofputil_action_bitmap ofputil_actions,
|
|
|
|
|
const struct ofputil_action_bit_translation *x)
|
|
|
|
|
{
|
|
|
|
|
uint32_t of_actions;
|
|
|
|
|
|
|
|
|
|
of_actions = 0;
|
|
|
|
|
for (; x->ofputil_bit; x++) {
|
|
|
|
|
if (ofputil_actions & x->ofputil_bit) {
|
|
|
|
|
of_actions |= 1 << x->of_bit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return htonl(of_actions);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns a buffer owned by the caller that encodes 'features' in the format
|
|
|
|
|
* required by 'protocol' with the given 'xid'. The caller should append port
|
|
|
|
|
* information to the buffer with subsequent calls to
|
|
|
|
|
* ofputil_put_switch_features_port(). */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_switch_features(const struct ofputil_switch_features *features,
|
|
|
|
|
enum ofputil_protocol protocol, ovs_be32 xid)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_switch_features *osf;
|
|
|
|
|
struct ofpbuf *b;
|
|
|
|
|
|
|
|
|
|
osf = make_openflow_xid(sizeof *osf, OFPT_FEATURES_REPLY, xid, &b);
|
|
|
|
|
osf->header.version = ofputil_protocol_to_ofp_version(protocol);
|
|
|
|
|
osf->datapath_id = htonll(features->datapath_id);
|
|
|
|
|
osf->n_buffers = htonl(features->n_buffers);
|
|
|
|
|
osf->n_tables = features->n_tables;
|
|
|
|
|
|
|
|
|
|
osf->capabilities = htonl(features->capabilities & OFPC_COMMON);
|
|
|
|
|
if (osf->header.version == OFP10_VERSION) {
|
|
|
|
|
if (features->capabilities & OFPUTIL_C_STP) {
|
|
|
|
|
osf->capabilities |= htonl(OFPC10_STP);
|
|
|
|
|
}
|
|
|
|
|
osf->actions = encode_action_bits(features->actions, of10_action_bits);
|
|
|
|
|
} else {
|
|
|
|
|
if (features->capabilities & OFPUTIL_C_GROUP_STATS) {
|
|
|
|
|
osf->capabilities |= htonl(OFPC11_GROUP_STATS);
|
|
|
|
|
}
|
|
|
|
|
osf->actions = encode_action_bits(features->actions, of11_action_bits);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Encodes 'pp' into the format required by the switch_features message already
|
|
|
|
|
* in 'b', which should have been returned by ofputil_encode_switch_features(),
|
|
|
|
|
* and appends the encoded version to 'b'. */
|
|
|
|
|
void
|
|
|
|
|
ofputil_put_switch_features_port(const struct ofputil_phy_port *pp,
|
|
|
|
|
struct ofpbuf *b)
|
|
|
|
|
{
|
|
|
|
|
const struct ofp_switch_features *osf = b->data;
|
|
|
|
|
|
|
|
|
|
ofputil_put_phy_port(osf->header.version, pp, b);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ofputil_port_status */
|
|
|
|
|
|
|
|
|
|
/* Decodes the OpenFlow "port status" message in '*ops' into an abstract form
|
|
|
|
|
* in '*ps'. Returns 0 if successful, otherwise an OFPERR_* value. */
|
|
|
|
|
enum ofperr
|
|
|
|
|
ofputil_decode_port_status(const struct ofp_port_status *ops,
|
|
|
|
|
struct ofputil_port_status *ps)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf b;
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
if (ops->reason != OFPPR_ADD &&
|
|
|
|
|
ops->reason != OFPPR_DELETE &&
|
|
|
|
|
ops->reason != OFPPR_MODIFY) {
|
|
|
|
|
return OFPERR_NXBRC_BAD_REASON;
|
|
|
|
|
}
|
|
|
|
|
ps->reason = ops->reason;
|
|
|
|
|
|
|
|
|
|
ofpbuf_use_const(&b, ops, ntohs(ops->header.length));
|
|
|
|
|
ofpbuf_pull(&b, sizeof *ops);
|
|
|
|
|
retval = ofputil_pull_phy_port(ops->header.version, &b, &ps->desc);
|
|
|
|
|
assert(retval != EOF);
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts the abstract form of a "port status" message in '*ps' into an
|
|
|
|
|
* OpenFlow message suitable for 'protocol', and returns that encoded form in
|
|
|
|
|
* a buffer owned by the caller. */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_port_status(const struct ofputil_port_status *ps,
|
|
|
|
|
enum ofputil_protocol protocol)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_port_status *ops;
|
|
|
|
|
struct ofpbuf *b;
|
|
|
|
|
|
|
|
|
|
b = ofpbuf_new(sizeof *ops + sizeof(struct ofp11_port));
|
|
|
|
|
ops = put_openflow_xid(sizeof *ops, OFPT_PORT_STATUS, htonl(0), b);
|
|
|
|
|
ops->header.version = ofputil_protocol_to_ofp_version(protocol);
|
|
|
|
|
ops->reason = ps->reason;
|
|
|
|
|
ofputil_put_phy_port(ops->header.version, &ps->desc, b);
|
|
|
|
|
update_openflow_length(b);
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ofputil_port_mod */
|
|
|
|
|
|
|
|
|
|
/* Decodes the OpenFlow "port mod" message in '*oh' into an abstract form in
|
|
|
|
|
* '*pm'. Returns 0 if successful, otherwise an OFPERR_* value. */
|
|
|
|
|
enum ofperr
|
|
|
|
|
ofputil_decode_port_mod(const struct ofp_header *oh,
|
|
|
|
|
struct ofputil_port_mod *pm)
|
|
|
|
|
{
|
|
|
|
|
if (oh->version == OFP10_VERSION) {
|
|
|
|
|
const struct ofp10_port_mod *opm = (const struct ofp10_port_mod *) oh;
|
|
|
|
|
|
|
|
|
|
if (oh->length != htons(sizeof *opm)) {
|
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pm->port_no = ntohs(opm->port_no);
|
|
|
|
|
memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
|
|
|
|
|
pm->config = ntohl(opm->config) & OFPPC10_ALL;
|
|
|
|
|
pm->mask = ntohl(opm->mask) & OFPPC10_ALL;
|
|
|
|
|
pm->advertise = netdev_port_features_from_ofp10(opm->advertise);
|
|
|
|
|
} else if (oh->version == OFP11_VERSION) {
|
|
|
|
|
const struct ofp11_port_mod *opm = (const struct ofp11_port_mod *) oh;
|
|
|
|
|
enum ofperr error;
|
|
|
|
|
|
|
|
|
|
if (oh->length != htons(sizeof *opm)) {
|
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
error = ofputil_port_from_ofp11(opm->port_no, &pm->port_no);
|
|
|
|
|
if (error) {
|
|
|
|
|
return error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
memcpy(pm->hw_addr, opm->hw_addr, ETH_ADDR_LEN);
|
|
|
|
|
pm->config = ntohl(opm->config) & OFPPC11_ALL;
|
|
|
|
|
pm->mask = ntohl(opm->mask) & OFPPC11_ALL;
|
|
|
|
|
pm->advertise = netdev_port_features_from_ofp11(opm->advertise);
|
|
|
|
|
} else {
|
|
|
|
|
return OFPERR_OFPBRC_BAD_VERSION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pm->config &= pm->mask;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts the abstract form of a "port mod" message in '*pm' into an OpenFlow
|
|
|
|
|
* message suitable for 'protocol', and returns that encoded form in a buffer
|
|
|
|
|
* owned by the caller. */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_port_mod(const struct ofputil_port_mod *pm,
|
|
|
|
|
enum ofputil_protocol protocol)
|
|
|
|
|
{
|
|
|
|
|
uint8_t ofp_version = ofputil_protocol_to_ofp_version(protocol);
|
|
|
|
|
struct ofpbuf *b;
|
|
|
|
|
|
|
|
|
|
if (ofp_version == OFP10_VERSION) {
|
|
|
|
|
struct ofp10_port_mod *opm;
|
|
|
|
|
|
|
|
|
|
opm = make_openflow(sizeof *opm, OFPT10_PORT_MOD, &b);
|
|
|
|
|
opm->port_no = htons(pm->port_no);
|
|
|
|
|
memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
|
|
|
|
|
opm->config = htonl(pm->config & OFPPC10_ALL);
|
|
|
|
|
opm->mask = htonl(pm->mask & OFPPC10_ALL);
|
|
|
|
|
opm->advertise = netdev_port_features_to_ofp10(pm->advertise);
|
|
|
|
|
} else if (ofp_version == OFP11_VERSION) {
|
|
|
|
|
struct ofp11_port_mod *opm;
|
|
|
|
|
|
|
|
|
|
opm = make_openflow(sizeof *opm, OFPT11_PORT_MOD, &b);
|
|
|
|
|
opm->port_no = htonl(pm->port_no);
|
|
|
|
|
memcpy(opm->hw_addr, pm->hw_addr, ETH_ADDR_LEN);
|
|
|
|
|
opm->config = htonl(pm->config & OFPPC11_ALL);
|
|
|
|
|
opm->mask = htonl(pm->mask & OFPPC11_ALL);
|
|
|
|
|
opm->advertise = netdev_port_features_to_ofp11(pm->advertise);
|
|
|
|
|
} else {
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-06 14:17:49 -08:00
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_packet_out(const struct ofputil_packet_out *po)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_packet_out *opo;
|
|
|
|
|
size_t actions_len;
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
|
|
actions_len = po->n_actions * sizeof *po->actions;
|
|
|
|
|
size = sizeof *opo + actions_len;
|
|
|
|
|
if (po->buffer_id == UINT32_MAX) {
|
|
|
|
|
size += po->packet_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
msg = ofpbuf_new(size);
|
2012-02-14 16:58:39 -08:00
|
|
|
|
opo = put_openflow(sizeof *opo, OFPT10_PACKET_OUT, msg);
|
2012-02-06 14:17:49 -08:00
|
|
|
|
opo->buffer_id = htonl(po->buffer_id);
|
|
|
|
|
opo->in_port = htons(po->in_port);
|
|
|
|
|
opo->actions_len = htons(actions_len);
|
|
|
|
|
ofpbuf_put(msg, po->actions, actions_len);
|
|
|
|
|
if (po->buffer_id == UINT32_MAX) {
|
|
|
|
|
ofpbuf_put(msg, po->packet, po->packet_len);
|
|
|
|
|
}
|
|
|
|
|
update_openflow_length(msg);
|
|
|
|
|
|
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-06 10:20:20 -08:00
|
|
|
|
/* Returns a string representing the message type of 'type'. The string is the
|
|
|
|
|
* enumeration constant for the type, e.g. "OFPT_HELLO". For statistics
|
|
|
|
|
* messages, the constant is followed by "request" or "reply",
|
|
|
|
|
* e.g. "OFPST_AGGREGATE reply". */
|
|
|
|
|
const char *
|
|
|
|
|
ofputil_msg_type_name(const struct ofputil_msg_type *type)
|
|
|
|
|
{
|
|
|
|
|
return type->name;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-27 13:14:05 -07:00
|
|
|
|
/* Allocates and stores in '*bufferp' a new ofpbuf with a size of
|
|
|
|
|
* 'openflow_len', starting with an OpenFlow header with the given 'type' and
|
|
|
|
|
* an arbitrary transaction id. Allocated bytes beyond the header, if any, are
|
|
|
|
|
* zeroed.
|
|
|
|
|
*
|
|
|
|
|
* The caller is responsible for freeing '*bufferp' when it is no longer
|
|
|
|
|
* needed.
|
|
|
|
|
*
|
|
|
|
|
* The OpenFlow header length is initially set to 'openflow_len'; if the
|
|
|
|
|
* message is later extended, the length should be updated with
|
|
|
|
|
* update_openflow_length() before sending.
|
|
|
|
|
*
|
|
|
|
|
* Returns the header. */
|
|
|
|
|
void *
|
|
|
|
|
make_openflow(size_t openflow_len, uint8_t type, struct ofpbuf **bufferp)
|
|
|
|
|
{
|
|
|
|
|
*bufferp = ofpbuf_new(openflow_len);
|
|
|
|
|
return put_openflow_xid(openflow_len, type, alloc_xid(), *bufferp);
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-18 16:25:52 -07:00
|
|
|
|
/* Similar to make_openflow() but creates a Nicira vendor extension message
|
|
|
|
|
* with the specific 'subtype'. 'subtype' should be in host byte order. */
|
|
|
|
|
void *
|
|
|
|
|
make_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf **bufferp)
|
|
|
|
|
{
|
|
|
|
|
return make_nxmsg_xid(openflow_len, subtype, alloc_xid(), bufferp);
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-27 13:14:05 -07:00
|
|
|
|
/* Allocates and stores in '*bufferp' a new ofpbuf with a size of
|
|
|
|
|
* 'openflow_len', starting with an OpenFlow header with the given 'type' and
|
|
|
|
|
* transaction id 'xid'. Allocated bytes beyond the header, if any, are
|
|
|
|
|
* zeroed.
|
|
|
|
|
*
|
|
|
|
|
* The caller is responsible for freeing '*bufferp' when it is no longer
|
|
|
|
|
* needed.
|
|
|
|
|
*
|
|
|
|
|
* The OpenFlow header length is initially set to 'openflow_len'; if the
|
|
|
|
|
* message is later extended, the length should be updated with
|
|
|
|
|
* update_openflow_length() before sending.
|
|
|
|
|
*
|
|
|
|
|
* Returns the header. */
|
|
|
|
|
void *
|
2010-11-16 11:00:25 -08:00
|
|
|
|
make_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
|
2010-05-27 13:14:05 -07:00
|
|
|
|
struct ofpbuf **bufferp)
|
|
|
|
|
{
|
|
|
|
|
*bufferp = ofpbuf_new(openflow_len);
|
|
|
|
|
return put_openflow_xid(openflow_len, type, xid, *bufferp);
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-18 16:25:52 -07:00
|
|
|
|
/* Similar to make_openflow_xid() but creates a Nicira vendor extension message
|
|
|
|
|
* with the specific 'subtype'. 'subtype' should be in host byte order. */
|
|
|
|
|
void *
|
2010-11-16 11:00:25 -08:00
|
|
|
|
make_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
|
2010-10-18 16:25:52 -07:00
|
|
|
|
struct ofpbuf **bufferp)
|
|
|
|
|
{
|
2010-11-23 13:31:07 -08:00
|
|
|
|
*bufferp = ofpbuf_new(openflow_len);
|
|
|
|
|
return put_nxmsg_xid(openflow_len, subtype, xid, *bufferp);
|
2010-10-18 16:25:52 -07:00
|
|
|
|
}
|
|
|
|
|
|
2010-05-27 13:14:05 -07:00
|
|
|
|
/* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
|
|
|
|
|
* with the given 'type' and an arbitrary transaction id. Allocated bytes
|
|
|
|
|
* beyond the header, if any, are zeroed.
|
|
|
|
|
*
|
|
|
|
|
* The OpenFlow header length is initially set to 'openflow_len'; if the
|
|
|
|
|
* message is later extended, the length should be updated with
|
|
|
|
|
* update_openflow_length() before sending.
|
|
|
|
|
*
|
|
|
|
|
* Returns the header. */
|
|
|
|
|
void *
|
|
|
|
|
put_openflow(size_t openflow_len, uint8_t type, struct ofpbuf *buffer)
|
|
|
|
|
{
|
|
|
|
|
return put_openflow_xid(openflow_len, type, alloc_xid(), buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Appends 'openflow_len' bytes to 'buffer', starting with an OpenFlow header
|
|
|
|
|
* with the given 'type' and an transaction id 'xid'. Allocated bytes beyond
|
|
|
|
|
* the header, if any, are zeroed.
|
|
|
|
|
*
|
|
|
|
|
* The OpenFlow header length is initially set to 'openflow_len'; if the
|
|
|
|
|
* message is later extended, the length should be updated with
|
|
|
|
|
* update_openflow_length() before sending.
|
|
|
|
|
*
|
|
|
|
|
* Returns the header. */
|
|
|
|
|
void *
|
2010-11-16 11:00:25 -08:00
|
|
|
|
put_openflow_xid(size_t openflow_len, uint8_t type, ovs_be32 xid,
|
2010-05-27 13:14:05 -07:00
|
|
|
|
struct ofpbuf *buffer)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_header *oh;
|
|
|
|
|
|
|
|
|
|
assert(openflow_len >= sizeof *oh);
|
|
|
|
|
assert(openflow_len <= UINT16_MAX);
|
|
|
|
|
|
|
|
|
|
oh = ofpbuf_put_uninit(buffer, openflow_len);
|
2011-12-06 22:33:49 -08:00
|
|
|
|
oh->version = OFP10_VERSION;
|
2010-05-27 13:14:05 -07:00
|
|
|
|
oh->type = type;
|
|
|
|
|
oh->length = htons(openflow_len);
|
|
|
|
|
oh->xid = xid;
|
|
|
|
|
memset(oh + 1, 0, openflow_len - sizeof *oh);
|
|
|
|
|
return oh;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-23 13:31:07 -08:00
|
|
|
|
/* Similar to put_openflow() but append a Nicira vendor extension message with
|
|
|
|
|
* the specific 'subtype'. 'subtype' should be in host byte order. */
|
|
|
|
|
void *
|
|
|
|
|
put_nxmsg(size_t openflow_len, uint32_t subtype, struct ofpbuf *buffer)
|
|
|
|
|
{
|
|
|
|
|
return put_nxmsg_xid(openflow_len, subtype, alloc_xid(), buffer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Similar to put_openflow_xid() but append a Nicira vendor extension message
|
|
|
|
|
* with the specific 'subtype'. 'subtype' should be in host byte order. */
|
|
|
|
|
void *
|
|
|
|
|
put_nxmsg_xid(size_t openflow_len, uint32_t subtype, ovs_be32 xid,
|
|
|
|
|
struct ofpbuf *buffer)
|
|
|
|
|
{
|
|
|
|
|
struct nicira_header *nxh;
|
|
|
|
|
|
|
|
|
|
nxh = put_openflow_xid(openflow_len, OFPT_VENDOR, xid, buffer);
|
|
|
|
|
nxh->vendor = htonl(NX_VENDOR_ID);
|
|
|
|
|
nxh->subtype = htonl(subtype);
|
|
|
|
|
return nxh;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-27 13:14:05 -07:00
|
|
|
|
/* Updates the 'length' field of the OpenFlow message in 'buffer' to
|
|
|
|
|
* 'buffer->size'. */
|
|
|
|
|
void
|
2010-08-30 00:24:53 -07:00
|
|
|
|
update_openflow_length(struct ofpbuf *buffer)
|
2010-05-27 13:14:05 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofp_header *oh = ofpbuf_at_assert(buffer, 0, sizeof *oh);
|
2010-08-30 00:24:53 -07:00
|
|
|
|
oh->length = htons(buffer->size);
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-05-31 16:49:06 -07:00
|
|
|
|
static void
|
|
|
|
|
put_stats__(ovs_be32 xid, uint8_t ofp_type,
|
|
|
|
|
ovs_be16 ofpst_type, ovs_be32 nxst_subtype,
|
|
|
|
|
struct ofpbuf *msg)
|
|
|
|
|
{
|
|
|
|
|
if (ofpst_type == htons(OFPST_VENDOR)) {
|
|
|
|
|
struct nicira_stats_msg *nsm;
|
|
|
|
|
|
|
|
|
|
nsm = put_openflow_xid(sizeof *nsm, ofp_type, xid, msg);
|
|
|
|
|
nsm->vsm.osm.type = ofpst_type;
|
|
|
|
|
nsm->vsm.vendor = htonl(NX_VENDOR_ID);
|
|
|
|
|
nsm->subtype = nxst_subtype;
|
|
|
|
|
} else {
|
|
|
|
|
struct ofp_stats_msg *osm;
|
|
|
|
|
|
|
|
|
|
osm = put_openflow_xid(sizeof *osm, ofp_type, xid, msg);
|
|
|
|
|
osm->type = ofpst_type;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Creates a statistics request message with total length 'openflow_len'
|
|
|
|
|
* (including all headers) and the given 'ofpst_type', and stores the buffer
|
|
|
|
|
* containing the new message in '*bufferp'. If 'ofpst_type' is OFPST_VENDOR
|
|
|
|
|
* then 'nxst_subtype' is used as the Nicira vendor extension statistics
|
|
|
|
|
* subtype (otherwise 'nxst_subtype' is ignored).
|
|
|
|
|
*
|
|
|
|
|
* Initializes bytes following the headers to all-bits-zero.
|
|
|
|
|
*
|
|
|
|
|
* Returns the first byte of the new message. */
|
2010-11-23 13:31:07 -08:00
|
|
|
|
void *
|
2011-05-31 16:49:06 -07:00
|
|
|
|
ofputil_make_stats_request(size_t openflow_len, uint16_t ofpst_type,
|
|
|
|
|
uint32_t nxst_subtype, struct ofpbuf **bufferp)
|
2010-11-23 13:31:07 -08:00
|
|
|
|
{
|
2011-05-31 16:49:06 -07:00
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
msg = *bufferp = ofpbuf_new(openflow_len);
|
2012-02-14 16:58:39 -08:00
|
|
|
|
put_stats__(alloc_xid(), OFPT10_STATS_REQUEST,
|
2011-05-31 16:49:06 -07:00
|
|
|
|
htons(ofpst_type), htonl(nxst_subtype), msg);
|
|
|
|
|
ofpbuf_padto(msg, openflow_len);
|
|
|
|
|
|
|
|
|
|
return msg->data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
put_stats_reply__(const struct ofp_stats_msg *request, struct ofpbuf *msg)
|
|
|
|
|
{
|
2012-02-14 16:58:39 -08:00
|
|
|
|
assert(request->header.type == OFPT10_STATS_REQUEST ||
|
|
|
|
|
request->header.type == OFPT10_STATS_REPLY);
|
|
|
|
|
put_stats__(request->header.xid, OFPT10_STATS_REPLY, request->type,
|
2011-05-31 16:49:06 -07:00
|
|
|
|
(request->type != htons(OFPST_VENDOR)
|
|
|
|
|
? htonl(0)
|
|
|
|
|
: ((const struct nicira_stats_msg *) request)->subtype),
|
|
|
|
|
msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Creates a statistics reply message with total length 'openflow_len'
|
|
|
|
|
* (including all headers) and the same type (either a standard OpenFlow
|
|
|
|
|
* statistics type or a Nicira extension type and subtype) as 'request', and
|
|
|
|
|
* stores the buffer containing the new message in '*bufferp'.
|
|
|
|
|
*
|
|
|
|
|
* Initializes bytes following the headers to all-bits-zero.
|
|
|
|
|
*
|
|
|
|
|
* Returns the first byte of the new message. */
|
|
|
|
|
void *
|
|
|
|
|
ofputil_make_stats_reply(size_t openflow_len,
|
|
|
|
|
const struct ofp_stats_msg *request,
|
|
|
|
|
struct ofpbuf **bufferp)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
msg = *bufferp = ofpbuf_new(openflow_len);
|
|
|
|
|
put_stats_reply__(request, msg);
|
|
|
|
|
ofpbuf_padto(msg, openflow_len);
|
|
|
|
|
|
|
|
|
|
return msg->data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Initializes 'replies' as a list of ofpbufs that will contain a series of
|
|
|
|
|
* replies to 'request', which should be an OpenFlow or Nicira extension
|
|
|
|
|
* statistics request. Initially 'replies' will have a single reply message
|
|
|
|
|
* that has only a header. The functions ofputil_reserve_stats_reply() and
|
|
|
|
|
* ofputil_append_stats_reply() may be used to add to the reply. */
|
|
|
|
|
void
|
|
|
|
|
ofputil_start_stats_reply(const struct ofp_stats_msg *request,
|
|
|
|
|
struct list *replies)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
|
|
|
|
msg = ofpbuf_new(1024);
|
|
|
|
|
put_stats_reply__(request, msg);
|
|
|
|
|
|
|
|
|
|
list_init(replies);
|
|
|
|
|
list_push_back(replies, &msg->list_node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Prepares to append up to 'len' bytes to the series of statistics replies in
|
|
|
|
|
* 'replies', which should have been initialized with
|
|
|
|
|
* ofputil_start_stats_reply(). Returns an ofpbuf with at least 'len' bytes of
|
|
|
|
|
* tailroom. (The 'len' bytes have not actually be allocated; the caller must
|
|
|
|
|
* do so with e.g. ofpbuf_put_uninit().) */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_reserve_stats_reply(size_t len, struct list *replies)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *msg = ofpbuf_from_list(list_back(replies));
|
|
|
|
|
struct ofp_stats_msg *osm = msg->data;
|
|
|
|
|
|
|
|
|
|
if (msg->size + len <= UINT16_MAX) {
|
|
|
|
|
ofpbuf_prealloc_tailroom(msg, len);
|
|
|
|
|
} else {
|
|
|
|
|
osm->flags |= htons(OFPSF_REPLY_MORE);
|
|
|
|
|
|
|
|
|
|
msg = ofpbuf_new(MAX(1024, sizeof(struct nicira_stats_msg) + len));
|
|
|
|
|
put_stats_reply__(osm, msg);
|
|
|
|
|
list_push_back(replies, &msg->list_node);
|
|
|
|
|
}
|
|
|
|
|
return msg;
|
2010-11-23 13:31:07 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-05-31 16:49:06 -07:00
|
|
|
|
/* Appends 'len' bytes to the series of statistics replies in 'replies', and
|
|
|
|
|
* returns the first byte. */
|
2010-11-23 13:31:07 -08:00
|
|
|
|
void *
|
2011-05-31 16:49:06 -07:00
|
|
|
|
ofputil_append_stats_reply(size_t len, struct list *replies)
|
2010-11-23 13:31:07 -08:00
|
|
|
|
{
|
2011-05-31 16:49:06 -07:00
|
|
|
|
return ofpbuf_put_uninit(ofputil_reserve_stats_reply(len, replies), len);
|
2010-11-23 13:31:07 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-05-26 09:34:05 -07:00
|
|
|
|
/* Returns the first byte past the ofp_stats_msg header in 'oh'. */
|
2010-12-06 10:20:20 -08:00
|
|
|
|
const void *
|
|
|
|
|
ofputil_stats_body(const struct ofp_header *oh)
|
|
|
|
|
{
|
2012-02-14 16:58:39 -08:00
|
|
|
|
assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
|
2011-05-26 09:30:25 -07:00
|
|
|
|
return (const struct ofp_stats_msg *) oh + 1;
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-05-26 09:34:05 -07:00
|
|
|
|
/* Returns the number of bytes past the ofp_stats_msg header in 'oh'. */
|
2010-12-06 10:20:20 -08:00
|
|
|
|
size_t
|
|
|
|
|
ofputil_stats_body_len(const struct ofp_header *oh)
|
|
|
|
|
{
|
2012-02-14 16:58:39 -08:00
|
|
|
|
assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
|
2011-05-26 09:30:25 -07:00
|
|
|
|
return ntohs(oh->length) - sizeof(struct ofp_stats_msg);
|
2010-12-06 10:20:20 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-05-26 09:34:05 -07:00
|
|
|
|
/* Returns the first byte past the nicira_stats_msg header in 'oh'. */
|
2010-12-07 14:21:38 -08:00
|
|
|
|
const void *
|
|
|
|
|
ofputil_nxstats_body(const struct ofp_header *oh)
|
|
|
|
|
{
|
2012-02-14 16:58:39 -08:00
|
|
|
|
assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
|
2010-12-07 14:21:38 -08:00
|
|
|
|
return ((const struct nicira_stats_msg *) oh) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-26 09:34:05 -07:00
|
|
|
|
/* Returns the number of bytes past the nicira_stats_msg header in 'oh'. */
|
2010-12-07 14:21:38 -08:00
|
|
|
|
size_t
|
|
|
|
|
ofputil_nxstats_body_len(const struct ofp_header *oh)
|
|
|
|
|
{
|
2012-02-14 16:58:39 -08:00
|
|
|
|
assert(oh->type == OFPT10_STATS_REQUEST || oh->type == OFPT10_STATS_REPLY);
|
2010-12-07 14:21:38 -08:00
|
|
|
|
return ntohs(oh->length) - sizeof(struct nicira_stats_msg);
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-27 13:14:05 -07:00
|
|
|
|
struct ofpbuf *
|
2010-11-10 14:51:49 -08:00
|
|
|
|
make_flow_mod(uint16_t command, const struct cls_rule *rule,
|
|
|
|
|
size_t actions_len)
|
2010-05-27 13:14:05 -07:00
|
|
|
|
{
|
|
|
|
|
struct ofp_flow_mod *ofm;
|
|
|
|
|
size_t size = sizeof *ofm + actions_len;
|
|
|
|
|
struct ofpbuf *out = ofpbuf_new(size);
|
|
|
|
|
ofm = ofpbuf_put_zeros(out, sizeof *ofm);
|
2011-12-06 22:33:49 -08:00
|
|
|
|
ofm->header.version = OFP10_VERSION;
|
2012-02-14 16:58:39 -08:00
|
|
|
|
ofm->header.type = OFPT10_FLOW_MOD;
|
2010-05-27 13:14:05 -07:00
|
|
|
|
ofm->header.length = htons(size);
|
|
|
|
|
ofm->cookie = 0;
|
2010-11-10 14:51:49 -08:00
|
|
|
|
ofm->priority = htons(MIN(rule->priority, UINT16_MAX));
|
2011-04-26 09:42:18 -07:00
|
|
|
|
ofputil_cls_rule_to_match(rule, &ofm->match);
|
2010-05-27 13:14:05 -07:00
|
|
|
|
ofm->command = htons(command);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpbuf *
|
2010-11-10 14:51:49 -08:00
|
|
|
|
make_add_flow(const struct cls_rule *rule, uint32_t buffer_id,
|
2010-05-27 13:14:05 -07:00
|
|
|
|
uint16_t idle_timeout, size_t actions_len)
|
|
|
|
|
{
|
2010-11-10 14:51:49 -08:00
|
|
|
|
struct ofpbuf *out = make_flow_mod(OFPFC_ADD, rule, actions_len);
|
2010-05-27 13:14:05 -07:00
|
|
|
|
struct ofp_flow_mod *ofm = out->data;
|
|
|
|
|
ofm->idle_timeout = htons(idle_timeout);
|
|
|
|
|
ofm->hard_timeout = htons(OFP_FLOW_PERMANENT);
|
|
|
|
|
ofm->buffer_id = htonl(buffer_id);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpbuf *
|
2010-11-10 14:51:49 -08:00
|
|
|
|
make_del_flow(const struct cls_rule *rule)
|
2010-05-27 13:14:05 -07:00
|
|
|
|
{
|
2010-11-10 14:51:49 -08:00
|
|
|
|
struct ofpbuf *out = make_flow_mod(OFPFC_DELETE_STRICT, rule, 0);
|
2010-05-27 13:14:05 -07:00
|
|
|
|
struct ofp_flow_mod *ofm = out->data;
|
|
|
|
|
ofm->out_port = htons(OFPP_NONE);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpbuf *
|
2010-11-10 14:51:49 -08:00
|
|
|
|
make_add_simple_flow(const struct cls_rule *rule,
|
2010-05-27 13:14:05 -07:00
|
|
|
|
uint32_t buffer_id, uint16_t out_port,
|
|
|
|
|
uint16_t idle_timeout)
|
|
|
|
|
{
|
2010-07-20 11:10:45 -07:00
|
|
|
|
if (out_port != OFPP_NONE) {
|
|
|
|
|
struct ofp_action_output *oao;
|
|
|
|
|
struct ofpbuf *buffer;
|
|
|
|
|
|
2010-11-10 14:51:49 -08:00
|
|
|
|
buffer = make_add_flow(rule, buffer_id, idle_timeout, sizeof *oao);
|
2012-02-15 16:22:18 -08:00
|
|
|
|
ofputil_put_OFPAT10_OUTPUT(buffer)->port = htons(out_port);
|
2010-07-20 11:10:45 -07:00
|
|
|
|
return buffer;
|
|
|
|
|
} else {
|
2010-11-10 14:51:49 -08:00
|
|
|
|
return make_add_flow(rule, buffer_id, idle_timeout, 0);
|
2010-07-20 11:10:45 -07:00
|
|
|
|
}
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
make_packet_in(uint32_t buffer_id, uint16_t in_port, uint8_t reason,
|
|
|
|
|
const struct ofpbuf *payload, int max_send_len)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_packet_in *opi;
|
|
|
|
|
struct ofpbuf *buf;
|
|
|
|
|
int send_len;
|
|
|
|
|
|
|
|
|
|
send_len = MIN(max_send_len, payload->size);
|
|
|
|
|
buf = ofpbuf_new(sizeof *opi + send_len);
|
|
|
|
|
opi = put_openflow_xid(offsetof(struct ofp_packet_in, data),
|
|
|
|
|
OFPT_PACKET_IN, 0, buf);
|
|
|
|
|
opi->buffer_id = htonl(buffer_id);
|
|
|
|
|
opi->total_len = htons(payload->size);
|
|
|
|
|
opi->in_port = htons(in_port);
|
|
|
|
|
opi->reason = reason;
|
|
|
|
|
ofpbuf_put(buf, payload->data, send_len);
|
|
|
|
|
update_openflow_length(buf);
|
|
|
|
|
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Creates and returns an OFPT_ECHO_REQUEST message with an empty payload. */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
make_echo_request(void)
|
|
|
|
|
{
|
|
|
|
|
struct ofp_header *rq;
|
|
|
|
|
struct ofpbuf *out = ofpbuf_new(sizeof *rq);
|
|
|
|
|
rq = ofpbuf_put_uninit(out, sizeof *rq);
|
2011-12-06 22:33:49 -08:00
|
|
|
|
rq->version = OFP10_VERSION;
|
2010-05-27 13:14:05 -07:00
|
|
|
|
rq->type = OFPT_ECHO_REQUEST;
|
|
|
|
|
rq->length = htons(sizeof *rq);
|
2010-11-16 11:00:25 -08:00
|
|
|
|
rq->xid = htonl(0);
|
2010-05-27 13:14:05 -07:00
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Creates and returns an OFPT_ECHO_REPLY message matching the
|
|
|
|
|
* OFPT_ECHO_REQUEST message in 'rq'. */
|
|
|
|
|
struct ofpbuf *
|
|
|
|
|
make_echo_reply(const struct ofp_header *rq)
|
|
|
|
|
{
|
|
|
|
|
size_t size = ntohs(rq->length);
|
|
|
|
|
struct ofpbuf *out = ofpbuf_new(size);
|
|
|
|
|
struct ofp_header *reply = ofpbuf_put(out, rq, size);
|
|
|
|
|
reply->type = OFPT_ECHO_REPLY;
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-26 13:17:12 -08:00
|
|
|
|
struct ofpbuf *
|
|
|
|
|
ofputil_encode_barrier_request(void)
|
|
|
|
|
{
|
|
|
|
|
struct ofpbuf *msg;
|
|
|
|
|
|
2012-02-14 16:58:39 -08:00
|
|
|
|
make_openflow(sizeof(struct ofp_header), OFPT10_BARRIER_REQUEST, &msg);
|
2012-01-26 13:17:12 -08:00
|
|
|
|
return msg;
|
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
const char *
|
|
|
|
|
ofputil_frag_handling_to_string(enum ofp_config_flags flags)
|
|
|
|
|
{
|
|
|
|
|
switch (flags & OFPC_FRAG_MASK) {
|
|
|
|
|
case OFPC_FRAG_NORMAL: return "normal";
|
|
|
|
|
case OFPC_FRAG_DROP: return "drop";
|
|
|
|
|
case OFPC_FRAG_REASM: return "reassemble";
|
|
|
|
|
case OFPC_FRAG_NX_MATCH: return "nx-match";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ofputil_frag_handling_from_string(const char *s, enum ofp_config_flags *flags)
|
|
|
|
|
{
|
|
|
|
|
if (!strcasecmp(s, "normal")) {
|
|
|
|
|
*flags = OFPC_FRAG_NORMAL;
|
|
|
|
|
} else if (!strcasecmp(s, "drop")) {
|
|
|
|
|
*flags = OFPC_FRAG_DROP;
|
|
|
|
|
} else if (!strcasecmp(s, "reassemble")) {
|
|
|
|
|
*flags = OFPC_FRAG_REASM;
|
|
|
|
|
} else if (!strcasecmp(s, "nx-match")) {
|
|
|
|
|
*flags = OFPC_FRAG_NX_MATCH;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-14 17:08:03 -08:00
|
|
|
|
/* Converts the OpenFlow 1.1+ port number 'ofp11_port' into an OpenFlow 1.0
|
|
|
|
|
* port number and stores the latter in '*ofp10_port', for the purpose of
|
|
|
|
|
* decoding OpenFlow 1.1+ protocol messages. Returns 0 if successful,
|
|
|
|
|
* otherwise an OFPERR_* number.
|
|
|
|
|
*
|
|
|
|
|
* See the definition of OFP11_MAX for an explanation of the mapping. */
|
|
|
|
|
enum ofperr
|
|
|
|
|
ofputil_port_from_ofp11(ovs_be32 ofp11_port, uint16_t *ofp10_port)
|
|
|
|
|
{
|
|
|
|
|
uint32_t ofp11_port_h = ntohl(ofp11_port);
|
|
|
|
|
|
|
|
|
|
if (ofp11_port_h < OFPP_MAX) {
|
|
|
|
|
*ofp10_port = ofp11_port_h;
|
|
|
|
|
return 0;
|
|
|
|
|
} else if (ofp11_port_h >= OFPP11_MAX) {
|
|
|
|
|
*ofp10_port = ofp11_port_h - OFPP11_OFFSET;
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "port %"PRIu32" is outside the supported "
|
|
|
|
|
"range 0 through %d or 0x%"PRIx32" through 0x%"PRIx32,
|
|
|
|
|
ofp11_port_h, OFPP_MAX - 1,
|
|
|
|
|
(uint32_t) OFPP11_MAX, UINT32_MAX);
|
|
|
|
|
return OFPERR_OFPBAC_BAD_OUT_PORT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Returns the OpenFlow 1.1+ port number equivalent to the OpenFlow 1.0 port
|
|
|
|
|
* number 'ofp10_port', for encoding OpenFlow 1.1+ protocol messages.
|
|
|
|
|
*
|
|
|
|
|
* See the definition of OFP11_MAX for an explanation of the mapping. */
|
|
|
|
|
ovs_be32
|
|
|
|
|
ofputil_port_to_ofp11(uint16_t ofp10_port)
|
|
|
|
|
{
|
|
|
|
|
return htonl(ofp10_port < OFPP_MAX
|
|
|
|
|
? ofp10_port
|
|
|
|
|
: ofp10_port + OFPP11_OFFSET);
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
/* Checks that 'port' is a valid output port for the OFPAT10_OUTPUT action, given
|
2010-06-17 15:04:12 -07:00
|
|
|
|
* that the switch will never have more than 'max_ports' ports. Returns 0 if
|
2012-01-12 15:48:19 -08:00
|
|
|
|
* 'port' is valid, otherwise an OpenFlow return code. */
|
|
|
|
|
enum ofperr
|
2011-07-14 14:23:02 -07:00
|
|
|
|
ofputil_check_output_port(uint16_t port, int max_ports)
|
2010-05-27 13:14:05 -07:00
|
|
|
|
{
|
|
|
|
|
switch (port) {
|
|
|
|
|
case OFPP_IN_PORT:
|
|
|
|
|
case OFPP_TABLE:
|
|
|
|
|
case OFPP_NORMAL:
|
|
|
|
|
case OFPP_FLOOD:
|
|
|
|
|
case OFPP_ALL:
|
|
|
|
|
case OFPP_CONTROLLER:
|
2012-05-10 11:24:36 -07:00
|
|
|
|
case OFPP_NONE:
|
2010-05-27 13:14:05 -07:00
|
|
|
|
case OFPP_LOCAL:
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
default:
|
2010-06-17 15:04:12 -07:00
|
|
|
|
if (port < max_ports) {
|
2010-05-27 13:14:05 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBAC_BAD_OUT_PORT;
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-16 15:26:18 -07:00
|
|
|
|
#define OFPUTIL_NAMED_PORTS \
|
|
|
|
|
OFPUTIL_NAMED_PORT(IN_PORT) \
|
|
|
|
|
OFPUTIL_NAMED_PORT(TABLE) \
|
|
|
|
|
OFPUTIL_NAMED_PORT(NORMAL) \
|
|
|
|
|
OFPUTIL_NAMED_PORT(FLOOD) \
|
|
|
|
|
OFPUTIL_NAMED_PORT(ALL) \
|
|
|
|
|
OFPUTIL_NAMED_PORT(CONTROLLER) \
|
|
|
|
|
OFPUTIL_NAMED_PORT(LOCAL) \
|
|
|
|
|
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. */
|
|
|
|
|
bool
|
|
|
|
|
ofputil_port_from_string(const char *name, uint16_t *port)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
if (str_to_int(name, 0, &i) && i >= 0 && i < UINT16_MAX) {
|
|
|
|
|
*port = i;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < n_pairs; i++) {
|
|
|
|
|
if (!strcasecmp(name, pairs[i].name)) {
|
|
|
|
|
*port = pairs[i].value;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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(uint16_t port, struct ds *s)
|
|
|
|
|
{
|
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
|
|
switch (port) {
|
|
|
|
|
#define OFPUTIL_NAMED_PORT(NAME) case OFPP_##NAME: name = #NAME; break;
|
|
|
|
|
OFPUTIL_NAMED_PORTS
|
|
|
|
|
#undef OFPUTIL_NAMED_PORT
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ds_put_format(s, "%"PRIu16, port);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
ds_put_cstr(s, name);
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-04 14:42:04 -07:00
|
|
|
|
/* Given a buffer 'b' that contains an array of OpenFlow ports of type
|
|
|
|
|
* 'ofp_version', tries to pull the first element from the array. If
|
|
|
|
|
* successful, initializes '*pp' with an abstract representation of the
|
|
|
|
|
* port and returns 0. If no ports remain to be decoded, returns EOF.
|
|
|
|
|
* On an error, returns a positive OFPERR_* value. */
|
|
|
|
|
int
|
|
|
|
|
ofputil_pull_phy_port(uint8_t ofp_version, struct ofpbuf *b,
|
|
|
|
|
struct ofputil_phy_port *pp)
|
|
|
|
|
{
|
|
|
|
|
if (ofp_version == OFP10_VERSION) {
|
|
|
|
|
const struct ofp10_phy_port *opp = ofpbuf_try_pull(b, sizeof *opp);
|
|
|
|
|
return opp ? ofputil_decode_ofp10_phy_port(pp, opp) : EOF;
|
|
|
|
|
} else {
|
|
|
|
|
const struct ofp11_port *op = ofpbuf_try_pull(b, sizeof *op);
|
|
|
|
|
return op ? ofputil_decode_ofp11_port(pp, op) : EOF;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Given a buffer 'b' that contains an array of OpenFlow ports of type
|
|
|
|
|
* 'ofp_version', returns the number of elements. */
|
|
|
|
|
size_t ofputil_count_phy_ports(uint8_t ofp_version, struct ofpbuf *b)
|
|
|
|
|
{
|
2012-05-08 00:01:11 -07:00
|
|
|
|
return b->size / ofputil_get_phy_port_size(ofp_version);
|
2012-05-04 14:42:04 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-08-09 09:24:18 -07:00
|
|
|
|
check_resubmit_table(const struct nx_action_resubmit *nar)
|
|
|
|
|
{
|
|
|
|
|
if (nar->pad[0] || nar->pad[1] || nar->pad[2]) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
2011-08-09 09:24:18 -07:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
static enum ofperr
|
2011-08-10 13:05:17 -07:00
|
|
|
|
check_output_reg(const struct nx_action_output_reg *naor,
|
|
|
|
|
const struct flow *flow)
|
|
|
|
|
{
|
2011-12-28 12:42:14 -08:00
|
|
|
|
struct mf_subfield src;
|
2011-08-10 13:05:17 -07:00
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof naor->zero; i++) {
|
|
|
|
|
if (naor->zero[i]) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBAC_BAD_ARGUMENT;
|
2011-08-10 13:05:17 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-28 12:42:14 -08:00
|
|
|
|
nxm_decode(&src, naor->src, naor->ofs_nbits);
|
|
|
|
|
return mf_check_src(&src, flow);
|
2011-08-10 13:05:17 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr
|
2011-06-30 10:05:52 -07:00
|
|
|
|
validate_actions(const union ofp_action *actions, size_t n_actions,
|
|
|
|
|
const struct flow *flow, int max_ports)
|
2010-06-17 15:04:12 -07:00
|
|
|
|
{
|
2011-06-30 10:05:52 -07:00
|
|
|
|
const union ofp_action *a;
|
|
|
|
|
size_t left;
|
2010-06-17 15:04:12 -07:00
|
|
|
|
|
2011-06-30 10:05:52 -07:00
|
|
|
|
OFPUTIL_ACTION_FOR_EACH (a, left, actions, n_actions) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr error;
|
2011-06-30 10:05:52 -07:00
|
|
|
|
uint16_t port;
|
|
|
|
|
int code;
|
2010-06-17 15:04:12 -07:00
|
|
|
|
|
2011-06-30 10:05:52 -07:00
|
|
|
|
code = ofputil_decode_action(a);
|
|
|
|
|
if (code < 0) {
|
|
|
|
|
error = -code;
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl,
|
|
|
|
|
"action decoding error at offset %td (%s)",
|
2012-01-12 15:48:19 -08:00
|
|
|
|
(a - actions) * sizeof *a, ofperr_get_name(error));
|
2010-05-27 13:14:05 -07:00
|
|
|
|
|
2011-06-30 10:05:52 -07:00
|
|
|
|
return error;
|
|
|
|
|
}
|
2010-05-27 13:14:05 -07:00
|
|
|
|
|
2011-06-30 10:05:52 -07:00
|
|
|
|
error = 0;
|
|
|
|
|
switch ((enum ofputil_action_code) code) {
|
2012-02-15 16:22:18 -08:00
|
|
|
|
case OFPUTIL_OFPAT10_OUTPUT:
|
2011-07-14 14:23:02 -07:00
|
|
|
|
error = ofputil_check_output_port(ntohs(a->output.port),
|
|
|
|
|
max_ports);
|
2011-06-30 10:05:52 -07:00
|
|
|
|
break;
|
2010-12-09 10:41:32 -08:00
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
case OFPUTIL_OFPAT10_SET_VLAN_VID:
|
2011-06-30 10:05:52 -07:00
|
|
|
|
if (a->vlan_vid.vlan_vid & ~htons(0xfff)) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
error = OFPERR_OFPBAC_BAD_ARGUMENT;
|
2011-06-30 10:05:52 -07:00
|
|
|
|
}
|
|
|
|
|
break;
|
2010-11-12 16:23:26 -08:00
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
case OFPUTIL_OFPAT10_SET_VLAN_PCP:
|
2011-06-30 10:05:52 -07:00
|
|
|
|
if (a->vlan_pcp.vlan_pcp & ~7) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
error = OFPERR_OFPBAC_BAD_ARGUMENT;
|
2011-06-30 10:05:52 -07:00
|
|
|
|
}
|
|
|
|
|
break;
|
2010-11-12 16:23:26 -08:00
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
case OFPUTIL_OFPAT10_ENQUEUE:
|
2011-06-30 10:05:52 -07:00
|
|
|
|
port = ntohs(((const struct ofp_action_enqueue *) a)->port);
|
2011-11-18 19:00:34 -08:00
|
|
|
|
if (port >= max_ports && port != OFPP_IN_PORT
|
|
|
|
|
&& port != OFPP_LOCAL) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
error = OFPERR_OFPBAC_BAD_OUT_PORT;
|
2011-06-30 10:05:52 -07:00
|
|
|
|
}
|
|
|
|
|
break;
|
2010-11-12 16:23:26 -08:00
|
|
|
|
|
2011-06-30 10:05:52 -07:00
|
|
|
|
case OFPUTIL_NXAST_REG_MOVE:
|
|
|
|
|
error = nxm_check_reg_move((const struct nx_action_reg_move *) a,
|
|
|
|
|
flow);
|
|
|
|
|
break;
|
2010-11-12 16:23:26 -08:00
|
|
|
|
|
2011-06-30 10:05:52 -07:00
|
|
|
|
case OFPUTIL_NXAST_REG_LOAD:
|
|
|
|
|
error = nxm_check_reg_load((const struct nx_action_reg_load *) a,
|
|
|
|
|
flow);
|
|
|
|
|
break;
|
2010-12-10 10:42:42 -08:00
|
|
|
|
|
2011-06-30 10:05:52 -07:00
|
|
|
|
case OFPUTIL_NXAST_MULTIPATH:
|
2011-07-19 15:47:02 -07:00
|
|
|
|
error = multipath_check((const struct nx_action_multipath *) a,
|
|
|
|
|
flow);
|
2011-06-30 10:05:52 -07:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case OFPUTIL_NXAST_AUTOPATH:
|
2011-07-19 15:47:02 -07:00
|
|
|
|
error = autopath_check((const struct nx_action_autopath *) a,
|
|
|
|
|
flow);
|
2011-06-30 10:05:52 -07:00
|
|
|
|
break;
|
|
|
|
|
|
2011-06-10 17:45:45 -07:00
|
|
|
|
case OFPUTIL_NXAST_BUNDLE:
|
2011-07-20 15:07:46 -07:00
|
|
|
|
case OFPUTIL_NXAST_BUNDLE_LOAD:
|
2011-06-10 17:45:45 -07:00
|
|
|
|
error = bundle_check((const struct nx_action_bundle *) a,
|
2011-07-20 15:07:46 -07:00
|
|
|
|
max_ports, flow);
|
2011-06-10 17:45:45 -07:00
|
|
|
|
break;
|
|
|
|
|
|
2011-08-10 13:05:17 -07:00
|
|
|
|
case OFPUTIL_NXAST_OUTPUT_REG:
|
|
|
|
|
error = check_output_reg((const struct nx_action_output_reg *) a,
|
|
|
|
|
flow);
|
|
|
|
|
break;
|
|
|
|
|
|
2011-08-09 09:24:18 -07:00
|
|
|
|
case OFPUTIL_NXAST_RESUBMIT_TABLE:
|
|
|
|
|
error = check_resubmit_table(
|
|
|
|
|
(const struct nx_action_resubmit *) a);
|
|
|
|
|
break;
|
|
|
|
|
|
2011-09-12 16:19:57 -07:00
|
|
|
|
case OFPUTIL_NXAST_LEARN:
|
|
|
|
|
error = learn_check((const struct nx_action_learn *) a, flow);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-02-09 14:17:33 -08:00
|
|
|
|
case OFPUTIL_NXAST_CONTROLLER:
|
|
|
|
|
if (((const struct nx_action_controller *) a)->zero) {
|
|
|
|
|
error = OFPERR_NXBAC_MUST_BE_ZERO;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
case OFPUTIL_OFPAT10_STRIP_VLAN:
|
|
|
|
|
case OFPUTIL_OFPAT10_SET_NW_SRC:
|
|
|
|
|
case OFPUTIL_OFPAT10_SET_NW_DST:
|
|
|
|
|
case OFPUTIL_OFPAT10_SET_NW_TOS:
|
|
|
|
|
case OFPUTIL_OFPAT10_SET_TP_SRC:
|
|
|
|
|
case OFPUTIL_OFPAT10_SET_TP_DST:
|
|
|
|
|
case OFPUTIL_OFPAT10_SET_DL_SRC:
|
|
|
|
|
case OFPUTIL_OFPAT10_SET_DL_DST:
|
2011-06-30 10:05:52 -07:00
|
|
|
|
case OFPUTIL_NXAST_RESUBMIT:
|
|
|
|
|
case OFPUTIL_NXAST_SET_TUNNEL:
|
|
|
|
|
case OFPUTIL_NXAST_SET_QUEUE:
|
|
|
|
|
case OFPUTIL_NXAST_POP_QUEUE:
|
|
|
|
|
case OFPUTIL_NXAST_NOTE:
|
|
|
|
|
case OFPUTIL_NXAST_SET_TUNNEL64:
|
2011-10-28 14:46:42 -07:00
|
|
|
|
case OFPUTIL_NXAST_EXIT:
|
2012-01-13 17:54:04 -08:00
|
|
|
|
case OFPUTIL_NXAST_DEC_TTL:
|
2012-02-15 10:37:03 -08:00
|
|
|
|
case OFPUTIL_NXAST_FIN_TIMEOUT:
|
2011-06-30 10:05:52 -07:00
|
|
|
|
break;
|
2010-12-17 14:38:50 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-05 12:37:52 -07:00
|
|
|
|
if (error) {
|
2011-06-30 10:05:52 -07:00
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "bad action at offset %td (%s)",
|
2012-01-12 15:48:19 -08:00
|
|
|
|
(a - actions) * sizeof *a, ofperr_get_name(error));
|
2011-04-05 12:37:52 -07:00
|
|
|
|
return error;
|
|
|
|
|
}
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
2011-06-30 10:05:52 -07:00
|
|
|
|
if (left) {
|
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "bad action format at offset %zu",
|
|
|
|
|
(n_actions - left) * sizeof *a);
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBAC_BAD_LEN;
|
2011-06-30 10:05:52 -07:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-08-05 15:48:45 -07:00
|
|
|
|
struct ofputil_action {
|
|
|
|
|
int code;
|
2011-06-30 10:05:52 -07:00
|
|
|
|
unsigned int min_len;
|
|
|
|
|
unsigned int max_len;
|
|
|
|
|
};
|
2010-10-18 11:18:10 -07:00
|
|
|
|
|
2011-08-05 15:48:45 -07:00
|
|
|
|
static const struct ofputil_action action_bad_type
|
2012-01-12 15:48:19 -08:00
|
|
|
|
= { -OFPERR_OFPBAC_BAD_TYPE, 0, UINT_MAX };
|
2011-08-05 15:48:45 -07:00
|
|
|
|
static const struct ofputil_action action_bad_len
|
2012-01-12 15:48:19 -08:00
|
|
|
|
= { -OFPERR_OFPBAC_BAD_LEN, 0, UINT_MAX };
|
2011-08-05 15:48:45 -07:00
|
|
|
|
static const struct ofputil_action action_bad_vendor
|
2012-01-12 15:48:19 -08:00
|
|
|
|
= { -OFPERR_OFPBAC_BAD_VENDOR, 0, UINT_MAX };
|
2010-10-18 11:18:10 -07:00
|
|
|
|
|
2011-08-05 15:48:45 -07:00
|
|
|
|
static const struct ofputil_action *
|
2011-06-30 10:05:52 -07:00
|
|
|
|
ofputil_decode_ofpat_action(const union ofp_action *a)
|
|
|
|
|
{
|
2012-02-15 16:22:18 -08:00
|
|
|
|
enum ofp10_action_type type = ntohs(a->type);
|
2010-05-27 13:14:05 -07:00
|
|
|
|
|
2011-08-05 15:48:45 -07:00
|
|
|
|
switch (type) {
|
2012-02-15 16:22:18 -08:00
|
|
|
|
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
|
2011-08-05 15:48:45 -07:00
|
|
|
|
case ENUM: { \
|
|
|
|
|
static const struct ofputil_action action = { \
|
2011-08-17 11:01:17 -07:00
|
|
|
|
OFPUTIL_##ENUM, \
|
|
|
|
|
sizeof(struct STRUCT), \
|
|
|
|
|
sizeof(struct STRUCT) \
|
2011-08-05 15:48:45 -07:00
|
|
|
|
}; \
|
|
|
|
|
return &action; \
|
|
|
|
|
}
|
2011-08-17 11:01:17 -07:00
|
|
|
|
#include "ofp-util.def"
|
2011-08-05 15:48:45 -07:00
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
case OFPAT10_VENDOR:
|
2011-08-05 15:48:45 -07:00
|
|
|
|
default:
|
|
|
|
|
return &action_bad_type;
|
2011-06-30 10:05:52 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2010-05-27 13:14:05 -07:00
|
|
|
|
|
2011-08-05 15:48:45 -07:00
|
|
|
|
static const struct ofputil_action *
|
2011-06-30 10:05:52 -07:00
|
|
|
|
ofputil_decode_nxast_action(const union ofp_action *a)
|
|
|
|
|
{
|
2011-08-05 15:48:45 -07:00
|
|
|
|
const struct nx_action_header *nah = (const struct nx_action_header *) a;
|
|
|
|
|
enum nx_action_subtype subtype = ntohs(nah->subtype);
|
|
|
|
|
|
|
|
|
|
switch (subtype) {
|
2011-08-17 11:01:17 -07:00
|
|
|
|
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
|
|
|
|
|
case ENUM: { \
|
|
|
|
|
static const struct ofputil_action action = { \
|
|
|
|
|
OFPUTIL_##ENUM, \
|
|
|
|
|
sizeof(struct STRUCT), \
|
|
|
|
|
EXTENSIBLE ? UINT_MAX : sizeof(struct STRUCT) \
|
|
|
|
|
}; \
|
|
|
|
|
return &action; \
|
2011-06-30 10:05:52 -07:00
|
|
|
|
}
|
2011-08-17 11:01:17 -07:00
|
|
|
|
#include "ofp-util.def"
|
2011-08-05 15:48:45 -07:00
|
|
|
|
|
|
|
|
|
case NXAST_SNAT__OBSOLETE:
|
|
|
|
|
case NXAST_DROP_SPOOFED_ARP__OBSOLETE:
|
|
|
|
|
default:
|
|
|
|
|
return &action_bad_type;
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
/* Parses 'a' to determine its type. Returns a nonnegative OFPUTIL_OFPAT10_* or
|
2012-01-12 15:48:19 -08:00
|
|
|
|
* OFPUTIL_NXAST_* constant if successful, otherwise a negative OFPERR_* error
|
|
|
|
|
* code.
|
2011-06-30 10:05:52 -07:00
|
|
|
|
*
|
|
|
|
|
* The caller must have already verified that 'a''s length is correct (that is,
|
|
|
|
|
* a->header.len is nonzero and a multiple of sizeof(union ofp_action) and no
|
|
|
|
|
* longer than the amount of space allocated to 'a').
|
|
|
|
|
*
|
|
|
|
|
* This function verifies that 'a''s length is correct for the type of action
|
|
|
|
|
* that it represents. */
|
2010-05-27 13:14:05 -07:00
|
|
|
|
int
|
2011-06-30 10:05:52 -07:00
|
|
|
|
ofputil_decode_action(const union ofp_action *a)
|
2010-05-27 13:14:05 -07:00
|
|
|
|
{
|
2011-08-05 15:48:45 -07:00
|
|
|
|
const struct ofputil_action *action;
|
|
|
|
|
uint16_t len = ntohs(a->header.len);
|
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
if (a->type != htons(OFPAT10_VENDOR)) {
|
2011-08-05 15:48:45 -07:00
|
|
|
|
action = ofputil_decode_ofpat_action(a);
|
2011-06-30 10:05:52 -07:00
|
|
|
|
} else {
|
2011-08-05 15:48:45 -07:00
|
|
|
|
switch (ntohl(a->vendor.vendor)) {
|
|
|
|
|
case NX_VENDOR_ID:
|
|
|
|
|
if (len < sizeof(struct nx_action_header)) {
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return -OFPERR_OFPBAC_BAD_LEN;
|
2011-08-05 15:48:45 -07:00
|
|
|
|
}
|
|
|
|
|
action = ofputil_decode_nxast_action(a);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
action = &action_bad_vendor;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-06-30 10:05:52 -07:00
|
|
|
|
}
|
2011-08-05 15:48:45 -07:00
|
|
|
|
|
|
|
|
|
return (len >= action->min_len && len <= action->max_len
|
|
|
|
|
? action->code
|
2012-01-12 15:48:19 -08:00
|
|
|
|
: -OFPERR_OFPBAC_BAD_LEN);
|
2011-06-30 10:05:52 -07:00
|
|
|
|
}
|
2010-05-27 13:14:05 -07:00
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
/* Parses 'a' and returns its type as an OFPUTIL_OFPAT10_* or OFPUTIL_NXAST_*
|
2011-06-30 10:05:52 -07:00
|
|
|
|
* constant. The caller must have already validated that 'a' is a valid action
|
|
|
|
|
* understood by Open vSwitch (e.g. by a previous successful call to
|
|
|
|
|
* ofputil_decode_action()). */
|
|
|
|
|
enum ofputil_action_code
|
|
|
|
|
ofputil_decode_action_unsafe(const union ofp_action *a)
|
|
|
|
|
{
|
2011-08-05 15:48:45 -07:00
|
|
|
|
const struct ofputil_action *action;
|
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
if (a->type != htons(OFPAT10_VENDOR)) {
|
2011-08-05 15:48:45 -07:00
|
|
|
|
action = ofputil_decode_ofpat_action(a);
|
2011-06-30 10:05:52 -07:00
|
|
|
|
} else {
|
2011-08-05 15:48:45 -07:00
|
|
|
|
action = ofputil_decode_nxast_action(a);
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
2011-08-05 15:48:45 -07:00
|
|
|
|
|
|
|
|
|
return action->code;
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-08-17 11:01:17 -07:00
|
|
|
|
/* Returns the 'enum ofputil_action_code' corresponding to 'name' (e.g. if
|
2012-02-15 16:22:18 -08:00
|
|
|
|
* 'name' is "output" then the return value is OFPUTIL_OFPAT10_OUTPUT), or -1 if
|
2011-08-17 11:01:17 -07:00
|
|
|
|
* 'name' is not the name of any action.
|
|
|
|
|
*
|
|
|
|
|
* ofp-util.def lists the mapping from names to action. */
|
|
|
|
|
int
|
|
|
|
|
ofputil_action_code_from_name(const char *name)
|
|
|
|
|
{
|
|
|
|
|
static const char *names[OFPUTIL_N_ACTIONS] = {
|
2012-02-15 16:22:18 -08:00
|
|
|
|
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) NAME,
|
2011-08-17 11:01:17 -07:00
|
|
|
|
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) NAME,
|
|
|
|
|
#include "ofp-util.def"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const char **p;
|
|
|
|
|
|
|
|
|
|
for (p = names; p < &names[ARRAY_SIZE(names)]; p++) {
|
|
|
|
|
if (*p && !strcasecmp(name, *p)) {
|
|
|
|
|
return p - names;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-16 16:30:57 -07:00
|
|
|
|
/* Appends an action of the type specified by 'code' to 'buf' and returns the
|
|
|
|
|
* action. Initializes the parts of 'action' that identify it as having type
|
|
|
|
|
* <ENUM> and length 'sizeof *action' and zeros the rest. For actions that
|
|
|
|
|
* have variable length, the length used and cleared is that of struct
|
|
|
|
|
* <STRUCT>. */
|
|
|
|
|
void *
|
|
|
|
|
ofputil_put_action(enum ofputil_action_code code, struct ofpbuf *buf)
|
|
|
|
|
{
|
|
|
|
|
switch (code) {
|
2012-02-15 16:22:18 -08:00
|
|
|
|
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
|
2011-08-16 16:30:57 -07:00
|
|
|
|
case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
|
|
|
|
|
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
|
|
|
|
|
case OFPUTIL_##ENUM: return ofputil_put_##ENUM(buf);
|
|
|
|
|
#include "ofp-util.def"
|
|
|
|
|
}
|
|
|
|
|
NOT_REACHED();
|
|
|
|
|
}
|
|
|
|
|
|
2012-02-15 16:22:18 -08:00
|
|
|
|
#define OFPAT10_ACTION(ENUM, STRUCT, NAME) \
|
2011-08-16 16:30:57 -07:00
|
|
|
|
void \
|
|
|
|
|
ofputil_init_##ENUM(struct STRUCT *s) \
|
|
|
|
|
{ \
|
|
|
|
|
memset(s, 0, sizeof *s); \
|
|
|
|
|
s->type = htons(ENUM); \
|
|
|
|
|
s->len = htons(sizeof *s); \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
struct STRUCT * \
|
|
|
|
|
ofputil_put_##ENUM(struct ofpbuf *buf) \
|
|
|
|
|
{ \
|
|
|
|
|
struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
|
|
|
|
|
ofputil_init_##ENUM(s); \
|
|
|
|
|
return s; \
|
|
|
|
|
}
|
|
|
|
|
#define NXAST_ACTION(ENUM, STRUCT, EXTENSIBLE, NAME) \
|
|
|
|
|
void \
|
|
|
|
|
ofputil_init_##ENUM(struct STRUCT *s) \
|
|
|
|
|
{ \
|
|
|
|
|
memset(s, 0, sizeof *s); \
|
2012-02-15 16:22:18 -08:00
|
|
|
|
s->type = htons(OFPAT10_VENDOR); \
|
2011-08-16 16:30:57 -07:00
|
|
|
|
s->len = htons(sizeof *s); \
|
|
|
|
|
s->vendor = htonl(NX_VENDOR_ID); \
|
|
|
|
|
s->subtype = htons(ENUM); \
|
|
|
|
|
} \
|
|
|
|
|
\
|
|
|
|
|
struct STRUCT * \
|
|
|
|
|
ofputil_put_##ENUM(struct ofpbuf *buf) \
|
|
|
|
|
{ \
|
|
|
|
|
struct STRUCT *s = ofpbuf_put_uninit(buf, sizeof *s); \
|
|
|
|
|
ofputil_init_##ENUM(s); \
|
|
|
|
|
return s; \
|
|
|
|
|
}
|
|
|
|
|
#include "ofp-util.def"
|
|
|
|
|
|
2011-03-29 14:42:20 -07:00
|
|
|
|
/* Returns true if 'action' outputs to 'port', false otherwise. */
|
2010-06-17 15:04:12 -07:00
|
|
|
|
bool
|
2011-03-29 14:42:20 -07:00
|
|
|
|
action_outputs_to_port(const union ofp_action *action, ovs_be16 port)
|
2010-06-17 15:04:12 -07:00
|
|
|
|
{
|
2012-05-02 14:51:51 -07:00
|
|
|
|
switch (ofputil_decode_action(action)) {
|
|
|
|
|
case OFPUTIL_OFPAT10_OUTPUT:
|
2010-06-17 15:04:12 -07:00
|
|
|
|
return action->output.port == port;
|
2012-05-02 14:51:51 -07:00
|
|
|
|
case OFPUTIL_OFPAT10_ENQUEUE:
|
2010-06-17 15:04:12 -07:00
|
|
|
|
return ((const struct ofp_action_enqueue *) action)->port == port;
|
2012-05-02 14:51:51 -07:00
|
|
|
|
case OFPUTIL_NXAST_CONTROLLER:
|
|
|
|
|
return port == htons(OFPP_CONTROLLER);
|
2010-06-17 15:04:12 -07:00
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-09 16:25:48 -07:00
|
|
|
|
/* "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.
|
2012-02-10 13:30:23 -08:00
|
|
|
|
*/
|
2011-05-09 16:25:48 -07:00
|
|
|
|
void
|
2012-02-10 13:30:23 -08:00
|
|
|
|
ofputil_normalize_rule(struct cls_rule *rule)
|
2011-05-09 16:25:48 -07:00
|
|
|
|
{
|
|
|
|
|
enum {
|
|
|
|
|
MAY_NW_ADDR = 1 << 0, /* nw_src, nw_dst */
|
|
|
|
|
MAY_TP_ADDR = 1 << 1, /* tp_src, tp_dst */
|
|
|
|
|
MAY_NW_PROTO = 1 << 2, /* nw_proto */
|
2011-11-05 15:48:12 -07:00
|
|
|
|
MAY_IPVx = 1 << 3, /* tos, frag, ttl */
|
2011-05-09 16:25:48 -07:00
|
|
|
|
MAY_ARP_SHA = 1 << 4, /* arp_sha */
|
|
|
|
|
MAY_ARP_THA = 1 << 5, /* arp_tha */
|
2011-11-09 17:49:49 -08:00
|
|
|
|
MAY_IPV6 = 1 << 6, /* ipv6_src, ipv6_dst, ipv6_label */
|
2011-05-09 16:25:48 -07:00
|
|
|
|
MAY_ND_TARGET = 1 << 7 /* nd_target */
|
|
|
|
|
} may_match;
|
|
|
|
|
|
|
|
|
|
struct flow_wildcards wc;
|
|
|
|
|
|
|
|
|
|
/* Figure out what fields may be matched. */
|
|
|
|
|
if (rule->flow.dl_type == htons(ETH_TYPE_IP)) {
|
2011-11-02 18:17:36 -07:00
|
|
|
|
may_match = MAY_NW_PROTO | MAY_IPVx | MAY_NW_ADDR;
|
2011-05-09 16:25:48 -07:00
|
|
|
|
if (rule->flow.nw_proto == IPPROTO_TCP ||
|
|
|
|
|
rule->flow.nw_proto == IPPROTO_UDP ||
|
|
|
|
|
rule->flow.nw_proto == IPPROTO_ICMP) {
|
|
|
|
|
may_match |= MAY_TP_ADDR;
|
|
|
|
|
}
|
2012-02-10 13:30:23 -08:00
|
|
|
|
} else if (rule->flow.dl_type == htons(ETH_TYPE_IPV6)) {
|
2011-11-09 17:49:49 -08:00
|
|
|
|
may_match = MAY_NW_PROTO | MAY_IPVx | MAY_IPV6;
|
2011-05-09 16:25:48 -07:00
|
|
|
|
if (rule->flow.nw_proto == IPPROTO_TCP ||
|
|
|
|
|
rule->flow.nw_proto == IPPROTO_UDP) {
|
|
|
|
|
may_match |= MAY_TP_ADDR;
|
|
|
|
|
} else if (rule->flow.nw_proto == IPPROTO_ICMPV6) {
|
|
|
|
|
may_match |= MAY_TP_ADDR;
|
|
|
|
|
if (rule->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)) {
|
|
|
|
|
may_match |= MAY_ND_TARGET | MAY_ARP_THA;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (rule->flow.dl_type == htons(ETH_TYPE_ARP)) {
|
2012-02-10 13:30:23 -08:00
|
|
|
|
may_match = MAY_NW_PROTO | MAY_NW_ADDR | MAY_ARP_SHA | MAY_ARP_THA;
|
ofp-util: Revise OpenFlow 1.0 ofp_match normalization.
For a long time, Open vSwitch has "normalized" OpenFlow 1.0 flows in a
funny way: it tries to change fields that are wildcarded into fields
that are exact-match. For example, the normalize_match() function
knows that if dl_type is wildcarded, then all of the L3 and L4 fields
will always be extracted as 0, so it sets those fields to exact-match
and their values to 0.
The reason for this was originally that exact-match flows were much
cheaper for Open vSwitch to implement, because they could be implemented
with a hash table, whereas other kinds of flows had to be implemented
with an expensive linear search. But these days Open vSwitch has a
smarter classifier in which wildcarded flows have minimal cost. Also,
it is no longer possible for OpenFlow 1.0 to specify truly exact-match
flows, because Open vSwitch supports fields for which OpenFlow 1.0
cannot specify values and therefore will always be wildcarded.
Now, it no longer makes sense to do this transformation, so this commit
removes it. Presumably, this will be less surprising for users.
Reviewed-by: Simon Horman <horms@verge.net.au>
2011-05-02 11:04:33 -07:00
|
|
|
|
} else {
|
2011-05-09 16:25:48 -07:00
|
|
|
|
may_match = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Clear the fields that may not be matched. */
|
|
|
|
|
wc = rule->wc;
|
|
|
|
|
if (!(may_match & MAY_NW_ADDR)) {
|
|
|
|
|
wc.nw_src_mask = wc.nw_dst_mask = htonl(0);
|
|
|
|
|
}
|
|
|
|
|
if (!(may_match & MAY_TP_ADDR)) {
|
2012-01-27 17:16:05 -08:00
|
|
|
|
wc.tp_src_mask = wc.tp_dst_mask = htons(0);
|
2011-05-09 16:25:48 -07:00
|
|
|
|
}
|
|
|
|
|
if (!(may_match & MAY_NW_PROTO)) {
|
|
|
|
|
wc.wildcards |= FWW_NW_PROTO;
|
|
|
|
|
}
|
2011-11-02 18:17:36 -07:00
|
|
|
|
if (!(may_match & MAY_IPVx)) {
|
2011-11-09 23:39:16 -08:00
|
|
|
|
wc.wildcards |= FWW_NW_DSCP;
|
|
|
|
|
wc.wildcards |= FWW_NW_ECN;
|
2011-11-05 15:48:12 -07:00
|
|
|
|
wc.wildcards |= FWW_NW_TTL;
|
2011-05-09 16:25:48 -07:00
|
|
|
|
}
|
|
|
|
|
if (!(may_match & MAY_ARP_SHA)) {
|
|
|
|
|
wc.wildcards |= FWW_ARP_SHA;
|
|
|
|
|
}
|
|
|
|
|
if (!(may_match & MAY_ARP_THA)) {
|
|
|
|
|
wc.wildcards |= FWW_ARP_THA;
|
|
|
|
|
}
|
2011-11-09 17:49:49 -08:00
|
|
|
|
if (!(may_match & MAY_IPV6)) {
|
2011-05-09 16:25:48 -07:00
|
|
|
|
wc.ipv6_src_mask = wc.ipv6_dst_mask = in6addr_any;
|
2011-11-01 15:57:56 -07:00
|
|
|
|
wc.wildcards |= FWW_IPV6_LABEL;
|
2011-05-09 16:25:48 -07:00
|
|
|
|
}
|
|
|
|
|
if (!(may_match & MAY_ND_TARGET)) {
|
2012-04-25 15:48:40 -07:00
|
|
|
|
wc.nd_target_mask = in6addr_any;
|
2011-05-09 16:25:48 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
|
|
rule->wc = wc;
|
|
|
|
|
cls_rule_zero_wildcarded_fields(rule);
|
|
|
|
|
|
|
|
|
|
if (log) {
|
|
|
|
|
char *post = cls_rule_to_string(rule);
|
|
|
|
|
VLOG_INFO("normalization changed ofp_match, details:");
|
|
|
|
|
VLOG_INFO(" pre: %s", pre);
|
|
|
|
|
VLOG_INFO("post: %s", post);
|
|
|
|
|
free(pre);
|
|
|
|
|
free(post);
|
|
|
|
|
}
|
2010-05-27 13:14:05 -07:00
|
|
|
|
}
|
2010-06-24 14:20:45 -07:00
|
|
|
|
}
|
2010-08-20 10:29:03 -07:00
|
|
|
|
|
2010-11-08 10:43:19 -08:00
|
|
|
|
/* Attempts to pull 'actions_len' bytes from the front of 'b'. Returns 0 if
|
|
|
|
|
* successful, otherwise an OpenFlow error.
|
|
|
|
|
*
|
|
|
|
|
* If successful, the first action is stored in '*actionsp' and the number of
|
|
|
|
|
* "union ofp_action" size elements into '*n_actionsp'. Otherwise NULL and 0
|
|
|
|
|
* are stored, respectively.
|
|
|
|
|
*
|
|
|
|
|
* This function does not check that the actions are valid (the caller should
|
|
|
|
|
* do so, with validate_actions()). The caller is also responsible for making
|
|
|
|
|
* sure that 'b->data' is initially aligned appropriately for "union
|
|
|
|
|
* ofp_action". */
|
2012-01-12 15:48:19 -08:00
|
|
|
|
enum ofperr
|
2010-11-08 10:43:19 -08:00
|
|
|
|
ofputil_pull_actions(struct ofpbuf *b, unsigned int actions_len,
|
|
|
|
|
union ofp_action **actionsp, size_t *n_actionsp)
|
|
|
|
|
{
|
2010-11-10 16:22:18 -08:00
|
|
|
|
if (actions_len % OFP_ACTION_ALIGN != 0) {
|
2010-12-23 10:36:55 -08:00
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
|
|
|
|
|
"is not a multiple of %d", actions_len, OFP_ACTION_ALIGN);
|
2010-11-08 10:43:19 -08:00
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*actionsp = ofpbuf_try_pull(b, actions_len);
|
|
|
|
|
if (*actionsp == NULL) {
|
2010-12-23 10:36:55 -08:00
|
|
|
|
VLOG_WARN_RL(&bad_ofmsg_rl, "OpenFlow message actions length %u "
|
|
|
|
|
"exceeds remaining message length (%zu)",
|
|
|
|
|
actions_len, b->size);
|
2010-11-08 10:43:19 -08:00
|
|
|
|
goto error;
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-10 16:22:18 -08:00
|
|
|
|
*n_actionsp = actions_len / OFP_ACTION_ALIGN;
|
2010-11-08 10:43:19 -08:00
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
error:
|
|
|
|
|
*actionsp = NULL;
|
|
|
|
|
*n_actionsp = 0;
|
2012-01-12 15:48:19 -08:00
|
|
|
|
return OFPERR_OFPBRC_BAD_LEN;
|
2010-11-08 10:43:19 -08:00
|
|
|
|
}
|
2011-05-26 16:49:10 -07:00
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ofputil_actions_equal(const union ofp_action *a, size_t n_a,
|
|
|
|
|
const union ofp_action *b, size_t n_b)
|
|
|
|
|
{
|
|
|
|
|
return n_a == n_b && (!n_a || !memcmp(a, b, n_a * sizeof *a));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
union ofp_action *
|
|
|
|
|
ofputil_actions_clone(const union ofp_action *actions, size_t n)
|
|
|
|
|
{
|
|
|
|
|
return n ? xmemdup(actions, n * sizeof *actions) : NULL;
|
|
|
|
|
}
|
2011-08-17 13:39:02 -07:00
|
|
|
|
|
|
|
|
|
/* Parses a key or a key-value pair from '*stringp'.
|
|
|
|
|
*
|
|
|
|
|
* On success: Stores the key into '*keyp'. Stores the value, if present, into
|
|
|
|
|
* '*valuep', otherwise an empty string. Advances '*stringp' past the end of
|
|
|
|
|
* the key-value pair, preparing it for another call. '*keyp' and '*valuep'
|
|
|
|
|
* are substrings of '*stringp' created by replacing some of its bytes by null
|
|
|
|
|
* terminators. Returns true.
|
|
|
|
|
*
|
|
|
|
|
* If '*stringp' is just white space or commas, sets '*keyp' and '*valuep' to
|
|
|
|
|
* NULL and returns false. */
|
|
|
|
|
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') {
|
|
|
|
|
*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':
|
2012-04-27 09:41:02 -07:00
|
|
|
|
level = 0;
|
|
|
|
|
break;
|
2011-08-17 13:39:02 -07:00
|
|
|
|
|
|
|
|
|
case '(':
|
|
|
|
|
level++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ')':
|
|
|
|
|
level--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
value[value_len - 1] = '\0';
|
|
|
|
|
pos = value + value_len;
|
|
|
|
|
} 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');
|
|
|
|
|
}
|
|
|
|
|
key[key_len] = '\0';
|
|
|
|
|
|
|
|
|
|
*stringp = pos;
|
|
|
|
|
*keyp = key;
|
|
|
|
|
*valuep = value;
|
|
|
|
|
return true;
|
|
|
|
|
}
|