Skip to content

Commit

Permalink
tunneling: Userspace datapath support for Geneve options.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
jessegross committed Jun 26, 2015
1 parent daab0ae commit 5bb08b0
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 88 deletions.
2 changes: 1 addition & 1 deletion datapath/linux/compat/include/linux/openvswitch.h
Expand Up @@ -618,7 +618,7 @@ struct ovs_action_hash {
};

#ifndef __KERNEL__
#define TNL_PUSH_HEADER_SIZE 128
#define TNL_PUSH_HEADER_SIZE 512

/*
* struct ovs_action_push_tnl - %OVS_ACTION_ATTR_TUNNEL_PUSH
Expand Down
29 changes: 20 additions & 9 deletions lib/netdev-vport.c
Expand Up @@ -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)) {
Expand All @@ -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));
Expand All @@ -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;
Expand All @@ -1253,19 +1255,28 @@ 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);
tnl_cfg = &dev->tnl_cfg;

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;
}
Expand Down
94 changes: 71 additions & 23 deletions lib/odp-util.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -1937,6 +1971,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)
Expand Down Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions lib/packets.h
Expand Up @@ -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)

Expand Down

0 comments on commit 5bb08b0

Please sign in to comment.