summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJesse Gross <jesse@nicira.com>2015-06-22 14:23:37 -0700
committerJesse Gross <jesse@nicira.com>2015-06-26 14:18:07 -0700
commit5bb08b0ef605a9b164399bd74a194ae1b921c3ea (patch)
treedc63682400cab6eb2fd50cc563d88aeb12a60ee6 /lib
parentdaab0ae6bb558411f24cee6a7bb32599a5f9e363 (diff)
downloadopenvswitch-5bb08b0ef605a9b164399bd74a194ae1b921c3ea.tar.gz
tunneling: Userspace datapath support for Geneve options.
Currently the userspace datapath only supports Geneve in a basic mode - without options - since the rest of userspace previously didn't support options either. This enables the userspace datapath to send and receive options as well. The receive path for extracting the tunnel options isn't entirely optimal because it does a lookup on the options on a per-packet basis, rather than per-flow like the kernel does. This is not as straightforward to do in the userspace datapath since there is no translation step between packet formats used in packet vs. flow lookup. This can be optimized in the future and in the meantime option support is still useful for testing and simulation. Signed-off-by: Jesse Gross <jesse@nicira.com> Acked-by: Ben Pfaff <blp@nicira.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/netdev-vport.c29
-rw-r--r--lib/odp-util.c94
-rw-r--r--lib/packets.h1
-rw-r--r--lib/tun-metadata.c149
-rw-r--r--lib/tun-metadata.h7
5 files changed, 193 insertions, 87 deletions
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index ea9abf9e7..259d0ed4d 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -1199,6 +1199,7 @@ netdev_geneve_pop_header(struct dp_packet *packet)
struct flow_tnl *tnl = &md->tunnel;
struct genevehdr *gnh;
unsigned int hlen;
+ int err;
memset(md, 0, sizeof *md);
if (GENEVE_BASE_HLEN > dp_packet_size(packet)) {
@@ -1224,12 +1225,6 @@ netdev_geneve_pop_header(struct dp_packet *packet)
return EINVAL;
}
- if (gnh->opt_len && gnh->critical) {
- VLOG_WARN_RL(&err_rl, "unknown geneve critical options: %"PRIu8" bytes\n",
- gnh->opt_len * 4);
- return EINVAL;
- }
-
if (gnh->proto_type != htons(ETH_TYPE_TEB)) {
VLOG_WARN_RL(&err_rl, "unknown geneve encapsulated protocol: %#x\n",
ntohs(gnh->proto_type));
@@ -1240,6 +1235,13 @@ netdev_geneve_pop_header(struct dp_packet *packet)
tnl->tun_id = htonll(ntohl(get_16aligned_be32(&gnh->vni)) >> 8);
tnl->flags |= FLOW_TNL_F_KEY;
+ err = tun_metadata_from_geneve_header(gnh->options, gnh->opt_len * 4,
+ &tnl->metadata);
+ if (err) {
+ VLOG_WARN_RL(&err_rl, "invalid geneve options");
+ return err;
+ }
+
dp_packet_reset_packet(packet, hlen);
return 0;
@@ -1253,6 +1255,8 @@ netdev_geneve_build_header(const struct netdev *netdev,
struct netdev_vport *dev = netdev_vport_cast(netdev);
struct netdev_tunnel_config *tnl_cfg;
struct genevehdr *gnh;
+ int opt_len;
+ bool crit_opt;
/* XXX: RCUfy tnl_cfg. */
ovs_mutex_lock(&dev->mutex);
@@ -1260,12 +1264,19 @@ netdev_geneve_build_header(const struct netdev *netdev,
gnh = udp_build_header(tnl_cfg, tnl_flow, data);
- gnh->oam = !!(tnl_flow->tunnel.flags & FLOW_TNL_F_OAM);
- gnh->proto_type = htons(ETH_TYPE_TEB);
put_16aligned_be32(&gnh->vni, htonl(ntohll(tnl_flow->tunnel.tun_id) << 8));
ovs_mutex_unlock(&dev->mutex);
- data->header_len = GENEVE_BASE_HLEN;
+
+ opt_len = tun_metadata_to_geneve_header(&tnl_flow->tunnel.metadata,
+ gnh->options, &crit_opt);
+
+ gnh->opt_len = opt_len / 4;
+ gnh->oam = !!(tnl_flow->tunnel.flags & FLOW_TNL_F_OAM);
+ gnh->critical = crit_opt ? 1 : 0;
+ gnh->proto_type = htons(ETH_TYPE_TEB);
+
+ data->header_len = GENEVE_BASE_HLEN + opt_len;
data->tnl_type = OVS_VPORT_TYPE_GENEVE;
return 0;
}
diff --git a/lib/odp-util.c b/lib/odp-util.c
index efdc65179..c70ee0f24 100644
--- a/lib/odp-util.c
+++ b/lib/odp-util.c
@@ -69,6 +69,17 @@ static void format_odp_key_attr(const struct nlattr *a,
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);
@@ -581,9 +592,19 @@ format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
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;
@@ -939,17 +960,41 @@ ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
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;
}
@@ -1873,21 +1918,10 @@ format_odp_tun_vxlan_opt(const struct nlattr *attr,
#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;
@@ -1938,6 +1972,25 @@ format_odp_tun_geneve(const struct nlattr *attr,
}
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)
{
@@ -2875,11 +2928,6 @@ scan_vxlan_gbp(const char *s, uint32_t *key, uint32_t *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)
{
diff --git a/lib/packets.h b/lib/packets.h
index 04ee914d2..c401d4e69 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -754,6 +754,7 @@ static inline bool dl_type_is_ip_any(ovs_be16 dl_type)
/* Tunnel header */
#define GENEVE_MAX_OPT_SIZE 124
+#define GENEVE_TOT_OPT_SIZE 252
#define GENEVE_CRIT_OPT_TYPE (1 << 7)
diff --git a/lib/tun-metadata.c b/lib/tun-metadata.c
index d9ce9f276..7d82fb762 100644
--- a/lib/tun-metadata.c
+++ b/lib/tun-metadata.c
@@ -578,55 +578,15 @@ tun_metadata_del_entry(struct tun_table *map, uint8_t idx)
memset(&entry->loc, 0, sizeof entry->loc);
}
-int
-tun_metadata_from_geneve_nlattr(const struct nlattr *attr,
- const struct nlattr *flow_attrs,
- size_t flow_attr_len,
- const struct tun_metadata *flow_metadata,
- struct tun_metadata *metadata)
+static int
+tun_metadata_from_geneve__(struct tun_table *map, const struct geneve_opt *opt,
+ const struct geneve_opt *flow_opt, int opts_len,
+ struct tun_metadata *metadata)
{
- bool is_mask = !!flow_attrs;
- struct tun_table *map;
- const struct nlattr *flow;
- int opts_len;
- const struct geneve_opt *flow_opt;
- const struct geneve_opt *opt = nl_attr_get(attr);
-
- if (!is_mask) {
- map = ovsrcu_get(struct tun_table *, &metadata_tab);
- metadata->tab = map;
- } else {
- map = flow_metadata->tab;
- }
-
if (!map) {
return 0;
}
- if (is_mask) {
- const struct nlattr *tnl_key;
- int mask_len = nl_attr_get_size(attr);
-
- tnl_key = nl_attr_find__(flow_attrs, flow_attr_len, OVS_KEY_ATTR_TUNNEL);
- if (!tnl_key) {
- return mask_len ? EINVAL : 0;
- }
-
- flow = nl_attr_find_nested(tnl_key, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
- if (!flow) {
- return mask_len ? EINVAL : 0;
- }
-
- if (mask_len != nl_attr_get_size(flow)) {
- return EINVAL;
- }
- } else {
- flow = attr;
- }
-
- opts_len = nl_attr_get_size(flow);
- flow_opt = nl_attr_get(flow);
-
while (opts_len > 0) {
int len;
struct tun_meta_entry *entry;
@@ -662,27 +622,74 @@ tun_metadata_from_geneve_nlattr(const struct nlattr *attr,
return 0;
}
-void
-tun_metadata_to_geneve_nlattr_flow(const struct tun_metadata *flow,
- struct ofpbuf *b)
+int
+tun_metadata_from_geneve_nlattr(const struct nlattr *attr,
+ const struct nlattr *flow_attrs,
+ size_t flow_attr_len,
+ const struct tun_metadata *flow_metadata,
+ struct tun_metadata *metadata)
{
struct tun_table *map;
- size_t nlattr_offset;
- int i;
+ bool is_mask = !!flow_attrs;
+ const struct nlattr *flow;
- if (!flow->opt_map) {
- return;
+ if (is_mask) {
+ const struct nlattr *tnl_key;
+ int mask_len = nl_attr_get_size(attr);
+
+ tnl_key = nl_attr_find__(flow_attrs, flow_attr_len, OVS_KEY_ATTR_TUNNEL);
+ if (!tnl_key) {
+ return mask_len ? EINVAL : 0;
+ }
+
+ flow = nl_attr_find_nested(tnl_key, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
+ if (!flow) {
+ return mask_len ? EINVAL : 0;
+ }
+
+ if (mask_len != nl_attr_get_size(flow)) {
+ return EINVAL;
+ }
+ } else {
+ flow = attr;
}
+ if (!is_mask) {
+ map = ovsrcu_get(struct tun_table *, &metadata_tab);
+ metadata->tab = map;
+ } else {
+ map = flow_metadata->tab;
+ }
+
+ return tun_metadata_from_geneve__(map, nl_attr_get(attr), nl_attr_get(flow),
+ nl_attr_get_size(flow), metadata);
+}
+
+int
+tun_metadata_from_geneve_header(const struct geneve_opt *opts, int opt_len,
+ struct tun_metadata *metadata)
+{
+ struct tun_table *map;
+
+ map = ovsrcu_get(struct tun_table *, &metadata_tab);
+ metadata->tab = map;
+
+ return tun_metadata_from_geneve__(map, opts, opts, opt_len, metadata);
+}
+
+static void
+tun_metadata_to_geneve__(const struct tun_metadata *flow, struct ofpbuf *b,
+ bool *crit_opt)
+{
+ struct tun_table *map;
+ int i;
+
map = flow->tab;
if (!map) {
map = ovsrcu_get(struct tun_table *, &metadata_tab);
}
- /* For all intents and purposes, the Geneve options are nested
- * attributes even if this doesn't show up directly to netlink. It's
- * similar enough that we can use the same mechanism. */
- nlattr_offset = nl_msg_start_nested(b, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
+ *crit_opt = false;
ULLONG_FOR_EACH_1 (i, flow->opt_map) {
struct tun_meta_entry *entry = &map->entries[i];
@@ -698,11 +705,43 @@ tun_metadata_to_geneve_nlattr_flow(const struct tun_metadata *flow,
opt->r3 = 0;
memcpy_from_metadata(opt + 1, flow, &entry->loc);
+ *crit_opt |= !!(opt->type & GENEVE_CRIT_OPT_TYPE);
}
+}
+
+void
+tun_metadata_to_geneve_nlattr_flow(const struct tun_metadata *flow,
+ struct ofpbuf *b)
+{
+ size_t nlattr_offset;
+ bool crit_opt;
+
+ if (!flow->opt_map) {
+ return;
+ }
+
+ /* For all intents and purposes, the Geneve options are nested
+ * attributes even if this doesn't show up directly to netlink. It's
+ * similar enough that we can use the same mechanism. */
+ nlattr_offset = nl_msg_start_nested(b, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
+
+ tun_metadata_to_geneve__(flow, b, &crit_opt);
nl_msg_end_nested(b, nlattr_offset);
}
+int
+tun_metadata_to_geneve_header(const struct tun_metadata *flow,
+ struct geneve_opt *opts, bool *crit_opt)
+{
+ struct ofpbuf b;
+
+ ofpbuf_use_stack(&b, opts, GENEVE_TOT_OPT_SIZE);
+ tun_metadata_to_geneve__(flow, &b, crit_opt);
+
+ return b.size;
+}
+
void
tun_metadata_to_geneve_nlattr_mask(const struct ofpbuf *key,
const struct tun_metadata *mask,
diff --git a/lib/tun-metadata.h b/lib/tun-metadata.h
index 76a72e8ee..a1fbc0ab6 100644
--- a/lib/tun-metadata.h
+++ b/lib/tun-metadata.h
@@ -30,6 +30,7 @@ union mf_value;
struct ofputil_geneve_table_mod;
struct ofputil_geneve_table_reply;
struct tun_table;
+struct geneve_opt;
#define TUN_METADATA_NUM_OPTS 64
#define TUN_METADATA_TOT_OPT_SIZE 256
@@ -95,12 +96,18 @@ int tun_metadata_from_geneve_nlattr(const struct nlattr *attr,
size_t flow_attr_len,
const struct tun_metadata *flow_metadata,
struct tun_metadata *metadata);
+int tun_metadata_from_geneve_header(const struct geneve_opt *, int opt_len,
+ struct tun_metadata *metadata);
+
void tun_metadata_to_geneve_nlattr_flow(const struct tun_metadata *flow,
struct ofpbuf *);
void tun_metadata_to_geneve_nlattr_mask(const struct ofpbuf *key,
const struct tun_metadata *mask,
const struct tun_metadata *flow,
struct ofpbuf *);
+int tun_metadata_to_geneve_header(const struct tun_metadata *flow,
+ struct geneve_opt *, bool *crit_opt);
+
void tun_metadata_to_nx_match(struct ofpbuf *b, enum ofp_version oxm,
const struct match *);
void tun_metadata_match_format(struct ds *, const struct match *);