2010-07-28 15:14:28 -07:00
|
|
|
/*
|
2016-01-04 11:36:14 -08:00
|
|
|
* Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
|
2010-07-28 15:14:28 -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>
|
|
|
|
|
2010-11-23 13:33:48 -08:00
|
|
|
#include <ctype.h>
|
2010-07-28 15:14:28 -07:00
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
#include <netinet/in.h>
|
2010-07-28 15:14:28 -07:00
|
|
|
|
2010-10-28 17:13:18 -07:00
|
|
|
#include "byte-order.h"
|
2016-09-14 16:51:27 -07:00
|
|
|
#include "dp-packet.h"
|
2011-09-12 16:19:57 -07:00
|
|
|
#include "learn.h"
|
2010-12-17 14:38:50 -08:00
|
|
|
#include "multipath.h"
|
2012-07-03 22:17:14 -07:00
|
|
|
#include "netdev.h"
|
2010-12-09 11:03:35 -08:00
|
|
|
#include "nx-match.h"
|
2016-04-14 15:20:19 -07:00
|
|
|
#include "openflow/openflow.h"
|
|
|
|
#include "openvswitch/dynamic-string.h"
|
|
|
|
#include "openvswitch/meta-flow.h"
|
|
|
|
#include "openvswitch/ofp-actions.h"
|
|
|
|
#include "openvswitch/ofp-parse.h"
|
2016-04-04 21:32:09 -04:00
|
|
|
#include "openvswitch/ofp-util.h"
|
2016-03-25 14:10:24 -07:00
|
|
|
#include "openvswitch/ofpbuf.h"
|
2016-04-14 15:20:19 -07:00
|
|
|
#include "openvswitch/vconn.h"
|
2013-04-26 11:22:19 -07:00
|
|
|
#include "ovs-thread.h"
|
2010-07-28 15:14:28 -07:00
|
|
|
#include "packets.h"
|
2013-10-13 12:44:20 -07:00
|
|
|
#include "simap.h"
|
2010-07-28 15:14:28 -07:00
|
|
|
#include "socket-util.h"
|
2016-07-12 16:37:34 -05:00
|
|
|
#include "util.h"
|
2010-07-28 15:14:28 -07:00
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
/* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
|
|
|
|
*
|
|
|
|
* 'name' describes the value parsed in an error message, if any.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2013-07-08 10:15:00 -07:00
|
|
|
str_to_u8(const char *str, const char *name, uint8_t *valuep)
|
2011-09-27 16:58:55 -07:00
|
|
|
{
|
2013-06-20 17:26:18 +03:00
|
|
|
int value;
|
2011-09-27 16:58:55 -07:00
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
if (!str_to_int(str, 0, &value) || value < 0 || value > 255) {
|
|
|
|
return xasprintf("invalid %s \"%s\"", name, str);
|
2011-09-27 16:58:55 -07:00
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
*valuep = value;
|
|
|
|
return NULL;
|
2011-09-27 16:58:55 -07:00
|
|
|
}
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
/* Parses 'str' as a 16-bit unsigned integer into '*valuep'.
|
|
|
|
*
|
|
|
|
* 'name' describes the value parsed in an error message, if any.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2013-07-08 10:15:00 -07:00
|
|
|
str_to_u16(const char *str, const char *name, uint16_t *valuep)
|
2011-09-27 16:58:55 -07:00
|
|
|
{
|
|
|
|
int value;
|
|
|
|
|
|
|
|
if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("invalid %s \"%s\"", name, str);
|
2011-09-27 16:58:55 -07:00
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
*valuep = value;
|
|
|
|
return NULL;
|
2011-09-27 16:58:55 -07:00
|
|
|
}
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
/* Parses 'str' as a 32-bit unsigned integer into '*valuep'.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2013-07-08 10:15:00 -07:00
|
|
|
str_to_u32(const char *str, uint32_t *valuep)
|
2010-07-28 15:14:28 -07:00
|
|
|
{
|
|
|
|
char *tail;
|
|
|
|
uint32_t value;
|
|
|
|
|
2011-02-22 14:55:39 -08:00
|
|
|
if (!str[0]) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("missing required numeric argument");
|
2010-10-08 22:15:26 +00:00
|
|
|
}
|
|
|
|
|
2010-07-28 15:14:28 -07:00
|
|
|
errno = 0;
|
|
|
|
value = strtoul(str, &tail, 0);
|
|
|
|
if (errno == EINVAL || errno == ERANGE || *tail) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("invalid numeric format %s", str);
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
*valuep = value;
|
|
|
|
return NULL;
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
/* Parses 'str' as an 64-bit unsigned integer into '*valuep'.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2013-07-08 10:15:00 -07:00
|
|
|
str_to_u64(const char *str, uint64_t *valuep)
|
2010-07-28 15:14:28 -07:00
|
|
|
{
|
|
|
|
char *tail;
|
|
|
|
uint64_t value;
|
|
|
|
|
2011-02-22 14:55:39 -08:00
|
|
|
if (!str[0]) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("missing required numeric argument");
|
2011-02-22 14:55:39 -08:00
|
|
|
}
|
|
|
|
|
2010-07-28 15:14:28 -07:00
|
|
|
errno = 0;
|
|
|
|
value = strtoull(str, &tail, 0);
|
|
|
|
if (errno == EINVAL || errno == ERANGE || *tail) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("invalid numeric format %s", str);
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
*valuep = value;
|
|
|
|
return NULL;
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
/* Parses 'str' as an 64-bit unsigned integer in network byte order into
|
|
|
|
* '*valuep'.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2013-07-08 10:15:00 -07:00
|
|
|
str_to_be64(const char *str, ovs_be64 *valuep)
|
|
|
|
{
|
2013-08-21 14:49:09 -07:00
|
|
|
uint64_t value = 0;
|
2013-07-08 10:15:00 -07:00
|
|
|
char *error;
|
|
|
|
|
|
|
|
error = str_to_u64(str, &value);
|
|
|
|
if (!error) {
|
|
|
|
*valuep = htonll(value);
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parses 'str' as an Ethernet address into 'mac'.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2015-08-28 14:55:11 -07:00
|
|
|
str_to_mac(const char *str, struct eth_addr *mac)
|
2010-07-28 15:14:28 -07:00
|
|
|
{
|
2015-08-28 14:55:11 -07:00
|
|
|
if (!ovs_scan(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(*mac))) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("invalid mac address %s", str);
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
return NULL;
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
/* Parses 'str' as an IP address into '*ip'.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2011-09-12 12:11:50 -07:00
|
|
|
str_to_ip(const char *str, ovs_be32 *ip)
|
2011-06-07 09:22:24 -07:00
|
|
|
{
|
2010-07-28 15:14:28 -07:00
|
|
|
struct in_addr in_addr;
|
|
|
|
|
2011-09-12 12:11:50 -07:00
|
|
|
if (lookup_ip(str, &in_addr)) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("%s: could not convert to IP address", str);
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
|
|
|
*ip = in_addr.s_addr;
|
2013-07-08 10:15:00 -07:00
|
|
|
return NULL;
|
2010-12-29 19:03:46 -08:00
|
|
|
}
|
|
|
|
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
/* Parses 'str' as a conntrack helper into 'alg'.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
|
|
|
str_to_connhelper(const char *str, uint16_t *alg)
|
|
|
|
{
|
|
|
|
if (!strcmp(str, "ftp")) {
|
|
|
|
*alg = IPPORT_FTP;
|
|
|
|
return NULL;
|
2016-12-22 10:58:25 -08:00
|
|
|
}
|
|
|
|
if (!strcmp(str, "tftp")) {
|
|
|
|
*alg = IPPORT_TFTP;
|
|
|
|
return NULL;
|
Add support for connection tracking helper/ALGs.
This patch adds support for specifying a "helper" or ALG to assist
connection tracking for protocols that consist of multiple streams.
Initially, only support for FTP is included.
Below is an example set of flows to allow FTP control connections from
port 1->2 to establish active data connections in the reverse direction:
table=0,priority=1,action=drop
table=0,arp,action=normal
table=0,in_port=1,tcp,action=ct(alg=ftp,commit),2
table=0,in_port=2,tcp,ct_state=-trk,action=ct(table=1)
table=1,in_port=2,tcp,ct_state=+trk+est,action=1
table=1,in_port=2,tcp,ct_state=+trk+rel,action=ct(commit),1
Signed-off-by: Joe Stringer <joestringer@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-09-15 14:29:16 -07:00
|
|
|
}
|
|
|
|
return xasprintf("invalid conntrack helper \"%s\"", str);
|
|
|
|
}
|
|
|
|
|
2010-07-28 15:14:28 -07:00
|
|
|
struct protocol {
|
|
|
|
const char *name;
|
|
|
|
uint16_t dl_type;
|
|
|
|
uint8_t nw_proto;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool
|
|
|
|
parse_protocol(const char *name, const struct protocol **p_out)
|
|
|
|
{
|
|
|
|
static const struct protocol protocols[] = {
|
|
|
|
{ "ip", ETH_TYPE_IP, 0 },
|
2015-08-24 15:39:37 -07:00
|
|
|
{ "ipv4", ETH_TYPE_IP, 0 },
|
|
|
|
{ "ip4", ETH_TYPE_IP, 0 },
|
2010-07-28 15:14:28 -07:00
|
|
|
{ "arp", ETH_TYPE_ARP, 0 },
|
2011-02-02 11:33:20 -08:00
|
|
|
{ "icmp", ETH_TYPE_IP, IPPROTO_ICMP },
|
|
|
|
{ "tcp", ETH_TYPE_IP, IPPROTO_TCP },
|
|
|
|
{ "udp", ETH_TYPE_IP, IPPROTO_UDP },
|
2013-08-22 20:24:45 +12:00
|
|
|
{ "sctp", ETH_TYPE_IP, IPPROTO_SCTP },
|
2010-12-29 19:03:46 -08:00
|
|
|
{ "ipv6", ETH_TYPE_IPV6, 0 },
|
|
|
|
{ "ip6", ETH_TYPE_IPV6, 0 },
|
|
|
|
{ "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 },
|
|
|
|
{ "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
|
|
|
|
{ "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
|
2013-08-22 20:24:45 +12:00
|
|
|
{ "sctp6", ETH_TYPE_IPV6, IPPROTO_SCTP },
|
2012-11-02 11:43:46 -07:00
|
|
|
{ "rarp", ETH_TYPE_RARP, 0},
|
2013-01-25 16:22:07 +09:00
|
|
|
{ "mpls", ETH_TYPE_MPLS, 0 },
|
|
|
|
{ "mplsm", ETH_TYPE_MPLS_MCAST, 0 },
|
|
|
|
};
|
2010-07-28 15:14:28 -07:00
|
|
|
const struct protocol *p;
|
|
|
|
|
|
|
|
for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
|
|
|
|
if (!strcmp(p->name, name)) {
|
|
|
|
*p_out = p;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*p_out = NULL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
/* Parses 's' as the (possibly masked) value of field 'mf', and updates
|
2013-08-20 18:41:45 -07:00
|
|
|
* 'match' appropriately. Restricts the set of usable protocols to ones
|
|
|
|
* supporting the parsed field.
|
2013-07-08 10:15:00 -07:00
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
2013-08-20 18:41:45 -07:00
|
|
|
parse_field(const struct mf_field *mf, const char *s, struct match *match,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
2010-11-08 10:37:52 -08:00
|
|
|
{
|
2011-09-12 12:11:50 -07:00
|
|
|
union mf_value value, mask;
|
|
|
|
char *error;
|
2010-12-07 14:02:17 -08:00
|
|
|
|
2015-09-09 09:30:35 -07:00
|
|
|
if (!*s) {
|
|
|
|
/* If there's no string, we're just trying to match on the
|
|
|
|
* existence of the field, so use a no-op value. */
|
|
|
|
s = "0/0";
|
|
|
|
}
|
|
|
|
|
2011-09-12 12:11:50 -07:00
|
|
|
error = mf_parse(mf, s, &value, &mask);
|
2013-07-08 10:15:00 -07:00
|
|
|
if (!error) {
|
2015-08-31 14:20:17 -07:00
|
|
|
*usable_protocols &= mf_set(mf, &value, &mask, match, &error);
|
2010-11-08 10:37:52 -08:00
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
return error;
|
2010-11-23 13:33:48 -08:00
|
|
|
}
|
|
|
|
|
ofp-parse: Allow match field names in actions and brackets in matches.
Allow using match field names in addition to the canonical register
names in actions (including 'load', 'move', 'push', 'pop', 'output',
'multipath', 'bundle_load', and 'learn'). Allow also leaving out the
trailing '[]' to indicate full field. These changes allow simpler
syntax similar to 'set_field' to be used also elsewhere.
Correspondingly, allow the '[start..end]' syntax to be used in matches
in addition to the more explicit 'value/mask' notation. For example,
to match on the value 2 of the bits 14..15 of NXM_NX_REG0, the match
could include:
... reg0[14..15]=2 ...
instead of
... reg0=0x8000/0xc000 ...
Note that only contiguous masks can be specified with the bracket
notation.
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: Ben Pfaff <blp@ovn.org>
2017-01-04 16:10:56 -08:00
|
|
|
/* Parses 'str_value' as the value of subfield 'name', and updates
|
|
|
|
* 'match' appropriately. Restricts the set of usable protocols to ones
|
|
|
|
* supporting the parsed field.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
|
|
|
parse_subfield(const char *name, const char *str_value, struct match *match,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
|
|
|
struct mf_subfield sf;
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
error = mf_parse_subfield(&sf, name);
|
|
|
|
if (!error) {
|
|
|
|
union mf_value val;
|
|
|
|
char *tail;
|
|
|
|
if (parse_int_string(str_value, (uint8_t *)&val, sf.field->n_bytes,
|
|
|
|
&tail) || *tail != 0) {
|
|
|
|
return xasprintf("%s: cannot parse integer value: %s", name,
|
|
|
|
str_value);
|
|
|
|
}
|
|
|
|
if (!bitwise_is_all_zeros(&val, sf.field->n_bytes, sf.n_bits,
|
|
|
|
sf.field->n_bytes * 8 - sf.n_bits)) {
|
|
|
|
struct ds ds;
|
|
|
|
|
|
|
|
ds_init(&ds);
|
|
|
|
mf_format(sf.field, &val, NULL, &ds);
|
|
|
|
error = xasprintf("%s: value %s does not fit into %d bits",
|
|
|
|
name, ds_cstr(&ds), sf.n_bits);
|
|
|
|
ds_destroy(&ds);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
const struct mf_field *field = sf.field;
|
|
|
|
union mf_value value, mask;
|
2017-04-17 11:37:29 -07:00
|
|
|
unsigned int size = field->n_bytes;
|
ofp-parse: Allow match field names in actions and brackets in matches.
Allow using match field names in addition to the canonical register
names in actions (including 'load', 'move', 'push', 'pop', 'output',
'multipath', 'bundle_load', and 'learn'). Allow also leaving out the
trailing '[]' to indicate full field. These changes allow simpler
syntax similar to 'set_field' to be used also elsewhere.
Correspondingly, allow the '[start..end]' syntax to be used in matches
in addition to the more explicit 'value/mask' notation. For example,
to match on the value 2 of the bits 14..15 of NXM_NX_REG0, the match
could include:
... reg0[14..15]=2 ...
instead of
... reg0=0x8000/0xc000 ...
Note that only contiguous masks can be specified with the bracket
notation.
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: Ben Pfaff <blp@ovn.org>
2017-01-04 16:10:56 -08:00
|
|
|
|
|
|
|
mf_get(field, match, &value, &mask);
|
2017-04-17 11:37:29 -07:00
|
|
|
bitwise_copy(&val, size, 0, &value, size, sf.ofs, sf.n_bits);
|
|
|
|
bitwise_one ( &mask, size, sf.ofs, sf.n_bits);
|
ofp-parse: Allow match field names in actions and brackets in matches.
Allow using match field names in addition to the canonical register
names in actions (including 'load', 'move', 'push', 'pop', 'output',
'multipath', 'bundle_load', and 'learn'). Allow also leaving out the
trailing '[]' to indicate full field. These changes allow simpler
syntax similar to 'set_field' to be used also elsewhere.
Correspondingly, allow the '[start..end]' syntax to be used in matches
in addition to the more explicit 'value/mask' notation. For example,
to match on the value 2 of the bits 14..15 of NXM_NX_REG0, the match
could include:
... reg0[14..15]=2 ...
instead of
... reg0=0x8000/0xc000 ...
Note that only contiguous masks can be specified with the bracket
notation.
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: Ben Pfaff <blp@ovn.org>
2017-01-04 16:10:56 -08:00
|
|
|
*usable_protocols &= mf_set(field, &value, &mask, match, &error);
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
static char *
|
|
|
|
extract_actions(char *s)
|
|
|
|
{
|
|
|
|
s = strstr(s, "action");
|
|
|
|
if (s) {
|
|
|
|
*s = '\0';
|
|
|
|
s = strchr(s + 1, '=');
|
|
|
|
return s ? s + 1 : NULL;
|
|
|
|
} else {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-15 14:10:38 +01:00
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
2013-08-20 18:41:45 -07:00
|
|
|
parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
|
2013-11-15 14:19:57 -08:00
|
|
|
enum ofputil_protocol *usable_protocols)
|
2010-07-28 15:14:28 -07:00
|
|
|
{
|
2011-06-22 10:37:18 -07:00
|
|
|
enum {
|
|
|
|
F_OUT_PORT = 1 << 0,
|
|
|
|
F_ACTIONS = 1 << 1,
|
2014-11-07 18:18:48 +05:30
|
|
|
F_IMPORTANCE = 1 << 2,
|
2011-06-22 10:37:18 -07:00
|
|
|
F_TIMEOUT = 1 << 3,
|
2012-01-25 13:54:15 -08:00
|
|
|
F_PRIORITY = 1 << 4,
|
|
|
|
F_FLAGS = 1 << 5,
|
2011-06-22 10:37:18 -07:00
|
|
|
} fields;
|
2011-09-12 16:19:57 -07:00
|
|
|
char *act_str = NULL;
|
2015-08-31 18:05:44 -07:00
|
|
|
char *name, *value;
|
2010-07-28 15:14:28 -07:00
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
*usable_protocols = OFPUTIL_P_ANY;
|
|
|
|
|
2015-06-05 14:03:12 -07:00
|
|
|
if (command == -2) {
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
string += strspn(string, " \t\r\n"); /* Skip white space. */
|
|
|
|
len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */
|
|
|
|
|
|
|
|
if (!strncmp(string, "add", len)) {
|
|
|
|
command = OFPFC_ADD;
|
|
|
|
} else if (!strncmp(string, "delete", len)) {
|
|
|
|
command = OFPFC_DELETE;
|
|
|
|
} else if (!strncmp(string, "delete_strict", len)) {
|
|
|
|
command = OFPFC_DELETE_STRICT;
|
|
|
|
} else if (!strncmp(string, "modify", len)) {
|
|
|
|
command = OFPFC_MODIFY;
|
|
|
|
} else if (!strncmp(string, "modify_strict", len)) {
|
|
|
|
command = OFPFC_MODIFY_STRICT;
|
|
|
|
} else {
|
|
|
|
len = 0;
|
|
|
|
command = OFPFC_ADD;
|
|
|
|
}
|
|
|
|
string += len;
|
|
|
|
}
|
|
|
|
|
2011-06-22 10:37:18 -07:00
|
|
|
switch (command) {
|
|
|
|
case -1:
|
|
|
|
fields = F_OUT_PORT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPFC_ADD:
|
2014-11-07 18:18:48 +05:30
|
|
|
fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS | F_IMPORTANCE;
|
2011-06-22 10:37:18 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPFC_DELETE:
|
|
|
|
fields = F_OUT_PORT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPFC_DELETE_STRICT:
|
|
|
|
fields = F_OUT_PORT | F_PRIORITY;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPFC_MODIFY:
|
2012-01-25 13:54:15 -08:00
|
|
|
fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS;
|
2011-06-22 10:37:18 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPFC_MODIFY_STRICT:
|
2012-01-25 13:54:15 -08:00
|
|
|
fields = F_ACTIONS | F_TIMEOUT | F_PRIORITY | F_FLAGS;
|
2011-06-22 10:37:18 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
OVS_NOT_REACHED();
|
2011-06-22 10:37:18 -07:00
|
|
|
}
|
|
|
|
|
2016-01-04 11:36:14 -08:00
|
|
|
*fm = (struct ofputil_flow_mod) {
|
|
|
|
.match = MATCH_CATCHALL_INITIALIZER,
|
|
|
|
.priority = OFP_DEFAULT_PRIORITY,
|
|
|
|
.table_id = 0xff,
|
|
|
|
.command = command,
|
|
|
|
.buffer_id = UINT32_MAX,
|
|
|
|
.out_port = OFPP_ANY,
|
|
|
|
.out_group = OFPG_ANY,
|
|
|
|
};
|
|
|
|
/* For modify, by default, don't update the cookie. */
|
2012-03-24 01:02:26 -07:00
|
|
|
if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) {
|
2013-06-27 15:27:15 -07:00
|
|
|
fm->new_cookie = OVS_BE64_MAX;
|
2012-03-24 01:02:26 -07:00
|
|
|
}
|
2016-01-04 11:36:14 -08:00
|
|
|
|
2011-06-22 10:37:18 -07:00
|
|
|
if (fields & F_ACTIONS) {
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
act_str = extract_actions(string);
|
2010-07-28 15:14:28 -07:00
|
|
|
if (!act_str) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("must specify an action");
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
|
|
|
}
|
2015-08-31 18:05:44 -07:00
|
|
|
|
|
|
|
while (ofputil_parse_key_value(&string, &name, &value)) {
|
2010-07-28 15:14:28 -07:00
|
|
|
const struct protocol *p;
|
ofp-parse: Allow match field names in actions and brackets in matches.
Allow using match field names in addition to the canonical register
names in actions (including 'load', 'move', 'push', 'pop', 'output',
'multipath', 'bundle_load', and 'learn'). Allow also leaving out the
trailing '[]' to indicate full field. These changes allow simpler
syntax similar to 'set_field' to be used also elsewhere.
Correspondingly, allow the '[start..end]' syntax to be used in matches
in addition to the more explicit 'value/mask' notation. For example,
to match on the value 2 of the bits 14..15 of NXM_NX_REG0, the match
could include:
... reg0[14..15]=2 ...
instead of
... reg0=0x8000/0xc000 ...
Note that only contiguous masks can be specified with the bracket
notation.
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: Ben Pfaff <blp@ovn.org>
2017-01-04 16:10:56 -08:00
|
|
|
const struct mf_field *mf;
|
2013-07-08 10:15:00 -07:00
|
|
|
char *error = NULL;
|
2010-07-28 15:14:28 -07:00
|
|
|
|
|
|
|
if (parse_protocol(name, &p)) {
|
2012-08-07 15:28:18 -07:00
|
|
|
match_set_dl_type(&fm->match, htons(p->dl_type));
|
2010-07-28 15:14:28 -07:00
|
|
|
if (p->nw_proto) {
|
2012-08-07 15:28:18 -07:00
|
|
|
match_set_nw_proto(&fm->match, p->nw_proto);
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
2012-01-25 13:54:15 -08:00
|
|
|
} else if (fields & F_FLAGS && !strcmp(name, "send_flow_rem")) {
|
2013-08-26 16:23:50 -07:00
|
|
|
fm->flags |= OFPUTIL_FF_SEND_FLOW_REM;
|
2012-01-25 13:54:15 -08:00
|
|
|
} else if (fields & F_FLAGS && !strcmp(name, "check_overlap")) {
|
2013-08-26 16:23:50 -07:00
|
|
|
fm->flags |= OFPUTIL_FF_CHECK_OVERLAP;
|
2012-11-27 17:44:22 +02:00
|
|
|
} else if (fields & F_FLAGS && !strcmp(name, "reset_counts")) {
|
2013-08-26 16:23:50 -07:00
|
|
|
fm->flags |= OFPUTIL_FF_RESET_COUNTS;
|
2013-08-20 18:41:45 -07:00
|
|
|
*usable_protocols &= OFPUTIL_P_OF12_UP;
|
2012-11-27 17:44:22 +02:00
|
|
|
} else if (fields & F_FLAGS && !strcmp(name, "no_packet_counts")) {
|
2013-08-26 16:23:50 -07:00
|
|
|
fm->flags |= OFPUTIL_FF_NO_PKT_COUNTS;
|
2013-08-20 18:41:45 -07:00
|
|
|
*usable_protocols &= OFPUTIL_P_OF13_UP;
|
2012-11-27 17:44:22 +02:00
|
|
|
} else if (fields & F_FLAGS && !strcmp(name, "no_byte_counts")) {
|
2013-08-26 16:23:50 -07:00
|
|
|
fm->flags |= OFPUTIL_FF_NO_BYT_COUNTS;
|
2013-08-20 18:41:45 -07:00
|
|
|
*usable_protocols &= OFPUTIL_P_OF13_UP;
|
2014-03-05 15:27:31 -08:00
|
|
|
} else if (!strcmp(name, "no_readonly_table")
|
|
|
|
|| !strcmp(name, "allow_hidden_fields")) {
|
|
|
|
/* ignore these fields. */
|
ofp-parse: Allow match field names in actions and brackets in matches.
Allow using match field names in addition to the canonical register
names in actions (including 'load', 'move', 'push', 'pop', 'output',
'multipath', 'bundle_load', and 'learn'). Allow also leaving out the
trailing '[]' to indicate full field. These changes allow simpler
syntax similar to 'set_field' to be used also elsewhere.
Correspondingly, allow the '[start..end]' syntax to be used in matches
in addition to the more explicit 'value/mask' notation. For example,
to match on the value 2 of the bits 14..15 of NXM_NX_REG0, the match
could include:
... reg0[14..15]=2 ...
instead of
... reg0=0x8000/0xc000 ...
Note that only contiguous masks can be specified with the bracket
notation.
Signed-off-by: Jarno Rajahalme <jarno@ovn.org>
Acked-by: Ben Pfaff <blp@ovn.org>
2017-01-04 16:10:56 -08:00
|
|
|
} else if ((mf = mf_from_name(name)) != NULL) {
|
|
|
|
error = parse_field(mf, value, &fm->match, usable_protocols);
|
|
|
|
} else if (strchr(name, '[')) {
|
|
|
|
error = parse_subfield(name, value, &fm->match, usable_protocols);
|
2010-07-28 15:14:28 -07:00
|
|
|
} else {
|
2015-08-31 18:05:44 -07:00
|
|
|
if (!*value) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("field %s missing value", name);
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
|
|
|
|
2011-05-12 09:58:01 -07:00
|
|
|
if (!strcmp(name, "table")) {
|
2013-07-08 10:15:00 -07:00
|
|
|
error = str_to_u8(value, "table", &fm->table_id);
|
2013-08-20 18:41:45 -07:00
|
|
|
if (fm->table_id != 0xff) {
|
|
|
|
*usable_protocols &= OFPUTIL_P_TID;
|
|
|
|
}
|
2015-06-09 15:24:33 -07:00
|
|
|
} else if (fields & F_OUT_PORT && !strcmp(name, "out_port")) {
|
2013-06-19 14:01:43 -07:00
|
|
|
if (!ofputil_port_from_string(value, &fm->out_port)) {
|
2013-07-08 10:15:00 -07:00
|
|
|
error = xasprintf("%s is not a valid OpenFlow port",
|
|
|
|
value);
|
2012-09-20 08:40:29 -07:00
|
|
|
}
|
2015-09-08 15:21:45 -07:00
|
|
|
} else if (fields & F_OUT_PORT && !strcmp(name, "out_group")) {
|
|
|
|
*usable_protocols &= OFPUTIL_P_OF11_UP;
|
|
|
|
if (!ofputil_group_from_string(value, &fm->out_group)) {
|
|
|
|
error = xasprintf("%s is not a valid OpenFlow group",
|
|
|
|
value);
|
|
|
|
}
|
2011-06-22 10:37:18 -07:00
|
|
|
} else if (fields & F_PRIORITY && !strcmp(name, "priority")) {
|
2013-08-21 14:49:09 -07:00
|
|
|
uint16_t priority = 0;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
|
|
|
error = str_to_u16(value, name, &priority);
|
|
|
|
fm->priority = priority;
|
2011-06-22 10:37:18 -07:00
|
|
|
} else if (fields & F_TIMEOUT && !strcmp(name, "idle_timeout")) {
|
2013-07-08 10:15:00 -07:00
|
|
|
error = str_to_u16(value, name, &fm->idle_timeout);
|
2011-06-22 10:37:18 -07:00
|
|
|
} else if (fields & F_TIMEOUT && !strcmp(name, "hard_timeout")) {
|
2013-07-08 10:15:00 -07:00
|
|
|
error = str_to_u16(value, name, &fm->hard_timeout);
|
2014-11-07 18:18:48 +05:30
|
|
|
} else if (fields & F_IMPORTANCE && !strcmp(name, "importance")) {
|
|
|
|
error = str_to_u16(value, name, &fm->importance);
|
2011-12-23 12:23:24 -08:00
|
|
|
} else if (!strcmp(name, "cookie")) {
|
|
|
|
char *mask = strchr(value, '/');
|
2012-03-24 01:02:26 -07:00
|
|
|
|
2011-12-23 12:23:24 -08:00
|
|
|
if (mask) {
|
2012-03-24 01:02:26 -07:00
|
|
|
/* A mask means we're searching for a cookie. */
|
2011-12-23 12:23:24 -08:00
|
|
|
if (command == OFPFC_ADD) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("flow additions cannot use "
|
|
|
|
"a cookie mask");
|
2011-12-23 12:23:24 -08:00
|
|
|
}
|
|
|
|
*mask = '\0';
|
2013-07-08 10:15:00 -07:00
|
|
|
error = str_to_be64(value, &fm->cookie);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
error = str_to_be64(mask + 1, &fm->cookie_mask);
|
2013-08-20 18:41:45 -07:00
|
|
|
|
|
|
|
/* Matching of the cookie is only supported through NXM or
|
|
|
|
* OF1.1+. */
|
|
|
|
if (fm->cookie_mask != htonll(0)) {
|
|
|
|
*usable_protocols &= OFPUTIL_P_NXM_OF11_UP;
|
|
|
|
}
|
2011-12-23 12:23:24 -08:00
|
|
|
} else {
|
2012-03-24 01:02:26 -07:00
|
|
|
/* No mask means that the cookie is being set. */
|
|
|
|
if (command != OFPFC_ADD && command != OFPFC_MODIFY
|
2013-07-08 10:15:00 -07:00
|
|
|
&& command != OFPFC_MODIFY_STRICT) {
|
|
|
|
return xstrdup("cannot set cookie");
|
2012-03-24 01:02:26 -07:00
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
error = str_to_be64(value, &fm->new_cookie);
|
2013-04-17 13:02:15 -07:00
|
|
|
fm->modify_cookie = true;
|
2011-12-23 12:23:24 -08:00
|
|
|
}
|
2011-06-08 13:35:00 -07:00
|
|
|
} else if (!strcmp(name, "duration")
|
|
|
|
|| !strcmp(name, "n_packets")
|
2013-02-01 00:11:32 -08:00
|
|
|
|| !strcmp(name, "n_bytes")
|
|
|
|
|| !strcmp(name, "idle_age")
|
|
|
|
|| !strcmp(name, "hard_age")) {
|
2011-06-08 13:35:00 -07:00
|
|
|
/* Ignore these, so that users can feed the output of
|
|
|
|
* "ovs-ofctl dump-flows" back into commands that parse
|
|
|
|
* flows. */
|
2010-07-28 15:14:28 -07:00
|
|
|
} else {
|
2013-07-08 10:15:00 -07:00
|
|
|
error = xasprintf("unknown keyword %s", name);
|
|
|
|
}
|
2015-08-31 18:05:44 -07:00
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
|
2015-08-31 18:05:44 -07:00
|
|
|
if (error) {
|
|
|
|
return error;
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
|
|
|
}
|
2013-08-20 18:41:45 -07:00
|
|
|
/* Check for usable protocol interdependencies between match fields. */
|
|
|
|
if (fm->match.flow.dl_type == htons(ETH_TYPE_IPV6)) {
|
|
|
|
const struct flow_wildcards *wc = &fm->match.wc;
|
|
|
|
/* Only NXM and OXM support matching L3 and L4 fields within IPv6.
|
|
|
|
*
|
|
|
|
* (IPv6 specific fields as well as arp_sha, arp_tha, nw_frag, and
|
|
|
|
* nw_ttl are covered elsewhere so they don't need to be included in
|
|
|
|
* this test too.)
|
|
|
|
*/
|
|
|
|
if (wc->masks.nw_proto || wc->masks.nw_tos
|
|
|
|
|| wc->masks.tp_src || wc->masks.tp_dst) {
|
|
|
|
*usable_protocols &= OFPUTIL_P_NXM_OXM_ANY;
|
|
|
|
}
|
|
|
|
}
|
2013-06-27 15:27:15 -07:00
|
|
|
if (!fm->cookie_mask && fm->new_cookie == OVS_BE64_MAX
|
2013-07-08 10:15:00 -07:00
|
|
|
&& (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) {
|
2012-03-24 01:02:26 -07:00
|
|
|
/* On modifies without a mask, we are supposed to add a flow if
|
|
|
|
* one does not exist. If a cookie wasn't been specified, use a
|
|
|
|
* default of zero. */
|
|
|
|
fm->new_cookie = htonll(0);
|
|
|
|
}
|
2011-09-12 16:19:57 -07:00
|
|
|
if (fields & F_ACTIONS) {
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
enum ofputil_protocol action_usable_protocols;
|
2012-07-03 22:17:14 -07:00
|
|
|
struct ofpbuf ofpacts;
|
2013-07-08 10:15:00 -07:00
|
|
|
char *error;
|
2011-09-12 16:19:57 -07:00
|
|
|
|
2012-07-03 22:17:14 -07:00
|
|
|
ofpbuf_init(&ofpacts, 32);
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
error = ofpacts_parse_instructions(act_str, &ofpacts,
|
|
|
|
&action_usable_protocols);
|
|
|
|
*usable_protocols &= action_usable_protocols;
|
2013-07-08 10:15:00 -07:00
|
|
|
if (!error) {
|
|
|
|
enum ofperr err;
|
|
|
|
|
2017-03-08 17:18:22 -08:00
|
|
|
err = ofpacts_check(ofpacts.data, ofpacts.size, &fm->match,
|
2013-11-15 14:19:57 -08:00
|
|
|
OFPP_MAX, fm->table_id, 255, usable_protocols);
|
2015-07-09 14:40:53 -07:00
|
|
|
if (!err && !*usable_protocols) {
|
2013-11-15 14:19:57 -08:00
|
|
|
err = OFPERR_OFPBAC_MATCH_INCONSISTENT;
|
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
if (err) {
|
2013-11-15 14:19:57 -08:00
|
|
|
error = xasprintf("actions are invalid with specified match "
|
|
|
|
"(%s)", ofperr_to_string(err));
|
2013-07-08 10:15:00 -07:00
|
|
|
}
|
2013-11-15 14:19:57 -08:00
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
}
|
|
|
|
if (error) {
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
return error;
|
2013-05-08 10:50:14 +09:00
|
|
|
}
|
|
|
|
|
2015-03-02 17:29:44 -08:00
|
|
|
fm->ofpacts_len = ofpacts.size;
|
2013-07-08 10:15:00 -07:00
|
|
|
fm->ofpacts = ofpbuf_steal_data(&ofpacts);
|
2011-09-12 16:19:57 -07:00
|
|
|
} else {
|
2012-07-03 22:17:14 -07:00
|
|
|
fm->ofpacts_len = 0;
|
|
|
|
fm->ofpacts = NULL;
|
2011-09-12 16:19:57 -07:00
|
|
|
}
|
2011-06-17 12:24:54 -07:00
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
return NULL;
|
2010-07-28 15:14:28 -07:00
|
|
|
}
|
2010-09-23 13:19:49 -07:00
|
|
|
|
2013-06-20 17:26:18 +03:00
|
|
|
/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
|
2013-07-08 10:15:00 -07:00
|
|
|
* page) into 'fm' for sending the specified flow_mod 'command' to a switch.
|
2013-08-20 18:41:45 -07:00
|
|
|
* Returns the set of usable protocols in '*usable_protocols'.
|
2013-07-08 10:15:00 -07:00
|
|
|
*
|
|
|
|
* To parse syntax for an OFPT_FLOW_MOD (or NXT_FLOW_MOD), use an OFPFC_*
|
|
|
|
* constant for 'command'. To parse syntax for an OFPST_FLOW or
|
|
|
|
* OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'.
|
|
|
|
*
|
2015-06-05 14:03:12 -07:00
|
|
|
* If 'command' is given as -2, 'str_' may begin with a command name ("add",
|
|
|
|
* "modify", "delete", "modify_strict", or "delete_strict"). A missing command
|
|
|
|
* name is treated as "add".
|
|
|
|
*
|
2013-07-08 10:15:00 -07:00
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2013-08-20 18:41:45 -07:00
|
|
|
parse_ofp_str(struct ofputil_flow_mod *fm, int command, const char *str_,
|
2013-11-15 14:19:57 -08:00
|
|
|
enum ofputil_protocol *usable_protocols)
|
2013-07-08 10:15:00 -07:00
|
|
|
{
|
|
|
|
char *string = xstrdup(str_);
|
|
|
|
char *error;
|
|
|
|
|
2013-11-15 14:19:57 -08:00
|
|
|
error = parse_ofp_str__(fm, command, string, usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
if (error) {
|
|
|
|
fm->ofpacts = NULL;
|
|
|
|
fm->ofpacts_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(string);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2016-09-14 16:51:27 -07:00
|
|
|
/* Parse a string representation of a OFPT_PACKET_OUT to '*po'. If successful,
|
|
|
|
* both 'po->ofpacts' and 'po->packet' must be free()d by the caller. */
|
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
|
|
|
parse_ofp_packet_out_str__(struct ofputil_packet_out *po, char *string,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
|
|
|
enum ofputil_protocol action_usable_protocols;
|
|
|
|
uint64_t stub[256 / 8];
|
|
|
|
struct ofpbuf ofpacts = OFPBUF_STUB_INITIALIZER(stub);
|
|
|
|
struct dp_packet *packet = NULL;
|
|
|
|
char *act_str = NULL;
|
|
|
|
char *name, *value;
|
|
|
|
char *error = NULL;
|
|
|
|
|
|
|
|
*usable_protocols = OFPUTIL_P_ANY;
|
|
|
|
|
|
|
|
*po = (struct ofputil_packet_out) {
|
|
|
|
.buffer_id = UINT32_MAX,
|
|
|
|
};
|
2017-05-15 10:04:58 -07:00
|
|
|
match_init_catchall(&po->flow_metadata);
|
2017-05-15 10:04:55 -07:00
|
|
|
match_set_in_port(&po->flow_metadata, OFPP_CONTROLLER);
|
2016-09-14 16:51:27 -07:00
|
|
|
|
|
|
|
act_str = extract_actions(string);
|
|
|
|
|
|
|
|
while (ofputil_parse_key_value(&string, &name, &value)) {
|
|
|
|
if (!*value) {
|
|
|
|
error = xasprintf("field %s missing value", name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(name, "in_port")) {
|
2017-05-15 10:04:55 -07:00
|
|
|
ofp_port_t in_port;
|
|
|
|
if (!ofputil_port_from_string(value, &in_port)) {
|
2016-09-14 16:51:27 -07:00
|
|
|
error = xasprintf("%s is not a valid OpenFlow port", value);
|
|
|
|
goto out;
|
|
|
|
}
|
2017-05-15 10:04:55 -07:00
|
|
|
if (ofp_to_u16(in_port) > ofp_to_u16(OFPP_MAX)
|
|
|
|
&& in_port != OFPP_LOCAL
|
|
|
|
&& in_port != OFPP_NONE
|
|
|
|
&& in_port != OFPP_CONTROLLER) {
|
2016-09-14 16:51:27 -07:00
|
|
|
error = xasprintf(
|
|
|
|
"%s is not a valid OpenFlow port for PACKET_OUT",
|
|
|
|
value);
|
|
|
|
goto out;
|
|
|
|
}
|
2017-05-15 10:04:55 -07:00
|
|
|
match_set_in_port(&po->flow_metadata, in_port);
|
2016-09-14 16:51:27 -07:00
|
|
|
} else if (!strcmp(name, "packet")) {
|
|
|
|
const char *error_msg = eth_from_hex(value, &packet);
|
|
|
|
if (error_msg) {
|
|
|
|
error = xasprintf("%s: %s", name, error_msg);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
2017-05-15 10:04:58 -07:00
|
|
|
const struct mf_field *mf = mf_from_name(name);
|
|
|
|
if (!mf) {
|
|
|
|
error = xasprintf("unknown keyword %s", name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = parse_field(mf, value, &po->flow_metadata,
|
|
|
|
usable_protocols);
|
|
|
|
if (error) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!mf_is_pipeline_field(mf)) {
|
|
|
|
error = xasprintf("%s is not a valid pipeline field "
|
|
|
|
"for PACKET_OUT", name);
|
|
|
|
goto out;
|
|
|
|
}
|
2016-09-14 16:51:27 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!packet || !dp_packet_size(packet)) {
|
|
|
|
error = xstrdup("must specify packet");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (act_str) {
|
|
|
|
error = ofpacts_parse_actions(act_str, &ofpacts,
|
|
|
|
&action_usable_protocols);
|
|
|
|
*usable_protocols &= action_usable_protocols;
|
|
|
|
if (error) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
po->ofpacts_len = ofpacts.size;
|
|
|
|
po->ofpacts = ofpbuf_steal_data(&ofpacts);
|
|
|
|
|
|
|
|
po->packet_len = dp_packet_size(packet);
|
|
|
|
po->packet = dp_packet_steal_data(packet);
|
|
|
|
out:
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
dp_packet_delete(packet);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert 'str_' (as described in the Packet-Out Syntax section of the
|
|
|
|
* ovs-ofctl man page) into 'po' for sending a OFPT_PACKET_OUT message to a
|
|
|
|
* switch. Returns the set of usable protocols in '*usable_protocols'.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
|
|
|
parse_ofp_packet_out_str(struct ofputil_packet_out *po, const char *str_,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
|
|
|
char *string = xstrdup(str_);
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
error = parse_ofp_packet_out_str__(po, string, usable_protocols);
|
|
|
|
if (error) {
|
|
|
|
po->ofpacts = NULL;
|
|
|
|
po->ofpacts_len = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(string);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2014-12-15 14:10:38 +01:00
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
2013-07-08 10:15:00 -07:00
|
|
|
parse_ofp_meter_mod_str__(struct ofputil_meter_mod *mm, char *string,
|
2013-08-20 18:41:45 -07:00
|
|
|
struct ofpbuf *bands, int command,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
2013-06-20 17:26:18 +03:00
|
|
|
{
|
|
|
|
enum {
|
|
|
|
F_METER = 1 << 0,
|
|
|
|
F_FLAGS = 1 << 1,
|
|
|
|
F_BANDS = 1 << 2,
|
|
|
|
} fields;
|
|
|
|
char *save_ptr = NULL;
|
|
|
|
char *band_str = NULL;
|
|
|
|
char *name;
|
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
/* Meters require at least OF 1.3. */
|
2013-09-07 18:27:09 -07:00
|
|
|
*usable_protocols = OFPUTIL_P_OF13_UP;
|
2013-08-20 18:41:45 -07:00
|
|
|
|
2013-06-20 17:26:18 +03:00
|
|
|
switch (command) {
|
|
|
|
case -1:
|
|
|
|
fields = F_METER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPMC13_ADD:
|
|
|
|
fields = F_METER | F_FLAGS | F_BANDS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPMC13_DELETE:
|
|
|
|
fields = F_METER;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPMC13_MODIFY:
|
|
|
|
fields = F_METER | F_FLAGS | F_BANDS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
OVS_NOT_REACHED();
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
mm->command = command;
|
|
|
|
mm->meter.meter_id = 0;
|
|
|
|
mm->meter.flags = 0;
|
|
|
|
if (fields & F_BANDS) {
|
|
|
|
band_str = strstr(string, "band");
|
|
|
|
if (!band_str) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("must specify bands");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
*band_str = '\0';
|
|
|
|
|
|
|
|
band_str = strchr(band_str + 1, '=');
|
|
|
|
if (!band_str) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("must specify bands");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
band_str++;
|
|
|
|
}
|
|
|
|
for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
|
|
|
|
name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
|
|
|
|
|
|
|
|
if (fields & F_FLAGS && !strcmp(name, "kbps")) {
|
|
|
|
mm->meter.flags |= OFPMF13_KBPS;
|
|
|
|
} else if (fields & F_FLAGS && !strcmp(name, "pktps")) {
|
|
|
|
mm->meter.flags |= OFPMF13_PKTPS;
|
|
|
|
} else if (fields & F_FLAGS && !strcmp(name, "burst")) {
|
|
|
|
mm->meter.flags |= OFPMF13_BURST;
|
|
|
|
} else if (fields & F_FLAGS && !strcmp(name, "stats")) {
|
|
|
|
mm->meter.flags |= OFPMF13_STATS;
|
|
|
|
} else {
|
|
|
|
char *value;
|
|
|
|
|
|
|
|
value = strtok_r(NULL, ", \t\r\n", &save_ptr);
|
|
|
|
if (!value) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("field %s missing value", name);
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(name, "meter")) {
|
|
|
|
if (!strcmp(value, "all")) {
|
|
|
|
mm->meter.meter_id = OFPM13_ALL;
|
|
|
|
} else if (!strcmp(value, "controller")) {
|
|
|
|
mm->meter.meter_id = OFPM13_CONTROLLER;
|
|
|
|
} else if (!strcmp(value, "slowpath")) {
|
|
|
|
mm->meter.meter_id = OFPM13_SLOWPATH;
|
|
|
|
} else {
|
2013-07-08 10:15:00 -07:00
|
|
|
char *error = str_to_u32(value, &mm->meter.meter_id);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
2014-10-31 19:02:29 +13:00
|
|
|
if (mm->meter.meter_id > OFPM13_MAX
|
|
|
|
|| !mm->meter.meter_id) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("invalid value for %s", name);
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("unknown keyword %s", name);
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (fields & F_METER && !mm->meter.meter_id) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("must specify 'meter'");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
if (fields & F_FLAGS && !mm->meter.flags) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("meter must specify either 'kbps' or 'pktps'");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fields & F_BANDS) {
|
|
|
|
uint16_t n_bands = 0;
|
|
|
|
struct ofputil_meter_band *band = NULL;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (name = strtok_r(band_str, "=, \t\r\n", &save_ptr); name;
|
|
|
|
name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
|
|
|
|
|
|
|
|
char *value;
|
|
|
|
|
|
|
|
value = strtok_r(NULL, ", \t\r\n", &save_ptr);
|
|
|
|
if (!value) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("field %s missing value", name);
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(name, "type")) {
|
|
|
|
/* Start a new band */
|
2013-07-08 10:15:00 -07:00
|
|
|
band = ofpbuf_put_zeros(bands, sizeof *band);
|
2013-06-20 17:26:18 +03:00
|
|
|
n_bands++;
|
|
|
|
|
|
|
|
if (!strcmp(value, "drop")) {
|
|
|
|
band->type = OFPMBT13_DROP;
|
|
|
|
} else if (!strcmp(value, "dscp_remark")) {
|
|
|
|
band->type = OFPMBT13_DSCP_REMARK;
|
|
|
|
} else {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("field %s unknown value %s", name, value);
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
} else if (!band || !band->type) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("band must start with the 'type' keyword");
|
2013-06-20 17:26:18 +03:00
|
|
|
} else if (!strcmp(name, "rate")) {
|
2013-07-08 10:15:00 -07:00
|
|
|
char *error = str_to_u32(value, &band->rate);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
2013-06-20 17:26:18 +03:00
|
|
|
} else if (!strcmp(name, "burst_size")) {
|
2013-07-08 10:15:00 -07:00
|
|
|
char *error = str_to_u32(value, &band->burst_size);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
2013-06-20 17:26:18 +03:00
|
|
|
} else if (!strcmp(name, "prec_level")) {
|
2013-07-08 10:15:00 -07:00
|
|
|
char *error = str_to_u8(value, name, &band->prec_level);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
2013-06-20 17:26:18 +03:00
|
|
|
} else {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("unknown keyword %s", name);
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* validate bands */
|
|
|
|
if (!n_bands) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("meter must have bands");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
mm->meter.n_bands = n_bands;
|
2013-07-08 10:15:00 -07:00
|
|
|
mm->meter.bands = ofpbuf_steal_data(bands);
|
2013-06-20 17:26:18 +03:00
|
|
|
|
|
|
|
for (i = 0; i < n_bands; ++i) {
|
|
|
|
band = &mm->meter.bands[i];
|
|
|
|
|
|
|
|
if (!band->type) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("band must have 'type'");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
if (band->type == OFPMBT13_DSCP_REMARK) {
|
|
|
|
if (!band->prec_level) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("'dscp_remark' band must have"
|
|
|
|
" 'prec_level'");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (band->prec_level) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("Only 'dscp_remark' band may have"
|
|
|
|
" 'prec_level'");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!band->rate) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("band must have 'rate'");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
if (mm->meter.flags & OFPMF13_BURST) {
|
|
|
|
if (!band->burst_size) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("band must have 'burst_size' "
|
|
|
|
"when 'burst' flag is set");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (band->burst_size) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xstrdup("band may have 'burst_size' only "
|
|
|
|
"when 'burst' flag is set");
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mm->meter.n_bands = 0;
|
|
|
|
mm->meter.bands = NULL;
|
|
|
|
}
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert 'str_' (as described in the Flow Syntax section of the ovs-ofctl man
|
|
|
|
* page) into 'mm' for sending the specified meter_mod 'command' to a switch.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2013-07-08 10:15:00 -07:00
|
|
|
parse_ofp_meter_mod_str(struct ofputil_meter_mod *mm, const char *str_,
|
2013-08-20 18:41:45 -07:00
|
|
|
int command, enum ofputil_protocol *usable_protocols)
|
2013-07-08 10:15:00 -07:00
|
|
|
{
|
|
|
|
struct ofpbuf bands;
|
|
|
|
char *string;
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
ofpbuf_init(&bands, 64);
|
|
|
|
string = xstrdup(str_);
|
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
error = parse_ofp_meter_mod_str__(mm, string, &bands, command,
|
|
|
|
usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
|
2013-06-20 17:26:18 +03:00
|
|
|
free(string);
|
2013-07-08 10:15:00 -07:00
|
|
|
ofpbuf_uninit(&bands);
|
|
|
|
|
|
|
|
return error;
|
2013-06-20 17:26:18 +03:00
|
|
|
}
|
|
|
|
|
2014-12-15 14:10:38 +01:00
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
2013-07-08 10:15:00 -07:00
|
|
|
parse_flow_monitor_request__(struct ofputil_flow_monitor_request *fmr,
|
2013-08-20 18:41:45 -07:00
|
|
|
const char *str_, char *string,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
2012-07-12 14:18:05 -07:00
|
|
|
{
|
2014-08-29 10:34:52 -07:00
|
|
|
static atomic_count id = ATOMIC_COUNT_INIT(0);
|
2015-08-31 18:05:44 -07:00
|
|
|
char *name, *value;
|
2012-07-12 14:18:05 -07:00
|
|
|
|
2014-08-29 10:34:52 -07:00
|
|
|
fmr->id = atomic_count_inc(&id);
|
2013-04-26 11:22:19 -07:00
|
|
|
|
2012-07-12 14:18:05 -07:00
|
|
|
fmr->flags = (NXFMF_INITIAL | NXFMF_ADD | NXFMF_DELETE | NXFMF_MODIFY
|
|
|
|
| NXFMF_OWN | NXFMF_ACTIONS);
|
|
|
|
fmr->out_port = OFPP_NONE;
|
|
|
|
fmr->table_id = 0xff;
|
2012-08-07 15:28:18 -07:00
|
|
|
match_init_catchall(&fmr->match);
|
2012-07-12 14:18:05 -07:00
|
|
|
|
2015-08-31 18:05:44 -07:00
|
|
|
while (ofputil_parse_key_value(&string, &name, &value)) {
|
2012-07-12 14:18:05 -07:00
|
|
|
const struct protocol *p;
|
2015-09-09 09:30:35 -07:00
|
|
|
char *error = NULL;
|
2012-07-12 14:18:05 -07:00
|
|
|
|
|
|
|
if (!strcmp(name, "!initial")) {
|
|
|
|
fmr->flags &= ~NXFMF_INITIAL;
|
|
|
|
} else if (!strcmp(name, "!add")) {
|
|
|
|
fmr->flags &= ~NXFMF_ADD;
|
|
|
|
} else if (!strcmp(name, "!delete")) {
|
|
|
|
fmr->flags &= ~NXFMF_DELETE;
|
|
|
|
} else if (!strcmp(name, "!modify")) {
|
|
|
|
fmr->flags &= ~NXFMF_MODIFY;
|
|
|
|
} else if (!strcmp(name, "!actions")) {
|
|
|
|
fmr->flags &= ~NXFMF_ACTIONS;
|
|
|
|
} else if (!strcmp(name, "!own")) {
|
|
|
|
fmr->flags &= ~NXFMF_OWN;
|
|
|
|
} else if (parse_protocol(name, &p)) {
|
2012-08-07 15:28:18 -07:00
|
|
|
match_set_dl_type(&fmr->match, htons(p->dl_type));
|
2012-07-12 14:18:05 -07:00
|
|
|
if (p->nw_proto) {
|
2012-08-07 15:28:18 -07:00
|
|
|
match_set_nw_proto(&fmr->match, p->nw_proto);
|
2012-07-12 14:18:05 -07:00
|
|
|
}
|
2015-09-09 09:30:35 -07:00
|
|
|
} else if (mf_from_name(name)) {
|
|
|
|
error = parse_field(mf_from_name(name), value, &fmr->match,
|
|
|
|
usable_protocols);
|
2012-07-12 14:18:05 -07:00
|
|
|
} else {
|
2015-08-31 18:05:44 -07:00
|
|
|
if (!*value) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("%s: field %s missing value", str_, name);
|
2012-07-12 14:18:05 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(name, "table")) {
|
2015-09-09 09:30:35 -07:00
|
|
|
error = str_to_u8(value, "table", &fmr->table_id);
|
2012-07-12 14:18:05 -07:00
|
|
|
} else if (!strcmp(name, "out_port")) {
|
2013-06-19 16:58:44 -07:00
|
|
|
fmr->out_port = u16_to_ofp(atoi(value));
|
2012-07-12 14:18:05 -07:00
|
|
|
} else {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("%s: unknown keyword %s", str_, name);
|
2012-07-12 14:18:05 -07:00
|
|
|
}
|
|
|
|
}
|
2015-09-09 09:30:35 -07:00
|
|
|
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
2012-07-12 14:18:05 -07:00
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert 'str_' (as described in the documentation for the "monitor" command
|
|
|
|
* in the ovs-ofctl man page) into 'fmr'.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2013-07-08 10:15:00 -07:00
|
|
|
parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
|
2013-08-20 18:41:45 -07:00
|
|
|
const char *str_,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
2013-07-08 10:15:00 -07:00
|
|
|
{
|
|
|
|
char *string = xstrdup(str_);
|
2013-08-20 18:41:45 -07:00
|
|
|
char *error = parse_flow_monitor_request__(fmr, str_, string,
|
|
|
|
usable_protocols);
|
2012-07-12 14:18:05 -07:00
|
|
|
free(string);
|
2013-07-08 10:15:00 -07:00
|
|
|
return error;
|
2012-07-12 14:18:05 -07:00
|
|
|
}
|
|
|
|
|
2010-12-07 13:32:01 -08:00
|
|
|
/* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
|
2013-07-08 10:15:00 -07:00
|
|
|
* (one of OFPFC_*) into 'fm'.
|
|
|
|
*
|
2015-06-05 14:03:12 -07:00
|
|
|
* If 'command' is given as -2, 'string' may begin with a command name ("add",
|
|
|
|
* "modify", "delete", "modify_strict", or "delete_strict"). A missing command
|
|
|
|
* name is treated as "add".
|
|
|
|
*
|
2013-07-08 10:15:00 -07:00
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2012-02-10 13:30:23 -08:00
|
|
|
parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
|
2015-06-05 14:03:12 -07:00
|
|
|
int command,
|
2013-11-15 14:19:57 -08:00
|
|
|
enum ofputil_protocol *usable_protocols)
|
2010-09-23 13:19:49 -07:00
|
|
|
{
|
2013-11-15 14:19:57 -08:00
|
|
|
char *error = parse_ofp_str(fm, command, string, usable_protocols);
|
2015-06-05 14:03:12 -07:00
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
if (!error) {
|
|
|
|
/* Normalize a copy of the match. This ensures that non-normalized
|
|
|
|
* flows get logged but doesn't affect what gets sent to the switch, so
|
|
|
|
* that the switch can do whatever it likes with the flow. */
|
|
|
|
struct match match_copy = fm->match;
|
|
|
|
ofputil_normalize_match(&match_copy);
|
|
|
|
}
|
2010-12-07 13:32:01 -08:00
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
return error;
|
2010-09-23 13:19:49 -07:00
|
|
|
}
|
|
|
|
|
2015-11-24 17:49:42 +05:30
|
|
|
/* Convert 'setting' (as described for the "mod-table" command
|
|
|
|
* in ovs-ofctl man page) into 'tm->table_vacancy->vacancy_up' and
|
|
|
|
* 'tm->table_vacancy->vacancy_down' threshold values.
|
|
|
|
* For the two threshold values, value of vacancy_up is always greater
|
|
|
|
* than value of vacancy_down.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
|
|
|
parse_ofp_table_vacancy(struct ofputil_table_mod *tm, const char *setting)
|
|
|
|
{
|
|
|
|
char *save_ptr = NULL;
|
|
|
|
char *vac_up, *vac_down;
|
2016-01-11 09:21:58 -08:00
|
|
|
char *value = xstrdup(setting);
|
2016-01-05 13:38:43 -08:00
|
|
|
char *ret_msg;
|
2015-11-24 17:49:42 +05:30
|
|
|
int vacancy_up, vacancy_down;
|
|
|
|
|
|
|
|
strtok_r(value, ":", &save_ptr);
|
|
|
|
vac_down = strtok_r(NULL, ",", &save_ptr);
|
|
|
|
if (!vac_down) {
|
2016-01-05 13:38:43 -08:00
|
|
|
ret_msg = xasprintf("Vacancy down value missing");
|
|
|
|
goto exit;
|
2015-11-24 17:49:42 +05:30
|
|
|
}
|
|
|
|
if (!str_to_int(vac_down, 0, &vacancy_down) ||
|
|
|
|
vacancy_down < 0 || vacancy_down > 100) {
|
2016-01-05 13:38:43 -08:00
|
|
|
ret_msg = xasprintf("Invalid vacancy down value \"%s\"", vac_down);
|
|
|
|
goto exit;
|
2015-11-24 17:49:42 +05:30
|
|
|
}
|
|
|
|
vac_up = strtok_r(NULL, ",", &save_ptr);
|
|
|
|
if (!vac_up) {
|
2016-01-05 13:38:43 -08:00
|
|
|
ret_msg = xasprintf("Vacancy up value missing");
|
|
|
|
goto exit;
|
2015-11-24 17:49:42 +05:30
|
|
|
}
|
|
|
|
if (!str_to_int(vac_up, 0, &vacancy_up) ||
|
|
|
|
vacancy_up < 0 || vacancy_up > 100) {
|
2016-01-05 13:38:43 -08:00
|
|
|
ret_msg = xasprintf("Invalid vacancy up value \"%s\"", vac_up);
|
|
|
|
goto exit;
|
2015-11-24 17:49:42 +05:30
|
|
|
}
|
|
|
|
if (vacancy_down > vacancy_up) {
|
2016-01-05 13:38:43 -08:00
|
|
|
ret_msg = xasprintf("Invalid vacancy range, vacancy up should be "
|
|
|
|
"greater than vacancy down (%s)",
|
|
|
|
ofperr_to_string(OFPERR_OFPBPC_BAD_VALUE));
|
|
|
|
goto exit;
|
2015-11-24 17:49:42 +05:30
|
|
|
}
|
2016-01-05 13:38:43 -08:00
|
|
|
|
|
|
|
free(value);
|
2015-11-24 17:49:42 +05:30
|
|
|
tm->table_vacancy.vacancy_down = vacancy_down;
|
|
|
|
tm->table_vacancy.vacancy_up = vacancy_up;
|
|
|
|
return NULL;
|
2016-01-05 13:38:43 -08:00
|
|
|
|
|
|
|
exit:
|
|
|
|
free(value);
|
|
|
|
return ret_msg;
|
2015-11-24 17:49:42 +05:30
|
|
|
}
|
|
|
|
|
2015-07-02 20:33:08 -07:00
|
|
|
/* Convert 'table_id' and 'setting' (as described for the "mod-table" command
|
|
|
|
* in the ovs-ofctl man page) into 'tm' for sending a table_mod command to a
|
|
|
|
* switch.
|
|
|
|
*
|
|
|
|
* Stores a bitmap of the OpenFlow versions that are usable for 'tm' into
|
|
|
|
* '*usable_versions'.
|
2013-09-07 03:02:32 -07:00
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2013-09-07 03:02:32 -07:00
|
|
|
parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
|
2015-07-02 20:33:08 -07:00
|
|
|
const char *setting, uint32_t *usable_versions)
|
2013-09-07 03:02:32 -07:00
|
|
|
{
|
2015-07-02 20:33:08 -07:00
|
|
|
*usable_versions = 0;
|
2013-09-07 03:02:32 -07:00
|
|
|
if (!strcasecmp(table_id, "all")) {
|
2013-11-12 16:45:50 +09:00
|
|
|
tm->table_id = OFPTT_ALL;
|
2013-09-07 03:02:32 -07:00
|
|
|
} else {
|
|
|
|
char *error = str_to_u8(table_id, "table_id", &tm->table_id);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-02 20:33:08 -07:00
|
|
|
tm->miss = OFPUTIL_TABLE_MISS_DEFAULT;
|
|
|
|
tm->eviction = OFPUTIL_TABLE_EVICTION_DEFAULT;
|
|
|
|
tm->eviction_flags = UINT32_MAX;
|
2015-11-24 17:49:42 +05:30
|
|
|
tm->vacancy = OFPUTIL_TABLE_VACANCY_DEFAULT;
|
|
|
|
tm->table_vacancy.vacancy_down = 0;
|
|
|
|
tm->table_vacancy.vacancy_up = 0;
|
|
|
|
tm->table_vacancy.vacancy = 0;
|
2015-07-02 20:33:08 -07:00
|
|
|
/* Only OpenFlow 1.1 and 1.2 can configure table-miss via table_mod.
|
2015-11-24 17:49:42 +05:30
|
|
|
* Only OpenFlow 1.4+ can configure eviction and vacancy events
|
|
|
|
* via table_mod.
|
2015-07-02 20:33:08 -07:00
|
|
|
*/
|
|
|
|
if (!strcmp(setting, "controller")) {
|
|
|
|
tm->miss = OFPUTIL_TABLE_MISS_CONTROLLER;
|
|
|
|
*usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
|
|
|
|
} else if (!strcmp(setting, "continue")) {
|
|
|
|
tm->miss = OFPUTIL_TABLE_MISS_CONTINUE;
|
|
|
|
*usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
|
|
|
|
} else if (!strcmp(setting, "drop")) {
|
|
|
|
tm->miss = OFPUTIL_TABLE_MISS_DROP;
|
|
|
|
*usable_versions = (1u << OFP11_VERSION) | (1u << OFP12_VERSION);
|
|
|
|
} else if (!strcmp(setting, "evict")) {
|
|
|
|
tm->eviction = OFPUTIL_TABLE_EVICTION_ON;
|
|
|
|
*usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
|
|
|
|
} else if (!strcmp(setting, "noevict")) {
|
|
|
|
tm->eviction = OFPUTIL_TABLE_EVICTION_OFF;
|
|
|
|
*usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
|
2015-11-24 17:49:42 +05:30
|
|
|
} else if (!strncmp(setting, "vacancy", strcspn(setting, ":"))) {
|
|
|
|
tm->vacancy = OFPUTIL_TABLE_VACANCY_ON;
|
|
|
|
*usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
|
|
|
|
char *error = parse_ofp_table_vacancy(tm, setting);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
} else if (!strcmp(setting, "novacancy")) {
|
|
|
|
tm->vacancy = OFPUTIL_TABLE_VACANCY_OFF;
|
|
|
|
*usable_versions = (1 << OFP14_VERSION) | (1u << OFP15_VERSION);
|
2013-09-07 03:02:32 -07:00
|
|
|
} else {
|
2015-07-02 20:33:08 -07:00
|
|
|
return xasprintf("invalid table_mod setting %s", setting);
|
2013-09-07 03:02:32 -07:00
|
|
|
}
|
|
|
|
|
2014-08-11 11:31:45 -07:00
|
|
|
if (tm->table_id == 0xfe
|
2015-07-02 20:33:08 -07:00
|
|
|
&& tm->miss == OFPUTIL_TABLE_MISS_CONTINUE) {
|
2013-09-07 03:02:32 -07:00
|
|
|
return xstrdup("last table's flow miss handling can not be continue");
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
/* Opens file 'file_name' and reads each line as a flow_mod of the specified
|
|
|
|
* type (one of OFPFC_*). Stores each flow_mod in '*fm', an array allocated
|
|
|
|
* on the caller's behalf, and the number of flow_mods in '*n_fms'.
|
|
|
|
*
|
2015-06-05 14:03:12 -07:00
|
|
|
* If 'command' is given as -2, each line may start with a command name
|
|
|
|
* ("add", "modify", "delete", "modify_strict", or "delete_strict"). A missing
|
|
|
|
* command name is treated as "add".
|
|
|
|
*
|
2013-07-08 10:15:00 -07:00
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2015-06-05 14:03:12 -07:00
|
|
|
parse_ofp_flow_mod_file(const char *file_name, int command,
|
2013-08-20 18:41:45 -07:00
|
|
|
struct ofputil_flow_mod **fms, size_t *n_fms,
|
2013-11-15 14:19:57 -08:00
|
|
|
enum ofputil_protocol *usable_protocols)
|
2010-09-23 13:19:49 -07:00
|
|
|
{
|
2012-02-10 13:30:23 -08:00
|
|
|
size_t allocated_fms;
|
2013-07-08 10:15:00 -07:00
|
|
|
int line_number;
|
2012-02-10 13:30:23 -08:00
|
|
|
FILE *stream;
|
2011-03-11 11:52:12 -08:00
|
|
|
struct ds s;
|
2010-09-23 13:19:49 -07:00
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
*usable_protocols = OFPUTIL_P_ANY;
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
*fms = NULL;
|
|
|
|
*n_fms = 0;
|
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
|
|
|
|
if (stream == NULL) {
|
2013-07-08 10:15:00 -07:00
|
|
|
return xasprintf("%s: open failed (%s)",
|
|
|
|
file_name, ovs_strerror(errno));
|
2012-02-10 13:30:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
allocated_fms = *n_fms;
|
2011-03-11 11:52:12 -08:00
|
|
|
ds_init(&s);
|
2013-07-08 10:15:00 -07:00
|
|
|
line_number = 0;
|
|
|
|
while (!ds_get_preprocessed_line(&s, stream, &line_number)) {
|
|
|
|
char *error;
|
2013-08-20 18:41:45 -07:00
|
|
|
enum ofputil_protocol usable;
|
2013-07-08 10:15:00 -07:00
|
|
|
|
2012-02-10 13:30:23 -08:00
|
|
|
if (*n_fms >= allocated_fms) {
|
|
|
|
*fms = x2nrealloc(*fms, &allocated_fms, sizeof **fms);
|
|
|
|
}
|
2013-08-20 18:41:45 -07:00
|
|
|
error = parse_ofp_flow_mod_str(&(*fms)[*n_fms], ds_cstr(&s), command,
|
2013-11-15 14:19:57 -08:00
|
|
|
&usable);
|
2013-07-08 10:15:00 -07:00
|
|
|
if (error) {
|
2016-01-05 13:38:41 -08:00
|
|
|
char *err_msg;
|
2013-07-08 10:15:00 -07:00
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < *n_fms; i++) {
|
2014-04-29 15:50:38 -07:00
|
|
|
free(CONST_CAST(struct ofpact *, (*fms)[i].ofpacts));
|
2013-07-08 10:15:00 -07:00
|
|
|
}
|
|
|
|
free(*fms);
|
|
|
|
*fms = NULL;
|
|
|
|
*n_fms = 0;
|
|
|
|
|
|
|
|
ds_destroy(&s);
|
|
|
|
if (stream != stdin) {
|
|
|
|
fclose(stream);
|
|
|
|
}
|
|
|
|
|
2016-01-05 13:38:41 -08:00
|
|
|
err_msg = xasprintf("%s:%d: %s", file_name, line_number, error);
|
|
|
|
free(error);
|
|
|
|
return err_msg;
|
2013-07-08 10:15:00 -07:00
|
|
|
}
|
2013-08-20 18:41:45 -07:00
|
|
|
*usable_protocols &= usable; /* Each line can narrow the set. */
|
2012-02-10 13:30:23 -08:00
|
|
|
*n_fms += 1;
|
2010-09-23 13:19:49 -07:00
|
|
|
}
|
|
|
|
|
2013-07-08 10:15:00 -07:00
|
|
|
ds_destroy(&s);
|
2012-02-10 13:30:23 -08:00
|
|
|
if (stream != stdin) {
|
|
|
|
fclose(stream);
|
|
|
|
}
|
2013-07-08 10:15:00 -07:00
|
|
|
return NULL;
|
2010-12-07 13:32:01 -08:00
|
|
|
}
|
|
|
|
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2011-08-08 14:48:48 -07:00
|
|
|
parse_ofp_flow_stats_request_str(struct ofputil_flow_stats_request *fsr,
|
2013-08-20 18:41:45 -07:00
|
|
|
bool aggregate, const char *string,
|
2013-11-15 14:19:57 -08:00
|
|
|
enum ofputil_protocol *usable_protocols)
|
2010-12-07 13:32:01 -08:00
|
|
|
{
|
2011-08-08 14:46:38 -07:00
|
|
|
struct ofputil_flow_mod fm;
|
2013-07-08 10:15:00 -07:00
|
|
|
char *error;
|
|
|
|
|
2013-11-15 14:19:57 -08:00
|
|
|
error = parse_ofp_str(&fm, -1, string, usable_protocols);
|
2013-07-08 10:15:00 -07:00
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
2010-12-07 13:32:01 -08:00
|
|
|
|
2013-08-20 18:41:45 -07:00
|
|
|
/* Special table ID support not required for stats requests. */
|
|
|
|
if (*usable_protocols & OFPUTIL_P_OF10_STD_TID) {
|
|
|
|
*usable_protocols |= OFPUTIL_P_OF10_STD;
|
|
|
|
}
|
|
|
|
if (*usable_protocols & OFPUTIL_P_OF10_NXM_TID) {
|
|
|
|
*usable_protocols |= OFPUTIL_P_OF10_NXM;
|
|
|
|
}
|
|
|
|
|
2010-12-07 13:32:01 -08:00
|
|
|
fsr->aggregate = aggregate;
|
2011-12-23 12:23:24 -08:00
|
|
|
fsr->cookie = fm.cookie;
|
|
|
|
fsr->cookie_mask = fm.cookie_mask;
|
2012-08-07 15:28:18 -07:00
|
|
|
fsr->match = fm.match;
|
2010-12-07 13:32:01 -08:00
|
|
|
fsr->out_port = fm.out_port;
|
2013-09-01 18:30:17 -07:00
|
|
|
fsr->out_group = fm.out_group;
|
2011-05-12 09:58:01 -07:00
|
|
|
fsr->table_id = fm.table_id;
|
2013-07-08 10:15:00 -07:00
|
|
|
return NULL;
|
2010-09-23 13:19:49 -07:00
|
|
|
}
|
2012-05-04 09:52:37 -07:00
|
|
|
|
|
|
|
/* Parses a specification of a flow from 's' into 'flow'. 's' must take the
|
|
|
|
* form FIELD=VALUE[,FIELD=VALUE]... where each FIELD is the name of a
|
|
|
|
* mf_field. Fields must be specified in a natural order for satisfying
|
2016-07-29 16:52:03 -07:00
|
|
|
* prerequisites. If 'wc' is specified, masks the field in 'wc' for each of the
|
2013-10-13 12:44:20 -07:00
|
|
|
* field specified in flow. If the map, 'names_portno' is specfied, converts
|
|
|
|
* the in_port name into port no while setting the 'flow'.
|
2012-05-04 09:52:37 -07:00
|
|
|
*
|
|
|
|
* Returns NULL on success, otherwise a malloc()'d string that explains the
|
|
|
|
* problem. */
|
|
|
|
char *
|
2016-07-29 16:52:03 -07:00
|
|
|
parse_ofp_exact_flow(struct flow *flow, struct flow_wildcards *wc,
|
2016-04-19 18:36:04 -07:00
|
|
|
const struct tun_table *tun_table, const char *s,
|
|
|
|
const struct simap *portno_names)
|
2012-05-04 09:52:37 -07:00
|
|
|
{
|
|
|
|
char *pos, *key, *value_s;
|
|
|
|
char *error = NULL;
|
|
|
|
char *copy;
|
|
|
|
|
|
|
|
memset(flow, 0, sizeof *flow);
|
2016-07-29 16:52:03 -07:00
|
|
|
if (wc) {
|
|
|
|
memset(wc, 0, sizeof *wc);
|
2013-10-13 12:44:20 -07:00
|
|
|
}
|
2016-04-19 18:36:04 -07:00
|
|
|
flow->tunnel.metadata.tab = tun_table;
|
2012-05-04 09:52:37 -07:00
|
|
|
|
|
|
|
pos = copy = xstrdup(s);
|
|
|
|
while (ofputil_parse_key_value(&pos, &key, &value_s)) {
|
|
|
|
const struct protocol *p;
|
|
|
|
if (parse_protocol(key, &p)) {
|
|
|
|
if (flow->dl_type) {
|
|
|
|
error = xasprintf("%s: Ethernet type set multiple times", s);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
flow->dl_type = htons(p->dl_type);
|
2016-07-29 16:52:03 -07:00
|
|
|
if (wc) {
|
|
|
|
wc->masks.dl_type = OVS_BE16_MAX;
|
2013-10-13 12:44:20 -07:00
|
|
|
}
|
2012-05-04 09:52:37 -07:00
|
|
|
|
|
|
|
if (p->nw_proto) {
|
|
|
|
if (flow->nw_proto) {
|
|
|
|
error = xasprintf("%s: network protocol set "
|
|
|
|
"multiple times", s);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
flow->nw_proto = p->nw_proto;
|
2016-07-29 16:52:03 -07:00
|
|
|
if (wc) {
|
|
|
|
wc->masks.nw_proto = UINT8_MAX;
|
2013-10-13 12:44:20 -07:00
|
|
|
}
|
2012-05-04 09:52:37 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const struct mf_field *mf;
|
|
|
|
union mf_value value;
|
|
|
|
char *field_error;
|
|
|
|
|
|
|
|
mf = mf_from_name(key);
|
|
|
|
if (!mf) {
|
|
|
|
error = xasprintf("%s: unknown field %s", s, key);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2016-07-29 16:52:03 -07:00
|
|
|
if (!mf_are_prereqs_ok(mf, flow, NULL)) {
|
2012-05-04 09:52:37 -07:00
|
|
|
error = xasprintf("%s: prerequisites not met for setting %s",
|
|
|
|
s, key);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2015-08-11 18:41:37 -07:00
|
|
|
if (mf_is_set(mf, flow)) {
|
2012-05-04 09:52:37 -07:00
|
|
|
error = xasprintf("%s: field %s set multiple times", s, key);
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2013-10-13 12:44:20 -07:00
|
|
|
if (!strcmp(key, "in_port")
|
|
|
|
&& portno_names
|
|
|
|
&& simap_contains(portno_names, value_s)) {
|
|
|
|
flow->in_port.ofp_port = u16_to_ofp(
|
|
|
|
simap_get(portno_names, value_s));
|
2016-07-29 16:52:03 -07:00
|
|
|
if (wc) {
|
|
|
|
wc->masks.in_port.ofp_port
|
|
|
|
= u16_to_ofp(ntohs(OVS_BE16_MAX));
|
2013-10-13 12:44:20 -07:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
field_error = mf_parse_value(mf, value_s, &value);
|
|
|
|
if (field_error) {
|
|
|
|
error = xasprintf("%s: bad value for %s (%s)",
|
|
|
|
s, key, field_error);
|
|
|
|
free(field_error);
|
|
|
|
goto exit;
|
|
|
|
}
|
2012-05-04 09:52:37 -07:00
|
|
|
|
2013-10-13 12:44:20 -07:00
|
|
|
mf_set_flow_value(mf, &value, flow);
|
2016-07-29 16:52:03 -07:00
|
|
|
if (wc) {
|
|
|
|
mf_mask_field(mf, wc);
|
2013-10-13 12:44:20 -07:00
|
|
|
}
|
|
|
|
}
|
2012-05-04 09:52:37 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-19 16:58:44 -07:00
|
|
|
if (!flow->in_port.ofp_port) {
|
|
|
|
flow->in_port.ofp_port = OFPP_NONE;
|
2013-01-23 17:05:54 -08:00
|
|
|
}
|
|
|
|
|
2012-05-04 09:52:37 -07:00
|
|
|
exit:
|
|
|
|
free(copy);
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
memset(flow, 0, sizeof *flow);
|
2016-07-29 16:52:03 -07:00
|
|
|
if (wc) {
|
|
|
|
memset(wc, 0, sizeof *wc);
|
2013-10-13 12:44:20 -07:00
|
|
|
}
|
2012-05-04 09:52:37 -07:00
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
2013-09-01 18:30:17 -07:00
|
|
|
|
2014-12-15 14:10:38 +01:00
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
2015-07-28 22:01:24 -07:00
|
|
|
parse_bucket_str(struct ofputil_bucket *bucket, char *str_, uint8_t group_type,
|
2013-09-01 18:30:17 -07:00
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
char *pos, *key, *value;
|
2013-09-01 18:30:17 -07:00
|
|
|
struct ofpbuf ofpacts;
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
struct ds actions;
|
|
|
|
char *error;
|
2013-09-01 18:30:17 -07:00
|
|
|
|
2015-07-28 22:01:24 -07:00
|
|
|
bucket->weight = group_type == OFPGT11_SELECT ? 1 : 0;
|
2014-11-11 12:39:19 +09:00
|
|
|
bucket->bucket_id = OFPG15_BUCKET_ALL;
|
2013-09-01 18:30:17 -07:00
|
|
|
bucket->watch_port = OFPP_ANY;
|
2015-11-24 10:01:23 -08:00
|
|
|
bucket->watch_group = OFPG_ANY;
|
2013-09-01 18:30:17 -07:00
|
|
|
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
ds_init(&actions);
|
2013-09-01 18:30:17 -07:00
|
|
|
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
pos = str_;
|
|
|
|
error = NULL;
|
|
|
|
while (ofputil_parse_key_value(&pos, &key, &value)) {
|
|
|
|
if (!strcasecmp(key, "weight")) {
|
|
|
|
error = str_to_u16(value, "weight", &bucket->weight);
|
|
|
|
} else if (!strcasecmp(key, "watch_port")) {
|
|
|
|
if (!ofputil_port_from_string(value, &bucket->watch_port)
|
2013-09-01 18:30:17 -07:00
|
|
|
|| (ofp_to_u16(bucket->watch_port) >= ofp_to_u16(OFPP_MAX)
|
|
|
|
&& bucket->watch_port != OFPP_ANY)) {
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
error = xasprintf("%s: invalid watch_port", value);
|
2013-09-01 18:30:17 -07:00
|
|
|
}
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
} else if (!strcasecmp(key, "watch_group")) {
|
|
|
|
error = str_to_u32(value, &bucket->watch_group);
|
2013-09-01 18:30:17 -07:00
|
|
|
if (!error && bucket->watch_group > OFPG_MAX) {
|
|
|
|
error = xasprintf("invalid watch_group id %"PRIu32,
|
|
|
|
bucket->watch_group);
|
|
|
|
}
|
2014-11-11 12:39:22 +09:00
|
|
|
} else if (!strcasecmp(key, "bucket_id")) {
|
|
|
|
error = str_to_u32(value, &bucket->bucket_id);
|
|
|
|
if (!error && bucket->bucket_id > OFPG15_BUCKET_MAX) {
|
|
|
|
error = xasprintf("invalid bucket_id id %"PRIu32,
|
|
|
|
bucket->bucket_id);
|
|
|
|
}
|
|
|
|
*usable_protocols &= OFPUTIL_P_OF15_UP;
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
} else if (!strcasecmp(key, "action") || !strcasecmp(key, "actions")) {
|
|
|
|
ds_put_format(&actions, "%s,", value);
|
2013-09-01 18:30:17 -07:00
|
|
|
} else {
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
ds_put_format(&actions, "%s(%s),", key, value);
|
2013-09-01 18:30:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
ds_destroy(&actions);
|
2013-09-01 18:30:17 -07:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
ofp-actions: Centralize all OpenFlow action code for maintainability.
Until now, knowledge about OpenFlow has been somewhat scattered around the
tree. Some of it is in ofp-actions, some of it is in ofp-util, some in
separate files for individual actions, and most of the wire format
declarations are in include/openflow. This commit centralizes all of that
in ofp-actions.
Encoding and decoding OpenFlow actions was previously broken up by OpenFlow
version. This was OK with only OpenFlow 1.0 and 1.1, but each additional
version added a new wrapper around the existing ones, which started to
become hard to understand. This commit merges all of the processing for
the different versions, to the extent that they are similar, making the
version differences clearer.
Previously, ofp-actions contained OpenFlow encoding and decoding, plus
ofpact formatting, but OpenFlow parsing was separated into ofp-parse, which
seems an odd division. This commit moves the parsing code into ofp-actions
with the rest of the code.
Before this commit, the four main bits of code associated with a particular
ofpact--OpenFlow encoding and decoding, ofpact formatting and parsing--were
all found far away from each other. This often made it hard to see what
was going on for a particular ofpact, since you had to search around to
many different pieces of code. This commit reorganizes so that all of the
code for a given ofpact is in a single place.
As a code refactoring, this commit has little visible behavioral change.
The update to ofproto-dpif.at illustrates one minor bug fix as a side
effect: a flow that was added with the action "dec_ttl" (a standard
OpenFlow action) was previously formatted as "dec_ttl(0)" (using a Nicira
extension to specifically direct packets bounced to the controller because
of too-low TTL), but after this commit it is correctly formatted as
"dec_ttl".
The other visible effect is to drop support for the Nicira extension
dec_ttl action in OpenFlow 1.1 and later in favor of the equivalent
standard action. It seems unlikely that anyone was really using the
Nicira extension in OF1.1 or later.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Jarno Rajahalme <jrajahalme@nicira.com>
2014-08-11 12:50:36 -07:00
|
|
|
if (!actions.length) {
|
|
|
|
return xstrdup("bucket must specify actions");
|
|
|
|
}
|
|
|
|
ds_chomp(&actions, ',');
|
|
|
|
|
|
|
|
ofpbuf_init(&ofpacts, 0);
|
|
|
|
error = ofpacts_parse_actions(ds_cstr(&actions), &ofpacts,
|
|
|
|
usable_protocols);
|
|
|
|
ds_destroy(&actions);
|
|
|
|
if (error) {
|
|
|
|
ofpbuf_uninit(&ofpacts);
|
|
|
|
return error;
|
|
|
|
}
|
2015-03-02 17:29:44 -08:00
|
|
|
bucket->ofpacts = ofpacts.data;
|
|
|
|
bucket->ofpacts_len = ofpacts.size;
|
2013-09-01 18:30:17 -07:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-03-20 13:50:33 +09:00
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
|
|
|
parse_select_group_field(char *s, struct field_array *fa,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
ofp-parse: Fix parsing, formatting of multiple fields in NTR extension.
Until now, the only way to specify multiple fields in the "fields"
parameter for the Netronome groups extension, was to specify "fields"
more than once, e.g. fields=eth_dst,fields=ip_dst
However, this wasn't documented and the code in ofp-print didn't use it,
generating output that couldn't be parsed.
This commit fixes the situation by introducing a more straightforward
syntax, e.g. fields(eth_dst,ip_dst), documents it, and adjusts ofp-print
code to use it when there is more than one field (it retains the previous
format for backward compatibility when there is exactly one field)
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Simon Horman <simon.horman@netronome.com>
2015-10-15 09:46:21 -07:00
|
|
|
char *name, *value_str;
|
2015-03-20 13:50:33 +09:00
|
|
|
|
ofp-parse: Fix parsing, formatting of multiple fields in NTR extension.
Until now, the only way to specify multiple fields in the "fields"
parameter for the Netronome groups extension, was to specify "fields"
more than once, e.g. fields=eth_dst,fields=ip_dst
However, this wasn't documented and the code in ofp-print didn't use it,
generating output that couldn't be parsed.
This commit fixes the situation by introducing a more straightforward
syntax, e.g. fields(eth_dst,ip_dst), documents it, and adjusts ofp-print
code to use it when there is more than one field (it retains the previous
format for backward compatibility when there is exactly one field)
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Simon Horman <simon.horman@netronome.com>
2015-10-15 09:46:21 -07:00
|
|
|
while (ofputil_parse_key_value(&s, &name, &value_str)) {
|
2015-03-20 13:50:33 +09:00
|
|
|
const struct mf_field *mf = mf_from_name(name);
|
|
|
|
|
|
|
|
if (mf) {
|
|
|
|
char *error;
|
|
|
|
union mf_value value;
|
|
|
|
|
|
|
|
if (bitmap_is_set(fa->used.bm, mf->id)) {
|
|
|
|
return xasprintf("%s: duplicate field", name);
|
|
|
|
}
|
|
|
|
|
ofp-parse: Fix parsing, formatting of multiple fields in NTR extension.
Until now, the only way to specify multiple fields in the "fields"
parameter for the Netronome groups extension, was to specify "fields"
more than once, e.g. fields=eth_dst,fields=ip_dst
However, this wasn't documented and the code in ofp-print didn't use it,
generating output that couldn't be parsed.
This commit fixes the situation by introducing a more straightforward
syntax, e.g. fields(eth_dst,ip_dst), documents it, and adjusts ofp-print
code to use it when there is more than one field (it retains the previous
format for backward compatibility when there is exactly one field)
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Simon Horman <simon.horman@netronome.com>
2015-10-15 09:46:21 -07:00
|
|
|
if (*value_str) {
|
2015-03-20 13:50:33 +09:00
|
|
|
error = mf_parse_value(mf, value_str, &value);
|
|
|
|
if (error) {
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The mask cannot be all-zeros */
|
2015-08-11 18:41:37 -07:00
|
|
|
if (!mf_is_tun_metadata(mf) &&
|
|
|
|
is_all_zeros(&value, mf->n_bytes)) {
|
2015-03-20 13:50:33 +09:00
|
|
|
return xasprintf("%s: values are wildcards here "
|
|
|
|
"and must not be all-zeros", s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The values parsed are masks for fields used
|
|
|
|
* by the selection method */
|
|
|
|
if (!mf_is_mask_valid(mf, &value)) {
|
|
|
|
return xasprintf("%s: invalid mask for field %s",
|
|
|
|
value_str, mf->name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
memset(&value, 0xff, mf->n_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
field_array_set(mf->id, &value, fa);
|
|
|
|
|
|
|
|
if (is_all_ones(&value, mf->n_bytes)) {
|
|
|
|
*usable_protocols &= mf->usable_protocols_exact;
|
|
|
|
} else if (mf->usable_protocols_bitwise == mf->usable_protocols_cidr
|
|
|
|
|| ip_is_cidr(value.be32)) {
|
|
|
|
*usable_protocols &= mf->usable_protocols_cidr;
|
|
|
|
} else {
|
|
|
|
*usable_protocols &= mf->usable_protocols_bitwise;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return xasprintf("%s: unknown field %s", s, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-12-15 14:10:38 +01:00
|
|
|
static char * OVS_WARN_UNUSED_RESULT
|
2016-07-29 16:52:04 -07:00
|
|
|
parse_ofp_group_mod_str__(struct ofputil_group_mod *gm, int command,
|
2013-09-01 18:30:17 -07:00
|
|
|
char *string,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
|
|
|
enum {
|
2014-11-12 15:24:12 +09:00
|
|
|
F_GROUP_TYPE = 1 << 0,
|
|
|
|
F_BUCKETS = 1 << 1,
|
|
|
|
F_COMMAND_BUCKET_ID = 1 << 2,
|
|
|
|
F_COMMAND_BUCKET_ID_ALL = 1 << 3,
|
2013-09-01 18:30:17 -07:00
|
|
|
} fields;
|
|
|
|
bool had_type = false;
|
2014-11-12 15:24:12 +09:00
|
|
|
bool had_command_bucket_id = false;
|
2013-09-01 18:30:17 -07:00
|
|
|
struct ofputil_bucket *bucket;
|
|
|
|
char *error = NULL;
|
|
|
|
|
|
|
|
*usable_protocols = OFPUTIL_P_OF11_UP;
|
|
|
|
|
2016-07-29 16:52:04 -07:00
|
|
|
if (command == -2) {
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
string += strspn(string, " \t\r\n"); /* Skip white space. */
|
|
|
|
len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */
|
|
|
|
|
|
|
|
if (!strncmp(string, "add", len)) {
|
|
|
|
command = OFPGC11_ADD;
|
|
|
|
} else if (!strncmp(string, "delete", len)) {
|
|
|
|
command = OFPGC11_DELETE;
|
|
|
|
} else if (!strncmp(string, "modify", len)) {
|
|
|
|
command = OFPGC11_MODIFY;
|
|
|
|
} else if (!strncmp(string, "add_or_mod", len)) {
|
|
|
|
command = OFPGC11_ADD_OR_MOD;
|
|
|
|
} else if (!strncmp(string, "insert_bucket", len)) {
|
|
|
|
command = OFPGC15_INSERT_BUCKET;
|
|
|
|
} else if (!strncmp(string, "remove_bucket", len)) {
|
|
|
|
command = OFPGC15_REMOVE_BUCKET;
|
|
|
|
} else {
|
|
|
|
len = 0;
|
|
|
|
command = OFPGC11_ADD;
|
|
|
|
}
|
|
|
|
string += len;
|
|
|
|
}
|
|
|
|
|
2013-09-01 18:30:17 -07:00
|
|
|
switch (command) {
|
|
|
|
case OFPGC11_ADD:
|
|
|
|
fields = F_GROUP_TYPE | F_BUCKETS;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPGC11_DELETE:
|
|
|
|
fields = 0;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPGC11_MODIFY:
|
|
|
|
fields = F_GROUP_TYPE | F_BUCKETS;
|
|
|
|
break;
|
|
|
|
|
ofproto: Add relaxed group_mod command ADD_OR_MOD
This patch adds support for a new Group Mod command OFPGC_ADD_OR_MOD to
OVS for all OpenFlow versions that support groups (OF11 and higher).
The new ADD_OR_MOD creates a group that does not yet exist (like ADD)
and modifies an existing group (like MODIFY).
Rational: In OpenFlow 1.x the Group Mod commands OFPGC_ADD and
OFPGC_MODIFY have strict semantics: ADD fails if the group exists,
while MODIFY fails if the group does not exist. This requires a
controller to exactly know the state of the switch when programming a
group in order not run the risk of getting an OFP Error message in
response. This is hard to achieve and maintain at all times in view of
possible switch and controller restarts or other connection losses
between switch and controller.
Due to the un-acknowledged nature of the Group Mod message programming
groups safely and efficiently at the same time is virtually impossible
as the controller has to either query the existence of the group prior
to each Group Mod message or to insert a Barrier Request/Reply after
every group to be sure that no Error can be received at a later stage
and require a complicated roll-back of any dependent actions taken
between the failed Group Mod and the Error.
In the ovs-ofctl command line the ADD_OR_MOD command is made available
through the new option --may-create in the mod-group command:
$ ovs-ofctl -Oopenflow13 del-groups br-int group_id=100
$ ovs-ofctl -Oopenflow13 mod-group br-int
group_id=100,type=indirect,bucket=actions=2 OFPT_ERROR (OF1.3)
(xid=0x2): OFPGMFC_UNKNOWN_GROUP OFPT_GROUP_MOD (OF1.3) (xid=0x2):
MOD group_id=100,type=indirect,bucket=actions=output:2
$ ovs-ofctl -Oopenflow13 --may-create mod-group br-int
group_id=100,type=indirect,bucket=actions=2
$ ovs-ofctl -Oopenflow13 dump-groups br-int
OFPST_GROUP_DESC reply (OF1.3) (xid=0x2):
group_id=100,type=indirect,bucket=actions=output:2
$ ovs-ofctl -Oopenflow13 --may-create mod-group br-int
group_id=100,type=indirect,bucket=actions=3
$ ovs-ofctl -Oopenflow13 dump-groups br-int
OFPST_GROUP_DESC reply (OF1.3) (xid=0x2):
group_id=100,type=indirect,bucket=actions=output:3
Signed-off-by: Jan Scheurich <jan.scheurich at web.de>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2016-06-29 00:29:25 +02:00
|
|
|
case OFPGC11_ADD_OR_MOD:
|
|
|
|
fields = F_GROUP_TYPE | F_BUCKETS;
|
|
|
|
break;
|
|
|
|
|
2014-11-12 15:24:12 +09:00
|
|
|
case OFPGC15_INSERT_BUCKET:
|
|
|
|
fields = F_BUCKETS | F_COMMAND_BUCKET_ID;
|
|
|
|
*usable_protocols &= OFPUTIL_P_OF15_UP;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OFPGC15_REMOVE_BUCKET:
|
|
|
|
fields = F_COMMAND_BUCKET_ID | F_COMMAND_BUCKET_ID_ALL;
|
|
|
|
*usable_protocols &= OFPUTIL_P_OF15_UP;
|
|
|
|
break;
|
|
|
|
|
2013-09-01 18:30:17 -07:00
|
|
|
default:
|
2013-12-17 10:32:12 -08:00
|
|
|
OVS_NOT_REACHED();
|
2013-09-01 18:30:17 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
memset(gm, 0, sizeof *gm);
|
|
|
|
gm->command = command;
|
|
|
|
gm->group_id = OFPG_ANY;
|
2014-11-11 12:39:19 +09:00
|
|
|
gm->command_bucket_id = OFPG15_BUCKET_ALL;
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_init(&gm->buckets);
|
2013-09-01 18:30:17 -07:00
|
|
|
if (command == OFPGC11_DELETE && string[0] == '\0') {
|
|
|
|
gm->group_id = OFPG_ALL;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*usable_protocols = OFPUTIL_P_OF11_UP;
|
|
|
|
|
2015-07-28 22:01:24 -07:00
|
|
|
/* Strip the buckets off the end of 'string', if there are any, saving a
|
|
|
|
* pointer for later. We want to parse the buckets last because the bucket
|
|
|
|
* type influences bucket defaults. */
|
|
|
|
char *bkt_str = strstr(string, "bucket=");
|
|
|
|
if (bkt_str) {
|
|
|
|
if (!(fields & F_BUCKETS)) {
|
|
|
|
error = xstrdup("bucket is not needed");
|
|
|
|
goto out;
|
2013-09-01 18:30:17 -07:00
|
|
|
}
|
2015-07-28 22:01:24 -07:00
|
|
|
*bkt_str = '\0';
|
2013-09-01 18:30:17 -07:00
|
|
|
}
|
|
|
|
|
2015-07-28 22:01:24 -07:00
|
|
|
/* Parse everything before the buckets. */
|
ofp-parse: Fix parsing, formatting of multiple fields in NTR extension.
Until now, the only way to specify multiple fields in the "fields"
parameter for the Netronome groups extension, was to specify "fields"
more than once, e.g. fields=eth_dst,fields=ip_dst
However, this wasn't documented and the code in ofp-print didn't use it,
generating output that couldn't be parsed.
This commit fixes the situation by introducing a more straightforward
syntax, e.g. fields(eth_dst,ip_dst), documents it, and adjusts ofp-print
code to use it when there is more than one field (it retains the previous
format for backward compatibility when there is exactly one field)
Signed-off-by: Ben Pfaff <blp@nicira.com>
Acked-by: Simon Horman <simon.horman@netronome.com>
2015-10-15 09:46:21 -07:00
|
|
|
char *pos = string;
|
|
|
|
char *name, *value;
|
|
|
|
while (ofputil_parse_key_value(&pos, &name, &value)) {
|
2014-11-12 15:24:12 +09:00
|
|
|
if (!strcmp(name, "command_bucket_id")) {
|
|
|
|
if (!(fields & F_COMMAND_BUCKET_ID)) {
|
|
|
|
error = xstrdup("command bucket id is not needed");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!strcmp(value, "all")) {
|
|
|
|
gm->command_bucket_id = OFPG15_BUCKET_ALL;
|
|
|
|
} else if (!strcmp(value, "first")) {
|
|
|
|
gm->command_bucket_id = OFPG15_BUCKET_FIRST;
|
|
|
|
} else if (!strcmp(value, "last")) {
|
|
|
|
gm->command_bucket_id = OFPG15_BUCKET_LAST;
|
|
|
|
} else {
|
2015-04-16 13:40:04 -07:00
|
|
|
error = str_to_u32(value, &gm->command_bucket_id);
|
2014-11-12 15:24:12 +09:00
|
|
|
if (error) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (gm->command_bucket_id > OFPG15_BUCKET_MAX
|
|
|
|
&& (gm->command_bucket_id != OFPG15_BUCKET_FIRST
|
|
|
|
&& gm->command_bucket_id != OFPG15_BUCKET_LAST
|
|
|
|
&& gm->command_bucket_id != OFPG15_BUCKET_ALL)) {
|
|
|
|
error = xasprintf("invalid command bucket id %"PRIu32,
|
|
|
|
gm->command_bucket_id);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (gm->command_bucket_id == OFPG15_BUCKET_ALL
|
|
|
|
&& !(fields & F_COMMAND_BUCKET_ID_ALL)) {
|
|
|
|
error = xstrdup("command_bucket_id=all is not permitted");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
had_command_bucket_id = true;
|
|
|
|
} else if (!strcmp(name, "group_id")) {
|
2013-09-01 18:30:17 -07:00
|
|
|
if(!strcmp(value, "all")) {
|
|
|
|
gm->group_id = OFPG_ALL;
|
|
|
|
} else {
|
2015-04-16 13:56:46 -07:00
|
|
|
error = str_to_u32(value, &gm->group_id);
|
2013-09-01 18:30:17 -07:00
|
|
|
if (error) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (gm->group_id != OFPG_ALL && gm->group_id > OFPG_MAX) {
|
|
|
|
error = xasprintf("invalid group id %"PRIu32,
|
|
|
|
gm->group_id);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (!strcmp(name, "type")){
|
|
|
|
if (!(fields & F_GROUP_TYPE)) {
|
|
|
|
error = xstrdup("type is not needed");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (!strcmp(value, "all")) {
|
|
|
|
gm->type = OFPGT11_ALL;
|
|
|
|
} else if (!strcmp(value, "select")) {
|
|
|
|
gm->type = OFPGT11_SELECT;
|
|
|
|
} else if (!strcmp(value, "indirect")) {
|
|
|
|
gm->type = OFPGT11_INDIRECT;
|
|
|
|
} else if (!strcmp(value, "ff") ||
|
|
|
|
!strcmp(value, "fast_failover")) {
|
|
|
|
gm->type = OFPGT11_FF;
|
|
|
|
} else {
|
|
|
|
error = xasprintf("invalid group type %s", value);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
had_type = true;
|
2015-03-20 13:50:33 +09:00
|
|
|
} else if (!strcmp(name, "selection_method")) {
|
|
|
|
if (!(fields & F_GROUP_TYPE)) {
|
|
|
|
error = xstrdup("selection method is not needed");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (strlen(value) >= NTR_MAX_SELECTION_METHOD_LEN) {
|
|
|
|
error = xasprintf("selection method is longer than %u"
|
|
|
|
" bytes long",
|
|
|
|
NTR_MAX_SELECTION_METHOD_LEN - 1);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
memset(gm->props.selection_method, '\0',
|
|
|
|
NTR_MAX_SELECTION_METHOD_LEN);
|
|
|
|
strcpy(gm->props.selection_method, value);
|
|
|
|
*usable_protocols &= OFPUTIL_P_OF15_UP;
|
|
|
|
} else if (!strcmp(name, "selection_method_param")) {
|
|
|
|
if (!(fields & F_GROUP_TYPE)) {
|
|
|
|
error = xstrdup("selection method param is not needed");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
error = str_to_u64(value, &gm->props.selection_method_param);
|
2015-04-26 10:17:27 -07:00
|
|
|
if (error) {
|
|
|
|
goto out;
|
|
|
|
}
|
2015-03-20 13:50:33 +09:00
|
|
|
*usable_protocols &= OFPUTIL_P_OF15_UP;
|
|
|
|
} else if (!strcmp(name, "fields")) {
|
|
|
|
if (!(fields & F_GROUP_TYPE)) {
|
|
|
|
error = xstrdup("fields are not needed");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
error = parse_select_group_field(value, &gm->props.fields,
|
|
|
|
usable_protocols);
|
|
|
|
if (error) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
*usable_protocols &= OFPUTIL_P_OF15_UP;
|
2013-09-01 18:30:17 -07:00
|
|
|
} else {
|
|
|
|
error = xasprintf("unknown keyword %s", name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (gm->group_id == OFPG_ANY) {
|
|
|
|
error = xstrdup("must specify a group_id");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (fields & F_GROUP_TYPE && !had_type) {
|
|
|
|
error = xstrdup("must specify a type");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-09-16 09:39:13 -07:00
|
|
|
/* Exclude fields for non "hash" selection method. */
|
|
|
|
if (strcmp(gm->props.selection_method, "hash") &&
|
|
|
|
gm->props.fields.values_size) {
|
|
|
|
error = xstrdup("fields may only be specified with \"selection_method=hash\"");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
/* Exclude selection_method_param if no selection_method is given. */
|
|
|
|
if (gm->props.selection_method[0] == 0
|
|
|
|
&& gm->props.selection_method_param != 0) {
|
|
|
|
error = xstrdup("selection_method_param is only allowed with \"selection_method\"");
|
|
|
|
goto out;
|
|
|
|
}
|
2014-11-12 15:24:12 +09:00
|
|
|
if (fields & F_COMMAND_BUCKET_ID) {
|
|
|
|
if (!(fields & F_COMMAND_BUCKET_ID_ALL || had_command_bucket_id)) {
|
|
|
|
error = xstrdup("must specify a command bucket id");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else if (had_command_bucket_id) {
|
|
|
|
error = xstrdup("command bucket id is not needed");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-07-28 22:01:24 -07:00
|
|
|
/* Now parse the buckets, if any. */
|
|
|
|
while (bkt_str) {
|
|
|
|
char *next_bkt_str;
|
|
|
|
|
|
|
|
bkt_str = strchr(bkt_str + 1, '=');
|
|
|
|
if (!bkt_str) {
|
|
|
|
error = xstrdup("must specify bucket content");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
bkt_str++;
|
|
|
|
|
|
|
|
next_bkt_str = strstr(bkt_str, "bucket=");
|
|
|
|
if (next_bkt_str) {
|
|
|
|
*next_bkt_str = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
bucket = xzalloc(sizeof(struct ofputil_bucket));
|
|
|
|
error = parse_bucket_str(bucket, bkt_str, gm->type, usable_protocols);
|
|
|
|
if (error) {
|
|
|
|
free(bucket);
|
|
|
|
goto out;
|
|
|
|
}
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_push_back(&gm->buckets, &bucket->list_node);
|
2015-07-28 22:01:24 -07:00
|
|
|
|
|
|
|
if (gm->type != OFPGT11_SELECT && bucket->weight) {
|
2013-09-01 18:30:17 -07:00
|
|
|
error = xstrdup("Only select groups can have bucket weights.");
|
|
|
|
goto out;
|
|
|
|
}
|
2015-07-28 22:01:24 -07:00
|
|
|
|
|
|
|
bkt_str = next_bkt_str;
|
2013-09-01 18:30:17 -07:00
|
|
|
}
|
2016-03-25 14:10:22 -07:00
|
|
|
if (gm->type == OFPGT11_INDIRECT && !ovs_list_is_short(&gm->buckets)) {
|
2013-09-01 18:30:17 -07:00
|
|
|
error = xstrdup("Indirect groups can have at most one bucket.");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
out:
|
2016-07-29 16:52:04 -07:00
|
|
|
ofputil_uninit_group_mod(gm);
|
2013-09-01 18:30:17 -07:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2016-07-29 16:52:04 -07:00
|
|
|
/* If 'command' is given as -2, each line may start with a command name ("add",
|
|
|
|
* "modify", "add_or_mod", "delete", "insert_bucket", or "remove_bucket"). A
|
|
|
|
* missing command name is treated as "add".
|
|
|
|
*/
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2016-07-29 16:52:04 -07:00
|
|
|
parse_ofp_group_mod_str(struct ofputil_group_mod *gm, int command,
|
2013-09-01 18:30:17 -07:00
|
|
|
const char *str_,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
|
|
|
char *string = xstrdup(str_);
|
|
|
|
char *error = parse_ofp_group_mod_str__(gm, command, string,
|
|
|
|
usable_protocols);
|
|
|
|
free(string);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2016-07-29 16:52:04 -07:00
|
|
|
/* If 'command' is given as -2, each line may start with a command name ("add",
|
|
|
|
* "modify", "add_or_mod", "delete", "insert_bucket", or "remove_bucket"). A
|
|
|
|
* missing command name is treated as "add".
|
|
|
|
*/
|
2014-12-15 14:10:38 +01:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2016-07-29 16:52:04 -07:00
|
|
|
parse_ofp_group_mod_file(const char *file_name, int command,
|
2013-09-01 18:30:17 -07:00
|
|
|
struct ofputil_group_mod **gms, size_t *n_gms,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
|
|
|
size_t allocated_gms;
|
|
|
|
int line_number;
|
|
|
|
FILE *stream;
|
|
|
|
struct ds s;
|
|
|
|
|
|
|
|
*gms = NULL;
|
|
|
|
*n_gms = 0;
|
|
|
|
|
|
|
|
stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
|
|
|
|
if (stream == NULL) {
|
|
|
|
return xasprintf("%s: open failed (%s)",
|
|
|
|
file_name, ovs_strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
allocated_gms = *n_gms;
|
|
|
|
ds_init(&s);
|
|
|
|
line_number = 0;
|
|
|
|
*usable_protocols = OFPUTIL_P_OF11_UP;
|
|
|
|
while (!ds_get_preprocessed_line(&s, stream, &line_number)) {
|
|
|
|
enum ofputil_protocol usable;
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
if (*n_gms >= allocated_gms) {
|
2015-02-13 14:31:21 -08:00
|
|
|
struct ofputil_group_mod *new_gms;
|
2013-10-30 18:17:15 +09:00
|
|
|
size_t i;
|
|
|
|
|
2015-02-13 14:31:21 -08:00
|
|
|
new_gms = x2nrealloc(*gms, &allocated_gms, sizeof **gms);
|
2013-10-30 18:17:15 +09:00
|
|
|
for (i = 0; i < *n_gms; i++) {
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_moved(&new_gms[i].buckets, &(*gms)[i].buckets);
|
2013-10-30 18:17:15 +09:00
|
|
|
}
|
2015-02-13 14:31:21 -08:00
|
|
|
*gms = new_gms;
|
2013-09-01 18:30:17 -07:00
|
|
|
}
|
|
|
|
error = parse_ofp_group_mod_str(&(*gms)[*n_gms], command, ds_cstr(&s),
|
|
|
|
&usable);
|
|
|
|
if (error) {
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < *n_gms; i++) {
|
2016-07-29 16:52:04 -07:00
|
|
|
ofputil_uninit_group_mod(&(*gms)[i]);
|
2013-09-01 18:30:17 -07:00
|
|
|
}
|
|
|
|
free(*gms);
|
|
|
|
*gms = NULL;
|
|
|
|
*n_gms = 0;
|
|
|
|
|
|
|
|
ds_destroy(&s);
|
|
|
|
if (stream != stdin) {
|
|
|
|
fclose(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
return xasprintf("%s:%d: %s", file_name, line_number, error);
|
|
|
|
}
|
|
|
|
*usable_protocols &= usable;
|
|
|
|
*n_gms += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ds_destroy(&s);
|
|
|
|
if (stream != stdin) {
|
|
|
|
fclose(stream);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
openflow: Table maintenance commands for Geneve options.
In order to work with Geneve options, we need to maintain a mapping
table between an option (defined by <class, type, length>) and
an NXM field that can be operated on for the purposes of matches,
actions, etc. This mapping must be explicitly specified by the
user.
Conceptually, this table could be communicated using either OpenFlow
or OVSDB. Using OVSDB requires less code and definition of extensions
than OpenFlow but introduces the possibility that mapping table
updates and flow modifications are desynchronized from each other.
This is dangerous because the mapping table signifcantly impacts the
way that flows using Geneve options are installed and processed by
OVS. Therefore, the mapping table is maintained using OpenFlow commands
instead, which opens the possibility of using synchronization between
table changes and flow modifications through barriers, bundles, etc.
There are two primary groups of OpenFlow messages that are introduced
as Nicira extensions: modification commands (add, delete, clear mappings)
and table status request/reply to dump the current table along with switch
information.
Note that mappings should not be changed while they are in active use by
a flow. The result of doing so is undefined.
This only adds the OpenFlow infrastructure but doesn't actually
do anything with the information yet after the messages have been
decoded.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-06-02 15:11:00 -07:00
|
|
|
|
2016-07-29 16:52:04 -07:00
|
|
|
/* Opens file 'file_name' and reads each line as a flow_mod or a group_mod,
|
|
|
|
* depending on the first keyword on each line. Stores each flow and group
|
|
|
|
* mods in '*bms', an array allocated on the caller's behalf, and the number of
|
|
|
|
* messages in '*n_bms'.
|
|
|
|
*
|
|
|
|
* Returns NULL if successful, otherwise a malloc()'d string describing the
|
|
|
|
* error. The caller is responsible for freeing the returned string. */
|
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
|
|
|
parse_ofp_bundle_file(const char *file_name,
|
|
|
|
struct ofputil_bundle_msg **bms, size_t *n_bms,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
|
|
|
size_t allocated_bms;
|
|
|
|
char *error = NULL;
|
|
|
|
int line_number;
|
|
|
|
FILE *stream;
|
|
|
|
struct ds ds;
|
|
|
|
|
|
|
|
*usable_protocols = OFPUTIL_P_ANY;
|
|
|
|
|
|
|
|
*bms = NULL;
|
|
|
|
*n_bms = 0;
|
|
|
|
|
|
|
|
stream = !strcmp(file_name, "-") ? stdin : fopen(file_name, "r");
|
|
|
|
if (stream == NULL) {
|
|
|
|
return xasprintf("%s: open failed (%s)",
|
|
|
|
file_name, ovs_strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
allocated_bms = *n_bms;
|
|
|
|
ds_init(&ds);
|
|
|
|
line_number = 0;
|
|
|
|
while (!ds_get_preprocessed_line(&ds, stream, &line_number)) {
|
|
|
|
enum ofputil_protocol usable;
|
|
|
|
char *s = ds_cstr(&ds);
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
if (*n_bms >= allocated_bms) {
|
|
|
|
struct ofputil_bundle_msg *new_bms;
|
|
|
|
|
|
|
|
new_bms = x2nrealloc(*bms, &allocated_bms, sizeof **bms);
|
|
|
|
for (size_t i = 0; i < *n_bms; i++) {
|
|
|
|
if (new_bms[i].type == OFPTYPE_GROUP_MOD) {
|
|
|
|
ovs_list_moved(&new_bms[i].gm.buckets,
|
|
|
|
&(*bms)[i].gm.buckets);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*bms = new_bms;
|
|
|
|
}
|
|
|
|
|
|
|
|
s += strspn(s, " \t\r\n"); /* Skip white space. */
|
|
|
|
len = strcspn(s, ", \t\r\n"); /* Get length of the first token. */
|
|
|
|
|
|
|
|
if (!strncmp(s, "flow", len)) {
|
|
|
|
s += len;
|
|
|
|
error = parse_ofp_flow_mod_str(&(*bms)[*n_bms].fm, s, -2, &usable);
|
|
|
|
if (error) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
(*bms)[*n_bms].type = OFPTYPE_FLOW_MOD;
|
|
|
|
} else if (!strncmp(s, "group", len)) {
|
|
|
|
s += len;
|
|
|
|
error = parse_ofp_group_mod_str(&(*bms)[*n_bms].gm, -2, s,
|
|
|
|
&usable);
|
|
|
|
if (error) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
(*bms)[*n_bms].type = OFPTYPE_GROUP_MOD;
|
2016-09-14 16:51:27 -07:00
|
|
|
} else if (!strncmp(s, "packet-out", len)) {
|
|
|
|
s += len;
|
|
|
|
error = parse_ofp_packet_out_str(&(*bms)[*n_bms].po, s, &usable);
|
|
|
|
if (error) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
(*bms)[*n_bms].type = OFPTYPE_PACKET_OUT;
|
2016-07-29 16:52:04 -07:00
|
|
|
} else {
|
|
|
|
error = xasprintf("Unsupported bundle message type: %.*s",
|
|
|
|
(int)len, s);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*usable_protocols &= usable; /* Each line can narrow the set. */
|
|
|
|
*n_bms += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ds_destroy(&ds);
|
|
|
|
if (stream != stdin) {
|
|
|
|
fclose(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
char *err_msg = xasprintf("%s:%d: %s", file_name, line_number, error);
|
|
|
|
free(error);
|
|
|
|
|
2016-09-14 16:51:27 -07:00
|
|
|
ofputil_free_bundle_msgs(*bms, *n_bms);
|
|
|
|
*bms = NULL;
|
|
|
|
*n_bms = 0;
|
2016-07-29 16:52:04 -07:00
|
|
|
return err_msg;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
openflow: Table maintenance commands for Geneve options.
In order to work with Geneve options, we need to maintain a mapping
table between an option (defined by <class, type, length>) and
an NXM field that can be operated on for the purposes of matches,
actions, etc. This mapping must be explicitly specified by the
user.
Conceptually, this table could be communicated using either OpenFlow
or OVSDB. Using OVSDB requires less code and definition of extensions
than OpenFlow but introduces the possibility that mapping table
updates and flow modifications are desynchronized from each other.
This is dangerous because the mapping table signifcantly impacts the
way that flows using Geneve options are installed and processed by
OVS. Therefore, the mapping table is maintained using OpenFlow commands
instead, which opens the possibility of using synchronization between
table changes and flow modifications through barriers, bundles, etc.
There are two primary groups of OpenFlow messages that are introduced
as Nicira extensions: modification commands (add, delete, clear mappings)
and table status request/reply to dump the current table along with switch
information.
Note that mappings should not be changed while they are in active use by
a flow. The result of doing so is undefined.
This only adds the OpenFlow infrastructure but doesn't actually
do anything with the information yet after the messages have been
decoded.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-06-02 15:11:00 -07:00
|
|
|
char * OVS_WARN_UNUSED_RESULT
|
2015-12-16 02:47:50 +08:00
|
|
|
parse_ofp_tlv_table_mod_str(struct ofputil_tlv_table_mod *ttm,
|
openflow: Table maintenance commands for Geneve options.
In order to work with Geneve options, we need to maintain a mapping
table between an option (defined by <class, type, length>) and
an NXM field that can be operated on for the purposes of matches,
actions, etc. This mapping must be explicitly specified by the
user.
Conceptually, this table could be communicated using either OpenFlow
or OVSDB. Using OVSDB requires less code and definition of extensions
than OpenFlow but introduces the possibility that mapping table
updates and flow modifications are desynchronized from each other.
This is dangerous because the mapping table signifcantly impacts the
way that flows using Geneve options are installed and processed by
OVS. Therefore, the mapping table is maintained using OpenFlow commands
instead, which opens the possibility of using synchronization between
table changes and flow modifications through barriers, bundles, etc.
There are two primary groups of OpenFlow messages that are introduced
as Nicira extensions: modification commands (add, delete, clear mappings)
and table status request/reply to dump the current table along with switch
information.
Note that mappings should not be changed while they are in active use by
a flow. The result of doing so is undefined.
This only adds the OpenFlow infrastructure but doesn't actually
do anything with the information yet after the messages have been
decoded.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-06-02 15:11:00 -07:00
|
|
|
uint16_t command, const char *s,
|
|
|
|
enum ofputil_protocol *usable_protocols)
|
|
|
|
{
|
|
|
|
*usable_protocols = OFPUTIL_P_NXM_OXM_ANY;
|
|
|
|
|
2015-12-16 02:47:50 +08:00
|
|
|
ttm->command = command;
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_init(&ttm->mappings);
|
openflow: Table maintenance commands for Geneve options.
In order to work with Geneve options, we need to maintain a mapping
table between an option (defined by <class, type, length>) and
an NXM field that can be operated on for the purposes of matches,
actions, etc. This mapping must be explicitly specified by the
user.
Conceptually, this table could be communicated using either OpenFlow
or OVSDB. Using OVSDB requires less code and definition of extensions
than OpenFlow but introduces the possibility that mapping table
updates and flow modifications are desynchronized from each other.
This is dangerous because the mapping table signifcantly impacts the
way that flows using Geneve options are installed and processed by
OVS. Therefore, the mapping table is maintained using OpenFlow commands
instead, which opens the possibility of using synchronization between
table changes and flow modifications through barriers, bundles, etc.
There are two primary groups of OpenFlow messages that are introduced
as Nicira extensions: modification commands (add, delete, clear mappings)
and table status request/reply to dump the current table along with switch
information.
Note that mappings should not be changed while they are in active use by
a flow. The result of doing so is undefined.
This only adds the OpenFlow infrastructure but doesn't actually
do anything with the information yet after the messages have been
decoded.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-06-02 15:11:00 -07:00
|
|
|
|
|
|
|
while (*s) {
|
2015-12-16 02:47:50 +08:00
|
|
|
struct ofputil_tlv_map *map = xmalloc(sizeof *map);
|
openflow: Table maintenance commands for Geneve options.
In order to work with Geneve options, we need to maintain a mapping
table between an option (defined by <class, type, length>) and
an NXM field that can be operated on for the purposes of matches,
actions, etc. This mapping must be explicitly specified by the
user.
Conceptually, this table could be communicated using either OpenFlow
or OVSDB. Using OVSDB requires less code and definition of extensions
than OpenFlow but introduces the possibility that mapping table
updates and flow modifications are desynchronized from each other.
This is dangerous because the mapping table signifcantly impacts the
way that flows using Geneve options are installed and processed by
OVS. Therefore, the mapping table is maintained using OpenFlow commands
instead, which opens the possibility of using synchronization between
table changes and flow modifications through barriers, bundles, etc.
There are two primary groups of OpenFlow messages that are introduced
as Nicira extensions: modification commands (add, delete, clear mappings)
and table status request/reply to dump the current table along with switch
information.
Note that mappings should not be changed while they are in active use by
a flow. The result of doing so is undefined.
This only adds the OpenFlow infrastructure but doesn't actually
do anything with the information yet after the messages have been
decoded.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-06-02 15:11:00 -07:00
|
|
|
int n;
|
|
|
|
|
|
|
|
if (*s == ',') {
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
|
2016-03-25 14:10:22 -07:00
|
|
|
ovs_list_push_back(&ttm->mappings, &map->list_node);
|
openflow: Table maintenance commands for Geneve options.
In order to work with Geneve options, we need to maintain a mapping
table between an option (defined by <class, type, length>) and
an NXM field that can be operated on for the purposes of matches,
actions, etc. This mapping must be explicitly specified by the
user.
Conceptually, this table could be communicated using either OpenFlow
or OVSDB. Using OVSDB requires less code and definition of extensions
than OpenFlow but introduces the possibility that mapping table
updates and flow modifications are desynchronized from each other.
This is dangerous because the mapping table signifcantly impacts the
way that flows using Geneve options are installed and processed by
OVS. Therefore, the mapping table is maintained using OpenFlow commands
instead, which opens the possibility of using synchronization between
table changes and flow modifications through barriers, bundles, etc.
There are two primary groups of OpenFlow messages that are introduced
as Nicira extensions: modification commands (add, delete, clear mappings)
and table status request/reply to dump the current table along with switch
information.
Note that mappings should not be changed while they are in active use by
a flow. The result of doing so is undefined.
This only adds the OpenFlow infrastructure but doesn't actually
do anything with the information yet after the messages have been
decoded.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-06-02 15:11:00 -07:00
|
|
|
|
|
|
|
if (!ovs_scan(s, "{class=%"SCNi16",type=%"SCNi8",len=%"SCNi8"}->tun_metadata%"SCNi16"%n",
|
|
|
|
&map->option_class, &map->option_type, &map->option_len,
|
|
|
|
&map->index, &n)) {
|
2015-12-16 02:47:50 +08:00
|
|
|
ofputil_uninit_tlv_table(&ttm->mappings);
|
|
|
|
return xstrdup("invalid tlv mapping");
|
openflow: Table maintenance commands for Geneve options.
In order to work with Geneve options, we need to maintain a mapping
table between an option (defined by <class, type, length>) and
an NXM field that can be operated on for the purposes of matches,
actions, etc. This mapping must be explicitly specified by the
user.
Conceptually, this table could be communicated using either OpenFlow
or OVSDB. Using OVSDB requires less code and definition of extensions
than OpenFlow but introduces the possibility that mapping table
updates and flow modifications are desynchronized from each other.
This is dangerous because the mapping table signifcantly impacts the
way that flows using Geneve options are installed and processed by
OVS. Therefore, the mapping table is maintained using OpenFlow commands
instead, which opens the possibility of using synchronization between
table changes and flow modifications through barriers, bundles, etc.
There are two primary groups of OpenFlow messages that are introduced
as Nicira extensions: modification commands (add, delete, clear mappings)
and table status request/reply to dump the current table along with switch
information.
Note that mappings should not be changed while they are in active use by
a flow. The result of doing so is undefined.
This only adds the OpenFlow infrastructure but doesn't actually
do anything with the information yet after the messages have been
decoded.
Signed-off-by: Jesse Gross <jesse@nicira.com>
Acked-by: Ben Pfaff <blp@nicira.com>
2015-06-02 15:11:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
s += n;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|