2009-07-08 13:19:16 -07:00
|
|
|
|
/*
|
2014-02-04 10:32:35 -08:00
|
|
|
|
* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -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:
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*
|
2009-06-15 15:11:30 -07:00
|
|
|
|
* 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.
|
2009-07-08 13:19:16 -07:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
2012-06-21 10:42:20 -07:00
|
|
|
|
#include <arpa/inet.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "odp-util.h"
|
2011-01-23 18:44:44 -08:00
|
|
|
|
#include <errno.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include <inttypes.h>
|
2011-11-11 15:22:56 -08:00
|
|
|
|
#include <math.h>
|
2011-11-01 13:25:49 +01:00
|
|
|
|
#include <netinet/in.h>
|
2011-02-01 22:54:11 -08:00
|
|
|
|
#include <netinet/icmp6.h>
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
2010-12-10 10:40:58 -08:00
|
|
|
|
#include "byte-order.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "coverage.h"
|
2013-12-30 15:58:58 -08:00
|
|
|
|
#include "dpif.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "dynamic-string.h"
|
|
|
|
|
#include "flow.h"
|
2010-12-10 10:40:58 -08:00
|
|
|
|
#include "netlink.h"
|
2011-08-04 16:20:34 -07:00
|
|
|
|
#include "ofpbuf.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "packets.h"
|
2012-05-22 10:32:02 -07:00
|
|
|
|
#include "simap.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "timeval.h"
|
2014-11-11 11:53:47 -08:00
|
|
|
|
#include "unaligned.h"
|
2009-07-08 13:19:16 -07:00
|
|
|
|
#include "util.h"
|
2014-12-15 14:10:38 +01:00
|
|
|
|
#include "openvswitch/vlog.h"
|
2011-11-14 15:09:01 -08:00
|
|
|
|
|
|
|
|
|
VLOG_DEFINE_THIS_MODULE(odp_util);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
/* The interface between userspace and kernel uses an "OVS_*" prefix.
|
|
|
|
|
* Since this is fairly non-specific for the OVS userspace components,
|
|
|
|
|
* "ODP_*" (Open vSwitch Datapath) is used as the prefix for
|
|
|
|
|
* interactions with the datapath.
|
|
|
|
|
*/
|
|
|
|
|
|
2011-11-11 15:22:56 -08:00
|
|
|
|
/* The set of characters that may separate one action or one key attribute
|
|
|
|
|
* from another. */
|
|
|
|
|
static const char *delimiters = ", \t\r\n";
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
static int parse_odp_key_mask_attr(const char *, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *, struct ofpbuf *);
|
|
|
|
|
static void format_odp_key_attr(const struct nlattr *a,
|
2013-09-23 22:58:46 -07:00
|
|
|
|
const struct nlattr *ma,
|
|
|
|
|
const struct hmap *portno_names, struct ds *ds,
|
2013-08-03 12:23:14 -07:00
|
|
|
|
bool verbose);
|
2011-11-07 09:14:46 -08:00
|
|
|
|
|
2011-10-12 16:24:54 -07:00
|
|
|
|
/* Returns one the following for the action with the given OVS_ACTION_ATTR_*
|
|
|
|
|
* 'type':
|
|
|
|
|
*
|
|
|
|
|
* - For an action whose argument has a fixed length, returned that
|
|
|
|
|
* nonnegative length in bytes.
|
|
|
|
|
*
|
|
|
|
|
* - For an action with a variable-length argument, returns -2.
|
|
|
|
|
*
|
|
|
|
|
* - For an invalid 'type', returns -1. */
|
2011-10-21 14:38:54 -07:00
|
|
|
|
static int
|
2010-12-10 10:40:58 -08:00
|
|
|
|
odp_action_len(uint16_t type)
|
|
|
|
|
{
|
2011-08-18 10:35:40 -07:00
|
|
|
|
if (type > OVS_ACTION_ATTR_MAX) {
|
2010-12-10 10:40:58 -08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-05 09:59:51 -07:00
|
|
|
|
switch ((enum ovs_action_attr) type) {
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t);
|
2014-11-11 11:53:47 -08:00
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_PUSH: return -2;
|
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t);
|
2011-10-12 16:24:54 -07:00
|
|
|
|
case OVS_ACTION_ATTR_USERSPACE: return -2;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
|
|
|
|
|
case OVS_ACTION_ATTR_POP_VLAN: return 0;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
case OVS_ACTION_ATTR_PUSH_MPLS: return sizeof(struct ovs_action_push_mpls);
|
|
|
|
|
case OVS_ACTION_ATTR_POP_MPLS: return sizeof(ovs_be16);
|
2014-04-08 18:42:39 -07:00
|
|
|
|
case OVS_ACTION_ATTR_RECIRC: return sizeof(uint32_t);
|
|
|
|
|
case OVS_ACTION_ATTR_HASH: return sizeof(struct ovs_action_hash);
|
2011-10-21 14:38:54 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SET: return -2;
|
2014-09-05 15:44:19 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SET_MASKED: return -2;
|
2011-10-12 16:24:54 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SAMPLE: return -2;
|
2011-08-18 10:35:40 -07:00
|
|
|
|
|
|
|
|
|
case OVS_ACTION_ATTR_UNSPEC:
|
|
|
|
|
case __OVS_ACTION_ATTR_MAX:
|
2010-12-10 10:40:58 -08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-15 15:40:21 -07:00
|
|
|
|
/* Returns a string form of 'attr'. The return value is either a statically
|
|
|
|
|
* allocated constant string or the 'bufsize'-byte buffer 'namebuf'. 'bufsize'
|
|
|
|
|
* should be at least OVS_KEY_ATTR_BUFSIZE. */
|
|
|
|
|
enum { OVS_KEY_ATTR_BUFSIZE = 3 + INT_STRLEN(unsigned int) + 1 };
|
2011-11-07 13:13:36 -08:00
|
|
|
|
static const char *
|
2013-04-15 15:40:21 -07:00
|
|
|
|
ovs_key_attr_to_string(enum ovs_key_attr attr, char *namebuf, size_t bufsize)
|
2011-11-07 13:13:36 -08:00
|
|
|
|
{
|
|
|
|
|
switch (attr) {
|
|
|
|
|
case OVS_KEY_ATTR_UNSPEC: return "unspec";
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_ENCAP: return "encap";
|
2012-11-29 14:36:49 -08:00
|
|
|
|
case OVS_KEY_ATTR_PRIORITY: return "skb_priority";
|
2012-11-13 19:19:36 +02:00
|
|
|
|
case OVS_KEY_ATTR_SKB_MARK: return "skb_mark";
|
2013-01-18 18:10:59 -08:00
|
|
|
|
case OVS_KEY_ATTR_TUNNEL: return "tunnel";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
case OVS_KEY_ATTR_IN_PORT: return "in_port";
|
|
|
|
|
case OVS_KEY_ATTR_ETHERNET: return "eth";
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_VLAN: return "vlan";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
case OVS_KEY_ATTR_ETHERTYPE: return "eth_type";
|
|
|
|
|
case OVS_KEY_ATTR_IPV4: return "ipv4";
|
|
|
|
|
case OVS_KEY_ATTR_IPV6: return "ipv6";
|
|
|
|
|
case OVS_KEY_ATTR_TCP: return "tcp";
|
2013-10-28 13:54:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_TCP_FLAGS: return "tcp_flags";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
case OVS_KEY_ATTR_UDP: return "udp";
|
2013-08-22 20:24:44 +12:00
|
|
|
|
case OVS_KEY_ATTR_SCTP: return "sctp";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
case OVS_KEY_ATTR_ICMP: return "icmp";
|
|
|
|
|
case OVS_KEY_ATTR_ICMPV6: return "icmpv6";
|
|
|
|
|
case OVS_KEY_ATTR_ARP: return "arp";
|
|
|
|
|
case OVS_KEY_ATTR_ND: return "nd";
|
2013-01-25 16:22:07 +09:00
|
|
|
|
case OVS_KEY_ATTR_MPLS: return "mpls";
|
2014-03-04 15:36:03 -08:00
|
|
|
|
case OVS_KEY_ATTR_DP_HASH: return "dp_hash";
|
|
|
|
|
case OVS_KEY_ATTR_RECIRC_ID: return "recirc_id";
|
2011-11-07 13:13:36 -08:00
|
|
|
|
|
|
|
|
|
case __OVS_KEY_ATTR_MAX:
|
|
|
|
|
default:
|
2013-04-15 15:40:21 -07:00
|
|
|
|
snprintf(namebuf, bufsize, "key%u", (unsigned int) attr);
|
|
|
|
|
return namebuf;
|
2011-11-07 13:13:36 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-12-10 10:40:58 -08:00
|
|
|
|
static void
|
|
|
|
|
format_generic_odp_action(struct ds *ds, const struct nlattr *a)
|
|
|
|
|
{
|
2010-12-23 14:21:01 -08:00
|
|
|
|
size_t len = nl_attr_get_size(a);
|
|
|
|
|
|
2010-12-10 10:40:58 -08:00
|
|
|
|
ds_put_format(ds, "action%"PRId16, nl_attr_type(a));
|
2010-12-23 14:21:01 -08:00
|
|
|
|
if (len) {
|
2010-12-10 10:40:58 -08:00
|
|
|
|
const uint8_t *unspec;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
unspec = nl_attr_get(a);
|
2010-12-23 14:21:01 -08:00
|
|
|
|
for (i = 0; i < len; i++) {
|
2010-12-10 10:40:58 -08:00
|
|
|
|
ds_put_char(ds, i ? ' ': '(');
|
|
|
|
|
ds_put_format(ds, "%02x", unspec[i]);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-28 10:43:07 -07:00
|
|
|
|
static void
|
|
|
|
|
format_odp_sample_action(struct ds *ds, const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
static const struct nl_policy ovs_sample_policy[] = {
|
2014-03-19 17:34:55 -07:00
|
|
|
|
[OVS_SAMPLE_ATTR_PROBABILITY] = { .type = NL_A_U32 },
|
|
|
|
|
[OVS_SAMPLE_ATTR_ACTIONS] = { .type = NL_A_NESTED }
|
2011-09-28 10:43:07 -07:00
|
|
|
|
};
|
|
|
|
|
struct nlattr *a[ARRAY_SIZE(ovs_sample_policy)];
|
|
|
|
|
double percentage;
|
|
|
|
|
const struct nlattr *nla_acts;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(ds, "sample");
|
|
|
|
|
|
2011-10-04 12:58:25 -07:00
|
|
|
|
if (!nl_parse_nested(attr, ovs_sample_policy, a, ARRAY_SIZE(a))) {
|
2011-09-28 10:43:07 -07:00
|
|
|
|
ds_put_cstr(ds, "(error)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
percentage = (100.0 * nl_attr_get_u32(a[OVS_SAMPLE_ATTR_PROBABILITY])) /
|
|
|
|
|
UINT32_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "(sample=%.1f%%,", percentage);
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(ds, "actions(");
|
|
|
|
|
nla_acts = nl_attr_get(a[OVS_SAMPLE_ATTR_ACTIONS]);
|
|
|
|
|
len = nl_attr_get_size(a[OVS_SAMPLE_ATTR_ACTIONS]);
|
|
|
|
|
format_odp_actions(ds, nla_acts, len);
|
|
|
|
|
ds_put_format(ds, "))");
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-04 14:52:36 -07:00
|
|
|
|
static const char *
|
2013-09-20 12:54:51 -07:00
|
|
|
|
slow_path_reason_to_string(uint32_t reason)
|
2012-05-04 14:52:36 -07:00
|
|
|
|
{
|
2013-09-20 12:54:51 -07:00
|
|
|
|
switch ((enum slow_path_reason) reason) {
|
|
|
|
|
#define SPR(ENUM, STRING, EXPLANATION) case ENUM: return STRING;
|
|
|
|
|
SLOW_PATH_REASONS
|
|
|
|
|
#undef SPR
|
2012-05-04 14:52:36 -07:00
|
|
|
|
}
|
2013-09-20 12:54:51 -07:00
|
|
|
|
|
|
|
|
|
return NULL;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-09-20 12:54:51 -07:00
|
|
|
|
const char *
|
|
|
|
|
slow_path_reason_to_explanation(enum slow_path_reason reason)
|
2013-05-28 11:43:43 -07:00
|
|
|
|
{
|
2013-09-20 12:54:51 -07:00
|
|
|
|
switch (reason) {
|
|
|
|
|
#define SPR(ENUM, STRING, EXPLANATION) case ENUM: return EXPLANATION;
|
|
|
|
|
SLOW_PATH_REASONS
|
|
|
|
|
#undef SPR
|
2013-05-28 11:43:43 -07:00
|
|
|
|
}
|
|
|
|
|
|
2013-09-20 12:54:51 -07:00
|
|
|
|
return "<unknown>";
|
2013-05-28 11:43:43 -07:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-14 21:10:22 -08:00
|
|
|
|
static int
|
|
|
|
|
parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
|
2014-09-05 15:44:20 -07:00
|
|
|
|
uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask)
|
2012-11-14 21:10:22 -08:00
|
|
|
|
{
|
|
|
|
|
uint32_t result = 0;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
int n;
|
2012-11-14 21:10:22 -08:00
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
/* Parse masked flags in numeric format? */
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (res_mask && ovs_scan(s, "%"SCNi32"/%"SCNi32"%n",
|
2014-09-05 15:44:20 -07:00
|
|
|
|
res_flags, res_mask, &n) && n > 0) {
|
|
|
|
|
if (*res_flags & ~allowed || *res_mask & ~allowed) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
n = 0;
|
2012-11-14 21:10:22 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (res_mask && (*s == '+' || *s == '-')) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
uint32_t flags = 0, mask = 0;
|
|
|
|
|
|
|
|
|
|
/* Parse masked flags. */
|
|
|
|
|
while (s[n] != ')') {
|
|
|
|
|
bool set;
|
|
|
|
|
uint32_t bit;
|
|
|
|
|
int name_len;
|
|
|
|
|
|
|
|
|
|
if (s[n] == '+') {
|
|
|
|
|
set = true;
|
|
|
|
|
} else if (s[n] == '-') {
|
|
|
|
|
set = false;
|
|
|
|
|
} else {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
n++;
|
|
|
|
|
|
|
|
|
|
name_len = strcspn(s + n, "+-)");
|
|
|
|
|
|
|
|
|
|
for (bit = 1; bit; bit <<= 1) {
|
|
|
|
|
const char *fname = bit_to_string(bit);
|
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
|
|
if (!fname) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = strlen(fname);
|
|
|
|
|
if (len != name_len) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!strncmp(s + n, fname, len)) {
|
|
|
|
|
if (mask & bit) {
|
|
|
|
|
/* bit already set. */
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (!(bit & allowed)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (set) {
|
|
|
|
|
flags |= bit;
|
|
|
|
|
}
|
|
|
|
|
mask |= bit;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bit) {
|
|
|
|
|
return -EINVAL; /* Unknown flag name */
|
|
|
|
|
}
|
|
|
|
|
s += name_len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*res_flags = flags;
|
|
|
|
|
*res_mask = mask;
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parse unmasked flags. If a flag is present, it is set, otherwise
|
|
|
|
|
* it is not set. */
|
2012-11-14 21:10:22 -08:00
|
|
|
|
while (s[n] != ')') {
|
|
|
|
|
unsigned long long int flags;
|
|
|
|
|
uint32_t bit;
|
|
|
|
|
int n0;
|
|
|
|
|
|
2013-11-09 15:44:23 -08:00
|
|
|
|
if (ovs_scan(&s[n], "%lli%n", &flags, &n0)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
if (flags & ~allowed) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2012-11-14 21:10:22 -08:00
|
|
|
|
n += n0 + (s[n + n0] == ',');
|
|
|
|
|
result |= flags;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (bit = 1; bit; bit <<= 1) {
|
|
|
|
|
const char *name = bit_to_string(bit);
|
|
|
|
|
size_t len;
|
|
|
|
|
|
|
|
|
|
if (!name) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = strlen(name);
|
|
|
|
|
if (!strncmp(s + n, name, len) &&
|
|
|
|
|
(s[n + len] == ',' || s[n + len] == ')')) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
if (!(bit & allowed)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2012-11-14 21:10:22 -08:00
|
|
|
|
result |= bit;
|
|
|
|
|
n += len + (s[n + len] == ',');
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!bit) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
*res_flags = result;
|
|
|
|
|
if (res_mask) {
|
|
|
|
|
*res_mask = UINT32_MAX;
|
|
|
|
|
}
|
2012-11-14 21:10:22 -08:00
|
|
|
|
return n;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:24:54 -07:00
|
|
|
|
static void
|
|
|
|
|
format_odp_userspace_action(struct ds *ds, const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
static const struct nl_policy ovs_userspace_policy[] = {
|
2014-03-19 17:34:55 -07:00
|
|
|
|
[OVS_USERSPACE_ATTR_PID] = { .type = NL_A_U32 },
|
|
|
|
|
[OVS_USERSPACE_ATTR_USERDATA] = { .type = NL_A_UNSPEC,
|
|
|
|
|
.optional = true },
|
2014-08-17 20:19:36 -07:00
|
|
|
|
[OVS_USERSPACE_ATTR_EGRESS_TUN_PORT] = { .type = NL_A_U32,
|
|
|
|
|
.optional = true },
|
2011-10-12 16:24:54 -07:00
|
|
|
|
};
|
|
|
|
|
struct nlattr *a[ARRAY_SIZE(ovs_userspace_policy)];
|
2013-02-15 16:48:32 -08:00
|
|
|
|
const struct nlattr *userdata_attr;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
const struct nlattr *tunnel_out_port_attr;
|
2011-10-12 16:24:54 -07:00
|
|
|
|
|
|
|
|
|
if (!nl_parse_nested(attr, ovs_userspace_policy, a, ARRAY_SIZE(a))) {
|
|
|
|
|
ds_put_cstr(ds, "userspace(error)");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "userspace(pid=%"PRIu32,
|
|
|
|
|
nl_attr_get_u32(a[OVS_USERSPACE_ATTR_PID]));
|
|
|
|
|
|
2013-02-15 16:48:32 -08:00
|
|
|
|
userdata_attr = a[OVS_USERSPACE_ATTR_USERDATA];
|
2013-04-22 10:01:14 -07:00
|
|
|
|
|
|
|
|
|
if (userdata_attr) {
|
|
|
|
|
const uint8_t *userdata = nl_attr_get(userdata_attr);
|
|
|
|
|
size_t userdata_len = nl_attr_get_size(userdata_attr);
|
|
|
|
|
bool userdata_unspec = true;
|
2012-05-04 14:56:40 -07:00
|
|
|
|
union user_action_cookie cookie;
|
2011-10-12 16:24:54 -07:00
|
|
|
|
|
2013-04-22 10:01:14 -07:00
|
|
|
|
if (userdata_len >= sizeof cookie.type
|
|
|
|
|
&& userdata_len <= sizeof cookie) {
|
|
|
|
|
|
|
|
|
|
memset(&cookie, 0, sizeof cookie);
|
|
|
|
|
memcpy(&cookie, userdata, userdata_len);
|
|
|
|
|
|
|
|
|
|
userdata_unspec = false;
|
|
|
|
|
|
|
|
|
|
if (userdata_len == sizeof cookie.sflow
|
|
|
|
|
&& cookie.type == USER_ACTION_COOKIE_SFLOW) {
|
|
|
|
|
ds_put_format(ds, ",sFlow("
|
|
|
|
|
"vid=%"PRIu16",pcp=%"PRIu8",output=%"PRIu32")",
|
|
|
|
|
vlan_tci_to_vid(cookie.sflow.vlan_tci),
|
|
|
|
|
vlan_tci_to_pcp(cookie.sflow.vlan_tci),
|
|
|
|
|
cookie.sflow.output);
|
|
|
|
|
} else if (userdata_len == sizeof cookie.slow_path
|
|
|
|
|
&& cookie.type == USER_ACTION_COOKIE_SLOW_PATH) {
|
2013-09-20 12:54:51 -07:00
|
|
|
|
ds_put_cstr(ds, ",slow_path(");
|
|
|
|
|
format_flags(ds, slow_path_reason_to_string,
|
|
|
|
|
cookie.slow_path.reason, ',');
|
|
|
|
|
ds_put_format(ds, ")");
|
2013-04-22 10:01:14 -07:00
|
|
|
|
} else if (userdata_len == sizeof cookie.flow_sample
|
|
|
|
|
&& cookie.type == USER_ACTION_COOKIE_FLOW_SAMPLE) {
|
|
|
|
|
ds_put_format(ds, ",flow_sample(probability=%"PRIu16
|
|
|
|
|
",collector_set_id=%"PRIu32
|
|
|
|
|
",obs_domain_id=%"PRIu32
|
|
|
|
|
",obs_point_id=%"PRIu32")",
|
|
|
|
|
cookie.flow_sample.probability,
|
|
|
|
|
cookie.flow_sample.collector_set_id,
|
|
|
|
|
cookie.flow_sample.obs_domain_id,
|
|
|
|
|
cookie.flow_sample.obs_point_id);
|
2013-11-11 13:32:23 -08:00
|
|
|
|
} else if (userdata_len >= sizeof cookie.ipfix
|
2013-04-22 10:01:14 -07:00
|
|
|
|
&& cookie.type == USER_ACTION_COOKIE_IPFIX) {
|
2014-08-17 20:19:36 -07:00
|
|
|
|
ds_put_format(ds, ",ipfix(output_port=%"PRIu32")",
|
|
|
|
|
cookie.ipfix.output_odp_port);
|
2013-04-22 10:01:14 -07:00
|
|
|
|
} else {
|
|
|
|
|
userdata_unspec = true;
|
|
|
|
|
}
|
2011-10-12 16:24:54 -07:00
|
|
|
|
}
|
2013-02-15 16:48:32 -08:00
|
|
|
|
|
2013-04-22 10:01:14 -07:00
|
|
|
|
if (userdata_unspec) {
|
|
|
|
|
size_t i;
|
|
|
|
|
ds_put_format(ds, ",userdata(");
|
|
|
|
|
for (i = 0; i < userdata_len; i++) {
|
|
|
|
|
ds_put_format(ds, "%02x", userdata[i]);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
2013-02-15 16:48:32 -08:00
|
|
|
|
}
|
2011-10-12 16:24:54 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
tunnel_out_port_attr = a[OVS_USERSPACE_ATTR_EGRESS_TUN_PORT];
|
|
|
|
|
if (tunnel_out_port_attr) {
|
|
|
|
|
ds_put_format(ds, ",tunnel_out_port=%"PRIu32,
|
|
|
|
|
nl_attr_get_u32(tunnel_out_port_attr));
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-12 16:24:54 -07:00
|
|
|
|
ds_put_char(ds, ')');
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-14 17:19:41 -08:00
|
|
|
|
static void
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_vlan_tci(struct ds *ds, ovs_be16 tci, ovs_be16 mask, bool verbose)
|
2011-11-14 17:19:41 -08:00
|
|
|
|
{
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (verbose || vlan_tci_to_vid(tci) || vlan_tci_to_vid(mask)) {
|
|
|
|
|
ds_put_format(ds, "vid=%"PRIu16, vlan_tci_to_vid(tci));
|
|
|
|
|
if (vlan_tci_to_vid(mask) != VLAN_VID_MASK) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/0x%"PRIx16, vlan_tci_to_vid(mask));
|
|
|
|
|
};
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
if (verbose || vlan_tci_to_pcp(tci) || vlan_tci_to_pcp(mask)) {
|
|
|
|
|
ds_put_format(ds, "pcp=%d", vlan_tci_to_pcp(tci));
|
|
|
|
|
if (vlan_tci_to_pcp(mask) != (VLAN_PCP_MASK >> VLAN_PCP_SHIFT)) {
|
|
|
|
|
ds_put_format(ds, "/0x%x", vlan_tci_to_pcp(mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
if (!(tci & htons(VLAN_CFI))) {
|
|
|
|
|
ds_put_cstr(ds, "cfi=0");
|
|
|
|
|
ds_put_char(ds, ',');
|
2011-11-14 17:19:41 -08:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
ds_chomp(ds, ',');
|
2011-11-14 17:19:41 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-25 16:22:07 +09:00
|
|
|
|
static void
|
|
|
|
|
format_mpls_lse(struct ds *ds, ovs_be32 mpls_lse)
|
|
|
|
|
{
|
|
|
|
|
ds_put_format(ds, "label=%"PRIu32",tc=%d,ttl=%d,bos=%d",
|
|
|
|
|
mpls_lse_to_label(mpls_lse),
|
|
|
|
|
mpls_lse_to_tc(mpls_lse),
|
|
|
|
|
mpls_lse_to_ttl(mpls_lse),
|
|
|
|
|
mpls_lse_to_bos(mpls_lse));
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
static void
|
|
|
|
|
format_mpls(struct ds *ds, const struct ovs_key_mpls *mpls_key,
|
2014-02-04 10:32:35 -08:00
|
|
|
|
const struct ovs_key_mpls *mpls_mask, int n)
|
2013-06-19 07:15:10 +00:00
|
|
|
|
{
|
2014-02-04 10:32:35 -08:00
|
|
|
|
if (n == 1) {
|
|
|
|
|
ovs_be32 key = mpls_key->mpls_lse;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
if (mpls_mask == NULL) {
|
|
|
|
|
format_mpls_lse(ds, key);
|
|
|
|
|
} else {
|
|
|
|
|
ovs_be32 mask = mpls_mask->mpls_lse;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "label=%"PRIu32"/0x%x,tc=%d/%x,ttl=%d/0x%x,bos=%d/%x",
|
|
|
|
|
mpls_lse_to_label(key), mpls_lse_to_label(mask),
|
|
|
|
|
mpls_lse_to_tc(key), mpls_lse_to_tc(mask),
|
|
|
|
|
mpls_lse_to_ttl(key), mpls_lse_to_ttl(mask),
|
|
|
|
|
mpls_lse_to_bos(key), mpls_lse_to_bos(mask));
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
} else {
|
2014-02-04 10:32:35 -08:00
|
|
|
|
int i;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
ds_put_format(ds, "lse%d=%#"PRIx32,
|
|
|
|
|
i, ntohl(mpls_key[i].mpls_lse));
|
|
|
|
|
if (mpls_mask) {
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx32, ntohl(mpls_mask[i].mpls_lse));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
ds_chomp(ds, ',');
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-04 15:36:03 -08:00
|
|
|
|
static void
|
2014-04-08 18:42:39 -07:00
|
|
|
|
format_odp_recirc_action(struct ds *ds, uint32_t recirc_id)
|
2014-03-04 15:36:03 -08:00
|
|
|
|
{
|
2014-04-08 18:42:39 -07:00
|
|
|
|
ds_put_format(ds, "recirc(%"PRIu32")", recirc_id);
|
|
|
|
|
}
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-04-08 18:42:39 -07:00
|
|
|
|
static void
|
|
|
|
|
format_odp_hash_action(struct ds *ds, const struct ovs_action_hash *hash_act)
|
|
|
|
|
{
|
|
|
|
|
ds_put_format(ds, "hash(");
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-04-08 18:42:39 -07:00
|
|
|
|
if (hash_act->hash_alg == OVS_HASH_ALG_L4) {
|
2014-04-17 20:06:58 -07:00
|
|
|
|
ds_put_format(ds, "hash_l4(%"PRIu32")", hash_act->hash_basis);
|
2014-04-08 18:42:39 -07:00
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "Unknown hash algorithm(%"PRIu32")",
|
|
|
|
|
hash_act->hash_alg);
|
|
|
|
|
}
|
|
|
|
|
ds_put_format(ds, ")");
|
2014-03-04 15:36:03 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-11 11:53:47 -08:00
|
|
|
|
static void
|
|
|
|
|
format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
|
|
|
|
|
{
|
|
|
|
|
const struct eth_header *eth;
|
|
|
|
|
const struct ip_header *ip;
|
|
|
|
|
const void *l3;
|
|
|
|
|
|
|
|
|
|
eth = (const struct eth_header *)data->header;
|
|
|
|
|
|
|
|
|
|
l3 = eth + 1;
|
|
|
|
|
ip = (const struct ip_header *)l3;
|
|
|
|
|
|
|
|
|
|
/* Ethernet */
|
|
|
|
|
ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
|
|
|
|
|
data->header_len, data->tnl_type);
|
|
|
|
|
ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
|
|
|
|
|
ds_put_format(ds, ",src=");
|
|
|
|
|
ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
|
|
|
|
|
ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));
|
|
|
|
|
|
|
|
|
|
/* IPv4 */
|
|
|
|
|
ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
|
|
|
|
|
",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
|
|
|
|
|
IP_ARGS(get_16aligned_be32(&ip->ip_src)),
|
|
|
|
|
IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
|
|
|
|
|
ip->ip_proto, ip->ip_tos,
|
|
|
|
|
ip->ip_ttl,
|
|
|
|
|
ip->ip_frag_off);
|
|
|
|
|
|
|
|
|
|
if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
|
|
|
|
|
const struct vxlanhdr *vxh;
|
|
|
|
|
const struct udp_header *udp;
|
|
|
|
|
|
|
|
|
|
/* UDP */
|
|
|
|
|
udp = (const struct udp_header *) (ip + 1);
|
|
|
|
|
ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16"),",
|
|
|
|
|
ntohs(udp->udp_src), ntohs(udp->udp_dst));
|
|
|
|
|
|
|
|
|
|
/* VxLan */
|
|
|
|
|
vxh = (const struct vxlanhdr *) (udp + 1);
|
|
|
|
|
ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
|
|
|
|
|
ntohl(get_16aligned_be32(&vxh->vx_flags)),
|
|
|
|
|
ntohl(get_16aligned_be32(&vxh->vx_vni)));
|
|
|
|
|
} else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
|
|
|
|
|
const struct gre_base_hdr *greh;
|
|
|
|
|
ovs_16aligned_be32 *options;
|
|
|
|
|
void *l4;
|
|
|
|
|
|
|
|
|
|
l4 = ((uint8_t *)l3 + sizeof(struct ip_header));
|
|
|
|
|
greh = (const struct gre_base_hdr *) l4;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "gre((flags=0x%"PRIx16",proto=0x%"PRIx16")",
|
|
|
|
|
greh->flags, ntohs(greh->protocol));
|
|
|
|
|
options = (ovs_16aligned_be32 *)(greh + 1);
|
|
|
|
|
if (greh->flags & htons(GRE_CSUM)) {
|
|
|
|
|
ds_put_format(ds, ",csum=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
if (greh->flags & htons(GRE_KEY)) {
|
|
|
|
|
ds_put_format(ds, ",key=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
if (greh->flags & htons(GRE_SEQ)) {
|
|
|
|
|
ds_put_format(ds, ",seq=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
ds_put_format(ds, ")");
|
|
|
|
|
}
|
|
|
|
|
ds_put_format(ds, ")");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_action_push_tnl *data;
|
|
|
|
|
|
|
|
|
|
data = (struct ovs_action_push_tnl *) nl_attr_get(attr);
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "tnl_push(tnl_port(%"PRIu32"),", data->tnl_port);
|
|
|
|
|
format_odp_tnl_push_header(ds, data);
|
|
|
|
|
ds_put_format(ds, ",out_port(%"PRIu32"))", data->out_port);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-21 14:38:54 -07:00
|
|
|
|
static void
|
2010-12-10 10:40:58 -08:00
|
|
|
|
format_odp_action(struct ds *ds, const struct nlattr *a)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2011-10-12 16:24:54 -07:00
|
|
|
|
int expected_len;
|
2011-10-21 14:38:54 -07:00
|
|
|
|
enum ovs_action_attr type = nl_attr_type(a);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
const struct ovs_action_push_vlan *vlan;
|
2014-09-05 15:44:19 -07:00
|
|
|
|
size_t size;
|
2010-12-10 10:40:58 -08:00
|
|
|
|
|
2011-10-12 16:24:54 -07:00
|
|
|
|
expected_len = odp_action_len(nl_attr_type(a));
|
|
|
|
|
if (expected_len != -2 && nl_attr_get_size(a) != expected_len) {
|
2013-11-25 23:38:48 -08:00
|
|
|
|
ds_put_format(ds, "bad length %"PRIuSIZE", expected %d for: ",
|
2011-10-12 16:24:54 -07:00
|
|
|
|
nl_attr_get_size(a), expected_len);
|
2010-12-10 10:40:58 -08:00
|
|
|
|
format_generic_odp_action(ds, a);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-21 14:38:54 -07:00
|
|
|
|
switch (type) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_ACTION_ATTR_OUTPUT:
|
2013-06-14 17:09:34 +03:00
|
|
|
|
ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
2014-11-11 11:53:47 -08:00
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_POP:
|
|
|
|
|
ds_put_format(ds, "tnl_pop(%"PRIu32")", nl_attr_get_u32(a));
|
|
|
|
|
break;
|
|
|
|
|
case OVS_ACTION_ATTR_TUNNEL_PUSH:
|
|
|
|
|
format_odp_tnl_push_action(ds, a);
|
|
|
|
|
break;
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_ACTION_ATTR_USERSPACE:
|
2011-10-12 16:24:54 -07:00
|
|
|
|
format_odp_userspace_action(ds, a);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
2014-03-04 15:36:03 -08:00
|
|
|
|
case OVS_ACTION_ATTR_RECIRC:
|
2014-04-08 18:42:39 -07:00
|
|
|
|
format_odp_recirc_action(ds, nl_attr_get_u32(a));
|
|
|
|
|
break;
|
|
|
|
|
case OVS_ACTION_ATTR_HASH:
|
|
|
|
|
format_odp_hash_action(ds, nl_attr_get(a));
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
2014-09-05 15:44:19 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SET_MASKED:
|
|
|
|
|
a = nl_attr_get(a);
|
|
|
|
|
size = nl_attr_get_size(a) / 2;
|
|
|
|
|
ds_put_cstr(ds, "set(");
|
|
|
|
|
|
|
|
|
|
/* Masked set action not supported for tunnel key, which is bigger. */
|
|
|
|
|
if (size <= sizeof(struct ovs_key_ipv6)) {
|
|
|
|
|
struct nlattr attr[1 + DIV_ROUND_UP(sizeof(struct ovs_key_ipv6),
|
|
|
|
|
sizeof(struct nlattr))];
|
|
|
|
|
struct nlattr mask[1 + DIV_ROUND_UP(sizeof(struct ovs_key_ipv6),
|
|
|
|
|
sizeof(struct nlattr))];
|
|
|
|
|
|
|
|
|
|
mask->nla_type = attr->nla_type = nl_attr_type(a);
|
|
|
|
|
mask->nla_len = attr->nla_len = NLA_HDRLEN + size;
|
|
|
|
|
memcpy(attr + 1, (char *)(a + 1), size);
|
|
|
|
|
memcpy(mask + 1, (char *)(a + 1) + size, size);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_odp_key_attr(attr, mask, NULL, ds, false);
|
2014-09-05 15:44:19 -07:00
|
|
|
|
} else {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_odp_key_attr(a, NULL, NULL, ds, false);
|
2014-09-05 15:44:19 -07:00
|
|
|
|
}
|
|
|
|
|
ds_put_cstr(ds, ")");
|
|
|
|
|
break;
|
2011-10-21 14:38:54 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SET:
|
|
|
|
|
ds_put_cstr(ds, "set(");
|
2013-09-23 22:58:46 -07:00
|
|
|
|
format_odp_key_attr(nl_attr_get(a), NULL, NULL, ds, true);
|
2011-10-21 14:38:54 -07:00
|
|
|
|
ds_put_cstr(ds, ")");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_ACTION_ATTR_PUSH_VLAN:
|
|
|
|
|
vlan = nl_attr_get(a);
|
|
|
|
|
ds_put_cstr(ds, "push_vlan(");
|
|
|
|
|
if (vlan->vlan_tpid != htons(ETH_TYPE_VLAN)) {
|
|
|
|
|
ds_put_format(ds, "tpid=0x%04"PRIx16",", ntohs(vlan->vlan_tpid));
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_vlan_tci(ds, vlan->vlan_tci, OVS_BE16_MAX, false);
|
2011-11-14 17:19:41 -08:00
|
|
|
|
ds_put_char(ds, ')');
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_ACTION_ATTR_POP_VLAN:
|
|
|
|
|
ds_put_cstr(ds, "pop_vlan");
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
case OVS_ACTION_ATTR_PUSH_MPLS: {
|
|
|
|
|
const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
|
|
|
|
|
ds_put_cstr(ds, "push_mpls(");
|
|
|
|
|
format_mpls_lse(ds, mpls->mpls_lse);
|
|
|
|
|
ds_put_format(ds, ",eth_type=0x%"PRIx16")", ntohs(mpls->mpls_ethertype));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case OVS_ACTION_ATTR_POP_MPLS: {
|
|
|
|
|
ovs_be16 ethertype = nl_attr_get_be16(a);
|
|
|
|
|
ds_put_format(ds, "pop_mpls(eth_type=0x%"PRIx16")", ntohs(ethertype));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-09-28 10:43:07 -07:00
|
|
|
|
case OVS_ACTION_ATTR_SAMPLE:
|
|
|
|
|
format_odp_sample_action(ds, a);
|
|
|
|
|
break;
|
2011-10-21 14:38:54 -07:00
|
|
|
|
case OVS_ACTION_ATTR_UNSPEC:
|
|
|
|
|
case __OVS_ACTION_ATTR_MAX:
|
2009-07-08 13:19:16 -07:00
|
|
|
|
default:
|
2010-12-10 10:40:58 -08:00
|
|
|
|
format_generic_odp_action(ds, a);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
2010-12-10 10:40:58 -08:00
|
|
|
|
format_odp_actions(struct ds *ds, const struct nlattr *actions,
|
2010-12-11 22:51:31 -08:00
|
|
|
|
size_t actions_len)
|
2009-07-08 13:19:16 -07:00
|
|
|
|
{
|
2010-12-10 10:40:58 -08:00
|
|
|
|
if (actions_len) {
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
unsigned int left;
|
|
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (a, left, actions, actions_len) {
|
|
|
|
|
if (a != actions) {
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
format_odp_action(ds, a);
|
2009-07-08 13:19:16 -07:00
|
|
|
|
}
|
2010-12-10 10:40:58 -08:00
|
|
|
|
if (left) {
|
2012-01-26 16:21:50 -08:00
|
|
|
|
int i;
|
|
|
|
|
|
2011-01-11 15:35:56 -08:00
|
|
|
|
if (left == actions_len) {
|
|
|
|
|
ds_put_cstr(ds, "<empty>");
|
|
|
|
|
}
|
2012-01-26 16:21:50 -08:00
|
|
|
|
ds_put_format(ds, ",***%u leftover bytes*** (", left);
|
|
|
|
|
for (i = 0; i < left; i++) {
|
|
|
|
|
ds_put_format(ds, "%02x", ((const uint8_t *) a)[i]);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
2010-12-10 10:40:58 -08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2009-07-08 13:19:16 -07:00
|
|
|
|
ds_put_cstr(ds, "drop");
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-11 15:22:56 -08:00
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
/* Separate out parse_odp_userspace_action() function. */
|
2011-11-11 15:22:56 -08:00
|
|
|
|
static int
|
2014-08-17 20:19:36 -07:00
|
|
|
|
parse_odp_userspace_action(const char *s, struct ofpbuf *actions)
|
2011-11-11 15:22:56 -08:00
|
|
|
|
{
|
2014-08-17 20:19:36 -07:00
|
|
|
|
uint32_t pid;
|
|
|
|
|
union user_action_cookie cookie;
|
|
|
|
|
struct ofpbuf buf;
|
|
|
|
|
odp_port_t tunnel_out_port;
|
|
|
|
|
int n = -1;
|
|
|
|
|
void *user_data = NULL;
|
|
|
|
|
size_t user_data_size = 0;
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan(s, "userspace(pid=%"SCNi32"%n", &pid, &n)) {
|
|
|
|
|
return -EINVAL;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
2013-11-09 15:44:23 -08:00
|
|
|
|
uint32_t output;
|
|
|
|
|
uint32_t probability;
|
|
|
|
|
uint32_t collector_set_id;
|
|
|
|
|
uint32_t obs_domain_id;
|
|
|
|
|
uint32_t obs_point_id;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
int vid, pcp;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
int n1 = -1;
|
|
|
|
|
if (ovs_scan(&s[n], ",sFlow(vid=%i,"
|
|
|
|
|
"pcp=%i,output=%"SCNi32")%n",
|
|
|
|
|
&vid, &pcp, &output, &n1)) {
|
2011-11-11 15:22:56 -08:00
|
|
|
|
uint16_t tci;
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
n += n1;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
tci = vid | (pcp << VLAN_PCP_SHIFT);
|
|
|
|
|
if (tci) {
|
|
|
|
|
tci |= VLAN_CFI;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cookie.type = USER_ACTION_COOKIE_SFLOW;
|
2012-05-04 14:56:40 -07:00
|
|
|
|
cookie.sflow.vlan_tci = htons(tci);
|
|
|
|
|
cookie.sflow.output = output;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
user_data = &cookie;
|
|
|
|
|
user_data_size = sizeof cookie.sflow;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
} else if (ovs_scan(&s[n], ",slow_path(%n",
|
2014-08-17 20:19:36 -07:00
|
|
|
|
&n1)) {
|
2013-09-20 12:54:51 -07:00
|
|
|
|
int res;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
n += n1;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
cookie.type = USER_ACTION_COOKIE_SLOW_PATH;
|
|
|
|
|
cookie.slow_path.unused = 0;
|
2013-09-20 12:54:51 -07:00
|
|
|
|
cookie.slow_path.reason = 0;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
|
2013-09-20 12:54:51 -07:00
|
|
|
|
res = parse_flags(&s[n], slow_path_reason_to_string,
|
2014-09-05 15:44:20 -07:00
|
|
|
|
&cookie.slow_path.reason,
|
|
|
|
|
SLOW_PATH_REASON_MASK, NULL);
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (res < 0 || s[n + res] != ')') {
|
2013-09-20 12:54:51 -07:00
|
|
|
|
return res;
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
n += res + 1;
|
2012-05-04 14:52:36 -07:00
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
user_data = &cookie;
|
|
|
|
|
user_data_size = sizeof cookie.slow_path;
|
|
|
|
|
} else if (ovs_scan(&s[n], ",flow_sample(probability=%"SCNi32","
|
2013-11-09 15:44:23 -08:00
|
|
|
|
"collector_set_id=%"SCNi32","
|
|
|
|
|
"obs_domain_id=%"SCNi32","
|
2014-08-17 20:19:36 -07:00
|
|
|
|
"obs_point_id=%"SCNi32")%n",
|
|
|
|
|
&probability, &collector_set_id,
|
|
|
|
|
&obs_domain_id, &obs_point_id, &n1)) {
|
|
|
|
|
n += n1;
|
2013-04-22 10:01:14 -07:00
|
|
|
|
|
|
|
|
|
cookie.type = USER_ACTION_COOKIE_FLOW_SAMPLE;
|
|
|
|
|
cookie.flow_sample.probability = probability;
|
|
|
|
|
cookie.flow_sample.collector_set_id = collector_set_id;
|
|
|
|
|
cookie.flow_sample.obs_domain_id = obs_domain_id;
|
|
|
|
|
cookie.flow_sample.obs_point_id = obs_point_id;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
user_data = &cookie;
|
|
|
|
|
user_data_size = sizeof cookie.flow_sample;
|
|
|
|
|
} else if (ovs_scan(&s[n], ",ipfix(output_port=%"SCNi32")%n",
|
|
|
|
|
&output, &n1) ) {
|
|
|
|
|
n += n1;
|
2013-04-22 10:01:14 -07:00
|
|
|
|
cookie.type = USER_ACTION_COOKIE_IPFIX;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
cookie.ipfix.output_odp_port = u32_to_odp(output);
|
|
|
|
|
user_data = &cookie;
|
|
|
|
|
user_data_size = sizeof cookie.ipfix;
|
|
|
|
|
} else if (ovs_scan(&s[n], ",userdata(%n",
|
|
|
|
|
&n1)) {
|
2013-02-15 16:48:32 -08:00
|
|
|
|
char *end;
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
n += n1;
|
2013-02-15 16:48:32 -08:00
|
|
|
|
ofpbuf_init(&buf, 16);
|
|
|
|
|
end = ofpbuf_put_hex(&buf, &s[n], NULL);
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (end[0] != ')') {
|
|
|
|
|
return -EINVAL;
|
2013-02-15 16:48:32 -08:00
|
|
|
|
}
|
2014-08-17 20:19:36 -07:00
|
|
|
|
user_data = ofpbuf_data(&buf);
|
|
|
|
|
user_data_size = ofpbuf_size(&buf);
|
|
|
|
|
n = (end + 1) - s;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
int n1 = -1;
|
|
|
|
|
if (ovs_scan(&s[n], ",tunnel_out_port=%"SCNi32")%n",
|
|
|
|
|
&tunnel_out_port, &n1)) {
|
|
|
|
|
odp_put_userspace_action(pid, user_data, user_data_size, tunnel_out_port, actions);
|
|
|
|
|
return n + n1;
|
|
|
|
|
} else if (s[n] == ')') {
|
|
|
|
|
odp_put_userspace_action(pid, user_data, user_data_size, ODPP_NONE, actions);
|
|
|
|
|
return n + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-11 11:53:47 -08:00
|
|
|
|
static int
|
|
|
|
|
ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
|
|
|
|
|
{
|
|
|
|
|
struct eth_header *eth;
|
|
|
|
|
struct ip_header *ip;
|
|
|
|
|
struct udp_header *udp;
|
|
|
|
|
struct gre_base_hdr *greh;
|
|
|
|
|
uint16_t gre_proto, dl_type, udp_src, udp_dst;
|
|
|
|
|
ovs_be32 sip, dip;
|
|
|
|
|
uint32_t tnl_type = 0, header_len = 0;
|
|
|
|
|
void *l3, *l4;
|
|
|
|
|
int n = 0;
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, "tnl_push(tnl_port(%"SCNi32"),", &data->tnl_port)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
eth = (struct eth_header *) data->header;
|
|
|
|
|
l3 = (data->header + sizeof *eth);
|
|
|
|
|
l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
|
|
|
|
|
ip = (struct ip_header *) l3;
|
|
|
|
|
if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
|
|
|
|
|
"eth(dst="ETH_ADDR_SCAN_FMT",",
|
|
|
|
|
&data->header_len,
|
|
|
|
|
&data->tnl_type,
|
|
|
|
|
ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
|
|
|
|
|
ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
eth->eth_type = htons(dl_type);
|
|
|
|
|
|
|
|
|
|
/* IPv4 */
|
|
|
|
|
if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
|
|
|
|
|
",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
|
|
|
|
|
IP_SCAN_ARGS(&sip),
|
|
|
|
|
IP_SCAN_ARGS(&dip),
|
|
|
|
|
&ip->ip_proto, &ip->ip_tos,
|
|
|
|
|
&ip->ip_ttl, &ip->ip_frag_off)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
put_16aligned_be32(&ip->ip_src, sip);
|
|
|
|
|
put_16aligned_be32(&ip->ip_dst, dip);
|
|
|
|
|
|
|
|
|
|
/* Tunnel header */
|
|
|
|
|
udp = (struct udp_header *) l4;
|
|
|
|
|
greh = (struct gre_base_hdr *) l4;
|
|
|
|
|
if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16"),",
|
|
|
|
|
&udp_src, &udp_dst)) {
|
|
|
|
|
struct vxlanhdr *vxh;
|
|
|
|
|
uint32_t vx_flags, vx_vni;
|
|
|
|
|
|
|
|
|
|
udp->udp_src = htons(udp_src);
|
|
|
|
|
udp->udp_dst = htons(udp_dst);
|
|
|
|
|
udp->udp_len = 0;
|
|
|
|
|
udp->udp_csum = 0;
|
|
|
|
|
|
|
|
|
|
vxh = (struct vxlanhdr *) (udp + 1);
|
|
|
|
|
if (!ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
|
|
|
|
|
&vx_flags, &vx_vni)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
|
|
|
|
|
put_16aligned_be32(&vxh->vx_vni, htonl(vx_vni));
|
|
|
|
|
tnl_type = OVS_VPORT_TYPE_VXLAN;
|
|
|
|
|
header_len = sizeof *eth + sizeof *ip +
|
|
|
|
|
sizeof *udp + sizeof *vxh;
|
|
|
|
|
} else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
|
|
|
|
|
&greh->flags, &gre_proto)){
|
|
|
|
|
|
|
|
|
|
tnl_type = OVS_VPORT_TYPE_GRE;
|
|
|
|
|
greh->protocol = htons(gre_proto);
|
|
|
|
|
ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
|
|
|
|
|
|
|
|
|
|
if (greh->flags & htons(GRE_CSUM)) {
|
|
|
|
|
uint32_t csum;
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, ",csum=0x%"SCNx32, &csum)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
put_16aligned_be32(options, htonl(csum));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
if (greh->flags & htons(GRE_KEY)) {
|
|
|
|
|
uint32_t key;
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, ",key=0x%"SCNx32, &key)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
put_16aligned_be32(options, htonl(key));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
if (greh->flags & htons(GRE_SEQ)) {
|
|
|
|
|
uint32_t seq;
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, ",seq=0x%"SCNx32, &seq)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
put_16aligned_be32(options, htonl(seq));
|
|
|
|
|
options++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ovs_scan_len(s, &n, "))")) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header_len = sizeof *eth + sizeof *ip +
|
|
|
|
|
((uint8_t *) options - (uint8_t *) greh);
|
|
|
|
|
} else {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check tunnel meta data. */
|
|
|
|
|
if (data->tnl_type != tnl_type) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
if (data->header_len != header_len) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Out port */
|
|
|
|
|
if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
static int
|
|
|
|
|
parse_odp_action(const char *s, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *actions)
|
|
|
|
|
{
|
|
|
|
|
{
|
|
|
|
|
uint32_t port;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "%"SCNi32"%n", &port, &n)) {
|
|
|
|
|
nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, port);
|
|
|
|
|
return n;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (port_names) {
|
|
|
|
|
int len = strcspn(s, delimiters);
|
|
|
|
|
struct simap_node *node;
|
|
|
|
|
|
|
|
|
|
node = simap_find_len(port_names, s, len);
|
|
|
|
|
if (node) {
|
|
|
|
|
nl_msg_put_u32(actions, OVS_ACTION_ATTR_OUTPUT, node->data);
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-03 15:04:15 -07:00
|
|
|
|
{
|
|
|
|
|
uint32_t recirc_id;
|
|
|
|
|
int n = -1;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "recirc(%"PRIu32")%n", &recirc_id, &n)) {
|
|
|
|
|
nl_msg_put_u32(actions, OVS_ACTION_ATTR_RECIRC, recirc_id);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (!strncmp(s, "userspace(", 10)) {
|
|
|
|
|
return parse_odp_userspace_action(s, actions);
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-11 15:22:56 -08:00
|
|
|
|
if (!strncmp(s, "set(", 4)) {
|
|
|
|
|
size_t start_ofs;
|
|
|
|
|
int retval;
|
2014-09-05 15:44:19 -07:00
|
|
|
|
struct nlattr mask[128 / sizeof(struct nlattr)];
|
|
|
|
|
struct ofpbuf maskbuf;
|
|
|
|
|
struct nlattr *nested, *key;
|
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
|
|
/* 'mask' is big enough to hold any key. */
|
|
|
|
|
ofpbuf_use_stack(&maskbuf, mask, sizeof mask);
|
2011-11-11 15:22:56 -08:00
|
|
|
|
|
|
|
|
|
start_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SET);
|
2014-09-05 15:44:19 -07:00
|
|
|
|
retval = parse_odp_key_mask_attr(s + 4, port_names, actions, &maskbuf);
|
2011-11-11 15:22:56 -08:00
|
|
|
|
if (retval < 0) {
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
if (s[retval + 4] != ')') {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2014-09-05 15:44:19 -07:00
|
|
|
|
|
|
|
|
|
nested = ofpbuf_at_assert(actions, start_ofs, sizeof *nested);
|
|
|
|
|
key = nested + 1;
|
|
|
|
|
|
|
|
|
|
size = nl_attr_get_size(mask);
|
|
|
|
|
if (size == nl_attr_get_size(key)) {
|
|
|
|
|
/* Change to masked set action if not fully masked. */
|
2014-09-05 15:44:19 -07:00
|
|
|
|
if (!is_all_ones(mask + 1, size)) {
|
2014-09-05 15:44:19 -07:00
|
|
|
|
key->nla_len += size;
|
|
|
|
|
ofpbuf_put(actions, mask + 1, size);
|
|
|
|
|
/* 'actions' may have been reallocated by ofpbuf_put(). */
|
|
|
|
|
nested = ofpbuf_at_assert(actions, start_ofs, sizeof *nested);
|
|
|
|
|
nested->nla_type = OVS_ACTION_ATTR_SET_MASKED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-11 15:22:56 -08:00
|
|
|
|
nl_msg_end_nested(actions, start_ofs);
|
|
|
|
|
return retval + 5;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-17 10:24:05 -08:00
|
|
|
|
{
|
|
|
|
|
struct ovs_action_push_vlan push;
|
|
|
|
|
int tpid = ETH_TYPE_VLAN;
|
|
|
|
|
int vid, pcp;
|
|
|
|
|
int cfi = 1;
|
|
|
|
|
int n = -1;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
|
2013-11-09 15:44:23 -08:00
|
|
|
|
if (ovs_scan(s, "push_vlan(vid=%i,pcp=%i)%n", &vid, &pcp, &n)
|
|
|
|
|
|| ovs_scan(s, "push_vlan(vid=%i,pcp=%i,cfi=%i)%n",
|
|
|
|
|
&vid, &pcp, &cfi, &n)
|
|
|
|
|
|| ovs_scan(s, "push_vlan(tpid=%i,vid=%i,pcp=%i)%n",
|
|
|
|
|
&tpid, &vid, &pcp, &n)
|
|
|
|
|
|| ovs_scan(s, "push_vlan(tpid=%i,vid=%i,pcp=%i,cfi=%i)%n",
|
|
|
|
|
&tpid, &vid, &pcp, &cfi, &n)) {
|
2011-11-17 10:24:05 -08:00
|
|
|
|
push.vlan_tpid = htons(tpid);
|
|
|
|
|
push.vlan_tci = htons((vid << VLAN_VID_SHIFT)
|
|
|
|
|
| (pcp << VLAN_PCP_SHIFT)
|
|
|
|
|
| (cfi ? VLAN_CFI : 0));
|
|
|
|
|
nl_msg_put_unspec(actions, OVS_ACTION_ATTR_PUSH_VLAN,
|
|
|
|
|
&push, sizeof push);
|
|
|
|
|
|
|
|
|
|
return n;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-17 10:24:05 -08:00
|
|
|
|
if (!strncmp(s, "pop_vlan", 8)) {
|
|
|
|
|
nl_msg_put_flag(actions, OVS_ACTION_ATTR_POP_VLAN);
|
|
|
|
|
return 8;
|
2011-11-11 15:22:56 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
double percentage;
|
|
|
|
|
int n = -1;
|
|
|
|
|
|
2013-11-09 15:44:23 -08:00
|
|
|
|
if (ovs_scan(s, "sample(sample=%lf%%,actions(%n", &percentage, &n)
|
|
|
|
|
&& percentage >= 0. && percentage <= 100.0) {
|
2011-11-11 15:22:56 -08:00
|
|
|
|
size_t sample_ofs, actions_ofs;
|
|
|
|
|
double probability;
|
|
|
|
|
|
|
|
|
|
probability = floor(UINT32_MAX * (percentage / 100.0) + .5);
|
|
|
|
|
sample_ofs = nl_msg_start_nested(actions, OVS_ACTION_ATTR_SAMPLE);
|
|
|
|
|
nl_msg_put_u32(actions, OVS_SAMPLE_ATTR_PROBABILITY,
|
|
|
|
|
(probability <= 0 ? 0
|
|
|
|
|
: probability >= UINT32_MAX ? UINT32_MAX
|
|
|
|
|
: probability));
|
|
|
|
|
|
|
|
|
|
actions_ofs = nl_msg_start_nested(actions,
|
|
|
|
|
OVS_SAMPLE_ATTR_ACTIONS);
|
|
|
|
|
for (;;) {
|
|
|
|
|
int retval;
|
|
|
|
|
|
2012-05-09 12:21:54 -07:00
|
|
|
|
n += strspn(s + n, delimiters);
|
2011-11-11 15:22:56 -08:00
|
|
|
|
if (s[n] == ')') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = parse_odp_action(s + n, port_names, actions);
|
|
|
|
|
if (retval < 0) {
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
n += retval;
|
|
|
|
|
}
|
|
|
|
|
nl_msg_end_nested(actions, actions_ofs);
|
|
|
|
|
nl_msg_end_nested(actions, sample_ofs);
|
|
|
|
|
|
|
|
|
|
return s[n + 1] == ')' ? n + 2 : -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-11 11:53:47 -08:00
|
|
|
|
{
|
|
|
|
|
uint32_t port;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "tnl_pop(%"SCNi32")%n", &port, &n)) {
|
|
|
|
|
nl_msg_put_u32(actions, OVS_ACTION_ATTR_TUNNEL_POP, port);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
struct ovs_action_push_tnl data;
|
|
|
|
|
int n;
|
|
|
|
|
|
|
|
|
|
n = ovs_parse_tnl_push(s, &data);
|
|
|
|
|
if (n > 0) {
|
|
|
|
|
odp_put_tnl_push_action(actions, &data);
|
|
|
|
|
return n;
|
|
|
|
|
} else if (n < 0) {
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-11 15:22:56 -08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parses the string representation of datapath actions, in the format output
|
|
|
|
|
* by format_odp_action(). Returns 0 if successful, otherwise a positive errno
|
|
|
|
|
* value. On success, the ODP actions are appended to 'actions' as a series of
|
|
|
|
|
* Netlink attributes. On failure, no data is appended to 'actions'. Either
|
|
|
|
|
* way, 'actions''s data might be reallocated. */
|
|
|
|
|
int
|
2012-05-22 10:32:02 -07:00
|
|
|
|
odp_actions_from_string(const char *s, const struct simap *port_names,
|
2011-11-11 15:22:56 -08:00
|
|
|
|
struct ofpbuf *actions)
|
|
|
|
|
{
|
|
|
|
|
size_t old_size;
|
|
|
|
|
|
|
|
|
|
if (!strcasecmp(s, "drop")) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
old_size = ofpbuf_size(actions);
|
2011-11-11 15:22:56 -08:00
|
|
|
|
for (;;) {
|
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
|
|
s += strspn(s, delimiters);
|
|
|
|
|
if (!*s) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
retval = parse_odp_action(s, port_names, actions);
|
|
|
|
|
if (retval < 0 || !strchr(delimiters, s[retval])) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpbuf_set_size(actions, old_size);
|
2011-11-11 15:22:56 -08:00
|
|
|
|
return -retval;
|
|
|
|
|
}
|
|
|
|
|
s += retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2010-10-11 13:31:35 -07:00
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
/* Returns the correct length of the payload for a flow key attribute of the
|
2011-11-14 15:56:43 -08:00
|
|
|
|
* specified 'type', -1 if 'type' is unknown, or -2 if the attribute's payload
|
|
|
|
|
* is variable length. */
|
2011-01-23 18:44:44 -08:00
|
|
|
|
static int
|
|
|
|
|
odp_flow_key_attr_len(uint16_t type)
|
|
|
|
|
{
|
2011-08-18 10:35:40 -07:00
|
|
|
|
if (type > OVS_KEY_ATTR_MAX) {
|
2011-01-23 18:44:44 -08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-05 09:59:51 -07:00
|
|
|
|
switch ((enum ovs_key_attr) type) {
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_ENCAP: return -2;
|
2011-11-01 10:13:16 -07:00
|
|
|
|
case OVS_KEY_ATTR_PRIORITY: return 4;
|
2012-11-13 19:19:36 +02:00
|
|
|
|
case OVS_KEY_ATTR_SKB_MARK: return 4;
|
2014-03-04 15:36:03 -08:00
|
|
|
|
case OVS_KEY_ATTR_DP_HASH: return 4;
|
|
|
|
|
case OVS_KEY_ATTR_RECIRC_ID: return 4;
|
2013-01-18 18:10:59 -08:00
|
|
|
|
case OVS_KEY_ATTR_TUNNEL: return -2;
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_IN_PORT: return 4;
|
|
|
|
|
case OVS_KEY_ATTR_ETHERNET: return sizeof(struct ovs_key_ethernet);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_VLAN: return sizeof(ovs_be16);
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_ETHERTYPE: return 2;
|
2014-02-04 10:32:35 -08:00
|
|
|
|
case OVS_KEY_ATTR_MPLS: return -2;
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_IPV4: return sizeof(struct ovs_key_ipv4);
|
|
|
|
|
case OVS_KEY_ATTR_IPV6: return sizeof(struct ovs_key_ipv6);
|
|
|
|
|
case OVS_KEY_ATTR_TCP: return sizeof(struct ovs_key_tcp);
|
2013-10-28 13:54:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_TCP_FLAGS: return 2;
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_UDP: return sizeof(struct ovs_key_udp);
|
2013-08-22 20:24:44 +12:00
|
|
|
|
case OVS_KEY_ATTR_SCTP: return sizeof(struct ovs_key_sctp);
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_ICMP: return sizeof(struct ovs_key_icmp);
|
|
|
|
|
case OVS_KEY_ATTR_ICMPV6: return sizeof(struct ovs_key_icmpv6);
|
|
|
|
|
case OVS_KEY_ATTR_ARP: return sizeof(struct ovs_key_arp);
|
|
|
|
|
case OVS_KEY_ATTR_ND: return sizeof(struct ovs_key_nd);
|
|
|
|
|
|
|
|
|
|
case OVS_KEY_ATTR_UNSPEC:
|
|
|
|
|
case __OVS_KEY_ATTR_MAX:
|
2011-01-23 18:44:44 -08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_generic_odp_key(const struct nlattr *a, struct ds *ds)
|
|
|
|
|
{
|
|
|
|
|
size_t len = nl_attr_get_size(a);
|
|
|
|
|
if (len) {
|
|
|
|
|
const uint8_t *unspec;
|
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
|
|
unspec = nl_attr_get(a);
|
|
|
|
|
for (i = 0; i < len; i++) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (i) {
|
|
|
|
|
ds_put_char(ds, ' ');
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
ds_put_format(ds, "%02x", unspec[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
static const char *
|
|
|
|
|
ovs_frag_type_to_string(enum ovs_frag_type type)
|
|
|
|
|
{
|
|
|
|
|
switch (type) {
|
|
|
|
|
case OVS_FRAG_TYPE_NONE:
|
|
|
|
|
return "no";
|
|
|
|
|
case OVS_FRAG_TYPE_FIRST:
|
|
|
|
|
return "first";
|
|
|
|
|
case OVS_FRAG_TYPE_LATER:
|
|
|
|
|
return "later";
|
|
|
|
|
case __OVS_FRAG_TYPE_MAX:
|
|
|
|
|
default:
|
|
|
|
|
return "<error>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-18 18:10:59 -08:00
|
|
|
|
static int
|
|
|
|
|
tunnel_key_attr_len(int type)
|
2012-11-14 21:10:54 -08:00
|
|
|
|
{
|
2013-01-18 18:10:59 -08:00
|
|
|
|
switch (type) {
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_ID: return 8;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: return 4;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV4_DST: return 4;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TOS: return 1;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TTL: return 1;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT: return 0;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_CSUM: return 0;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TP_SRC: return 2;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TP_DST: return 2;
|
2014-05-27 21:50:35 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_OAM: return 0;
|
2014-06-05 19:07:32 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: return -2;
|
2013-01-18 18:10:59 -08:00
|
|
|
|
case __OVS_TUNNEL_KEY_ATTR_MAX:
|
|
|
|
|
return -1;
|
2012-11-14 21:10:54 -08:00
|
|
|
|
}
|
2013-01-18 18:10:59 -08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 19:07:32 -07:00
|
|
|
|
#define GENEVE_OPT(class, type) ((OVS_FORCE uint32_t)(class) << 8 | (type))
|
|
|
|
|
static int
|
|
|
|
|
parse_geneve_opts(const struct nlattr *attr)
|
|
|
|
|
{
|
|
|
|
|
int opts_len = nl_attr_get_size(attr);
|
|
|
|
|
const struct geneve_opt *opt = nl_attr_get(attr);
|
|
|
|
|
|
|
|
|
|
while (opts_len > 0) {
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
if (opts_len < sizeof(*opt)) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
len = sizeof(*opt) + opt->length * 4;
|
|
|
|
|
if (len > opts_len) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (GENEVE_OPT(opt->opt_class, opt->type)) {
|
|
|
|
|
default:
|
|
|
|
|
if (opt->type & GENEVE_CRIT_OPT_TYPE) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
opt = opt + len / sizeof(*opt);
|
|
|
|
|
opts_len -= len;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-05 14:28:48 +09:00
|
|
|
|
enum odp_key_fitness
|
|
|
|
|
odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
|
2013-01-18 18:10:59 -08:00
|
|
|
|
{
|
|
|
|
|
unsigned int left;
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
bool ttl = false;
|
|
|
|
|
bool unknown = false;
|
|
|
|
|
|
|
|
|
|
NL_NESTED_FOR_EACH(a, left, attr) {
|
|
|
|
|
uint16_t type = nl_attr_type(a);
|
|
|
|
|
size_t len = nl_attr_get_size(a);
|
|
|
|
|
int expected_len = tunnel_key_attr_len(type);
|
|
|
|
|
|
|
|
|
|
if (len != expected_len && expected_len >= 0) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_ID:
|
|
|
|
|
tun->tun_id = nl_attr_get_be64(a);
|
|
|
|
|
tun->flags |= FLOW_TNL_F_KEY;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV4_SRC:
|
|
|
|
|
tun->ip_src = nl_attr_get_be32(a);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_IPV4_DST:
|
|
|
|
|
tun->ip_dst = nl_attr_get_be32(a);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TOS:
|
|
|
|
|
tun->ip_tos = nl_attr_get_u8(a);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TTL:
|
|
|
|
|
tun->ip_ttl = nl_attr_get_u8(a);
|
|
|
|
|
ttl = true;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT:
|
|
|
|
|
tun->flags |= FLOW_TNL_F_DONT_FRAGMENT;
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_CSUM:
|
|
|
|
|
tun->flags |= FLOW_TNL_F_CSUM;
|
|
|
|
|
break;
|
2014-08-17 20:19:36 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TP_SRC:
|
|
|
|
|
tun->tp_src = nl_attr_get_be16(a);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_TP_DST:
|
|
|
|
|
tun->tp_dst = nl_attr_get_be16(a);
|
|
|
|
|
break;
|
2014-05-27 21:50:35 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_OAM:
|
|
|
|
|
tun->flags |= FLOW_TNL_F_OAM;
|
|
|
|
|
break;
|
2014-06-05 19:07:32 -07:00
|
|
|
|
case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: {
|
|
|
|
|
if (parse_geneve_opts(a)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
/* It is necessary to reproduce options exactly (including order)
|
|
|
|
|
* so it's easiest to just echo them back. */
|
|
|
|
|
unknown = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2013-01-18 18:10:59 -08:00
|
|
|
|
default:
|
|
|
|
|
/* Allow this to show up as unexpected, if there are unknown
|
|
|
|
|
* tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
|
|
|
|
|
unknown = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!ttl) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
if (unknown) {
|
2013-12-30 15:58:58 -08:00
|
|
|
|
return ODP_FIT_TOO_MUCH;
|
2013-01-18 18:10:59 -08:00
|
|
|
|
}
|
|
|
|
|
return ODP_FIT_PERFECT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key)
|
|
|
|
|
{
|
|
|
|
|
size_t tun_key_ofs;
|
|
|
|
|
|
|
|
|
|
tun_key_ofs = nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL);
|
|
|
|
|
|
2014-03-01 17:11:02 -08:00
|
|
|
|
/* tun_id != 0 without FLOW_TNL_F_KEY is valid if tun_key is a mask. */
|
|
|
|
|
if (tun_key->tun_id || tun_key->flags & FLOW_TNL_F_KEY) {
|
2013-01-18 18:10:59 -08:00
|
|
|
|
nl_msg_put_be64(a, OVS_TUNNEL_KEY_ATTR_ID, tun_key->tun_id);
|
|
|
|
|
}
|
|
|
|
|
if (tun_key->ip_src) {
|
|
|
|
|
nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_SRC, tun_key->ip_src);
|
|
|
|
|
}
|
|
|
|
|
if (tun_key->ip_dst) {
|
|
|
|
|
nl_msg_put_be32(a, OVS_TUNNEL_KEY_ATTR_IPV4_DST, tun_key->ip_dst);
|
|
|
|
|
}
|
|
|
|
|
if (tun_key->ip_tos) {
|
|
|
|
|
nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TOS, tun_key->ip_tos);
|
|
|
|
|
}
|
|
|
|
|
nl_msg_put_u8(a, OVS_TUNNEL_KEY_ATTR_TTL, tun_key->ip_ttl);
|
|
|
|
|
if (tun_key->flags & FLOW_TNL_F_DONT_FRAGMENT) {
|
|
|
|
|
nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_DONT_FRAGMENT);
|
|
|
|
|
}
|
|
|
|
|
if (tun_key->flags & FLOW_TNL_F_CSUM) {
|
|
|
|
|
nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_CSUM);
|
|
|
|
|
}
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (tun_key->tp_src) {
|
|
|
|
|
nl_msg_put_be16(a, OVS_TUNNEL_KEY_ATTR_TP_SRC, tun_key->tp_src);
|
|
|
|
|
}
|
|
|
|
|
if (tun_key->tp_dst) {
|
|
|
|
|
nl_msg_put_be16(a, OVS_TUNNEL_KEY_ATTR_TP_DST, tun_key->tp_dst);
|
|
|
|
|
}
|
2014-05-27 21:50:35 -07:00
|
|
|
|
if (tun_key->flags & FLOW_TNL_F_OAM) {
|
|
|
|
|
nl_msg_put_flag(a, OVS_TUNNEL_KEY_ATTR_OAM);
|
|
|
|
|
}
|
2013-01-18 18:10:59 -08:00
|
|
|
|
|
|
|
|
|
nl_msg_end_nested(a, tun_key_ofs);
|
2012-11-14 21:10:54 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-03 12:23:14 -07:00
|
|
|
|
static bool
|
|
|
|
|
odp_mask_attr_is_wildcard(const struct nlattr *ma)
|
|
|
|
|
{
|
|
|
|
|
return is_all_zeros(nl_attr_get(ma), nl_attr_get_size(ma));
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
static bool
|
2014-09-05 16:00:49 -07:00
|
|
|
|
odp_mask_is_exact(enum ovs_key_attr attr, const void *mask, size_t size)
|
2013-06-19 07:15:10 +00:00
|
|
|
|
{
|
2014-09-05 15:44:20 -07:00
|
|
|
|
if (attr == OVS_KEY_ATTR_TCP_FLAGS) {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
return TCP_FLAGS(*(ovs_be16 *)mask) == TCP_FLAGS(OVS_BE16_MAX);
|
|
|
|
|
}
|
|
|
|
|
if (attr == OVS_KEY_ATTR_IPV6) {
|
|
|
|
|
const struct ovs_key_ipv6 *ipv6_mask = mask;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
return
|
|
|
|
|
((ipv6_mask->ipv6_label & htonl(IPV6_LABEL_MASK))
|
2014-09-05 15:44:20 -07:00
|
|
|
|
== htonl(IPV6_LABEL_MASK))
|
2014-09-05 16:00:49 -07:00
|
|
|
|
&& ipv6_mask->ipv6_proto == UINT8_MAX
|
|
|
|
|
&& ipv6_mask->ipv6_tclass == UINT8_MAX
|
|
|
|
|
&& ipv6_mask->ipv6_hlimit == UINT8_MAX
|
|
|
|
|
&& ipv6_mask->ipv6_frag == UINT8_MAX
|
|
|
|
|
&& ipv6_mask_is_exact((const struct in6_addr *)ipv6_mask->ipv6_src)
|
|
|
|
|
&& ipv6_mask_is_exact((const struct in6_addr *)ipv6_mask->ipv6_dst);
|
|
|
|
|
}
|
|
|
|
|
if (attr == OVS_KEY_ATTR_TUNNEL) {
|
|
|
|
|
const struct flow_tnl *tun_mask = mask;
|
|
|
|
|
|
|
|
|
|
return tun_mask->flags == FLOW_TNL_F_MASK
|
|
|
|
|
&& tun_mask->tun_id == OVS_BE64_MAX
|
|
|
|
|
&& tun_mask->ip_src == OVS_BE32_MAX
|
|
|
|
|
&& tun_mask->ip_dst == OVS_BE32_MAX
|
|
|
|
|
&& tun_mask->ip_tos == UINT8_MAX
|
|
|
|
|
&& tun_mask->ip_ttl == UINT8_MAX
|
|
|
|
|
&& tun_mask->tp_src == OVS_BE16_MAX
|
|
|
|
|
&& tun_mask->tp_dst == OVS_BE16_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (attr == OVS_KEY_ATTR_ARP) {
|
|
|
|
|
/* ARP key has padding, ignore it. */
|
|
|
|
|
BUILD_ASSERT_DECL(sizeof(struct ovs_key_arp) == 24);
|
|
|
|
|
BUILD_ASSERT_DECL(offsetof(struct ovs_key_arp, arp_tha) == 10 + 6);
|
|
|
|
|
size = offsetof(struct ovs_key_arp, arp_tha) + ETH_ADDR_LEN;
|
|
|
|
|
ovs_assert(((uint16_t *)mask)[size/2] == 0);
|
|
|
|
|
}
|
2014-09-05 15:44:20 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
return is_all_ones(mask, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
odp_mask_attr_is_exact(const struct nlattr *ma)
|
|
|
|
|
{
|
|
|
|
|
struct flow_tnl tun_mask;
|
|
|
|
|
enum ovs_key_attr attr = nl_attr_type(ma);
|
|
|
|
|
const void *mask;
|
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
|
|
if (attr == OVS_KEY_ATTR_TUNNEL) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
memset(&tun_mask, 0, sizeof tun_mask);
|
|
|
|
|
odp_tun_key_from_attr(ma, &tun_mask);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
mask = &tun_mask;
|
|
|
|
|
size = sizeof tun_mask;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
} else {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
mask = nl_attr_get(ma);
|
|
|
|
|
size = nl_attr_get_size(ma);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
return odp_mask_is_exact(attr, mask, size);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-09-23 22:58:46 -07:00
|
|
|
|
void
|
|
|
|
|
odp_portno_names_set(struct hmap *portno_names, odp_port_t port_no,
|
|
|
|
|
char *port_name)
|
|
|
|
|
{
|
|
|
|
|
struct odp_portno_names *odp_portno_names;
|
|
|
|
|
|
|
|
|
|
odp_portno_names = xmalloc(sizeof *odp_portno_names);
|
|
|
|
|
odp_portno_names->port_no = port_no;
|
|
|
|
|
odp_portno_names->name = xstrdup(port_name);
|
|
|
|
|
hmap_insert(portno_names, &odp_portno_names->hmap_node,
|
|
|
|
|
hash_odp_port(port_no));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
|
odp_portno_names_get(const struct hmap *portno_names, odp_port_t port_no)
|
|
|
|
|
{
|
|
|
|
|
struct odp_portno_names *odp_portno_names;
|
|
|
|
|
|
|
|
|
|
HMAP_FOR_EACH_IN_BUCKET (odp_portno_names, hmap_node,
|
|
|
|
|
hash_odp_port(port_no), portno_names) {
|
|
|
|
|
if (odp_portno_names->port_no == port_no) {
|
|
|
|
|
return odp_portno_names->name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
odp_portno_names_destroy(struct hmap *portno_names)
|
|
|
|
|
{
|
|
|
|
|
struct odp_portno_names *odp_portno_names, *odp_portno_names_next;
|
|
|
|
|
HMAP_FOR_EACH_SAFE (odp_portno_names, odp_portno_names_next,
|
|
|
|
|
hmap_node, portno_names) {
|
|
|
|
|
hmap_remove(portno_names, &odp_portno_names->hmap_node);
|
|
|
|
|
free(odp_portno_names->name);
|
|
|
|
|
free(odp_portno_names);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
/* Format helpers. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_eth(struct ds *ds, const char *name, const uint8_t key[ETH_ADDR_LEN],
|
|
|
|
|
const uint8_t (*mask)[ETH_ADDR_LEN], bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && eth_addr_is_zero(*mask);
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || eth_mask_is_exact(*mask);
|
|
|
|
|
|
|
|
|
|
if (mask_full) {
|
|
|
|
|
ds_put_format(ds, "%s="ETH_ADDR_FMT",", name, ETH_ADDR_ARGS(key));
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "%s=", name);
|
|
|
|
|
eth_format_masked(key, *mask, ds);
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_be64(struct ds *ds, const char *name, ovs_be64 key,
|
|
|
|
|
const ovs_be64 *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == OVS_BE64_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=0x%"PRIx64, name, ntohll(key));
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx64, ntohll(*mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_ipv4(struct ds *ds, const char *name, ovs_be32 key,
|
|
|
|
|
const ovs_be32 *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == OVS_BE32_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s="IP_FMT, name, IP_ARGS(key));
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/"IP_FMT, IP_ARGS(*mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_ipv6(struct ds *ds, const char *name, const ovs_be32 key_[4],
|
|
|
|
|
const ovs_be32 (*mask_)[4], bool verbose)
|
|
|
|
|
{
|
|
|
|
|
char buf[INET6_ADDRSTRLEN];
|
|
|
|
|
const struct in6_addr *key = (const struct in6_addr *)key_;
|
|
|
|
|
const struct in6_addr *mask = mask_ ? (const struct in6_addr *)*mask_
|
|
|
|
|
: NULL;
|
|
|
|
|
bool mask_empty = mask && ipv6_mask_is_any(mask);
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || ipv6_mask_is_exact(mask);
|
|
|
|
|
|
|
|
|
|
inet_ntop(AF_INET6, key, buf, sizeof buf);
|
|
|
|
|
ds_put_format(ds, "%s=%s", name, buf);
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
inet_ntop(AF_INET6, mask, buf, sizeof buf);
|
|
|
|
|
ds_put_format(ds, "/%s", buf);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_ipv6_label(struct ds *ds, const char *name, ovs_be32 key,
|
|
|
|
|
const ovs_be32 *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask
|
|
|
|
|
|| (*mask & htonl(IPV6_LABEL_MASK)) == htonl(IPV6_LABEL_MASK);
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=%#"PRIx32, name, ntohl(key));
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx32, ntohl(*mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_u8x(struct ds *ds, const char *name, uint8_t key,
|
|
|
|
|
const uint8_t *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == UINT8_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=%#"PRIx8, name, key);
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx8, *mask);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_u8u(struct ds *ds, const char *name, uint8_t key,
|
|
|
|
|
const uint8_t *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == UINT8_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=%"PRIu8, name, key);
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx8, *mask);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_be16(struct ds *ds, const char *name, ovs_be16 key,
|
|
|
|
|
const ovs_be16 *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == OVS_BE16_MAX;
|
|
|
|
|
|
|
|
|
|
ds_put_format(ds, "%s=%"PRIu16, name, ntohs(key));
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx16, ntohs(*mask));
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_tun_flags(struct ds *ds, const char *name, uint16_t key,
|
|
|
|
|
const uint16_t *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || (*mask & FLOW_TNL_F_MASK) == FLOW_TNL_F_MASK;
|
|
|
|
|
|
|
|
|
|
ds_put_cstr(ds, name);
|
|
|
|
|
ds_put_char(ds, '(');
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
format_flags_masked(ds, NULL, flow_tun_flag_to_string, key, *mask);
|
|
|
|
|
} else { /* Fully masked. */
|
|
|
|
|
format_flags(ds, flow_tun_flag_to_string, key, ',');
|
|
|
|
|
}
|
|
|
|
|
ds_put_cstr(ds, "),");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
format_frag(struct ds *ds, const char *name, uint8_t key,
|
|
|
|
|
const uint8_t *mask, bool verbose)
|
|
|
|
|
{
|
|
|
|
|
bool mask_empty = mask && !*mask;
|
|
|
|
|
|
|
|
|
|
/* ODP frag is an enumeration field; partial masks are not meaningful. */
|
|
|
|
|
if (verbose || !mask_empty) {
|
|
|
|
|
bool mask_full = !mask || *mask == UINT8_MAX;
|
|
|
|
|
|
|
|
|
|
if (!mask_full) { /* Partially masked. */
|
|
|
|
|
ds_put_format(ds, "error: partial mask not supported for frag (%#"
|
|
|
|
|
PRIx8"),", *mask);
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "%s=%s,", name, ovs_frag_type_to_string(key));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
|
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
static void
|
2013-06-19 07:15:10 +00:00
|
|
|
|
format_odp_key_attr(const struct nlattr *a, const struct nlattr *ma,
|
2013-09-23 22:58:46 -07:00
|
|
|
|
const struct hmap *portno_names, struct ds *ds,
|
|
|
|
|
bool verbose)
|
2011-01-23 18:44:44 -08:00
|
|
|
|
{
|
2011-11-07 09:13:53 -08:00
|
|
|
|
enum ovs_key_attr attr = nl_attr_type(a);
|
2013-04-15 15:40:21 -07:00
|
|
|
|
char namebuf[OVS_KEY_ATTR_BUFSIZE];
|
2011-11-14 15:56:43 -08:00
|
|
|
|
int expected_len;
|
2013-06-27 22:02:58 -07:00
|
|
|
|
bool is_exact;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2013-06-27 22:02:58 -07:00
|
|
|
|
is_exact = ma ? odp_mask_attr_is_exact(ma) : true;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2013-04-15 15:40:21 -07:00
|
|
|
|
ds_put_cstr(ds, ovs_key_attr_to_string(attr, namebuf, sizeof namebuf));
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
expected_len = odp_flow_key_attr_len(nl_attr_type(a));
|
|
|
|
|
if (expected_len != -2) {
|
|
|
|
|
bool bad_key_len = nl_attr_get_size(a) != expected_len;
|
2014-04-11 15:51:10 -07:00
|
|
|
|
bool bad_mask_len = ma && nl_attr_get_size(ma) != expected_len;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
|
|
|
|
if (bad_key_len || bad_mask_len) {
|
|
|
|
|
if (bad_key_len) {
|
2013-11-25 23:38:48 -08:00
|
|
|
|
ds_put_format(ds, "(bad key length %"PRIuSIZE", expected %d)(",
|
2014-04-11 15:51:10 -07:00
|
|
|
|
nl_attr_get_size(a), expected_len);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
format_generic_odp_key(a, ds);
|
2014-04-11 15:51:10 -07:00
|
|
|
|
if (ma) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_char(ds, '/');
|
2014-04-11 15:51:10 -07:00
|
|
|
|
if (bad_mask_len) {
|
|
|
|
|
ds_put_format(ds, "(bad mask length %"PRIuSIZE", expected %d)(",
|
|
|
|
|
nl_attr_get_size(ma), expected_len);
|
|
|
|
|
}
|
|
|
|
|
format_generic_odp_key(ma, ds);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_char(ds, '(');
|
2011-11-07 09:13:53 -08:00
|
|
|
|
switch (attr) {
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_ENCAP:
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (ma && nl_attr_get_size(ma) && nl_attr_get_size(a)) {
|
|
|
|
|
odp_flow_format(nl_attr_get(a), nl_attr_get_size(a),
|
2013-09-23 22:58:46 -07:00
|
|
|
|
nl_attr_get(ma), nl_attr_get_size(ma), NULL, ds,
|
2013-08-03 12:23:14 -07:00
|
|
|
|
verbose);
|
2013-09-23 22:58:46 -07:00
|
|
|
|
} else if (nl_attr_get_size(a)) {
|
|
|
|
|
odp_flow_format(nl_attr_get(a), nl_attr_get_size(a), NULL, 0, NULL,
|
|
|
|
|
ds, verbose);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2011-11-01 10:13:16 -07:00
|
|
|
|
case OVS_KEY_ATTR_PRIORITY:
|
2012-11-13 19:19:36 +02:00
|
|
|
|
case OVS_KEY_ATTR_SKB_MARK:
|
2014-03-04 15:36:03 -08:00
|
|
|
|
case OVS_KEY_ATTR_DP_HASH:
|
|
|
|
|
case OVS_KEY_ATTR_RECIRC_ID:
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_format(ds, "%#"PRIx32, nl_attr_get_u32(a));
|
2013-06-27 22:02:58 -07:00
|
|
|
|
if (!is_exact) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
|
|
|
|
|
}
|
2012-11-13 19:19:36 +02:00
|
|
|
|
break;
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
case OVS_KEY_ATTR_TUNNEL: {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
struct flow_tnl key, mask_;
|
|
|
|
|
struct flow_tnl *mask = ma ? &mask_ : NULL;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (mask) {
|
|
|
|
|
memset(mask, 0, sizeof *mask);
|
|
|
|
|
odp_tun_key_from_attr(ma, mask);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
memset(&key, 0, sizeof key);
|
|
|
|
|
if (odp_tun_key_from_attr(a, &key) == ODP_FIT_ERROR) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
ds_put_format(ds, "error");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_be64(ds, "tun_id", key.tun_id, MASK(mask, tun_id), verbose);
|
|
|
|
|
format_ipv4(ds, "src", key.ip_src, MASK(mask, ip_src), verbose);
|
|
|
|
|
format_ipv4(ds, "dst", key.ip_dst, MASK(mask, ip_dst), verbose);
|
|
|
|
|
format_u8x(ds, "tos", key.ip_tos, MASK(mask, ip_tos), verbose);
|
|
|
|
|
format_u8u(ds, "ttl", key.ip_ttl, MASK(mask, ip_ttl), verbose);
|
|
|
|
|
format_be16(ds, "tp_src", key.tp_src, MASK(mask, tp_src), verbose);
|
|
|
|
|
format_be16(ds, "tp_dst", key.tp_dst, MASK(mask, tp_dst), verbose);
|
|
|
|
|
format_tun_flags(ds, "flags", key.flags, MASK(mask, flags), verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2012-10-20 12:15:24 -07:00
|
|
|
|
break;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
}
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_IN_PORT:
|
2013-09-23 22:58:46 -07:00
|
|
|
|
if (portno_names && verbose && is_exact) {
|
|
|
|
|
char *name = odp_portno_names_get(portno_names,
|
|
|
|
|
u32_to_odp(nl_attr_get_u32(a)));
|
|
|
|
|
if (name) {
|
|
|
|
|
ds_put_format(ds, "%s", name);
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
|
|
|
|
|
if (!is_exact) {
|
|
|
|
|
ds_put_format(ds, "/%#"PRIx32, nl_attr_get_u32(ma));
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
case OVS_KEY_ATTR_ETHERNET: {
|
|
|
|
|
const struct ovs_key_ethernet *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
const struct ovs_key_ethernet *key = nl_attr_get(a);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_eth(ds, "src", key->eth_src, MASK(mask, eth_src), verbose);
|
|
|
|
|
format_eth(ds, "dst", key->eth_dst, MASK(mask, eth_dst), verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
case OVS_KEY_ATTR_VLAN:
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_vlan_tci(ds, nl_attr_get_be16(a),
|
|
|
|
|
ma ? nl_attr_get_be16(ma) : OVS_BE16_MAX, verbose);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2013-01-25 16:22:07 +09:00
|
|
|
|
case OVS_KEY_ATTR_MPLS: {
|
|
|
|
|
const struct ovs_key_mpls *mpls_key = nl_attr_get(a);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
const struct ovs_key_mpls *mpls_mask = NULL;
|
2014-02-04 10:32:35 -08:00
|
|
|
|
size_t size = nl_attr_get_size(a);
|
|
|
|
|
|
|
|
|
|
if (!size || size % sizeof *mpls_key) {
|
2014-09-05 15:44:19 -07:00
|
|
|
|
ds_put_format(ds, "(bad key length %"PRIuSIZE")", size);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2013-06-27 22:02:58 -07:00
|
|
|
|
if (!is_exact) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
mpls_mask = nl_attr_get(ma);
|
2014-09-05 15:44:19 -07:00
|
|
|
|
if (size != nl_attr_get_size(ma)) {
|
2014-02-04 10:32:35 -08:00
|
|
|
|
ds_put_format(ds, "(key length %"PRIuSIZE" != "
|
|
|
|
|
"mask length %"PRIuSIZE")",
|
2014-09-05 15:44:19 -07:00
|
|
|
|
size, nl_attr_get_size(ma));
|
2014-02-04 10:32:35 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
format_mpls(ds, mpls_key, mpls_mask, size / sizeof *mpls_key);
|
2013-01-25 16:22:07 +09:00
|
|
|
|
break;
|
|
|
|
|
}
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_ETHERTYPE:
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_format(ds, "0x%04"PRIx16, ntohs(nl_attr_get_be16(a)));
|
2013-06-27 22:02:58 -07:00
|
|
|
|
if (!is_exact) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_format(ds, "/0x%04"PRIx16, ntohs(nl_attr_get_be16(ma)));
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
case OVS_KEY_ATTR_IPV4: {
|
|
|
|
|
const struct ovs_key_ipv4 *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_ipv4 *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
|
|
|
|
|
format_ipv4(ds, "src", key->ipv4_src, MASK(mask, ipv4_src), verbose);
|
|
|
|
|
format_ipv4(ds, "dst", key->ipv4_dst, MASK(mask, ipv4_dst), verbose);
|
|
|
|
|
format_u8u(ds, "proto", key->ipv4_proto, MASK(mask, ipv4_proto),
|
|
|
|
|
verbose);
|
|
|
|
|
format_u8x(ds, "tos", key->ipv4_tos, MASK(mask, ipv4_tos), verbose);
|
|
|
|
|
format_u8u(ds, "ttl", key->ipv4_ttl, MASK(mask, ipv4_ttl), verbose);
|
|
|
|
|
format_frag(ds, "frag", key->ipv4_frag, MASK(mask, ipv4_frag),
|
|
|
|
|
verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
case OVS_KEY_ATTR_IPV6: {
|
|
|
|
|
const struct ovs_key_ipv6 *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_ipv6 *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
|
|
|
|
|
format_ipv6(ds, "src", key->ipv6_src, MASK(mask, ipv6_src), verbose);
|
|
|
|
|
format_ipv6(ds, "dst", key->ipv6_dst, MASK(mask, ipv6_dst), verbose);
|
|
|
|
|
format_ipv6_label(ds, "label", key->ipv6_label, MASK(mask, ipv6_label),
|
|
|
|
|
verbose);
|
|
|
|
|
format_u8u(ds, "proto", key->ipv6_proto, MASK(mask, ipv6_proto),
|
|
|
|
|
verbose);
|
|
|
|
|
format_u8x(ds, "tclass", key->ipv6_tclass, MASK(mask, ipv6_tclass),
|
|
|
|
|
verbose);
|
|
|
|
|
format_u8u(ds, "hlimit", key->ipv6_hlimit, MASK(mask, ipv6_hlimit),
|
|
|
|
|
verbose);
|
|
|
|
|
format_frag(ds, "frag", key->ipv6_frag, MASK(mask, ipv6_frag),
|
|
|
|
|
verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2010-12-29 19:03:46 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
/* These have the same structure and format. */
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_TCP:
|
2014-09-09 14:50:36 -07:00
|
|
|
|
case OVS_KEY_ATTR_UDP:
|
|
|
|
|
case OVS_KEY_ATTR_SCTP: {
|
|
|
|
|
const struct ovs_key_tcp *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_tcp *mask = ma ? nl_attr_get(ma) : NULL;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_be16(ds, "src", key->tcp_src, MASK(mask, tcp_src), verbose);
|
|
|
|
|
format_be16(ds, "dst", key->tcp_dst, MASK(mask, tcp_dst), verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
2013-10-28 13:54:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_TCP_FLAGS:
|
|
|
|
|
if (!is_exact) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
format_flags_masked(ds, NULL, packet_tcp_flag_to_string,
|
|
|
|
|
ntohs(nl_attr_get_be16(a)),
|
|
|
|
|
ntohs(nl_attr_get_be16(ma)));
|
|
|
|
|
} else {
|
|
|
|
|
format_flags(ds, packet_tcp_flag_to_string,
|
|
|
|
|
ntohs(nl_attr_get_be16(a)), ',');
|
2013-10-28 13:54:40 -07:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
case OVS_KEY_ATTR_ICMP: {
|
|
|
|
|
const struct ovs_key_icmp *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_icmp *mask = ma ? nl_attr_get(ma) : NULL;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_u8u(ds, "type", key->icmp_type, MASK(mask, icmp_type), verbose);
|
|
|
|
|
format_u8u(ds, "code", key->icmp_code, MASK(mask, icmp_code), verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
case OVS_KEY_ATTR_ICMPV6: {
|
|
|
|
|
const struct ovs_key_icmpv6 *key = nl_attr_get(a);
|
|
|
|
|
const struct ovs_key_icmpv6 *mask = ma ? nl_attr_get(ma) : NULL;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_u8u(ds, "type", key->icmpv6_type, MASK(mask, icmpv6_type),
|
|
|
|
|
verbose);
|
|
|
|
|
format_u8u(ds, "code", key->icmpv6_code, MASK(mask, icmpv6_code),
|
|
|
|
|
verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2010-12-29 19:03:46 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
|
|
|
|
case OVS_KEY_ATTR_ARP: {
|
|
|
|
|
const struct ovs_key_arp *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
const struct ovs_key_arp *key = nl_attr_get(a);
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_ipv4(ds, "sip", key->arp_sip, MASK(mask, arp_sip), verbose);
|
|
|
|
|
format_ipv4(ds, "tip", key->arp_tip, MASK(mask, arp_tip), verbose);
|
|
|
|
|
format_be16(ds, "op", key->arp_op, MASK(mask, arp_op), verbose);
|
|
|
|
|
format_eth(ds, "sha", key->arp_sha, MASK(mask, arp_sha), verbose);
|
|
|
|
|
format_eth(ds, "tha", key->arp_tha, MASK(mask, arp_tha), verbose);
|
|
|
|
|
ds_chomp(ds, ',');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
2014-09-09 14:50:36 -07:00
|
|
|
|
}
|
2011-08-18 10:35:40 -07:00
|
|
|
|
case OVS_KEY_ATTR_ND: {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
const struct ovs_key_nd *mask = ma ? nl_attr_get(ma) : NULL;
|
|
|
|
|
const struct ovs_key_nd *key = nl_attr_get(a);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
format_ipv6(ds, "target", key->nd_target, MASK(mask, nd_target),
|
|
|
|
|
verbose);
|
|
|
|
|
format_eth(ds, "sll", key->nd_sll, MASK(mask, nd_sll), verbose);
|
|
|
|
|
format_eth(ds, "tll", key->nd_tll, MASK(mask, nd_tll), verbose);
|
2011-02-01 22:54:11 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
ds_chomp(ds, ',');
|
2011-02-01 22:54:11 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2011-11-07 09:13:53 -08:00
|
|
|
|
case OVS_KEY_ATTR_UNSPEC:
|
|
|
|
|
case __OVS_KEY_ATTR_MAX:
|
2011-01-23 18:44:44 -08:00
|
|
|
|
default:
|
|
|
|
|
format_generic_odp_key(a, ds);
|
2013-06-27 22:02:58 -07:00
|
|
|
|
if (!is_exact) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_char(ds, '/');
|
|
|
|
|
format_generic_odp_key(ma, ds);
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
ds_put_char(ds, ')');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-27 22:02:58 -07:00
|
|
|
|
static struct nlattr *
|
|
|
|
|
generate_all_wildcard_mask(struct ofpbuf *ofp, const struct nlattr *key)
|
|
|
|
|
{
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
unsigned int left;
|
|
|
|
|
int type = nl_attr_type(key);
|
|
|
|
|
int size = nl_attr_get_size(key);
|
|
|
|
|
|
|
|
|
|
if (odp_flow_key_attr_len(type) >=0) {
|
2013-10-07 14:11:40 -07:00
|
|
|
|
nl_msg_put_unspec_zero(ofp, type, size);
|
2013-06-27 22:02:58 -07:00
|
|
|
|
} else {
|
|
|
|
|
size_t nested_mask;
|
|
|
|
|
|
|
|
|
|
nested_mask = nl_msg_start_nested(ofp, type);
|
|
|
|
|
NL_ATTR_FOR_EACH(a, left, key, nl_attr_get_size(key)) {
|
|
|
|
|
generate_all_wildcard_mask(ofp, nl_attr_get(a));
|
|
|
|
|
}
|
|
|
|
|
nl_msg_end_nested(ofp, nested_mask);
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-30 01:31:50 -07:00
|
|
|
|
return ofpbuf_base(ofp);
|
2013-06-27 22:02:58 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-11-12 09:49:22 -08:00
|
|
|
|
int
|
|
|
|
|
odp_ufid_from_string(const char *s_, ovs_u128 *ufid)
|
|
|
|
|
{
|
|
|
|
|
const char *s = s_;
|
|
|
|
|
|
|
|
|
|
if (ovs_scan(s, "ufid:")) {
|
|
|
|
|
size_t n;
|
|
|
|
|
|
|
|
|
|
s += 5;
|
|
|
|
|
if (ovs_scan(s, "0x")) {
|
|
|
|
|
s += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
n = strspn(s, "0123456789abcdefABCDEF");
|
|
|
|
|
if (n != 32) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-16 18:40:50 -08:00
|
|
|
|
if (!ovs_scan(s, "%16"SCNx64"%16"SCNx64, &ufid->u64.hi,
|
|
|
|
|
&ufid->u64.lo)) {
|
2014-11-12 09:49:22 -08:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2014-12-16 18:40:50 -08:00
|
|
|
|
s += n;
|
2014-11-12 09:49:22 -08:00
|
|
|
|
s += strspn(s, delimiters);
|
|
|
|
|
|
|
|
|
|
return s - s_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-24 16:26:35 +12:00
|
|
|
|
void
|
|
|
|
|
odp_format_ufid(const ovs_u128 *ufid, struct ds *ds)
|
|
|
|
|
{
|
2014-12-15 15:00:42 -08:00
|
|
|
|
ds_put_format(ds, "ufid:%016"PRIx64"%016"PRIx64, ufid->u64.hi,
|
|
|
|
|
ufid->u64.lo);
|
2014-09-24 16:26:35 +12:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
/* Appends to 'ds' a string representation of the 'key_len' bytes of
|
2013-06-19 07:15:10 +00:00
|
|
|
|
* OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the
|
2013-10-14 08:09:17 -07:00
|
|
|
|
* 'mask_len' bytes of 'mask' which apply to 'key'. If 'portno_names' is
|
|
|
|
|
* non-null and 'verbose' is true, translates odp port number to its name. */
|
2010-10-11 13:31:35 -07:00
|
|
|
|
void
|
2013-06-19 07:15:10 +00:00
|
|
|
|
odp_flow_format(const struct nlattr *key, size_t key_len,
|
|
|
|
|
const struct nlattr *mask, size_t mask_len,
|
2013-09-23 22:58:46 -07:00
|
|
|
|
const struct hmap *portno_names, struct ds *ds, bool verbose)
|
2010-10-11 13:31:35 -07:00
|
|
|
|
{
|
2011-01-23 18:44:44 -08:00
|
|
|
|
if (key_len) {
|
|
|
|
|
const struct nlattr *a;
|
|
|
|
|
unsigned int left;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
bool has_ethtype_key = false;
|
|
|
|
|
const struct nlattr *ma = NULL;
|
2013-06-27 22:02:58 -07:00
|
|
|
|
struct ofpbuf ofp;
|
2013-08-03 12:23:14 -07:00
|
|
|
|
bool first_field = true;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2013-06-27 22:02:58 -07:00
|
|
|
|
ofpbuf_init(&ofp, 100);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
NL_ATTR_FOR_EACH (a, left, key, key_len) {
|
2013-08-03 12:23:14 -07:00
|
|
|
|
bool is_nested_attr;
|
|
|
|
|
bool is_wildcard = false;
|
|
|
|
|
int attr_type = nl_attr_type(a);
|
|
|
|
|
|
|
|
|
|
if (attr_type == OVS_KEY_ATTR_ETHERTYPE) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
has_ethtype_key = true;
|
|
|
|
|
}
|
2013-08-03 12:23:14 -07:00
|
|
|
|
|
|
|
|
|
is_nested_attr = (odp_flow_key_attr_len(attr_type) == -2);
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask && mask_len) {
|
|
|
|
|
ma = nl_attr_find__(mask, mask_len, nl_attr_type(a));
|
2013-08-03 12:23:14 -07:00
|
|
|
|
is_wildcard = ma ? odp_mask_attr_is_wildcard(ma) : true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (verbose || !is_wildcard || is_nested_attr) {
|
|
|
|
|
if (is_wildcard && !ma) {
|
2013-06-27 22:02:58 -07:00
|
|
|
|
ma = generate_all_wildcard_mask(&ofp, a);
|
|
|
|
|
}
|
2013-08-03 12:23:14 -07:00
|
|
|
|
if (!first_field) {
|
|
|
|
|
ds_put_char(ds, ',');
|
|
|
|
|
}
|
2013-09-23 22:58:46 -07:00
|
|
|
|
format_odp_key_attr(a, ma, portno_names, ds, verbose);
|
2013-08-03 12:23:14 -07:00
|
|
|
|
first_field = false;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2013-06-27 22:02:58 -07:00
|
|
|
|
ofpbuf_clear(&ofp);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2013-06-27 22:02:58 -07:00
|
|
|
|
ofpbuf_uninit(&ofp);
|
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
if (left) {
|
2012-01-26 16:21:50 -08:00
|
|
|
|
int i;
|
2013-09-16 17:23:31 -07:00
|
|
|
|
|
2011-01-23 18:44:44 -08:00
|
|
|
|
if (left == key_len) {
|
|
|
|
|
ds_put_cstr(ds, "<empty>");
|
|
|
|
|
}
|
2012-01-26 16:21:50 -08:00
|
|
|
|
ds_put_format(ds, ",***%u leftover bytes*** (", left);
|
|
|
|
|
for (i = 0; i < left; i++) {
|
|
|
|
|
ds_put_format(ds, "%02x", ((const uint8_t *) a)[i]);
|
|
|
|
|
}
|
|
|
|
|
ds_put_char(ds, ')');
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (!has_ethtype_key) {
|
|
|
|
|
ma = nl_attr_find__(mask, mask_len, OVS_KEY_ATTR_ETHERTYPE);
|
|
|
|
|
if (ma) {
|
|
|
|
|
ds_put_format(ds, ",eth_type(0/0x%04"PRIx16")",
|
|
|
|
|
ntohs(nl_attr_get_be16(ma)));
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
} else {
|
|
|
|
|
ds_put_cstr(ds, "<empty>");
|
|
|
|
|
}
|
2010-10-11 13:31:35 -07:00
|
|
|
|
}
|
2009-07-08 13:19:16 -07:00
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
/* Appends to 'ds' a string representation of the 'key_len' bytes of
|
|
|
|
|
* OVS_KEY_ATTR_* attributes in 'key'. */
|
|
|
|
|
void
|
|
|
|
|
odp_flow_key_format(const struct nlattr *key,
|
|
|
|
|
size_t key_len, struct ds *ds)
|
|
|
|
|
{
|
2013-09-23 22:58:46 -07:00
|
|
|
|
odp_flow_format(key, key_len, NULL, 0, NULL, ds, true);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
static bool
|
|
|
|
|
ovs_frag_type_from_string(const char *s, enum ovs_frag_type *type)
|
|
|
|
|
{
|
|
|
|
|
if (!strcasecmp(s, "no")) {
|
|
|
|
|
*type = OVS_FRAG_TYPE_NONE;
|
|
|
|
|
} else if (!strcasecmp(s, "first")) {
|
|
|
|
|
*type = OVS_FRAG_TYPE_FIRST;
|
|
|
|
|
} else if (!strcasecmp(s, "later")) {
|
|
|
|
|
*type = OVS_FRAG_TYPE_LATER;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
/* Parsing. */
|
2013-01-25 16:22:07 +09:00
|
|
|
|
|
2011-08-04 16:20:34 -07:00
|
|
|
|
static int
|
2014-09-09 14:50:36 -07:00
|
|
|
|
scan_eth(const char *s, uint8_t (*key)[ETH_ADDR_LEN],
|
|
|
|
|
uint8_t (*mask)[ETH_ADDR_LEN])
|
2011-08-04 16:20:34 -07:00
|
|
|
|
{
|
2014-09-09 14:50:36 -07:00
|
|
|
|
int n;
|
2011-11-01 10:13:16 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(*key), &n)) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/"ETH_ADDR_SCAN_FMT"%n",
|
|
|
|
|
ETH_ADDR_SCAN_ARGS(*mask), &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
memset(mask, 0xff, sizeof *mask);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-11-01 10:13:16 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-11-01 10:13:16 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-11-01 10:13:16 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_ipv4(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
2012-11-13 19:19:36 +02:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, IP_SCAN_FMT"%n", IP_SCAN_ARGS(key), &n)) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/"IP_SCAN_FMT"%n",
|
|
|
|
|
IP_SCAN_ARGS(mask), &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
*mask = OVS_BE32_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2012-11-13 19:19:36 +02:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2012-11-13 19:19:36 +02:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-11-13 19:19:36 +02:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_ipv6(const char *s, ovs_be32 (*key)[4], ovs_be32 (*mask)[4])
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
char ipv6_s[IPV6_SCAN_LEN + 1];
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n)
|
|
|
|
|
&& inet_pton(AF_INET6, ipv6_s, key) == 1) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/"IPV6_SCAN_FMT"%n", ipv6_s, &n)
|
|
|
|
|
&& inet_pton(AF_INET6, ipv6_s, mask) == 1) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
memset(mask, 0xff, sizeof *mask);
|
2014-08-26 18:34:52 +02:00
|
|
|
|
}
|
2014-03-04 15:36:03 -08:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2014-03-04 15:36:03 -08:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_ipv6_label(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
int key_, mask_;
|
|
|
|
|
int n;
|
2014-03-04 15:36:03 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%i%n", &key_, &n)
|
|
|
|
|
&& (key_ & ~IPV6_LABEL_MASK) == 0) {
|
|
|
|
|
int len = n;
|
2012-11-14 21:10:54 -08:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*key = htonl(key_);
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%i%n", &mask_, &n)
|
|
|
|
|
&& (mask_ & ~IPV6_LABEL_MASK) == 0) {
|
|
|
|
|
len += n;
|
|
|
|
|
*mask = htonl(mask_);
|
|
|
|
|
} else {
|
|
|
|
|
*mask = htonl(IPV6_LABEL_MASK);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2012-11-01 15:50:16 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2012-11-01 15:50:16 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2012-11-01 15:50:16 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_u8(const char *s, uint8_t *key, uint8_t *mask)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi8"%n", key, &n)) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi8"%n", mask, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
*mask = UINT8_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_u32(const char *s, uint32_t *key, uint32_t *mask)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi32"%n", key, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi32"%n", mask, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
*mask = UINT32_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-10-26 10:01:32 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-10-26 10:01:32 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-10-26 10:01:32 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_be16(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
uint16_t key_, mask_;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi16"%n", &key_, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*key = htons(key_);
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi16"%n", &mask_, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
*mask = htons(mask_);
|
|
|
|
|
} else {
|
|
|
|
|
*mask = OVS_BE16_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_be64(const char *s, ovs_be64 *key, ovs_be64 *mask)
|
|
|
|
|
{
|
|
|
|
|
uint64_t key_, mask_;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi64"%n", &key_, &n)) {
|
|
|
|
|
int len = n;
|
|
|
|
|
|
|
|
|
|
*key = htonll(key_);
|
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi64"%n", &mask_, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
*mask = htonll(mask_);
|
|
|
|
|
} else {
|
|
|
|
|
*mask = OVS_BE64_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_tun_flags(const char *s, uint16_t *key, uint16_t *mask)
|
|
|
|
|
{
|
|
|
|
|
uint32_t flags, fmask;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
n = parse_flags(s, flow_tun_flag_to_string, &flags,
|
|
|
|
|
FLOW_TNL_F_MASK, mask ? &fmask : NULL);
|
|
|
|
|
if (n >= 0 && s[n] == ')') {
|
|
|
|
|
*key = flags;
|
|
|
|
|
if (mask) {
|
|
|
|
|
*mask = fmask;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return n + 1;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_tcp_flags(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
uint32_t flags, fmask;
|
|
|
|
|
int n;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
n = parse_flags(s, packet_tcp_flag_to_string, &flags,
|
|
|
|
|
TCP_FLAGS(OVS_BE16_MAX), mask ? &fmask : NULL);
|
|
|
|
|
if (n >= 0) {
|
|
|
|
|
*key = htons(flags);
|
|
|
|
|
if (mask) {
|
|
|
|
|
*mask = htons(fmask);
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return n;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_frag(const char *s, uint8_t *key, uint8_t *mask)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
char frag[8];
|
|
|
|
|
enum ovs_frag_type frag_type;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%7[a-z]%n", frag, &n)
|
|
|
|
|
&& ovs_frag_type_from_string(frag, &frag_type)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*key = frag_type;
|
|
|
|
|
if (mask) {
|
|
|
|
|
*mask = UINT8_MAX;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_port(const char *s, uint32_t *key, uint32_t *mask,
|
|
|
|
|
const struct simap *port_names)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi32"%n", key, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (mask) {
|
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi32"%n", mask, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
} else {
|
|
|
|
|
*mask = UINT32_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
|
|
|
|
} else if (port_names) {
|
|
|
|
|
const struct simap_node *node;
|
|
|
|
|
int len;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
len = strcspn(s, ")");
|
|
|
|
|
node = simap_find_len(port_names, s, len);
|
|
|
|
|
if (node) {
|
|
|
|
|
*key = node->data;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
|
|
|
|
if (mask) {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*mask = UINT32_MAX;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
/* Helper for vlan parsing. */
|
|
|
|
|
struct ovs_key_vlan__ {
|
|
|
|
|
ovs_be16 tci;
|
|
|
|
|
};
|
2013-10-28 13:54:40 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static bool
|
|
|
|
|
set_be16_bf(ovs_be16 *bf, uint8_t bits, uint8_t offset, uint16_t value)
|
|
|
|
|
{
|
|
|
|
|
const uint16_t mask = ((1U << bits) - 1) << offset;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (value >> bits) {
|
|
|
|
|
return false;
|
2013-10-28 13:54:40 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*bf = htons((ntohs(*bf) & ~mask) | (value << offset));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_be16_bf(const char *s, ovs_be16 *key, ovs_be16 *mask, uint8_t bits,
|
|
|
|
|
uint8_t offset)
|
|
|
|
|
{
|
|
|
|
|
uint16_t key_, mask_;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi16"%n", &key_, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (set_be16_bf(key, bits, offset, key_)) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask) {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi16"%n", &mask_, &n)) {
|
|
|
|
|
len += n;
|
|
|
|
|
|
|
|
|
|
if (!set_be16_bf(mask, bits, offset, mask_)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
*mask |= htons(((1U << bits) - 1) << offset);
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_vid(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be16_bf(s, key, mask, 12, VLAN_VID_SHIFT);
|
|
|
|
|
}
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_pcp(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be16_bf(s, key, mask, 3, VLAN_PCP_SHIFT);
|
|
|
|
|
}
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_cfi(const char *s, ovs_be16 *key, ovs_be16 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be16_bf(s, key, mask, 1, VLAN_CFI_SHIFT);
|
|
|
|
|
}
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
/* For MPLS. */
|
|
|
|
|
static bool
|
|
|
|
|
set_be32_bf(ovs_be32 *bf, uint8_t bits, uint8_t offset, uint32_t value)
|
|
|
|
|
{
|
|
|
|
|
const uint32_t mask = ((1U << bits) - 1) << offset;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (value >> bits) {
|
|
|
|
|
return false;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
*bf = htonl((ntohl(*bf) & ~mask) | (value << offset));
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_be32_bf(const char *s, ovs_be32 *key, ovs_be32 *mask, uint8_t bits,
|
|
|
|
|
uint8_t offset)
|
|
|
|
|
{
|
|
|
|
|
uint32_t key_, mask_;
|
|
|
|
|
int n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s, "%"SCNi32"%n", &key_, &n)) {
|
|
|
|
|
int len = n;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (set_be32_bf(key, bits, offset, key_)) {
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask) {
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (ovs_scan(s + len, "/%"SCNi32"%n", &mask_, &n)) {
|
|
|
|
|
len += n;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
if (!set_be32_bf(mask, bits, offset, mask_)) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
*mask |= htonl(((1U << bits) - 1) << offset);
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return len;
|
2011-08-04 16:20:34 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-09 14:50:36 -07:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_mpls_label(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be32_bf(s, key, mask, 20, MPLS_LABEL_SHIFT);
|
|
|
|
|
}
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_mpls_tc(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be32_bf(s, key, mask, 3, MPLS_TC_SHIFT);
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_mpls_ttl(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be32_bf(s, key, mask, 8, MPLS_TTL_SHIFT);
|
|
|
|
|
}
|
2013-06-19 07:15:10 +00:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
scan_mpls_bos(const char *s, ovs_be32 *key, ovs_be32 *mask)
|
|
|
|
|
{
|
|
|
|
|
return scan_be32_bf(s, key, mask, 1, MPLS_BOS_SHIFT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ATTR is compile-time constant, so only the case with correct data type
|
|
|
|
|
* will be used. However, the compiler complains about the data type for
|
|
|
|
|
* the other cases, so we must cast to make the compiler silent. */
|
|
|
|
|
#define SCAN_PUT_ATTR(BUF, ATTR, DATA) \
|
|
|
|
|
if ((ATTR) == OVS_KEY_ATTR_TUNNEL) { \
|
|
|
|
|
tun_key_to_attr(BUF, (const struct flow_tnl *)(void *)&(DATA)); \
|
|
|
|
|
} else { \
|
|
|
|
|
nl_msg_put_unspec(BUF, ATTR, &(DATA), sizeof (DATA)); \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_IF(NAME) \
|
|
|
|
|
if (strncmp(s, NAME, strlen(NAME)) == 0) { \
|
|
|
|
|
const char *start = s; \
|
|
|
|
|
int len; \
|
|
|
|
|
\
|
|
|
|
|
s += strlen(NAME)
|
|
|
|
|
|
|
|
|
|
/* Usually no special initialization is needed. */
|
|
|
|
|
#define SCAN_BEGIN(NAME, TYPE) \
|
|
|
|
|
SCAN_IF(NAME); \
|
|
|
|
|
TYPE skey, smask; \
|
|
|
|
|
memset(&skey, 0, sizeof skey); \
|
|
|
|
|
memset(&smask, 0, sizeof smask); \
|
|
|
|
|
do { \
|
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
|
|
/* VLAN needs special initialization. */
|
|
|
|
|
#define SCAN_BEGIN_INIT(NAME, TYPE, KEY_INIT, MASK_INIT) \
|
|
|
|
|
SCAN_IF(NAME); \
|
|
|
|
|
TYPE skey = KEY_INIT; \
|
|
|
|
|
TYPE smask = MASK_INIT; \
|
|
|
|
|
do { \
|
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
|
|
/* Scan unnamed entry as 'TYPE' */
|
|
|
|
|
#define SCAN_TYPE(TYPE, KEY, MASK) \
|
|
|
|
|
len = scan_##TYPE(s, KEY, MASK); \
|
|
|
|
|
if (len == 0) { \
|
|
|
|
|
return -EINVAL; \
|
|
|
|
|
} \
|
|
|
|
|
s += len
|
|
|
|
|
|
|
|
|
|
/* Scan named ('NAME') entry 'FIELD' as 'TYPE'. */
|
|
|
|
|
#define SCAN_FIELD(NAME, TYPE, FIELD) \
|
|
|
|
|
if (strncmp(s, NAME, strlen(NAME)) == 0) { \
|
|
|
|
|
s += strlen(NAME); \
|
|
|
|
|
SCAN_TYPE(TYPE, &skey.FIELD, mask ? &smask.FIELD : NULL); \
|
|
|
|
|
continue; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_FINISH() \
|
|
|
|
|
} while (*s++ == ',' && len != 0); \
|
|
|
|
|
if (s[-1] != ')') { \
|
|
|
|
|
return -EINVAL; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_FINISH_SINGLE() \
|
|
|
|
|
} while (false); \
|
|
|
|
|
if (*s++ != ')') { \
|
|
|
|
|
return -EINVAL; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_PUT(ATTR) \
|
|
|
|
|
if (!mask || !is_all_zeros(&smask, sizeof smask)) { \
|
|
|
|
|
SCAN_PUT_ATTR(key, ATTR, skey); \
|
|
|
|
|
if (mask) { \
|
|
|
|
|
SCAN_PUT_ATTR(mask, ATTR, smask); \
|
|
|
|
|
} \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_END(ATTR) \
|
|
|
|
|
SCAN_FINISH(); \
|
|
|
|
|
SCAN_PUT(ATTR); \
|
|
|
|
|
return s - start; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_END_SINGLE(ATTR) \
|
|
|
|
|
SCAN_FINISH_SINGLE(); \
|
|
|
|
|
SCAN_PUT(ATTR); \
|
|
|
|
|
return s - start; \
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define SCAN_SINGLE(NAME, TYPE, SCAN_AS, ATTR) \
|
|
|
|
|
SCAN_BEGIN(NAME, TYPE) { \
|
|
|
|
|
SCAN_TYPE(SCAN_AS, &skey, &smask); \
|
|
|
|
|
} SCAN_END_SINGLE(ATTR)
|
|
|
|
|
|
|
|
|
|
#define SCAN_SINGLE_NO_MASK(NAME, TYPE, SCAN_AS, ATTR) \
|
|
|
|
|
SCAN_BEGIN(NAME, TYPE) { \
|
|
|
|
|
SCAN_TYPE(SCAN_AS, &skey, NULL); \
|
|
|
|
|
} SCAN_END_SINGLE(ATTR)
|
|
|
|
|
|
|
|
|
|
/* scan_port needs one extra argument. */
|
|
|
|
|
#define SCAN_SINGLE_PORT(NAME, TYPE, ATTR) \
|
|
|
|
|
SCAN_BEGIN(NAME, TYPE) { \
|
|
|
|
|
len = scan_port(s, &skey, &smask, port_names); \
|
|
|
|
|
if (len == 0) { \
|
|
|
|
|
return -EINVAL; \
|
|
|
|
|
} \
|
|
|
|
|
s += len; \
|
|
|
|
|
} SCAN_END_SINGLE(ATTR)
|
2011-08-04 16:20:34 -07:00
|
|
|
|
|
2014-09-09 14:50:36 -07:00
|
|
|
|
static int
|
|
|
|
|
parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *key, struct ofpbuf *mask)
|
|
|
|
|
{
|
|
|
|
|
SCAN_SINGLE("skb_priority(", uint32_t, u32, OVS_KEY_ATTR_PRIORITY);
|
|
|
|
|
SCAN_SINGLE("skb_mark(", uint32_t, u32, OVS_KEY_ATTR_SKB_MARK);
|
|
|
|
|
SCAN_SINGLE_NO_MASK("recirc_id(", uint32_t, u32, OVS_KEY_ATTR_RECIRC_ID);
|
|
|
|
|
SCAN_SINGLE("dp_hash(", uint32_t, u32, OVS_KEY_ATTR_DP_HASH);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("tunnel(", struct flow_tnl) {
|
|
|
|
|
SCAN_FIELD("tun_id=", be64, tun_id);
|
|
|
|
|
SCAN_FIELD("src=", ipv4, ip_src);
|
|
|
|
|
SCAN_FIELD("dst=", ipv4, ip_dst);
|
|
|
|
|
SCAN_FIELD("tos=", u8, ip_tos);
|
|
|
|
|
SCAN_FIELD("ttl=", u8, ip_ttl);
|
|
|
|
|
SCAN_FIELD("tp_src=", be16, tp_src);
|
|
|
|
|
SCAN_FIELD("tp_dst=", be16, tp_dst);
|
|
|
|
|
SCAN_FIELD("flags(", tun_flags, flags);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_TUNNEL);
|
|
|
|
|
|
|
|
|
|
SCAN_SINGLE_PORT("in_port(", uint32_t, OVS_KEY_ATTR_IN_PORT);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("eth(", struct ovs_key_ethernet) {
|
|
|
|
|
SCAN_FIELD("src=", eth, eth_src);
|
|
|
|
|
SCAN_FIELD("dst=", eth, eth_dst);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ETHERNET);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN_INIT("vlan(", struct ovs_key_vlan__,
|
|
|
|
|
{ htons(VLAN_CFI) }, { htons(VLAN_CFI) }) {
|
|
|
|
|
SCAN_FIELD("vid=", vid, tci);
|
|
|
|
|
SCAN_FIELD("pcp=", pcp, tci);
|
|
|
|
|
SCAN_FIELD("cfi=", cfi, tci);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_VLAN);
|
|
|
|
|
|
|
|
|
|
SCAN_SINGLE("eth_type(", ovs_be16, be16, OVS_KEY_ATTR_ETHERTYPE);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("mpls(", struct ovs_key_mpls) {
|
|
|
|
|
SCAN_FIELD("label=", mpls_label, mpls_lse);
|
|
|
|
|
SCAN_FIELD("tc=", mpls_tc, mpls_lse);
|
|
|
|
|
SCAN_FIELD("ttl=", mpls_ttl, mpls_lse);
|
|
|
|
|
SCAN_FIELD("bos=", mpls_bos, mpls_lse);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_MPLS);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("ipv4(", struct ovs_key_ipv4) {
|
|
|
|
|
SCAN_FIELD("src=", ipv4, ipv4_src);
|
|
|
|
|
SCAN_FIELD("dst=", ipv4, ipv4_dst);
|
|
|
|
|
SCAN_FIELD("proto=", u8, ipv4_proto);
|
|
|
|
|
SCAN_FIELD("tos=", u8, ipv4_tos);
|
|
|
|
|
SCAN_FIELD("ttl=", u8, ipv4_ttl);
|
|
|
|
|
SCAN_FIELD("frag=", frag, ipv4_frag);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_IPV4);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("ipv6(", struct ovs_key_ipv6) {
|
|
|
|
|
SCAN_FIELD("src=", ipv6, ipv6_src);
|
|
|
|
|
SCAN_FIELD("dst=", ipv6, ipv6_dst);
|
|
|
|
|
SCAN_FIELD("label=", ipv6_label, ipv6_label);
|
|
|
|
|
SCAN_FIELD("proto=", u8, ipv6_proto);
|
|
|
|
|
SCAN_FIELD("tclass=", u8, ipv6_tclass);
|
|
|
|
|
SCAN_FIELD("hlimit=", u8, ipv6_hlimit);
|
|
|
|
|
SCAN_FIELD("frag=", frag, ipv6_frag);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_IPV6);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("tcp(", struct ovs_key_tcp) {
|
|
|
|
|
SCAN_FIELD("src=", be16, tcp_src);
|
|
|
|
|
SCAN_FIELD("dst=", be16, tcp_dst);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_TCP);
|
|
|
|
|
|
|
|
|
|
SCAN_SINGLE("tcp_flags(", ovs_be16, tcp_flags, OVS_KEY_ATTR_TCP_FLAGS);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("udp(", struct ovs_key_udp) {
|
|
|
|
|
SCAN_FIELD("src=", be16, udp_src);
|
|
|
|
|
SCAN_FIELD("dst=", be16, udp_dst);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_UDP);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("sctp(", struct ovs_key_sctp) {
|
|
|
|
|
SCAN_FIELD("src=", be16, sctp_src);
|
|
|
|
|
SCAN_FIELD("dst=", be16, sctp_dst);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_SCTP);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("icmp(", struct ovs_key_icmp) {
|
|
|
|
|
SCAN_FIELD("type=", u8, icmp_type);
|
|
|
|
|
SCAN_FIELD("code=", u8, icmp_code);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ICMP);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("icmpv6(", struct ovs_key_icmpv6) {
|
|
|
|
|
SCAN_FIELD("type=", u8, icmpv6_type);
|
|
|
|
|
SCAN_FIELD("code=", u8, icmpv6_code);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ICMPV6);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("arp(", struct ovs_key_arp) {
|
|
|
|
|
SCAN_FIELD("sip=", ipv4, arp_sip);
|
|
|
|
|
SCAN_FIELD("tip=", ipv4, arp_tip);
|
|
|
|
|
SCAN_FIELD("op=", be16, arp_op);
|
|
|
|
|
SCAN_FIELD("sha=", eth, arp_sha);
|
|
|
|
|
SCAN_FIELD("tha=", eth, arp_tha);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ARP);
|
|
|
|
|
|
|
|
|
|
SCAN_BEGIN("nd(", struct ovs_key_nd) {
|
|
|
|
|
SCAN_FIELD("target=", ipv6, nd_target);
|
|
|
|
|
SCAN_FIELD("sll=", eth, nd_sll);
|
|
|
|
|
SCAN_FIELD("tll=", eth, nd_tll);
|
|
|
|
|
} SCAN_END(OVS_KEY_ATTR_ND);
|
|
|
|
|
|
|
|
|
|
/* Encap open-coded. */
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (!strncmp(s, "encap(", 6)) {
|
|
|
|
|
const char *start = s;
|
2013-06-19 07:15:10 +00:00
|
|
|
|
size_t encap, encap_mask = 0;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
|
|
|
|
|
encap = nl_msg_start_nested(key, OVS_KEY_ATTR_ENCAP);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask) {
|
|
|
|
|
encap_mask = nl_msg_start_nested(mask, OVS_KEY_ATTR_ENCAP);
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
|
|
|
|
|
s += 6;
|
|
|
|
|
for (;;) {
|
|
|
|
|
int retval;
|
|
|
|
|
|
2014-12-05 14:07:19 -08:00
|
|
|
|
s += strspn(s, delimiters);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (!*s) {
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
} else if (*s == ')') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
retval = parse_odp_key_mask_attr(s, port_names, key, mask);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (retval < 0) {
|
|
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
s += retval;
|
|
|
|
|
}
|
|
|
|
|
s++;
|
|
|
|
|
|
|
|
|
|
nl_msg_end_nested(key, encap);
|
2013-06-19 07:15:10 +00:00
|
|
|
|
if (mask) {
|
|
|
|
|
nl_msg_end_nested(mask, encap_mask);
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
|
|
|
|
|
return s - start;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-04 16:20:34 -07:00
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
/* Parses the string representation of a datapath flow key, in the
|
|
|
|
|
* format output by odp_flow_key_format(). Returns 0 if successful,
|
|
|
|
|
* otherwise a positive errno value. On success, the flow key is
|
|
|
|
|
* appended to 'key' as a series of Netlink attributes. On failure, no
|
|
|
|
|
* data is appended to 'key'. Either way, 'key''s data might be
|
|
|
|
|
* reallocated.
|
2011-08-04 16:20:34 -07:00
|
|
|
|
*
|
2012-05-22 10:32:02 -07:00
|
|
|
|
* If 'port_names' is nonnull, it points to an simap that maps from a port name
|
|
|
|
|
* to a port number. (Port names may be used instead of port numbers in
|
|
|
|
|
* in_port.)
|
2011-10-26 10:01:32 -07:00
|
|
|
|
*
|
2011-08-04 16:20:34 -07:00
|
|
|
|
* On success, the attributes appended to 'key' are individually syntactically
|
|
|
|
|
* valid, but they may not be valid as a sequence. 'key' might, for example,
|
2011-11-14 15:09:01 -08:00
|
|
|
|
* have duplicated keys. odp_flow_key_to_flow() will detect those errors. */
|
2011-08-04 16:20:34 -07:00
|
|
|
|
int
|
2013-06-19 07:15:10 +00:00
|
|
|
|
odp_flow_from_string(const char *s, const struct simap *port_names,
|
|
|
|
|
struct ofpbuf *key, struct ofpbuf *mask)
|
2011-08-04 16:20:34 -07:00
|
|
|
|
{
|
2014-03-30 01:31:50 -07:00
|
|
|
|
const size_t old_size = ofpbuf_size(key);
|
2011-08-04 16:20:34 -07:00
|
|
|
|
for (;;) {
|
|
|
|
|
int retval;
|
|
|
|
|
|
2011-11-11 15:22:56 -08:00
|
|
|
|
s += strspn(s, delimiters);
|
2011-08-04 16:20:34 -07:00
|
|
|
|
if (!*s) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-19 07:15:10 +00:00
|
|
|
|
retval = parse_odp_key_mask_attr(s, port_names, key, mask);
|
2011-08-04 16:20:34 -07:00
|
|
|
|
if (retval < 0) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
ofpbuf_set_size(key, old_size);
|
2011-08-04 16:20:34 -07:00
|
|
|
|
return -retval;
|
|
|
|
|
}
|
|
|
|
|
s += retval;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
static uint8_t
|
2014-09-05 15:44:20 -07:00
|
|
|
|
ovs_to_odp_frag(uint8_t nw_frag, bool is_mask)
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
{
|
2014-09-05 15:44:20 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
/* Netlink interface 'enum ovs_frag_type' is an 8-bit enumeration type,
|
|
|
|
|
* not a set of flags or bitfields. Hence, if the struct flow nw_frag
|
|
|
|
|
* mask, which is a set of bits, has the FLOW_NW_FRAG_ANY as zero, we
|
|
|
|
|
* must use a zero mask for the netlink frag field, and all ones mask
|
|
|
|
|
* otherwise. */
|
|
|
|
|
return (nw_frag & FLOW_NW_FRAG_ANY) ? UINT8_MAX : 0;
|
|
|
|
|
}
|
2014-09-05 15:44:20 -07:00
|
|
|
|
return !(nw_frag & FLOW_NW_FRAG_ANY) ? OVS_FRAG_TYPE_NONE
|
|
|
|
|
: nw_frag & FLOW_NW_FRAG_LATER ? OVS_FRAG_TYPE_LATER
|
|
|
|
|
: OVS_FRAG_TYPE_FIRST;
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
static void get_ethernet_key(const struct flow *, struct ovs_key_ethernet *);
|
|
|
|
|
static void put_ethernet_key(const struct ovs_key_ethernet *, struct flow *);
|
|
|
|
|
static void get_ipv4_key(const struct flow *, struct ovs_key_ipv4 *,
|
|
|
|
|
bool is_mask);
|
|
|
|
|
static void put_ipv4_key(const struct ovs_key_ipv4 *, struct flow *,
|
|
|
|
|
bool is_mask);
|
|
|
|
|
static void get_ipv6_key(const struct flow *, struct ovs_key_ipv6 *,
|
|
|
|
|
bool is_mask);
|
|
|
|
|
static void put_ipv6_key(const struct ovs_key_ipv6 *, struct flow *,
|
|
|
|
|
bool is_mask);
|
|
|
|
|
static void get_arp_key(const struct flow *, struct ovs_key_arp *);
|
|
|
|
|
static void put_arp_key(const struct ovs_key_arp *, struct flow *);
|
2014-12-23 23:42:05 +00:00
|
|
|
|
static void get_nd_key(const struct flow *, struct ovs_key_nd *);
|
|
|
|
|
static void put_nd_key(const struct ovs_key_nd *, struct flow *);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
|
|
|
|
|
/* These share the same layout. */
|
|
|
|
|
union ovs_key_tp {
|
|
|
|
|
struct ovs_key_tcp tcp;
|
|
|
|
|
struct ovs_key_udp udp;
|
|
|
|
|
struct ovs_key_sctp sctp;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void get_tp_key(const struct flow *, union ovs_key_tp *);
|
|
|
|
|
static void put_tp_key(const union ovs_key_tp *, struct flow *);
|
2013-07-31 13:54:12 -07:00
|
|
|
|
|
2013-06-05 18:56:58 -07:00
|
|
|
|
static void
|
2014-04-17 23:13:46 -07:00
|
|
|
|
odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *flow,
|
|
|
|
|
const struct flow *mask, odp_port_t odp_in_port,
|
2014-05-09 13:58:32 +12:00
|
|
|
|
size_t max_mpls_depth, bool recirc, bool export_mask)
|
2010-10-11 13:31:35 -07:00
|
|
|
|
{
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_ethernet *eth_key;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
size_t encap;
|
2014-04-17 23:13:46 -07:00
|
|
|
|
const struct flow *data = export_mask ? mask : flow;
|
2013-06-05 18:56:58 -07:00
|
|
|
|
|
2013-08-03 12:23:15 -07:00
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, data->skb_priority);
|
2011-11-01 10:13:16 -07:00
|
|
|
|
|
2014-04-17 23:13:46 -07:00
|
|
|
|
if (flow->tunnel.ip_dst || export_mask) {
|
2013-06-05 18:56:58 -07:00
|
|
|
|
tun_key_to_attr(buf, &data->tunnel);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-06 12:57:13 -07:00
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->pkt_mark);
|
2012-11-13 19:19:36 +02:00
|
|
|
|
|
2014-05-09 13:58:32 +12:00
|
|
|
|
if (recirc) {
|
2014-03-04 15:36:03 -08:00
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_RECIRC_ID, data->recirc_id);
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_DP_HASH, data->dp_hash);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-05 18:56:58 -07:00
|
|
|
|
/* Add an ingress port attribute if this is a mask or 'odp_in_port'
|
|
|
|
|
* is not the magical value "ODPP_NONE". */
|
2014-04-17 23:13:46 -07:00
|
|
|
|
if (export_mask || odp_in_port != ODPP_NONE) {
|
2013-06-19 16:58:44 -07:00
|
|
|
|
nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, odp_in_port);
|
2011-09-08 16:30:20 -07:00
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
eth_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ETHERNET,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *eth_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ethernet_key(data, eth_key);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 17:19:41 -08:00
|
|
|
|
if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) {
|
2014-04-17 23:13:46 -07:00
|
|
|
|
if (export_mask) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
|
2013-06-27 17:57:57 -07:00
|
|
|
|
} else {
|
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
|
|
|
|
|
}
|
2013-06-05 18:56:58 -07:00
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_VLAN, data->vlan_tci);
|
2011-11-14 15:56:43 -08:00
|
|
|
|
encap = nl_msg_start_nested(buf, OVS_KEY_ATTR_ENCAP);
|
2011-11-14 17:19:41 -08:00
|
|
|
|
if (flow->vlan_tci == htons(0)) {
|
|
|
|
|
goto unencap;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
} else {
|
|
|
|
|
encap = 0;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ntohs(flow->dl_type) < ETH_TYPE_MIN) {
|
2013-06-05 18:56:58 -07:00
|
|
|
|
/* For backwards compatibility with kernels that don't support
|
|
|
|
|
* wildcarding, the following convention is used to encode the
|
|
|
|
|
* OVS_KEY_ATTR_ETHERTYPE for key and mask:
|
|
|
|
|
*
|
|
|
|
|
* key mask matches
|
|
|
|
|
* -------- -------- -------
|
|
|
|
|
* >0x5ff 0xffff Specified Ethernet II Ethertype.
|
|
|
|
|
* >0x5ff 0 Any Ethernet II or non-Ethernet II frame.
|
|
|
|
|
* <none> 0xffff Any non-Ethernet II frame (except valid
|
|
|
|
|
* 802.3 SNAP packet with valid eth_type).
|
|
|
|
|
*/
|
2014-04-17 23:13:46 -07:00
|
|
|
|
if (export_mask) {
|
2013-06-27 15:27:15 -07:00
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
goto unencap;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-05 18:56:58 -07:00
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, data->dl_type);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
|
|
|
|
if (flow->dl_type == htons(ETH_TYPE_IP)) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_ipv4 *ipv4_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
ipv4_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV4,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *ipv4_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv4_key(data, ipv4_key, export_mask);
|
2010-12-29 19:03:46 -08:00
|
|
|
|
} else if (flow->dl_type == htons(ETH_TYPE_IPV6)) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_ipv6 *ipv6_key;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
ipv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_IPV6,
|
2010-12-29 19:03:46 -08:00
|
|
|
|
sizeof *ipv6_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv6_key(data, ipv6_key, export_mask);
|
2012-11-02 11:43:46 -07:00
|
|
|
|
} else if (flow->dl_type == htons(ETH_TYPE_ARP) ||
|
|
|
|
|
flow->dl_type == htons(ETH_TYPE_RARP)) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_arp *arp_key;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
arp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ARP,
|
|
|
|
|
sizeof *arp_key);
|
|
|
|
|
get_arp_key(data, arp_key);
|
2013-09-27 06:55:19 +09:00
|
|
|
|
} else if (eth_type_mpls(flow->dl_type)) {
|
2013-01-25 16:22:07 +09:00
|
|
|
|
struct ovs_key_mpls *mpls_key;
|
2014-02-04 10:32:35 -08:00
|
|
|
|
int i, n;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
n = flow_count_mpls_labels(flow, NULL);
|
|
|
|
|
n = MIN(n, max_mpls_depth);
|
2013-01-25 16:22:07 +09:00
|
|
|
|
mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
|
2014-02-04 10:32:35 -08:00
|
|
|
|
n * sizeof *mpls_key);
|
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
|
mpls_key[i].mpls_lse = data->mpls_lse[i];
|
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
2013-01-22 19:38:32 -08:00
|
|
|
|
if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
2011-02-02 11:33:20 -08:00
|
|
|
|
if (flow->nw_proto == IPPROTO_TCP) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
union ovs_key_tp *tcp_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
tcp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_TCP,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *tcp_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_tp_key(data, tcp_key);
|
2013-10-28 13:54:40 -07:00
|
|
|
|
if (data->tcp_flags) {
|
|
|
|
|
nl_msg_put_be16(buf, OVS_KEY_ATTR_TCP_FLAGS, data->tcp_flags);
|
|
|
|
|
}
|
2011-02-02 11:33:20 -08:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_UDP) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
union ovs_key_tp *udp_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
udp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_UDP,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *udp_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_tp_key(data, udp_key);
|
2013-08-22 20:24:44 +12:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_SCTP) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
union ovs_key_tp *sctp_key;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
|
|
|
|
sctp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_SCTP,
|
|
|
|
|
sizeof *sctp_key);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_tp_key(data, sctp_key);
|
2010-12-29 19:03:46 -08:00
|
|
|
|
} else if (flow->dl_type == htons(ETH_TYPE_IP)
|
|
|
|
|
&& flow->nw_proto == IPPROTO_ICMP) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_icmp *icmp_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
icmp_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMP,
|
2011-01-23 18:44:44 -08:00
|
|
|
|
sizeof *icmp_key);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
icmp_key->icmp_type = ntohs(data->tp_src);
|
|
|
|
|
icmp_key->icmp_code = ntohs(data->tp_dst);
|
2010-12-29 19:03:46 -08:00
|
|
|
|
} else if (flow->dl_type == htons(ETH_TYPE_IPV6)
|
|
|
|
|
&& flow->nw_proto == IPPROTO_ICMPV6) {
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_icmpv6 *icmpv6_key;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
icmpv6_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ICMPV6,
|
2010-12-29 19:03:46 -08:00
|
|
|
|
sizeof *icmpv6_key);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
icmpv6_key->icmpv6_type = ntohs(data->tp_src);
|
|
|
|
|
icmpv6_key->icmpv6_code = ntohs(data->tp_dst);
|
2011-02-01 22:54:11 -08:00
|
|
|
|
|
2014-05-15 15:56:30 +12:00
|
|
|
|
if (flow->tp_dst == htons(0)
|
|
|
|
|
&& (flow->tp_src == htons(ND_NEIGHBOR_SOLICIT)
|
|
|
|
|
|| flow->tp_src == htons(ND_NEIGHBOR_ADVERT))
|
|
|
|
|
&& (!export_mask || (data->tp_src == htons(0xffff)
|
|
|
|
|
&& data->tp_dst == htons(0xffff)))) {
|
2013-08-30 09:57:13 -07:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
struct ovs_key_nd *nd_key;
|
2011-02-01 22:54:11 -08:00
|
|
|
|
|
2011-08-18 10:35:40 -07:00
|
|
|
|
nd_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_ND,
|
2011-02-01 22:54:11 -08:00
|
|
|
|
sizeof *nd_key);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
memcpy(nd_key->nd_target, &data->nd_target,
|
2011-02-01 22:54:11 -08:00
|
|
|
|
sizeof nd_key->nd_target);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
memcpy(nd_key->nd_sll, data->arp_sha, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(nd_key->nd_tll, data->arp_tha, ETH_ADDR_LEN);
|
2011-02-01 22:54:11 -08:00
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
|
|
|
|
|
unencap:
|
|
|
|
|
if (encap) {
|
|
|
|
|
nl_msg_end_nested(buf, encap);
|
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2013-06-05 18:56:58 -07:00
|
|
|
|
|
|
|
|
|
/* Appends a representation of 'flow' as OVS_KEY_ATTR_* attributes to 'buf'.
|
|
|
|
|
* 'flow->in_port' is ignored (since it is likely to be an OpenFlow port
|
|
|
|
|
* number rather than a datapath port number). Instead, if 'odp_in_port'
|
|
|
|
|
* is anything other than ODPP_NONE, it is included in 'buf' as the input
|
|
|
|
|
* port.
|
|
|
|
|
*
|
|
|
|
|
* 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
|
2014-05-09 13:58:32 +12:00
|
|
|
|
* capable of being expanded to allow for that much space.
|
|
|
|
|
*
|
|
|
|
|
* 'recirc' indicates support for recirculation fields. If this is true, then
|
|
|
|
|
* these fields will always be serialised. */
|
2013-06-05 18:56:58 -07:00
|
|
|
|
void
|
|
|
|
|
odp_flow_key_from_flow(struct ofpbuf *buf, const struct flow *flow,
|
2014-05-09 13:58:32 +12:00
|
|
|
|
const struct flow *mask, odp_port_t odp_in_port,
|
|
|
|
|
bool recirc)
|
2013-06-05 18:56:58 -07:00
|
|
|
|
{
|
2014-05-09 13:58:32 +12:00
|
|
|
|
odp_flow_key_from_flow__(buf, flow, mask, odp_in_port, SIZE_MAX, recirc,
|
|
|
|
|
false);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Appends a representation of 'mask' as OVS_KEY_ATTR_* attributes to
|
|
|
|
|
* 'buf'. 'flow' is used as a template to determine how to interpret
|
|
|
|
|
* 'mask'. For example, the 'dl_type' of 'mask' describes the mask, but
|
|
|
|
|
* it doesn't indicate whether the other fields should be interpreted as
|
|
|
|
|
* ARP, IPv4, IPv6, etc.
|
|
|
|
|
*
|
|
|
|
|
* 'buf' must have at least ODPUTIL_FLOW_KEY_BYTES bytes of space, or be
|
2014-05-09 13:58:32 +12:00
|
|
|
|
* capable of being expanded to allow for that much space.
|
|
|
|
|
*
|
|
|
|
|
* 'recirc' indicates support for recirculation fields. If this is true, then
|
|
|
|
|
* these fields will always be serialised. */
|
2013-06-05 18:56:58 -07:00
|
|
|
|
void
|
|
|
|
|
odp_flow_key_from_mask(struct ofpbuf *buf, const struct flow *mask,
|
2014-02-04 10:32:35 -08:00
|
|
|
|
const struct flow *flow, uint32_t odp_in_port_mask,
|
2014-05-09 13:58:32 +12:00
|
|
|
|
size_t max_mpls_depth, bool recirc)
|
2013-06-05 18:56:58 -07:00
|
|
|
|
{
|
2014-05-09 13:58:32 +12:00
|
|
|
|
odp_flow_key_from_flow__(buf, flow, mask, u32_to_odp(odp_in_port_mask),
|
|
|
|
|
max_mpls_depth, recirc, true);
|
2013-06-05 18:56:58 -07:00
|
|
|
|
}
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2013-12-30 15:58:58 -08:00
|
|
|
|
/* Generate ODP flow key from the given packet metadata */
|
|
|
|
|
void
|
|
|
|
|
odp_key_from_pkt_metadata(struct ofpbuf *buf, const struct pkt_metadata *md)
|
|
|
|
|
{
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_PRIORITY, md->skb_priority);
|
|
|
|
|
|
|
|
|
|
if (md->tunnel.ip_dst) {
|
|
|
|
|
tun_key_to_attr(buf, &md->tunnel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, md->pkt_mark);
|
|
|
|
|
|
|
|
|
|
/* Add an ingress port attribute if 'odp_in_port' is not the magical
|
|
|
|
|
* value "ODPP_NONE". */
|
2014-02-26 18:08:04 -08:00
|
|
|
|
if (md->in_port.odp_port != ODPP_NONE) {
|
|
|
|
|
nl_msg_put_odp_port(buf, OVS_KEY_ATTR_IN_PORT, md->in_port.odp_port);
|
2013-12-30 15:58:58 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generate packet metadata from the given ODP flow key. */
|
|
|
|
|
void
|
|
|
|
|
odp_key_to_pkt_metadata(const struct nlattr *key, size_t key_len,
|
|
|
|
|
struct pkt_metadata *md)
|
|
|
|
|
{
|
|
|
|
|
const struct nlattr *nla;
|
|
|
|
|
size_t left;
|
|
|
|
|
uint32_t wanted_attrs = 1u << OVS_KEY_ATTR_PRIORITY |
|
|
|
|
|
1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL |
|
|
|
|
|
1u << OVS_KEY_ATTR_IN_PORT;
|
|
|
|
|
|
2014-02-26 18:08:04 -08:00
|
|
|
|
*md = PKT_METADATA_INITIALIZER(ODPP_NONE);
|
2013-12-30 15:58:58 -08:00
|
|
|
|
|
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, key, key_len) {
|
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
|
size_t len = nl_attr_get_size(nla);
|
|
|
|
|
int expected_len = odp_flow_key_attr_len(type);
|
|
|
|
|
|
|
|
|
|
if (len != expected_len && expected_len >= 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-04 15:36:03 -08:00
|
|
|
|
switch (type) {
|
|
|
|
|
case OVS_KEY_ATTR_RECIRC_ID:
|
|
|
|
|
md->recirc_id = nl_attr_get_u32(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_RECIRC_ID);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_KEY_ATTR_DP_HASH:
|
|
|
|
|
md->dp_hash = nl_attr_get_u32(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_DP_HASH);
|
|
|
|
|
break;
|
|
|
|
|
case OVS_KEY_ATTR_PRIORITY:
|
2013-12-30 15:58:58 -08:00
|
|
|
|
md->skb_priority = nl_attr_get_u32(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_PRIORITY);
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
|
|
|
|
case OVS_KEY_ATTR_SKB_MARK:
|
2013-12-30 15:58:58 -08:00
|
|
|
|
md->pkt_mark = nl_attr_get_u32(nla);
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_SKB_MARK);
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
|
|
|
|
case OVS_KEY_ATTR_TUNNEL: {
|
2013-12-30 15:58:58 -08:00
|
|
|
|
enum odp_key_fitness res;
|
|
|
|
|
|
|
|
|
|
res = odp_tun_key_from_attr(nla, &md->tunnel);
|
|
|
|
|
if (res == ODP_FIT_ERROR) {
|
|
|
|
|
memset(&md->tunnel, 0, sizeof md->tunnel);
|
|
|
|
|
} else if (res == ODP_FIT_PERFECT) {
|
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_TUNNEL);
|
|
|
|
|
}
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case OVS_KEY_ATTR_IN_PORT:
|
2014-02-26 18:08:04 -08:00
|
|
|
|
md->in_port.odp_port = nl_attr_get_odp_port(nla);
|
2013-12-30 15:58:58 -08:00
|
|
|
|
wanted_attrs &= ~(1u << OVS_KEY_ATTR_IN_PORT);
|
2014-03-04 15:36:03 -08:00
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2013-12-30 15:58:58 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!wanted_attrs) {
|
|
|
|
|
return; /* Have everything. */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
uint32_t
|
|
|
|
|
odp_flow_key_hash(const struct nlattr *key, size_t key_len)
|
|
|
|
|
{
|
|
|
|
|
BUILD_ASSERT_DECL(!(NLA_ALIGNTO % sizeof(uint32_t)));
|
2013-07-22 15:47:19 -07:00
|
|
|
|
return hash_words(ALIGNED_CAST(const uint32_t *, key),
|
|
|
|
|
key_len / sizeof(uint32_t), 0);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
static void
|
|
|
|
|
log_odp_key_attributes(struct vlog_rate_limit *rl, const char *title,
|
2011-11-23 10:26:02 -08:00
|
|
|
|
uint64_t attrs, int out_of_range_attr,
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct nlattr *key, size_t key_len)
|
|
|
|
|
{
|
|
|
|
|
struct ds s;
|
|
|
|
|
int i;
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (VLOG_DROP_DBG(rl)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ds_init(&s);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
|
if (attrs & (UINT64_C(1) << i)) {
|
2013-04-15 15:40:21 -07:00
|
|
|
|
char namebuf[OVS_KEY_ATTR_BUFSIZE];
|
|
|
|
|
|
|
|
|
|
ds_put_format(&s, " %s",
|
|
|
|
|
ovs_key_attr_to_string(i, namebuf, sizeof namebuf));
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (out_of_range_attr) {
|
|
|
|
|
ds_put_format(&s, " %d (and possibly others)", out_of_range_attr);
|
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
|
|
|
|
|
ds_put_cstr(&s, ": ");
|
|
|
|
|
odp_flow_key_format(key, key_len, &s);
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
VLOG_DBG("%s:%s", title, ds_cstr(&s));
|
2011-11-14 15:09:01 -08:00
|
|
|
|
ds_destroy(&s);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
static uint8_t
|
|
|
|
|
odp_to_ovs_frag(uint8_t odp_frag, bool is_mask)
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
{
|
2011-11-14 15:09:01 -08:00
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
return odp_frag ? FLOW_NW_FRAG_MASK : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-02 18:17:36 -07:00
|
|
|
|
if (odp_frag > OVS_FRAG_TYPE_LATER) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "invalid frag %"PRIu8" in flow key", odp_frag);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
return 0xff; /* Error. */
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
return (odp_frag == OVS_FRAG_TYPE_NONE) ? 0
|
|
|
|
|
: (odp_frag == OVS_FRAG_TYPE_FIRST) ? FLOW_NW_FRAG_ANY
|
|
|
|
|
: FLOW_NW_FRAG_ANY | FLOW_NW_FRAG_LATER;
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static bool
|
2011-11-14 15:56:43 -08:00
|
|
|
|
parse_flow_nlattrs(const struct nlattr *key, size_t key_len,
|
2011-11-23 10:26:02 -08:00
|
|
|
|
const struct nlattr *attrs[], uint64_t *present_attrsp,
|
|
|
|
|
int *out_of_range_attrp)
|
2011-01-23 18:44:44 -08:00
|
|
|
|
{
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
|
2011-01-23 18:44:44 -08:00
|
|
|
|
const struct nlattr *nla;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
uint64_t present_attrs;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
size_t left;
|
|
|
|
|
|
2013-03-22 16:25:36 -07:00
|
|
|
|
BUILD_ASSERT(OVS_KEY_ATTR_MAX < CHAR_BIT * sizeof present_attrs);
|
2011-11-14 15:09:01 -08:00
|
|
|
|
present_attrs = 0;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
*out_of_range_attrp = 0;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
NL_ATTR_FOR_EACH (nla, left, key, key_len) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
uint16_t type = nl_attr_type(nla);
|
|
|
|
|
size_t len = nl_attr_get_size(nla);
|
|
|
|
|
int expected_len = odp_flow_key_attr_len(type);
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (len != expected_len && expected_len >= 0) {
|
2013-04-15 15:40:21 -07:00
|
|
|
|
char namebuf[OVS_KEY_ATTR_BUFSIZE];
|
|
|
|
|
|
2013-11-25 23:38:48 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "attribute %s has length %"PRIuSIZE" but should have "
|
2013-04-15 15:40:21 -07:00
|
|
|
|
"length %d", ovs_key_attr_to_string(type, namebuf,
|
|
|
|
|
sizeof namebuf),
|
2011-11-23 10:26:02 -08:00
|
|
|
|
len, expected_len);
|
|
|
|
|
return false;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-03-22 16:25:36 -07:00
|
|
|
|
if (type > OVS_KEY_ATTR_MAX) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
*out_of_range_attrp = type;
|
|
|
|
|
} else {
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << type)) {
|
2013-04-15 15:40:21 -07:00
|
|
|
|
char namebuf[OVS_KEY_ATTR_BUFSIZE];
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "duplicate %s attribute in flow key",
|
2013-04-15 15:40:21 -07:00
|
|
|
|
ovs_key_attr_to_string(type,
|
|
|
|
|
namebuf, sizeof namebuf));
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
present_attrs |= UINT64_C(1) << type;
|
|
|
|
|
attrs[type] = nla;
|
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
if (left) {
|
|
|
|
|
VLOG_ERR_RL(&rl, "trailing garbage in flow key");
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return false;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-14 15:56:43 -08:00
|
|
|
|
*present_attrsp = present_attrs;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return true;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static enum odp_key_fitness
|
|
|
|
|
check_expectations(uint64_t present_attrs, int out_of_range_attr,
|
|
|
|
|
uint64_t expected_attrs,
|
2011-11-14 15:56:43 -08:00
|
|
|
|
const struct nlattr *key, size_t key_len)
|
|
|
|
|
{
|
|
|
|
|
uint64_t missing_attrs;
|
|
|
|
|
uint64_t extra_attrs;
|
|
|
|
|
|
|
|
|
|
missing_attrs = expected_attrs & ~present_attrs;
|
|
|
|
|
if (missing_attrs) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
|
|
|
|
|
log_odp_key_attributes(&rl, "expected but not present",
|
|
|
|
|
missing_attrs, 0, key, key_len);
|
|
|
|
|
return ODP_FIT_TOO_LITTLE;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
extra_attrs = present_attrs & ~expected_attrs;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (extra_attrs || out_of_range_attr) {
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
|
|
|
|
|
log_odp_key_attributes(&rl, "present but not expected",
|
|
|
|
|
extra_attrs, out_of_range_attr, key, key_len);
|
|
|
|
|
return ODP_FIT_TOO_MUCH;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return ODP_FIT_PERFECT;
|
2011-11-14 15:56:43 -08:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
static bool
|
|
|
|
|
parse_ethertype(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
|
|
|
|
|
uint64_t present_attrs, uint64_t *expected_attrs,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
struct flow *flow, const struct flow *src_flow)
|
2011-11-14 15:56:43 -08:00
|
|
|
|
{
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
bool is_mask = flow != src_flow;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
flow->dl_type = nl_attr_get_be16(attrs[OVS_KEY_ATTR_ETHERTYPE]);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask && ntohs(flow->dl_type) < ETH_TYPE_MIN) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "invalid Ethertype %"PRIu16" in flow key",
|
|
|
|
|
ntohs(flow->dl_type));
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return false;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask && ntohs(src_flow->dl_type) < ETH_TYPE_MIN &&
|
|
|
|
|
flow->dl_type != htons(0xffff)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
*expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERTYPE;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
} else {
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
flow->dl_type = htons(FLOW_DL_TYPE_NONE);
|
|
|
|
|
} else if (ntohs(src_flow->dl_type) < ETH_TYPE_MIN) {
|
|
|
|
|
/* See comments in odp_flow_key_from_flow__(). */
|
|
|
|
|
VLOG_ERR_RL(&rl, "mask expected for non-Ethernet II frame");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum odp_key_fitness
|
2013-01-25 16:22:07 +09:00
|
|
|
|
parse_l2_5_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
|
|
|
|
|
uint64_t present_attrs, int out_of_range_attr,
|
|
|
|
|
uint64_t expected_attrs, struct flow *flow,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
const struct nlattr *key, size_t key_len,
|
2013-10-04 07:49:13 -07:00
|
|
|
|
const struct flow *src_flow)
|
2011-11-23 10:26:02 -08:00
|
|
|
|
{
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
bool is_mask = src_flow != flow;
|
|
|
|
|
const void *check_start = NULL;
|
|
|
|
|
size_t check_len = 0;
|
|
|
|
|
enum ovs_key_attr expected_bit = 0xff;
|
|
|
|
|
|
|
|
|
|
if (eth_type_mpls(src_flow->dl_type)) {
|
2014-10-31 14:05:46 -07:00
|
|
|
|
if (!is_mask || present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
|
2013-10-04 07:49:13 -07:00
|
|
|
|
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
|
2014-10-31 14:05:46 -07:00
|
|
|
|
}
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
|
|
|
|
|
size_t size = nl_attr_get_size(attrs[OVS_KEY_ATTR_MPLS]);
|
|
|
|
|
const ovs_be32 *mpls_lse = nl_attr_get(attrs[OVS_KEY_ATTR_MPLS]);
|
|
|
|
|
int n = size / sizeof(ovs_be32);
|
|
|
|
|
int i;
|
2013-08-20 10:40:50 -07:00
|
|
|
|
|
2014-10-31 14:05:46 -07:00
|
|
|
|
if (!size || size % sizeof(ovs_be32)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
2013-10-04 07:49:13 -07:00
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
if (flow->mpls_lse[0] && flow->dl_type != htons(0xffff)) {
|
2013-08-20 10:40:50 -07:00
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
|
2014-10-31 14:05:46 -07:00
|
|
|
|
for (i = 0; i < n && i < FLOW_MAX_MPLS_LABELS; i++) {
|
|
|
|
|
flow->mpls_lse[i] = mpls_lse[i];
|
|
|
|
|
}
|
|
|
|
|
if (n > FLOW_MAX_MPLS_LABELS) {
|
|
|
|
|
return ODP_FIT_TOO_MUCH;
|
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
|
2014-10-31 14:05:46 -07:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
/* BOS may be set only in the innermost label. */
|
|
|
|
|
for (i = 0; i < n - 1; i++) {
|
|
|
|
|
if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-31 14:05:46 -07:00
|
|
|
|
/* BOS must be set in the innermost label. */
|
|
|
|
|
if (n < FLOW_MAX_MPLS_LABELS
|
|
|
|
|
&& !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
|
|
|
|
|
return ODP_FIT_TOO_LITTLE;
|
|
|
|
|
}
|
2014-02-04 10:32:35 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
goto done;
|
|
|
|
|
} else if (src_flow->dl_type == htons(ETH_TYPE_IP)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV4;
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV4)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_ipv4 *ipv4_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
ipv4_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV4]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv4_key(ipv4_key, flow, is_mask);
|
|
|
|
|
if (flow->nw_frag > FLOW_NW_FRAG_MASK) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
check_start = ipv4_key;
|
|
|
|
|
check_len = sizeof *ipv4_key;
|
|
|
|
|
expected_bit = OVS_KEY_ATTR_IPV4;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (src_flow->dl_type == htons(ETH_TYPE_IPV6)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IPV6;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IPV6)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_ipv6 *ipv6_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
ipv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_IPV6]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv6_key(ipv6_key, flow, is_mask);
|
|
|
|
|
if (flow->nw_frag > FLOW_NW_FRAG_MASK) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
check_start = ipv6_key;
|
|
|
|
|
check_len = sizeof *ipv6_key;
|
|
|
|
|
expected_bit = OVS_KEY_ATTR_IPV6;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (src_flow->dl_type == htons(ETH_TYPE_ARP) ||
|
|
|
|
|
src_flow->dl_type == htons(ETH_TYPE_RARP)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ARP;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ARP)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_arp *arp_key;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
arp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ARP]);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask && (arp_key->arp_op & htons(0xff00))) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
VLOG_ERR_RL(&rl, "unsupported ARP opcode %"PRIu16" in flow "
|
|
|
|
|
"key", ntohs(arp_key->arp_op));
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return ODP_FIT_ERROR;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_arp_key(arp_key, flow);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
check_start = arp_key;
|
|
|
|
|
check_len = sizeof *arp_key;
|
|
|
|
|
expected_bit = OVS_KEY_ATTR_ARP;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
goto done;
|
|
|
|
|
}
|
2013-12-06 08:30:17 -08:00
|
|
|
|
if (check_len > 0) { /* Happens only when 'is_mask'. */
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_all_zeros(check_start, check_len) &&
|
|
|
|
|
flow->dl_type != htons(0xffff)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
} else {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << expected_bit;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_UNSPEC;
|
|
|
|
|
if (src_flow->nw_proto == IPPROTO_TCP
|
|
|
|
|
&& (src_flow->dl_type == htons(ETH_TYPE_IP) ||
|
|
|
|
|
src_flow->dl_type == htons(ETH_TYPE_IPV6))
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
const union ovs_key_tp *tcp_key;
|
2011-01-23 18:44:44 -08:00
|
|
|
|
|
2011-11-14 15:09:01 -08:00
|
|
|
|
tcp_key = nl_attr_get(attrs[OVS_KEY_ATTR_TCP]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_tp_key(tcp_key, flow);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_TCP;
|
|
|
|
|
}
|
2013-10-28 13:54:40 -07:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS)) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TCP_FLAGS;
|
|
|
|
|
flow->tcp_flags = nl_attr_get_be16(attrs[OVS_KEY_ATTR_TCP_FLAGS]);
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (src_flow->nw_proto == IPPROTO_UDP
|
|
|
|
|
&& (src_flow->dl_type == htons(ETH_TYPE_IP) ||
|
|
|
|
|
src_flow->dl_type == htons(ETH_TYPE_IPV6))
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_UDP;
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_UDP)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
const union ovs_key_tp *udp_key;
|
2011-11-14 15:09:01 -08:00
|
|
|
|
|
|
|
|
|
udp_key = nl_attr_get(attrs[OVS_KEY_ATTR_UDP]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_tp_key(udp_key, flow);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_UDP;
|
|
|
|
|
}
|
2013-10-30 00:44:32 -07:00
|
|
|
|
} else if (src_flow->nw_proto == IPPROTO_SCTP
|
|
|
|
|
&& (src_flow->dl_type == htons(ETH_TYPE_IP) ||
|
|
|
|
|
src_flow->dl_type == htons(ETH_TYPE_IPV6))
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
2013-08-22 20:24:44 +12:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SCTP;
|
|
|
|
|
}
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SCTP)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
const union ovs_key_tp *sctp_key;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
|
|
|
|
sctp_key = nl_attr_get(attrs[OVS_KEY_ATTR_SCTP]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_tp_key(sctp_key, flow);
|
2013-08-22 20:24:44 +12:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_SCTP;
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (src_flow->nw_proto == IPPROTO_ICMP
|
|
|
|
|
&& src_flow->dl_type == htons(ETH_TYPE_IP)
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMP;
|
2010-12-29 19:03:46 -08:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMP)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_icmp *icmp_key;
|
|
|
|
|
|
|
|
|
|
icmp_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMP]);
|
|
|
|
|
flow->tp_src = htons(icmp_key->icmp_type);
|
|
|
|
|
flow->tp_dst = htons(icmp_key->icmp_code);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_ICMP;
|
|
|
|
|
}
|
|
|
|
|
} else if (src_flow->nw_proto == IPPROTO_ICMPV6
|
|
|
|
|
&& src_flow->dl_type == htons(ETH_TYPE_IPV6)
|
|
|
|
|
&& !(src_flow->nw_frag & FLOW_NW_FRAG_LATER)) {
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ICMPV6;
|
2011-02-01 22:54:11 -08:00
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ICMPV6)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_icmpv6 *icmpv6_key;
|
|
|
|
|
|
|
|
|
|
icmpv6_key = nl_attr_get(attrs[OVS_KEY_ATTR_ICMPV6]);
|
|
|
|
|
flow->tp_src = htons(icmpv6_key->icmpv6_type);
|
|
|
|
|
flow->tp_dst = htons(icmpv6_key->icmpv6_code);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_bit = OVS_KEY_ATTR_ICMPV6;
|
2013-08-30 09:57:13 -07:00
|
|
|
|
if (src_flow->tp_dst == htons(0) &&
|
|
|
|
|
(src_flow->tp_src == htons(ND_NEIGHBOR_SOLICIT) ||
|
|
|
|
|
src_flow->tp_src == htons(ND_NEIGHBOR_ADVERT))) {
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND;
|
|
|
|
|
}
|
2011-11-14 15:56:43 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ND)) {
|
2011-11-14 15:09:01 -08:00
|
|
|
|
const struct ovs_key_nd *nd_key;
|
|
|
|
|
|
|
|
|
|
nd_key = nl_attr_get(attrs[OVS_KEY_ATTR_ND]);
|
|
|
|
|
memcpy(&flow->nd_target, nd_key->nd_target,
|
|
|
|
|
sizeof flow->nd_target);
|
|
|
|
|
memcpy(flow->arp_sha, nd_key->nd_sll, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(flow->arp_tha, nd_key->nd_tll, ETH_ADDR_LEN);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
2014-09-05 15:44:19 -07:00
|
|
|
|
if (!is_all_zeros(nd_key, sizeof *nd_key) &&
|
2013-08-30 09:57:13 -07:00
|
|
|
|
(flow->tp_src != htons(0xffff) ||
|
|
|
|
|
flow->tp_dst != htons(0xffff))) {
|
2013-08-20 10:40:50 -07:00
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
} else {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ND;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
}
|
2011-11-14 15:09:01 -08:00
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask && expected_bit != OVS_KEY_ATTR_UNSPEC) {
|
|
|
|
|
if ((flow->tp_src || flow->tp_dst) && flow->nw_proto != 0xff) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
} else {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << expected_bit;
|
|
|
|
|
}
|
|
|
|
|
}
|
Implement new fragment handling policy.
Until now, OVS has handled IP fragments more awkwardly than necessary. It
has not been possible to match on L4 headers, even in fragments with offset
0 where they are actually present. This means that there was no way to
implement ACLs that treat, say, different TCP ports differently, on
fragmented traffic; instead, all decisions for fragment forwarding had to
be made on the basis of L2 and L3 headers alone.
This commit improves the situation significantly. It is still not possible
to match on L4 headers in fragments with nonzero offset, because that
information is simply not present in such fragments, but this commit adds
the ability to match on L4 headers for fragments with zero offset. This
means that it becomes possible to implement ACLs that drop such "first
fragments" on the basis of L4 headers. In practice, that effectively
blocks even fragmented traffic on an L4 basis, because the receiving IP
stack cannot reassemble a full packet when the first fragment is missing.
This commit works by adding a new "fragment type" to the kernel flow match
and making it available through OpenFlow as a new NXM field named
NXM_NX_IP_FRAG. Because OpenFlow 1.0 explicitly says that the L4 fields
are always 0 for IP fragments, it adds a new OpenFlow fragment handling
mode that fills in the L4 fields for "first fragments". It also enhances
ovs-ofctl to allow users to configure this new fragment handling mode and
to parse the new field.
Signed-off-by: Ben Pfaff <blp@nicira.com>
Bug #7557.
2011-10-19 21:33:44 -07:00
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
done:
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return check_expectations(present_attrs, out_of_range_attr, expected_attrs,
|
|
|
|
|
key, key_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Parse 802.1Q header then encapsulated L3 attributes. */
|
|
|
|
|
static enum odp_key_fitness
|
|
|
|
|
parse_8021q_onward(const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1],
|
|
|
|
|
uint64_t present_attrs, int out_of_range_attr,
|
|
|
|
|
uint64_t expected_attrs, struct flow *flow,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
const struct nlattr *key, size_t key_len,
|
|
|
|
|
const struct flow *src_flow)
|
2011-11-23 10:26:02 -08:00
|
|
|
|
{
|
|
|
|
|
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
bool is_mask = src_flow != flow;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
|
|
|
|
|
const struct nlattr *encap
|
|
|
|
|
= (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)
|
|
|
|
|
? attrs[OVS_KEY_ATTR_ENCAP] : NULL);
|
|
|
|
|
enum odp_key_fitness encap_fitness;
|
|
|
|
|
enum odp_key_fitness fitness;
|
|
|
|
|
|
2013-06-04 17:35:36 -05:00
|
|
|
|
/* Calculate fitness of outer attributes. */
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= ((UINT64_C(1) << OVS_KEY_ATTR_VLAN) |
|
|
|
|
|
(UINT64_C(1) << OVS_KEY_ATTR_ENCAP));
|
|
|
|
|
} else {
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
|
|
|
|
|
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
|
|
|
|
|
}
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP)) {
|
|
|
|
|
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_ENCAP);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
fitness = check_expectations(present_attrs, out_of_range_attr,
|
|
|
|
|
expected_attrs, key, key_len);
|
|
|
|
|
|
2013-12-31 10:35:27 -08:00
|
|
|
|
/* Set vlan_tci.
|
|
|
|
|
* Remove the TPID from dl_type since it's not the real Ethertype. */
|
|
|
|
|
flow->dl_type = htons(0);
|
|
|
|
|
flow->vlan_tci = (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)
|
|
|
|
|
? nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN])
|
|
|
|
|
: htons(0));
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN))) {
|
|
|
|
|
return ODP_FIT_TOO_LITTLE;
|
|
|
|
|
} else if (flow->vlan_tci == htons(0)) {
|
|
|
|
|
/* Corner case for a truncated 802.1Q header. */
|
|
|
|
|
if (fitness == ODP_FIT_PERFECT && nl_attr_get_size(encap)) {
|
|
|
|
|
return ODP_FIT_TOO_MUCH;
|
|
|
|
|
}
|
|
|
|
|
return fitness;
|
|
|
|
|
} else if (!(flow->vlan_tci & htons(VLAN_CFI))) {
|
|
|
|
|
VLOG_ERR_RL(&rl, "OVS_KEY_ATTR_VLAN 0x%04"PRIx16" is nonzero "
|
|
|
|
|
"but CFI bit is not set", ntohs(flow->vlan_tci));
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else {
|
2013-12-31 10:35:27 -08:00
|
|
|
|
if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ENCAP))) {
|
|
|
|
|
return fitness;
|
2013-08-20 10:40:50 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
/* Now parse the encapsulated attributes. */
|
|
|
|
|
if (!parse_flow_nlattrs(nl_attr_get(encap), nl_attr_get_size(encap),
|
|
|
|
|
attrs, &present_attrs, &out_of_range_attr)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
expected_attrs = 0;
|
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow, src_flow)) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
encap_fitness = parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_attrs, flow, key, key_len,
|
|
|
|
|
src_flow);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
|
|
|
|
|
/* The overall fitness is the worse of the outer and inner attributes. */
|
|
|
|
|
return MAX(fitness, encap_fitness);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-20 10:40:50 -07:00
|
|
|
|
static enum odp_key_fitness
|
|
|
|
|
odp_flow_key_to_flow__(const struct nlattr *key, size_t key_len,
|
|
|
|
|
struct flow *flow, const struct flow *src_flow)
|
2011-11-23 10:26:02 -08:00
|
|
|
|
{
|
|
|
|
|
const struct nlattr *attrs[OVS_KEY_ATTR_MAX + 1];
|
|
|
|
|
uint64_t expected_attrs;
|
|
|
|
|
uint64_t present_attrs;
|
|
|
|
|
int out_of_range_attr;
|
2013-08-20 10:40:50 -07:00
|
|
|
|
bool is_mask = src_flow != flow;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
|
|
|
|
|
memset(flow, 0, sizeof *flow);
|
|
|
|
|
|
|
|
|
|
/* Parse attributes. */
|
|
|
|
|
if (!parse_flow_nlattrs(key, key_len, attrs, &present_attrs,
|
|
|
|
|
&out_of_range_attr)) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
expected_attrs = 0;
|
|
|
|
|
|
|
|
|
|
/* Metadata. */
|
2014-03-04 15:36:03 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID)) {
|
|
|
|
|
flow->recirc_id = nl_attr_get_u32(attrs[OVS_KEY_ATTR_RECIRC_ID]);
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_RECIRC_ID;
|
|
|
|
|
} else if (is_mask) {
|
2014-05-13 10:07:18 +12:00
|
|
|
|
/* Always exact match recirc_id if it is not specified. */
|
2014-03-04 15:36:03 -08:00
|
|
|
|
flow->recirc_id = UINT32_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_DP_HASH)) {
|
|
|
|
|
flow->dp_hash = nl_attr_get_u32(attrs[OVS_KEY_ATTR_DP_HASH]);
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_DP_HASH;
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_PRIORITY)) {
|
2011-12-21 15:52:23 -08:00
|
|
|
|
flow->skb_priority = nl_attr_get_u32(attrs[OVS_KEY_ATTR_PRIORITY]);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_PRIORITY;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-13 19:19:36 +02:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK)) {
|
2013-08-06 12:57:13 -07:00
|
|
|
|
flow->pkt_mark = nl_attr_get_u32(attrs[OVS_KEY_ATTR_SKB_MARK]);
|
2012-11-13 19:19:36 +02:00
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_SKB_MARK;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-18 18:10:59 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_TUNNEL)) {
|
|
|
|
|
enum odp_key_fitness res;
|
2012-12-21 10:44:38 +02:00
|
|
|
|
|
2013-06-05 14:28:48 +09:00
|
|
|
|
res = odp_tun_key_from_attr(attrs[OVS_KEY_ATTR_TUNNEL], &flow->tunnel);
|
2013-01-18 18:10:59 -08:00
|
|
|
|
if (res == ODP_FIT_ERROR) {
|
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
} else if (res == ODP_FIT_PERFECT) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_TUNNEL;
|
2012-12-21 10:44:38 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-23 10:26:02 -08:00
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_IN_PORT)) {
|
2013-06-19 16:58:44 -07:00
|
|
|
|
flow->in_port.odp_port
|
|
|
|
|
= nl_attr_get_odp_port(attrs[OVS_KEY_ATTR_IN_PORT]);
|
2011-11-23 10:26:02 -08:00
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_IN_PORT;
|
2013-08-20 10:40:50 -07:00
|
|
|
|
} else if (!is_mask) {
|
2013-06-19 16:58:44 -07:00
|
|
|
|
flow->in_port.odp_port = ODPP_NONE;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Ethernet header. */
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_ETHERNET)) {
|
|
|
|
|
const struct ovs_key_ethernet *eth_key;
|
|
|
|
|
|
|
|
|
|
eth_key = nl_attr_get(attrs[OVS_KEY_ATTR_ETHERNET]);
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ethernet_key(eth_key, flow);
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!is_mask) {
|
|
|
|
|
expected_attrs |= UINT64_C(1) << OVS_KEY_ATTR_ETHERNET;
|
2011-11-23 10:26:02 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get Ethertype or 802.1Q TPID or FLOW_DL_TYPE_NONE. */
|
2013-08-20 10:40:50 -07:00
|
|
|
|
if (!parse_ethertype(attrs, present_attrs, &expected_attrs, flow,
|
|
|
|
|
src_flow)) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return ODP_FIT_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-31 10:36:19 -08:00
|
|
|
|
if (is_mask
|
|
|
|
|
? (src_flow->vlan_tci & htons(VLAN_CFI)) != 0
|
|
|
|
|
: src_flow->dl_type == htons(ETH_TYPE_VLAN)) {
|
2011-11-23 10:26:02 -08:00
|
|
|
|
return parse_8021q_onward(attrs, present_attrs, out_of_range_attr,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_attrs, flow, key, key_len, src_flow);
|
|
|
|
|
}
|
|
|
|
|
if (is_mask) {
|
|
|
|
|
flow->vlan_tci = htons(0xffff);
|
|
|
|
|
if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_VLAN)) {
|
|
|
|
|
flow->vlan_tci = nl_attr_get_be16(attrs[OVS_KEY_ATTR_VLAN]);
|
|
|
|
|
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_VLAN);
|
|
|
|
|
}
|
2011-11-23 10:26:02 -08:00
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
return parse_l2_5_onward(attrs, present_attrs, out_of_range_attr,
|
2013-08-20 10:40:50 -07:00
|
|
|
|
expected_attrs, flow, key, key_len, src_flow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a flow
|
|
|
|
|
* structure in 'flow'. Returns an ODP_FIT_* value that indicates how well
|
|
|
|
|
* 'key' fits our expectations for what a flow key should contain.
|
|
|
|
|
*
|
|
|
|
|
* The 'in_port' will be the datapath's understanding of the port. The
|
|
|
|
|
* caller will need to translate with odp_port_to_ofp_port() if the
|
|
|
|
|
* OpenFlow port is needed.
|
|
|
|
|
*
|
|
|
|
|
* This function doesn't take the packet itself as an argument because none of
|
|
|
|
|
* the currently understood OVS_KEY_ATTR_* attributes require it. Currently,
|
|
|
|
|
* it is always possible to infer which additional attribute(s) should appear
|
|
|
|
|
* by looking at the attributes for lower-level protocols, e.g. if the network
|
|
|
|
|
* protocol in OVS_KEY_ATTR_IPV4 or OVS_KEY_ATTR_IPV6 is IPPROTO_TCP then we
|
|
|
|
|
* know that a OVS_KEY_ATTR_TCP attribute must appear and that otherwise it
|
|
|
|
|
* must be absent. */
|
|
|
|
|
enum odp_key_fitness
|
|
|
|
|
odp_flow_key_to_flow(const struct nlattr *key, size_t key_len,
|
|
|
|
|
struct flow *flow)
|
|
|
|
|
{
|
|
|
|
|
return odp_flow_key_to_flow__(key, key_len, flow, flow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Converts the 'key_len' bytes of OVS_KEY_ATTR_* attributes in 'key' to a mask
|
|
|
|
|
* structure in 'mask'. 'flow' must be a previously translated flow
|
|
|
|
|
* corresponding to 'mask'. Returns an ODP_FIT_* value that indicates how well
|
|
|
|
|
* 'key' fits our expectations for what a flow key should contain. */
|
|
|
|
|
enum odp_key_fitness
|
|
|
|
|
odp_flow_key_to_mask(const struct nlattr *key, size_t key_len,
|
|
|
|
|
struct flow *mask, const struct flow *flow)
|
|
|
|
|
{
|
|
|
|
|
return odp_flow_key_to_flow__(key, key_len, mask, flow);
|
2010-10-11 13:31:35 -07:00
|
|
|
|
}
|
2011-10-25 16:54:42 -07:00
|
|
|
|
|
2012-01-16 12:37:44 -08:00
|
|
|
|
/* Returns 'fitness' as a string, for use in debug messages. */
|
|
|
|
|
const char *
|
|
|
|
|
odp_key_fitness_to_string(enum odp_key_fitness fitness)
|
|
|
|
|
{
|
|
|
|
|
switch (fitness) {
|
|
|
|
|
case ODP_FIT_PERFECT:
|
|
|
|
|
return "OK";
|
|
|
|
|
case ODP_FIT_TOO_MUCH:
|
|
|
|
|
return "too_much";
|
|
|
|
|
case ODP_FIT_TOO_LITTLE:
|
|
|
|
|
return "too_little";
|
|
|
|
|
case ODP_FIT_ERROR:
|
|
|
|
|
return "error";
|
|
|
|
|
default:
|
|
|
|
|
return "<unknown>";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-25 16:54:42 -07:00
|
|
|
|
/* Appends an OVS_ACTION_ATTR_USERSPACE action to 'odp_actions' that specifies
|
2013-02-15 16:48:32 -08:00
|
|
|
|
* Netlink PID 'pid'. If 'userdata' is nonnull, adds a userdata attribute
|
|
|
|
|
* whose contents are the 'userdata_size' bytes at 'userdata' and returns the
|
|
|
|
|
* offset within 'odp_actions' of the start of the cookie. (If 'userdata' is
|
|
|
|
|
* null, then the return value is not meaningful.) */
|
2011-10-25 16:54:42 -07:00
|
|
|
|
size_t
|
2013-02-15 16:48:32 -08:00
|
|
|
|
odp_put_userspace_action(uint32_t pid,
|
|
|
|
|
const void *userdata, size_t userdata_size,
|
2014-08-17 20:19:36 -07:00
|
|
|
|
odp_port_t tunnel_out_port,
|
2011-10-25 16:54:42 -07:00
|
|
|
|
struct ofpbuf *odp_actions)
|
|
|
|
|
{
|
2013-02-15 16:48:32 -08:00
|
|
|
|
size_t userdata_ofs;
|
2011-10-25 16:54:42 -07:00
|
|
|
|
size_t offset;
|
|
|
|
|
|
|
|
|
|
offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_USERSPACE);
|
|
|
|
|
nl_msg_put_u32(odp_actions, OVS_USERSPACE_ATTR_PID, pid);
|
2013-02-15 16:48:32 -08:00
|
|
|
|
if (userdata) {
|
2014-03-30 01:31:50 -07:00
|
|
|
|
userdata_ofs = ofpbuf_size(odp_actions) + NLA_HDRLEN;
|
2013-10-07 14:26:28 -07:00
|
|
|
|
|
|
|
|
|
/* The OVS kernel module before OVS 1.11 and the upstream Linux kernel
|
|
|
|
|
* module before Linux 3.10 required the userdata to be exactly 8 bytes
|
|
|
|
|
* long:
|
|
|
|
|
*
|
|
|
|
|
* - The kernel rejected shorter userdata with -ERANGE.
|
|
|
|
|
*
|
|
|
|
|
* - The kernel silently dropped userdata beyond the first 8 bytes.
|
|
|
|
|
*
|
|
|
|
|
* Thus, for maximum compatibility, always put at least 8 bytes. (We
|
|
|
|
|
* separately disable features that required more than 8 bytes.) */
|
|
|
|
|
memcpy(nl_msg_put_unspec_zero(odp_actions, OVS_USERSPACE_ATTR_USERDATA,
|
|
|
|
|
MAX(8, userdata_size)),
|
|
|
|
|
userdata, userdata_size);
|
2013-02-15 16:48:32 -08:00
|
|
|
|
} else {
|
|
|
|
|
userdata_ofs = 0;
|
2011-10-25 16:54:42 -07:00
|
|
|
|
}
|
2014-08-17 20:19:36 -07:00
|
|
|
|
if (tunnel_out_port != ODPP_NONE) {
|
|
|
|
|
nl_msg_put_odp_port(odp_actions, OVS_USERSPACE_ATTR_EGRESS_TUN_PORT,
|
|
|
|
|
tunnel_out_port);
|
|
|
|
|
}
|
2011-10-25 16:54:42 -07:00
|
|
|
|
nl_msg_end_nested(odp_actions, offset);
|
|
|
|
|
|
2013-02-15 16:48:32 -08:00
|
|
|
|
return userdata_ofs;
|
2011-10-25 16:54:42 -07:00
|
|
|
|
}
|
2012-12-14 19:14:54 -08:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
odp_put_tunnel_action(const struct flow_tnl *tunnel,
|
|
|
|
|
struct ofpbuf *odp_actions)
|
|
|
|
|
{
|
|
|
|
|
size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
|
|
|
|
|
tun_key_to_attr(odp_actions, tunnel);
|
|
|
|
|
nl_msg_end_nested(odp_actions, offset);
|
|
|
|
|
}
|
2014-11-11 11:53:47 -08:00
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
odp_put_tnl_push_action(struct ofpbuf *odp_actions,
|
|
|
|
|
struct ovs_action_push_tnl *data)
|
|
|
|
|
{
|
|
|
|
|
int size = offsetof(struct ovs_action_push_tnl, header);
|
|
|
|
|
|
|
|
|
|
size += data->header_len;
|
|
|
|
|
nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_TUNNEL_PUSH, data, size);
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
|
|
|
|
/* The commit_odp_actions() function and its helpers. */
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
commit_set_action(struct ofpbuf *odp_actions, enum ovs_key_attr key_type,
|
|
|
|
|
const void *key, size_t key_size)
|
|
|
|
|
{
|
|
|
|
|
size_t offset = nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
|
|
|
|
|
nl_msg_put_unspec(odp_actions, key_type, key, key_size);
|
|
|
|
|
nl_msg_end_nested(odp_actions, offset);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 15:44:19 -07:00
|
|
|
|
/* Masked set actions have a mask following the data within the netlink
|
|
|
|
|
* attribute. The unmasked bits in the data will be cleared as the data
|
|
|
|
|
* is copied to the action. */
|
|
|
|
|
void
|
|
|
|
|
commit_masked_set_action(struct ofpbuf *odp_actions,
|
|
|
|
|
enum ovs_key_attr key_type,
|
|
|
|
|
const void *key_, const void *mask_, size_t key_size)
|
|
|
|
|
{
|
|
|
|
|
size_t offset = nl_msg_start_nested(odp_actions,
|
|
|
|
|
OVS_ACTION_ATTR_SET_MASKED);
|
|
|
|
|
char *data = nl_msg_put_unspec_uninit(odp_actions, key_type, key_size * 2);
|
|
|
|
|
const char *key = key_, *mask = mask_;
|
|
|
|
|
|
|
|
|
|
memcpy(data + key_size, mask, key_size);
|
|
|
|
|
/* Clear unmasked bits while copying. */
|
|
|
|
|
while (key_size--) {
|
|
|
|
|
*data++ = *key++ & *mask++;
|
|
|
|
|
}
|
|
|
|
|
nl_msg_end_nested(odp_actions, offset);
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-14 19:14:54 -08:00
|
|
|
|
/* If any of the flow key data that ODP actions can modify are different in
|
|
|
|
|
* 'base->tunnel' and 'flow->tunnel', appends a set_tunnel ODP action to
|
|
|
|
|
* 'odp_actions' that change the flow tunneling information in key from
|
|
|
|
|
* 'base->tunnel' into 'flow->tunnel', and then changes 'base->tunnel' in the
|
|
|
|
|
* same way. In other words, operates the same as commit_odp_actions(), but
|
|
|
|
|
* only on tunneling information. */
|
|
|
|
|
void
|
|
|
|
|
commit_odp_tunnel_action(const struct flow *flow, struct flow *base,
|
2011-11-28 14:14:23 -08:00
|
|
|
|
struct ofpbuf *odp_actions)
|
|
|
|
|
{
|
2012-12-21 10:44:38 +02:00
|
|
|
|
/* A valid IPV4_TUNNEL must have non-zero ip_dst. */
|
|
|
|
|
if (flow->tunnel.ip_dst) {
|
2013-04-18 18:07:39 +03:00
|
|
|
|
if (!memcmp(&base->tunnel, &flow->tunnel, sizeof base->tunnel)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
memcpy(&base->tunnel, &flow->tunnel, sizeof base->tunnel);
|
2012-12-14 19:14:54 -08:00
|
|
|
|
odp_put_tunnel_action(&base->tunnel, odp_actions);
|
2012-12-21 10:44:38 +02:00
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static bool
|
|
|
|
|
commit(enum ovs_key_attr attr, bool use_masked_set,
|
|
|
|
|
const void *key, void *base, void *mask, size_t size,
|
|
|
|
|
struct ofpbuf *odp_actions)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (memcmp(key, base, size)) {
|
|
|
|
|
bool fully_masked = odp_mask_is_exact(attr, mask, size);
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (use_masked_set && !fully_masked) {
|
|
|
|
|
commit_masked_set_action(odp_actions, attr, key, mask, size);
|
|
|
|
|
} else {
|
|
|
|
|
if (!fully_masked) {
|
|
|
|
|
memset(mask, 0xff, size);
|
|
|
|
|
}
|
|
|
|
|
commit_set_action(odp_actions, attr, key, size);
|
|
|
|
|
}
|
|
|
|
|
memcpy(base, key, size);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
/* Mask bits are set when we have either read or set the corresponding
|
|
|
|
|
* values. Masked bits will be exact-matched, no need to set them
|
|
|
|
|
* if the value did not actually change. */
|
|
|
|
|
return false;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
get_ethernet_key(const struct flow *flow, struct ovs_key_ethernet *eth)
|
|
|
|
|
{
|
|
|
|
|
memcpy(eth->eth_src, flow->dl_src, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(eth->eth_dst, flow->dl_dst, ETH_ADDR_LEN);
|
|
|
|
|
}
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
put_ethernet_key(const struct ovs_key_ethernet *eth, struct flow *flow)
|
|
|
|
|
{
|
|
|
|
|
memcpy(flow->dl_src, eth->eth_src, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(flow->dl_dst, eth->eth_dst, ETH_ADDR_LEN);
|
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
commit_set_ether_addr_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions,
|
|
|
|
|
struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_ethernet key, base, mask;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
get_ethernet_key(flow, &key);
|
|
|
|
|
get_ethernet_key(base_flow, &base);
|
|
|
|
|
get_ethernet_key(&wc->masks, &mask);
|
|
|
|
|
|
|
|
|
|
if (commit(OVS_KEY_ATTR_ETHERNET, use_masked,
|
|
|
|
|
&key, &base, &mask, sizeof key, odp_actions)) {
|
|
|
|
|
put_ethernet_key(&base, base_flow);
|
|
|
|
|
put_ethernet_key(&mask, &wc->masks);
|
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-02-04 10:32:35 -08:00
|
|
|
|
pop_vlan(struct flow *base,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2013-06-18 23:55:47 -07:00
|
|
|
|
memset(&wc->masks.vlan_tci, 0xff, sizeof wc->masks.vlan_tci);
|
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
if (base->vlan_tci & htons(VLAN_CFI)) {
|
|
|
|
|
nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
base->vlan_tci = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
commit_vlan_action(ovs_be16 vlan_tci, struct flow *base,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
|
|
|
|
|
{
|
|
|
|
|
if (base->vlan_tci == vlan_tci) {
|
|
|
|
|
return;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
pop_vlan(base, odp_actions, wc);
|
2013-09-27 09:18:30 +09:00
|
|
|
|
if (vlan_tci & htons(VLAN_CFI)) {
|
2011-11-28 14:14:23 -08:00
|
|
|
|
struct ovs_action_push_vlan vlan;
|
|
|
|
|
|
|
|
|
|
vlan.vlan_tpid = htons(ETH_TYPE_VLAN);
|
2013-09-27 09:18:30 +09:00
|
|
|
|
vlan.vlan_tci = vlan_tci;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
|
|
|
|
|
&vlan, sizeof vlan);
|
|
|
|
|
}
|
2013-09-27 09:18:30 +09:00
|
|
|
|
base->vlan_tci = vlan_tci;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-30 13:34:43 -07:00
|
|
|
|
/* Wildcarding already done at action translation time. */
|
2013-01-25 16:22:07 +09:00
|
|
|
|
static void
|
|
|
|
|
commit_mpls_action(const struct flow *flow, struct flow *base,
|
2014-09-30 13:34:43 -07:00
|
|
|
|
struct ofpbuf *odp_actions)
|
2013-01-25 16:22:07 +09:00
|
|
|
|
{
|
2014-09-30 13:34:43 -07:00
|
|
|
|
int base_n = flow_count_mpls_labels(base, NULL);
|
|
|
|
|
int flow_n = flow_count_mpls_labels(flow, NULL);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
int common_n = flow_count_common_mpls_labels(flow, flow_n, base, base_n,
|
2014-09-30 13:34:43 -07:00
|
|
|
|
NULL);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
|
|
|
|
|
while (base_n > common_n) {
|
|
|
|
|
if (base_n - 1 == common_n && flow_n > common_n) {
|
|
|
|
|
/* If there is only one more LSE in base than there are common
|
|
|
|
|
* between base and flow; and flow has at least one more LSE than
|
|
|
|
|
* is common then the topmost LSE of base may be updated using
|
|
|
|
|
* set */
|
|
|
|
|
struct ovs_key_mpls mpls_key;
|
|
|
|
|
|
|
|
|
|
mpls_key.mpls_lse = flow->mpls_lse[flow_n - base_n];
|
|
|
|
|
commit_set_action(odp_actions, OVS_KEY_ATTR_MPLS,
|
|
|
|
|
&mpls_key, sizeof mpls_key);
|
|
|
|
|
flow_set_mpls_lse(base, 0, mpls_key.mpls_lse);
|
|
|
|
|
common_n++;
|
|
|
|
|
} else {
|
|
|
|
|
/* Otherwise, if there more LSEs in base than are common between
|
|
|
|
|
* base and flow then pop the topmost one. */
|
|
|
|
|
ovs_be16 dl_type;
|
|
|
|
|
bool popped;
|
|
|
|
|
|
|
|
|
|
/* If all the LSEs are to be popped and this is not the outermost
|
|
|
|
|
* LSE then use ETH_TYPE_MPLS as the ethertype parameter of the
|
|
|
|
|
* POP_MPLS action instead of flow->dl_type.
|
|
|
|
|
*
|
|
|
|
|
* This is because the POP_MPLS action requires its ethertype
|
|
|
|
|
* argument to be an MPLS ethernet type but in this case
|
|
|
|
|
* flow->dl_type will be a non-MPLS ethernet type.
|
|
|
|
|
*
|
|
|
|
|
* When the final POP_MPLS action occurs it use flow->dl_type and
|
|
|
|
|
* the and the resulting packet will have the desired dl_type. */
|
|
|
|
|
if ((!eth_type_mpls(flow->dl_type)) && base_n > 1) {
|
|
|
|
|
dl_type = htons(ETH_TYPE_MPLS);
|
|
|
|
|
} else {
|
|
|
|
|
dl_type = flow->dl_type;
|
|
|
|
|
}
|
|
|
|
|
nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, dl_type);
|
2014-09-30 13:34:43 -07:00
|
|
|
|
popped = flow_pop_mpls(base, base_n, flow->dl_type, NULL);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
ovs_assert(popped);
|
|
|
|
|
base_n--;
|
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
/* If, after the above popping and setting, there are more LSEs in flow
|
|
|
|
|
* than base then some LSEs need to be pushed. */
|
|
|
|
|
while (base_n < flow_n) {
|
2013-01-25 16:22:07 +09:00
|
|
|
|
struct ovs_action_push_mpls *mpls;
|
|
|
|
|
|
2014-02-04 10:32:35 -08:00
|
|
|
|
mpls = nl_msg_put_unspec_zero(odp_actions,
|
|
|
|
|
OVS_ACTION_ATTR_PUSH_MPLS,
|
2013-10-07 14:11:40 -07:00
|
|
|
|
sizeof *mpls);
|
2013-01-25 16:22:07 +09:00
|
|
|
|
mpls->mpls_ethertype = flow->dl_type;
|
2014-02-04 10:32:35 -08:00
|
|
|
|
mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1];
|
2014-09-30 13:34:43 -07:00
|
|
|
|
flow_push_mpls(base, base_n, mpls->mpls_ethertype, NULL);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
flow_set_mpls_lse(base, 0, mpls->mpls_lse);
|
|
|
|
|
base_n++;
|
2013-09-27 06:55:19 +09:00
|
|
|
|
}
|
2013-01-25 16:22:07 +09:00
|
|
|
|
}
|
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv4_key(const struct flow *flow, struct ovs_key_ipv4 *ipv4, bool is_mask)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
ipv4->ipv4_src = flow->nw_src;
|
|
|
|
|
ipv4->ipv4_dst = flow->nw_dst;
|
|
|
|
|
ipv4->ipv4_proto = flow->nw_proto;
|
|
|
|
|
ipv4->ipv4_tos = flow->nw_tos;
|
|
|
|
|
ipv4->ipv4_ttl = flow->nw_ttl;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
ipv4->ipv4_frag = ovs_to_odp_frag(flow->nw_frag, is_mask);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv4_key(const struct ovs_key_ipv4 *ipv4, struct flow *flow, bool is_mask)
|
2014-09-05 16:00:49 -07:00
|
|
|
|
{
|
|
|
|
|
flow->nw_src = ipv4->ipv4_src;
|
|
|
|
|
flow->nw_dst = ipv4->ipv4_dst;
|
|
|
|
|
flow->nw_proto = ipv4->ipv4_proto;
|
|
|
|
|
flow->nw_tos = ipv4->ipv4_tos;
|
|
|
|
|
flow->nw_ttl = ipv4->ipv4_ttl;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
flow->nw_frag = odp_to_ovs_frag(ipv4->ipv4_frag, is_mask);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
commit_set_ipv4_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_ipv4 key, mask, base;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
/* Check that nw_proto and nw_frag remain unchanged. */
|
|
|
|
|
ovs_assert(flow->nw_proto == base_flow->nw_proto &&
|
|
|
|
|
flow->nw_frag == base_flow->nw_frag);
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv4_key(flow, &key, false);
|
|
|
|
|
get_ipv4_key(base_flow, &base, false);
|
|
|
|
|
get_ipv4_key(&wc->masks, &mask, true);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
mask.ipv4_proto = 0; /* Not writeable. */
|
|
|
|
|
mask.ipv4_frag = 0; /* Not writable. */
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_IPV4, use_masked, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv4_key(&base, base_flow, false);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (mask.ipv4_proto != 0) { /* Mask was changed by commit(). */
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv4_key(&mask, &wc->masks, true);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-01-11 12:44:23 -08:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv6_key(const struct flow *flow, struct ovs_key_ipv6 *ipv6, bool is_mask)
|
2012-01-11 12:44:23 -08:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
memcpy(ipv6->ipv6_src, &flow->ipv6_src, sizeof ipv6->ipv6_src);
|
|
|
|
|
memcpy(ipv6->ipv6_dst, &flow->ipv6_dst, sizeof ipv6->ipv6_dst);
|
|
|
|
|
ipv6->ipv6_label = flow->ipv6_label;
|
|
|
|
|
ipv6->ipv6_proto = flow->nw_proto;
|
|
|
|
|
ipv6->ipv6_tclass = flow->nw_tos;
|
|
|
|
|
ipv6->ipv6_hlimit = flow->nw_ttl;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
ipv6->ipv6_frag = ovs_to_odp_frag(flow->nw_frag, is_mask);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2012-01-11 12:44:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv6_key(const struct ovs_key_ipv6 *ipv6, struct flow *flow, bool is_mask)
|
2014-09-05 16:00:49 -07:00
|
|
|
|
{
|
|
|
|
|
memcpy(&flow->ipv6_src, ipv6->ipv6_src, sizeof flow->ipv6_src);
|
|
|
|
|
memcpy(&flow->ipv6_dst, ipv6->ipv6_dst, sizeof flow->ipv6_dst);
|
|
|
|
|
flow->ipv6_label = ipv6->ipv6_label;
|
|
|
|
|
flow->nw_proto = ipv6->ipv6_proto;
|
|
|
|
|
flow->nw_tos = ipv6->ipv6_tclass;
|
|
|
|
|
flow->nw_ttl = ipv6->ipv6_hlimit;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
flow->nw_frag = odp_to_ovs_frag(ipv6->ipv6_frag, is_mask);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
2012-01-11 12:44:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
commit_set_ipv6_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_ipv6 key, mask, base;
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
/* Check that nw_proto and nw_frag remain unchanged. */
|
|
|
|
|
ovs_assert(flow->nw_proto == base_flow->nw_proto &&
|
|
|
|
|
flow->nw_frag == base_flow->nw_frag);
|
2012-01-11 12:44:23 -08:00
|
|
|
|
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_ipv6_key(flow, &key, false);
|
|
|
|
|
get_ipv6_key(base_flow, &base, false);
|
|
|
|
|
get_ipv6_key(&wc->masks, &mask, true);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
mask.ipv6_proto = 0; /* Not writeable. */
|
|
|
|
|
mask.ipv6_frag = 0; /* Not writable. */
|
2012-01-11 12:44:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_IPV6, use_masked, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv6_key(&base, base_flow, false);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (mask.ipv6_proto != 0) { /* Mask was changed by commit(). */
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_ipv6_key(&mask, &wc->masks, true);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2012-01-11 12:44:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
get_arp_key(const struct flow *flow, struct ovs_key_arp *arp)
|
2013-10-09 17:37:30 -07:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
/* ARP key has padding, clear it. */
|
|
|
|
|
memset(arp, 0, sizeof *arp);
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
arp->arp_sip = flow->nw_src;
|
|
|
|
|
arp->arp_tip = flow->nw_dst;
|
|
|
|
|
arp->arp_op = htons(flow->nw_proto);
|
|
|
|
|
memcpy(arp->arp_sha, flow->arp_sha, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(arp->arp_tha, flow->arp_tha, ETH_ADDR_LEN);
|
|
|
|
|
}
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static void
|
|
|
|
|
put_arp_key(const struct ovs_key_arp *arp, struct flow *flow)
|
|
|
|
|
{
|
|
|
|
|
flow->nw_src = arp->arp_sip;
|
|
|
|
|
flow->nw_dst = arp->arp_tip;
|
|
|
|
|
flow->nw_proto = ntohs(arp->arp_op);
|
|
|
|
|
memcpy(flow->arp_sha, arp->arp_sha, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(flow->arp_tha, arp->arp_tha, ETH_ADDR_LEN);
|
|
|
|
|
}
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
static enum slow_path_reason
|
|
|
|
|
commit_set_arp_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_arp key, mask, base;
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
get_arp_key(flow, &key);
|
|
|
|
|
get_arp_key(base_flow, &base);
|
|
|
|
|
get_arp_key(&wc->masks, &mask);
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_ARP, true, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
|
|
|
|
put_arp_key(&base, base_flow);
|
|
|
|
|
put_arp_key(&mask, &wc->masks);
|
|
|
|
|
return SLOW_ACTION;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2013-10-09 17:37:30 -07:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-23 23:42:05 +00:00
|
|
|
|
static void
|
|
|
|
|
get_nd_key(const struct flow *flow, struct ovs_key_nd *nd)
|
|
|
|
|
{
|
|
|
|
|
memcpy(nd->nd_target, &flow->nd_target, sizeof flow->nd_target);
|
|
|
|
|
/* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
|
|
|
|
|
memcpy(nd->nd_sll, flow->arp_sha, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(nd->nd_tll, flow->arp_tha, ETH_ADDR_LEN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
put_nd_key(const struct ovs_key_nd *nd, struct flow *flow)
|
|
|
|
|
{
|
|
|
|
|
memcpy(&flow->nd_target, &flow->nd_target, sizeof flow->nd_target);
|
|
|
|
|
/* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
|
|
|
|
|
memcpy(flow->arp_sha, nd->nd_sll, ETH_ADDR_LEN);
|
|
|
|
|
memcpy(flow->arp_tha, nd->nd_tll, ETH_ADDR_LEN);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static enum slow_path_reason
|
|
|
|
|
commit_set_nd_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions,
|
|
|
|
|
struct flow_wildcards *wc, bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
struct ovs_key_nd key, mask, base;
|
|
|
|
|
|
|
|
|
|
get_nd_key(flow, &key);
|
|
|
|
|
get_nd_key(base_flow, &base);
|
|
|
|
|
get_nd_key(&wc->masks, &mask);
|
|
|
|
|
|
|
|
|
|
if (commit(OVS_KEY_ATTR_ND, use_masked, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
|
|
|
|
put_nd_key(&base, base_flow);
|
|
|
|
|
put_nd_key(&mask, &wc->masks);
|
|
|
|
|
return SLOW_ACTION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-09 17:37:30 -07:00
|
|
|
|
static enum slow_path_reason
|
2012-01-11 12:44:23 -08:00
|
|
|
|
commit_set_nw_action(const struct flow *flow, struct flow *base,
|
2014-09-05 16:00:49 -07:00
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
2012-01-11 12:44:23 -08:00
|
|
|
|
{
|
2013-10-09 17:37:30 -07:00
|
|
|
|
/* Check if 'flow' really has an L3 header. */
|
2012-01-11 12:44:23 -08:00
|
|
|
|
if (!flow->nw_proto) {
|
2013-10-09 17:37:30 -07:00
|
|
|
|
return 0;
|
2012-01-11 12:44:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2013-10-09 17:37:30 -07:00
|
|
|
|
switch (ntohs(base->dl_type)) {
|
|
|
|
|
case ETH_TYPE_IP:
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_ipv4_action(flow, base, odp_actions, wc, use_masked);
|
2013-10-09 17:37:30 -07:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case ETH_TYPE_IPV6:
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked);
|
2014-12-23 23:42:05 +00:00
|
|
|
|
return commit_set_nd_action(flow, base, odp_actions, wc, use_masked);
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
|
|
|
|
case ETH_TYPE_ARP:
|
|
|
|
|
return commit_set_arp_action(flow, base, odp_actions, wc);
|
2012-01-11 12:44:23 -08:00
|
|
|
|
}
|
2013-10-09 17:37:30 -07:00
|
|
|
|
|
|
|
|
|
return 0;
|
2012-01-11 12:44:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
/* TCP, UDP, and SCTP keys have the same layout. */
|
|
|
|
|
BUILD_ASSERT_DECL(sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_udp) &&
|
|
|
|
|
sizeof(struct ovs_key_tcp) == sizeof(struct ovs_key_sctp));
|
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
get_tp_key(const struct flow *flow, union ovs_key_tp *tp)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2014-09-05 15:44:20 -07:00
|
|
|
|
tp->tcp.tcp_src = flow->tp_src;
|
|
|
|
|
tp->tcp.tcp_dst = flow->tp_dst;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-09-05 15:44:20 -07:00
|
|
|
|
put_tp_key(const union ovs_key_tp *tp, struct flow *flow)
|
2014-09-05 16:00:49 -07:00
|
|
|
|
{
|
2014-09-05 15:44:20 -07:00
|
|
|
|
flow->tp_src = tp->tcp.tcp_src;
|
|
|
|
|
flow->tp_dst = tp->tcp.tcp_dst;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
commit_set_port_action(const struct flow *flow, struct flow *base_flow,
|
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
|
|
|
|
{
|
|
|
|
|
enum ovs_key_attr key_type;
|
2014-09-05 15:44:20 -07:00
|
|
|
|
union ovs_key_tp key, mask, base;
|
2014-09-05 16:00:49 -07:00
|
|
|
|
|
2014-06-04 16:56:09 +00:00
|
|
|
|
/* Check if 'flow' really has an L3 header. */
|
|
|
|
|
if (!flow->nw_proto) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (!is_ip_any(base_flow)) {
|
2011-11-28 14:14:23 -08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flow->nw_proto == IPPROTO_TCP) {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key_type = OVS_KEY_ATTR_TCP;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_UDP) {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key_type = OVS_KEY_ATTR_UDP;
|
2013-08-22 20:24:44 +12:00
|
|
|
|
} else if (flow->nw_proto == IPPROTO_SCTP) {
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key_type = OVS_KEY_ATTR_SCTP;
|
|
|
|
|
} else {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
get_tp_key(flow, &key);
|
|
|
|
|
get_tp_key(base_flow, &base);
|
|
|
|
|
get_tp_key(&wc->masks, &mask);
|
2013-08-22 20:24:44 +12:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(key_type, use_masked, &key, &base, &mask, sizeof key,
|
|
|
|
|
odp_actions)) {
|
|
|
|
|
put_tp_key(&base, base_flow);
|
|
|
|
|
put_tp_key(&mask, &wc->masks);
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_priority_action(const struct flow *flow, struct flow *base_flow,
|
2013-06-18 23:55:47 -07:00
|
|
|
|
struct ofpbuf *odp_actions,
|
2014-09-05 16:00:49 -07:00
|
|
|
|
struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
uint32_t key, mask, base;
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key = flow->skb_priority;
|
|
|
|
|
base = base_flow->skb_priority;
|
|
|
|
|
mask = wc->masks.skb_priority;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_PRIORITY, use_masked, &key, &base, &mask,
|
|
|
|
|
sizeof key, odp_actions)) {
|
|
|
|
|
base_flow->skb_priority = base;
|
|
|
|
|
wc->masks.skb_priority = mask;
|
|
|
|
|
}
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|
|
|
|
|
|
2012-11-13 19:19:36 +02:00
|
|
|
|
static void
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_pkt_mark_action(const struct flow *flow, struct flow *base_flow,
|
2013-06-18 23:55:47 -07:00
|
|
|
|
struct ofpbuf *odp_actions,
|
2014-09-05 16:00:49 -07:00
|
|
|
|
struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
2012-11-13 19:19:36 +02:00
|
|
|
|
{
|
2014-09-05 16:00:49 -07:00
|
|
|
|
uint32_t key, mask, base;
|
2013-06-18 23:55:47 -07:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
key = flow->pkt_mark;
|
|
|
|
|
base = base_flow->pkt_mark;
|
|
|
|
|
mask = wc->masks.pkt_mark;
|
2012-11-13 19:19:36 +02:00
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
if (commit(OVS_KEY_ATTR_SKB_MARK, use_masked, &key, &base, &mask,
|
|
|
|
|
sizeof key, odp_actions)) {
|
|
|
|
|
base_flow->pkt_mark = base;
|
|
|
|
|
wc->masks.pkt_mark = mask;
|
|
|
|
|
}
|
2012-11-13 19:19:36 +02:00
|
|
|
|
}
|
2013-10-09 17:28:05 -07:00
|
|
|
|
|
2011-11-28 14:14:23 -08:00
|
|
|
|
/* If any of the flow key data that ODP actions can modify are different in
|
|
|
|
|
* 'base' and 'flow', appends ODP actions to 'odp_actions' that change the flow
|
2012-12-14 19:14:54 -08:00
|
|
|
|
* key from 'base' into 'flow', and then changes 'base' the same way. Does not
|
|
|
|
|
* commit set_tunnel actions. Users should call commit_odp_tunnel_action()
|
2013-06-18 23:55:47 -07:00
|
|
|
|
* in addition to this function if needed. Sets fields in 'wc' that are
|
2013-10-09 17:28:05 -07:00
|
|
|
|
* used as part of the action.
|
|
|
|
|
*
|
|
|
|
|
* Returns a reason to force processing the flow's packets into the userspace
|
|
|
|
|
* slow path, if there is one, otherwise 0. */
|
|
|
|
|
enum slow_path_reason
|
2011-11-28 14:14:23 -08:00
|
|
|
|
commit_odp_actions(const struct flow *flow, struct flow *base,
|
2014-09-05 16:00:49 -07:00
|
|
|
|
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
|
|
|
|
|
bool use_masked)
|
2011-11-28 14:14:23 -08:00
|
|
|
|
{
|
2013-10-09 17:37:30 -07:00
|
|
|
|
enum slow_path_reason slow;
|
|
|
|
|
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_ether_addr_action(flow, base, odp_actions, wc, use_masked);
|
|
|
|
|
slow = commit_set_nw_action(flow, base, odp_actions, wc, use_masked);
|
|
|
|
|
commit_set_port_action(flow, base, odp_actions, wc, use_masked);
|
2014-09-30 13:34:43 -07:00
|
|
|
|
commit_mpls_action(flow, base, odp_actions);
|
2014-02-04 10:32:35 -08:00
|
|
|
|
commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
|
2014-09-05 16:00:49 -07:00
|
|
|
|
commit_set_priority_action(flow, base, odp_actions, wc, use_masked);
|
|
|
|
|
commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked);
|
2013-10-09 17:28:05 -07:00
|
|
|
|
|
2013-10-09 17:37:30 -07:00
|
|
|
|
return slow;
|
2011-11-28 14:14:23 -08:00
|
|
|
|
}
|