/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include "packets.h"
#include "simap.h"
#include "timeval.h"
+#include "tun-metadata.h"
#include "unaligned.h"
#include "util.h"
#include "uuid.h"
const struct hmap *portno_names, struct ds *ds,
bool verbose);
+struct geneve_scan {
+ struct geneve_opt d[63];
+ int len;
+};
+
+static int scan_geneve(const char *s, struct geneve_scan *key,
+ struct geneve_scan *mask);
+static void format_geneve_opts(const struct geneve_opt *opt,
+ const struct geneve_opt *mask, int opts_len,
+ struct ds *, bool verbose);
+
static struct nlattr *generate_all_wildcard_mask(const struct attr_len_tbl tbl[],
int max, struct ofpbuf *,
const struct nlattr *key);
}
static int
-parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
- uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask)
+parse_odp_flags(const char *s, const char *(*bit_to_string)(uint32_t),
+ uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask)
{
- uint32_t result = 0;
- int n;
-
- /* Parse masked flags in numeric format? */
- if (res_mask && ovs_scan(s, "%"SCNi32"/%"SCNi32"%n",
- res_flags, res_mask, &n) && n > 0) {
- if (*res_flags & ~allowed || *res_mask & ~allowed) {
- return -EINVAL;
- }
- return n;
- }
-
- n = 0;
-
- if (res_mask && (*s == '+' || *s == '-')) {
- uint32_t flags = 0, mask = 0;
-
- /* Parse masked flags. */
- while (s[0] != ')') {
- bool set;
- uint32_t bit;
- int name_len;
-
- if (s[0] == '+') {
- set = true;
- } else if (s[0] == '-') {
- set = false;
- } else {
- return -EINVAL;
- }
- s++;
- n++;
-
- name_len = strcspn(s, "+-)");
-
- 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, 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;
- n += 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. */
- while (s[n] != ')') {
- unsigned long long int flags;
- uint32_t bit;
- int n0;
-
- if (ovs_scan(&s[n], "%lli%n", &flags, &n0)) {
- if (flags & ~allowed) {
- return -EINVAL;
- }
- 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] == ')')) {
- if (!(bit & allowed)) {
- return -EINVAL;
- }
- result |= bit;
- n += len + (s[n + len] == ',');
- break;
- }
- }
-
- if (!bit) {
- return -EINVAL;
- }
- }
-
- *res_flags = result;
- if (res_mask) {
- *res_mask = UINT32_MAX;
- }
- return n;
+ return parse_flags(s, bit_to_string, ')', NULL, NULL,
+ res_flags, allowed, res_mask);
}
static void
.optional = true },
[OVS_USERSPACE_ATTR_EGRESS_TUN_PORT] = { .type = NL_A_U32,
.optional = true },
+ [OVS_USERSPACE_ATTR_ACTIONS] = { .type = NL_A_UNSPEC,
+ .optional = true },
};
struct nlattr *a[ARRAY_SIZE(ovs_userspace_policy)];
const struct nlattr *userdata_attr;
}
}
+ if (a[OVS_USERSPACE_ATTR_ACTIONS]) {
+ ds_put_cstr(ds, ",actions");
+ }
+
tunnel_out_port_attr = a[OVS_USERSPACE_ATTR_EGRESS_TUN_PORT];
if (tunnel_out_port_attr) {
ds_put_format(ds, ",tunnel_out_port=%"PRIu32,
gnh = format_udp_tnl_push_header(ds, ip);
- ds_put_format(ds, "geneve(%svni=0x%"PRIx32")",
+ ds_put_format(ds, "geneve(%s%svni=0x%"PRIx32,
gnh->oam ? "oam," : "",
+ gnh->critical ? "crit," : "",
ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
+
+ if (gnh->opt_len) {
+ ds_put_cstr(ds, ",options(");
+ format_geneve_opts(gnh->options, NULL, gnh->opt_len * 4,
+ ds, false);
+ ds_put_char(ds, ')');
+ }
+
+ ds_put_char(ds, ')');
} else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
const struct gre_base_hdr *greh;
ovs_16aligned_be32 *options;
int n = -1;
void *user_data = NULL;
size_t user_data_size = 0;
+ bool include_actions = false;
if (!ovs_scan(s, "userspace(pid=%"SCNi32"%n", &pid, &n)) {
return -EINVAL;
cookie.slow_path.unused = 0;
cookie.slow_path.reason = 0;
- res = parse_flags(&s[n], slow_path_reason_to_string,
- &cookie.slow_path.reason,
- SLOW_PATH_REASON_MASK, NULL);
+ res = parse_odp_flags(&s[n], slow_path_reason_to_string,
+ &cookie.slow_path.reason,
+ SLOW_PATH_REASON_MASK, NULL);
if (res < 0 || s[n + res] != ')') {
return res;
}
}
}
+ {
+ int n1 = -1;
+ if (ovs_scan(&s[n], ",actions%n", &n1)) {
+ n += n1;
+ include_actions = true;
+ }
+ }
+
{
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);
+ odp_put_userspace_action(pid, user_data, user_data_size,
+ tunnel_out_port, include_actions, actions);
return n + n1;
} else if (s[n] == ')') {
- odp_put_userspace_action(pid, user_data, user_data_size, ODPP_NONE, actions);
+ odp_put_userspace_action(pid, user_data, user_data_size,
+ ODPP_NONE, include_actions, actions);
return n + 1;
}
}
struct genevehdr *gnh = (struct genevehdr *) (udp + 1);
memset(gnh, 0, sizeof *gnh);
+ header_len = sizeof *eth + sizeof *ip +
+ sizeof *udp + sizeof *gnh;
+
if (ovs_scan_len(s, &n, "oam,")) {
gnh->oam = 1;
}
- if (!ovs_scan_len(s, &n, "vni=0x%"SCNx32"))", &vni)) {
+ if (ovs_scan_len(s, &n, "crit,")) {
+ gnh->critical = 1;
+ }
+ if (!ovs_scan_len(s, &n, "vni=%"SCNi32, &vni)) {
+ return -EINVAL;
+ }
+ if (ovs_scan_len(s, &n, ",options(")) {
+ struct geneve_scan options;
+ int len;
+
+ memset(&options, 0, sizeof options);
+ len = scan_geneve(s + n, &options, NULL);
+ if (!len) {
+ return -EINVAL;
+ }
+
+ memcpy(gnh->options, options.d, options.len);
+ gnh->opt_len = options.len / 4;
+ header_len += options.len;
+
+ n += len;
+ }
+ if (!ovs_scan_len(s, &n, "))")) {
return -EINVAL;
}
+
gnh->proto_type = htons(ETH_TYPE_TEB);
put_16aligned_be32(&gnh->vni, htonl(vni << 8));
tnl_type = OVS_VPORT_TYPE_GENEVE;
- header_len = sizeof *eth + sizeof *ip +
- sizeof *udp + sizeof *gnh;
} else {
return -EINVAL;
}
}
}
-#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;
-}
-
static enum odp_key_fitness
odp_tun_key_from_attr__(const struct nlattr *attr,
- const struct nlattr *flow_attrs OVS_UNUSED,
- size_t flow_attr_len OVS_UNUSED,
- const struct flow_tnl *src_tun OVS_UNUSED,
- struct flow_tnl *tun)
+ const struct nlattr *flow_attrs, size_t flow_attr_len,
+ const struct flow_tnl *src_tun, struct flow_tnl *tun)
{
unsigned int left;
const struct nlattr *a;
break;
}
- case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: {
- if (parse_geneve_opts(a)) {
+ case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS:
+ if (tun_metadata_from_geneve_nlattr(a, flow_attrs, flow_attr_len,
+ &src_tun->metadata,
+ &tun->metadata)) {
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;
- }
+
default:
/* Allow this to show up as unexpected, if there are unknown
* tunnel attribute, eventually resulting in ODP_FIT_TOO_MUCH. */
enum odp_key_fitness
odp_tun_key_from_attr(const struct nlattr *attr, struct flow_tnl *tun)
{
+ memset(tun, 0, sizeof *tun);
return odp_tun_key_from_attr__(attr, NULL, 0, NULL, tun);
}
static void
tun_key_to_attr(struct ofpbuf *a, const struct flow_tnl *tun_key,
- const struct flow_tnl *tun_flow_key OVS_UNUSED,
- const struct ofpbuf *key_buf OVS_UNUSED)
+ const struct flow_tnl *tun_flow_key,
+ const struct ofpbuf *key_buf)
{
size_t tun_key_ofs;
nl_msg_end_nested(a, vxlan_opts_ofs);
}
+ if (tun_key == tun_flow_key) {
+ tun_metadata_to_geneve_nlattr_flow(&tun_key->metadata, a);
+ } else {
+ tun_metadata_to_geneve_nlattr_mask(key_buf, &tun_key->metadata,
+ &tun_flow_key->metadata, a);
+ }
+
nl_msg_end_nested(a, tun_key_ofs);
}
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);
+ if (mask) {
+ format_flags_masked(ds, NULL, flow_tun_flag_to_string, key,
+ *mask & FLOW_TNL_F_MASK, FLOW_TNL_F_MASK);
} else { /* Fully masked. */
- format_flags(ds, flow_tun_flag_to_string, key, ',');
+ format_flags(ds, flow_tun_flag_to_string, key, '|');
}
ds_put_cstr(ds, "),");
}
#define MASK(PTR, FIELD) PTR ? &PTR->FIELD : NULL
static void
-format_odp_tun_geneve(const struct nlattr *attr,
- const struct nlattr *mask_attr, struct ds *ds,
- bool verbose)
+format_geneve_opts(const struct geneve_opt *opt,
+ const struct geneve_opt *mask, int opts_len,
+ struct ds *ds, bool verbose)
{
- int opts_len = nl_attr_get_size(attr);
- const struct geneve_opt *opt = nl_attr_get(attr);
- const struct geneve_opt *mask = mask_attr ?
- nl_attr_get(mask_attr) : NULL;
-
- if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) {
- ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE,
- nl_attr_get_size(attr), nl_attr_get_size(mask_attr));
- return;
- }
-
while (opts_len > 0) {
unsigned int len;
uint8_t data_len, data_len_mask;
};
}
+static void
+format_odp_tun_geneve(const struct nlattr *attr,
+ const struct nlattr *mask_attr, struct ds *ds,
+ bool verbose)
+{
+ int opts_len = nl_attr_get_size(attr);
+ const struct geneve_opt *opt = nl_attr_get(attr);
+ const struct geneve_opt *mask = mask_attr ?
+ nl_attr_get(mask_attr) : NULL;
+
+ if (mask && nl_attr_get_size(attr) != nl_attr_get_size(mask_attr)) {
+ ds_put_format(ds, "value len %"PRIuSIZE" different from mask len %"PRIuSIZE,
+ nl_attr_get_size(attr), nl_attr_get_size(mask_attr));
+ return;
+ }
+
+ format_geneve_opts(opt, mask, opts_len, ds, verbose);
+}
+
static void
format_odp_tun_attr(const struct nlattr *attr, const struct nlattr *mask_attr,
struct ds *ds, bool verbose)
if (!is_exact) {
format_flags_masked(ds, NULL, packet_tcp_flag_to_string,
ntohs(nl_attr_get_be16(a)),
- ntohs(nl_attr_get_be16(ma)));
+ TCP_FLAGS(nl_attr_get_be16(ma)),
+ TCP_FLAGS(OVS_BE16_MAX));
} else {
format_flags(ds, packet_tcp_flag_to_string,
- ntohs(nl_attr_get_be16(a)), ',');
+ ntohs(nl_attr_get_be16(a)), '|');
}
break;
uint32_t flags, fmask;
int n;
- n = parse_flags(s, flow_tun_flag_to_string, &flags,
- FLOW_TNL_F_MASK, mask ? &fmask : NULL);
+ n = parse_odp_flags(s, flow_tun_flag_to_string, &flags,
+ FLOW_TNL_F_MASK, mask ? &fmask : NULL);
if (n >= 0 && s[n] == ')') {
*key = flags;
if (mask) {
uint32_t flags, fmask;
int n;
- n = parse_flags(s, packet_tcp_flag_to_string, &flags,
- TCP_FLAGS(OVS_BE16_MAX), mask ? &fmask : NULL);
+ n = parse_odp_flags(s, packet_tcp_flag_to_string, &flags,
+ TCP_FLAGS(OVS_BE16_MAX), mask ? &fmask : NULL);
if (n >= 0) {
*key = htons(flags);
if (mask) {
return 0;
}
-struct geneve_scan {
- struct geneve_opt d[63];
- int len;
-};
-
static int
scan_geneve(const char *s, struct geneve_scan *key, struct geneve_scan *mask)
{
nl_msg_put_u32(buf, OVS_KEY_ATTR_SKB_MARK, data->pkt_mark);
- if (parms->recirc) {
+ if (parms->support.recirc) {
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);
}
n = flow_count_mpls_labels(flow, NULL);
if (export_mask) {
- n = MIN(n, parms->max_mpls_depth);
+ n = MIN(n, parms->support.max_mpls_depth);
}
mpls_key = nl_msg_put_unspec_uninit(buf, OVS_KEY_ATTR_MPLS,
n * sizeof *mpls_key);
1u << OVS_KEY_ATTR_SKB_MARK | 1u << OVS_KEY_ATTR_TUNNEL |
1u << OVS_KEY_ATTR_IN_PORT;
- *md = PKT_METADATA_INITIALIZER(ODPP_NONE);
+ pkt_metadata_init(md, ODPP_NONE);
NL_ATTR_FOR_EACH (nla, left, key, key_len) {
uint16_t type = nl_attr_type(nla);
odp_put_userspace_action(uint32_t pid,
const void *userdata, size_t userdata_size,
odp_port_t tunnel_out_port,
+ bool include_actions,
struct ofpbuf *odp_actions)
{
size_t userdata_ofs;
nl_msg_put_odp_port(odp_actions, OVS_USERSPACE_ATTR_EGRESS_TUN_PORT,
tunnel_out_port);
}
+ if (include_actions) {
+ nl_msg_put_flag(odp_actions, OVS_USERSPACE_ATTR_ACTIONS);
+ }
nl_msg_end_nested(odp_actions, offset);
return userdata_ofs;