mirror of
https://github.com/openvswitch/ovs
synced 2025-09-01 06:45:17 +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:
committed by
Simon Horman
parent
202469aa9e
commit
a468645c6d
@@ -204,10 +204,35 @@ enum {
|
||||
TCA_FLOWER_KEY_ENC_IP_TOS_MASK, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_IP_TTL, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_IP_TTL_MASK, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_OPTS,
|
||||
TCA_FLOWER_KEY_ENC_OPTS_MASK,
|
||||
|
||||
__TCA_FLOWER_MAX,
|
||||
};
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_KEY_ENC_OPTS_UNSPEC,
|
||||
TCA_FLOWER_KEY_ENC_OPTS_GENEVE, /* Nested
|
||||
* TCA_TUNNEL_KEY_ENC_OPTS_GENEVE
|
||||
* attributes
|
||||
*/
|
||||
__TCA_FLOWER_KEY_ENC_OPTS_MAX,
|
||||
};
|
||||
|
||||
#define TCA_FLOWER_KEY_ENC_OPTS_MAX (__TCA_FLOWER_KEY_ENC_OPTS_MAX - 1)
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_UNSPEC,
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_CLASS, /* u16 */
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_TYPE, /* u8 */
|
||||
TCA_FLOWER_KEY_ENC_OPT_GENEVE_DATA, /* 4 to 128 bytes */
|
||||
|
||||
__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX,
|
||||
};
|
||||
|
||||
#define TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX \
|
||||
(__TCA_FLOWER_KEY_ENC_OPT_GENEVE_MAX - 1)
|
||||
|
||||
enum {
|
||||
TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT = (1 << 0),
|
||||
TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST = (1 << 1),
|
||||
|
@@ -432,6 +432,38 @@ static void parse_tc_flower_geneve_opts(struct tc_action *action,
|
||||
nl_msg_end_nested(buf, geneve_off);
|
||||
}
|
||||
|
||||
static void
|
||||
flower_tun_opt_to_match(struct match *match, struct tc_flower *flower)
|
||||
{
|
||||
struct geneve_opt *opt, *opt_mask;
|
||||
int len, cnt = 0;
|
||||
|
||||
memcpy(match->flow.tunnel.metadata.opts.gnv,
|
||||
flower->key.tunnel.metadata.opts.gnv,
|
||||
flower->key.tunnel.metadata.present.len);
|
||||
match->flow.tunnel.metadata.present.len =
|
||||
flower->key.tunnel.metadata.present.len;
|
||||
match->flow.tunnel.flags |= FLOW_TNL_F_UDPIF;
|
||||
memcpy(match->wc.masks.tunnel.metadata.opts.gnv,
|
||||
flower->mask.tunnel.metadata.opts.gnv,
|
||||
flower->mask.tunnel.metadata.present.len);
|
||||
|
||||
len = flower->key.tunnel.metadata.present.len;
|
||||
while (len) {
|
||||
opt = &match->flow.tunnel.metadata.opts.gnv[cnt];
|
||||
opt_mask = &match->wc.masks.tunnel.metadata.opts.gnv[cnt];
|
||||
|
||||
opt_mask->length = 0x1f;
|
||||
|
||||
cnt += sizeof(struct geneve_opt) / 4 + opt->length;
|
||||
len -= sizeof(struct geneve_opt) + opt->length * 4;
|
||||
}
|
||||
|
||||
match->wc.masks.tunnel.metadata.present.len =
|
||||
flower->mask.tunnel.metadata.present.len;
|
||||
match->wc.masks.tunnel.flags |= FLOW_TNL_F_UDPIF;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_tc_flower_to_match(struct tc_flower *flower,
|
||||
struct match *match,
|
||||
@@ -548,6 +580,9 @@ parse_tc_flower_to_match(struct tc_flower *flower,
|
||||
if (flower->key.tunnel.tp_dst) {
|
||||
match_set_tun_tp_dst(match, flower->key.tunnel.tp_dst);
|
||||
}
|
||||
if (flower->key.tunnel.metadata.present.len) {
|
||||
flower_tun_opt_to_match(match, flower);
|
||||
}
|
||||
}
|
||||
|
||||
act_off = nl_msg_start_nested(buf, OVS_FLOW_ATTR_ACTIONS);
|
||||
@@ -968,6 +1003,34 @@ test_key_and_mask(struct match *match)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
flower_match_to_tun_opt(struct tc_flower *flower, const struct flow_tnl *tnl,
|
||||
const struct flow_tnl *tnl_mask)
|
||||
{
|
||||
struct geneve_opt *opt, *opt_mask;
|
||||
int len, cnt = 0;
|
||||
|
||||
memcpy(flower->key.tunnel.metadata.opts.gnv, tnl->metadata.opts.gnv,
|
||||
tnl->metadata.present.len);
|
||||
flower->key.tunnel.metadata.present.len = tnl->metadata.present.len;
|
||||
|
||||
memcpy(flower->mask.tunnel.metadata.opts.gnv, tnl_mask->metadata.opts.gnv,
|
||||
tnl->metadata.present.len);
|
||||
|
||||
len = flower->key.tunnel.metadata.present.len;
|
||||
while (len) {
|
||||
opt = &flower->key.tunnel.metadata.opts.gnv[cnt];
|
||||
opt_mask = &flower->mask.tunnel.metadata.opts.gnv[cnt];
|
||||
|
||||
opt_mask->length = opt->length;
|
||||
|
||||
cnt += sizeof(struct geneve_opt) / 4 + opt->length;
|
||||
len -= sizeof(struct geneve_opt) + opt->length * 4;
|
||||
}
|
||||
|
||||
flower->mask.tunnel.metadata.present.len = tnl->metadata.present.len;
|
||||
}
|
||||
|
||||
int
|
||||
netdev_tc_flow_put(struct netdev *netdev, struct match *match,
|
||||
struct nlattr *actions, size_t actions_len,
|
||||
@@ -1016,6 +1079,7 @@ netdev_tc_flow_put(struct netdev *netdev, struct match *match,
|
||||
flower.key.tunnel.tp_dst = tnl->tp_dst;
|
||||
flower.mask.tunnel.tos = tnl_mask->ip_tos;
|
||||
flower.mask.tunnel.ttl = tnl_mask->ip_ttl;
|
||||
flower_match_to_tun_opt(&flower, tnl, tnl_mask);
|
||||
flower.tunnel = true;
|
||||
}
|
||||
memset(&mask->tunnel, 0, sizeof mask->tunnel);
|
||||
|
198
lib/tc.c
198
lib/tc.c
@@ -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) \
|
||||
|
Reference in New Issue
Block a user