2
0
mirror of https://github.com/openvswitch/ovs synced 2025-08-31 14:25:26 +00:00

lib/tc: add geneve with option match offload

Add TC offload support for classifying geneve tunnels with options.

Signed-off-by: Pieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com>
Signed-off-by: Simon Horman <simon.horman@netronome.com>
This commit is contained in:
Pieter Jansen van Vuuren
2018-09-18 09:36:20 +01:00
committed by Simon Horman
parent 202469aa9e
commit a468645c6d
4 changed files with 286 additions and 2 deletions

198
lib/tc.c
View File

@@ -325,6 +325,9 @@ static const struct nl_policy tca_flower_policy[] = {
.optional = true, },
[TCA_FLOWER_KEY_ENC_IP_TTL_MASK] = { .type = NL_A_U8,
.optional = true, },
[TCA_FLOWER_KEY_ENC_OPTS] = { .type = NL_A_NESTED, .optional = true, },
[TCA_FLOWER_KEY_ENC_OPTS_MASK] = { .type = NL_A_NESTED,
.optional = true, },
};
static void
@@ -435,9 +438,130 @@ nl_parse_flower_vlan(struct nlattr **attrs, struct tc_flower *flower)
}
}
static void
static int
nl_parse_geneve_key(const struct nlattr *in_nlattr,
struct tun_metadata *metadata)
{
struct geneve_opt *opt = NULL;
const struct ofpbuf *msg;
uint16_t last_opt_type;
struct nlattr *nla;
struct ofpbuf buf;
size_t left;
int cnt;
nl_attr_get_nested(in_nlattr, &buf);
msg = &buf;
last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC;
cnt = 0;
NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
uint16_t type = nl_attr_type(nla);
switch (type) {
case TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS:
if (cnt && last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA) {
VLOG_ERR_RL(&error_rl, "failed to parse tun options class");
return EINVAL;
}
opt = &metadata->opts.gnv[cnt];
opt->opt_class = nl_attr_get_be16(nla);
cnt += sizeof(struct geneve_opt) / 4;
metadata->present.len += sizeof(struct geneve_opt);
last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS;
break;
case TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE:
if (last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS) {
VLOG_ERR_RL(&error_rl, "failed to parse tun options type");
return EINVAL;
}
opt->type = nl_attr_get_u8(nla);
last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE;
break;
case TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA:
if (last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE) {
VLOG_ERR_RL(&error_rl, "failed to parse tun options data");
return EINVAL;
}
opt->length = nl_attr_get_size(nla) / 4;
memcpy(opt + 1, nl_attr_get_unspec(nla, 1), opt->length * 4);
cnt += opt->length;
metadata->present.len += opt->length * 4;
last_opt_type = TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA;
break;
}
}
if (last_opt_type != TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA) {
VLOG_ERR_RL(&error_rl, "failed to parse tun options without data");
return EINVAL;
}
return 0;
}
static int
nl_parse_flower_tunnel_opts(struct nlattr *options,
struct tun_metadata *metadata)
{
const struct ofpbuf *msg;
struct nlattr *nla;
struct ofpbuf buf;
size_t left;
int err;
nl_attr_get_nested(options, &buf);
msg = &buf;
NL_ATTR_FOR_EACH (nla, left, ofpbuf_at(msg, 0, 0), msg->size) {
uint16_t type = nl_attr_type(nla);
switch (type) {
case TCA_FLOWER_KEY_ENC_OPTS_GENEVE:
err = nl_parse_geneve_key(nla, metadata);
if (err) {
return err;
}
break;
}
}
return 0;
}
static int
flower_tun_geneve_opt_check_len(struct tun_metadata *key,
struct tun_metadata *mask)
{
const struct geneve_opt *opt, *opt_mask;
int len, cnt = 0;
len = key->present.len;
while (len) {
opt = &key->opts.gnv[cnt];
opt_mask = &mask->opts.gnv[cnt];
if (opt->length != opt_mask->length) {
VLOG_ERR_RL(&error_rl,
"failed to parse tun options; key/mask length differ");
return EINVAL;
}
cnt += sizeof(struct geneve_opt) / 4 + opt->length;
len -= sizeof(struct geneve_opt) + opt->length * 4;
}
return 0;
}
static int
nl_parse_flower_tunnel(struct nlattr **attrs, struct tc_flower *flower)
{
int err;
if (attrs[TCA_FLOWER_KEY_ENC_KEY_ID]) {
ovs_be32 id = nl_attr_get_be32(attrs[TCA_FLOWER_KEY_ENC_KEY_ID]);
@@ -475,6 +599,35 @@ nl_parse_flower_tunnel(struct nlattr **attrs, struct tc_flower *flower)
flower->mask.tunnel.ttl =
nl_attr_get_u8(attrs[TCA_FLOWER_KEY_ENC_IP_TTL_MASK]);
}
if (attrs[TCA_FLOWER_KEY_ENC_OPTS] &&
attrs[TCA_FLOWER_KEY_ENC_OPTS_MASK]) {
err = nl_parse_flower_tunnel_opts(attrs[TCA_FLOWER_KEY_ENC_OPTS],
&flower->key.tunnel.metadata);
if (err) {
return err;
}
err = nl_parse_flower_tunnel_opts(attrs[TCA_FLOWER_KEY_ENC_OPTS_MASK],
&flower->mask.tunnel.metadata);
if (err) {
return err;
}
err = flower_tun_geneve_opt_check_len(&flower->key.tunnel.metadata,
&flower->mask.tunnel.metadata);
if (err) {
return err;
}
} else if (attrs[TCA_FLOWER_KEY_ENC_OPTS]) {
VLOG_ERR_RL(&error_rl,
"failed to parse tun options; no mask supplied");
return EINVAL;
} else if (attrs[TCA_FLOWER_KEY_ENC_OPTS_MASK]) {
VLOG_ERR_RL(&error_rl, "failed to parse tun options; no key supplied");
return EINVAL;
}
return 0;
}
static void
@@ -1188,6 +1341,7 @@ static int
nl_parse_flower_options(struct nlattr *nl_options, struct tc_flower *flower)
{
struct nlattr *attrs[ARRAY_SIZE(tca_flower_policy)];
int err;
if (!nl_parse_nested(nl_options, tca_flower_policy,
attrs, ARRAY_SIZE(tca_flower_policy))) {
@@ -1199,7 +1353,11 @@ nl_parse_flower_options(struct nlattr *nl_options, struct tc_flower *flower)
nl_parse_flower_mpls(attrs, flower);
nl_parse_flower_vlan(attrs, flower);
nl_parse_flower_ip(attrs, flower);
nl_parse_flower_tunnel(attrs, flower);
err = nl_parse_flower_tunnel(attrs, flower);
if (err) {
return err;
}
nl_parse_flower_flags(attrs, flower);
return nl_parse_flower_actions(attrs, flower);
}
@@ -1810,6 +1968,38 @@ nl_msg_put_masked_value(struct ofpbuf *request, uint16_t type,
nl_msg_put_unspec(request, type, data, len);
}
static void
nl_msg_put_flower_tunnel_opts(struct ofpbuf *request, uint16_t type,
struct tun_metadata metadata)
{
struct geneve_opt *opt;
size_t outer, inner;
int len, cnt = 0;
len = metadata.present.len;
if (!len) {
return;
}
outer = nl_msg_start_nested(request, type);
while (len) {
opt = &metadata.opts.gnv[cnt];
inner = nl_msg_start_nested(request, TCA_FLOWER_KEY_ENC_OPTS_GENEVE);
nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS,
opt->opt_class);
nl_msg_put_u8(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, opt->type);
nl_msg_put_unspec(request, TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, opt + 1,
opt->length * 4);
cnt += sizeof(struct geneve_opt) / 4 + opt->length;
len -= sizeof(struct geneve_opt) + opt->length * 4;
nl_msg_end_nested(request, inner);
}
nl_msg_end_nested(request, outer);
}
static void
nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower)
{
@@ -1841,6 +2031,10 @@ nl_msg_put_flower_tunnel(struct ofpbuf *request, struct tc_flower *flower)
}
nl_msg_put_be16(request, TCA_FLOWER_KEY_ENC_UDP_DST_PORT, tp_dst);
nl_msg_put_be32(request, TCA_FLOWER_KEY_ENC_KEY_ID, id);
nl_msg_put_flower_tunnel_opts(request, TCA_FLOWER_KEY_ENC_OPTS,
flower->key.tunnel.metadata);
nl_msg_put_flower_tunnel_opts(request, TCA_FLOWER_KEY_ENC_OPTS_MASK,
flower->mask.tunnel.metadata);
}
#define FLOWER_PUT_MASKED_VALUE(member, type) \