diff options
author | Pieter Jansen van Vuuren <pieter.jansenvanvuuren@netronome.com> | 2018-09-18 09:36:20 +0100 |
---|---|---|
committer | Simon Horman <simon.horman@netronome.com> | 2018-09-20 15:58:24 +0200 |
commit | a468645c6d330943dbe0c8d466e05b9af2d7df0c (patch) | |
tree | c93d153adce1f823e0e1fc3283b1234517c9044c | |
parent | 202469aa9e919e549e6166e072cc8332b3d12092 (diff) | |
download | openvswitch-a468645c6d330943dbe0c8d466e05b9af2d7df0c.tar.gz |
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>
-rw-r--r-- | include/linux/pkt_cls.h | 25 | ||||
-rw-r--r-- | lib/netdev-tc-offloads.c | 64 | ||||
-rw-r--r-- | lib/tc.c | 198 | ||||
-rw-r--r-- | lib/tc.h | 1 |
4 files changed, 286 insertions, 2 deletions
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h index a3300418e..1384d71f9 100644 --- a/include/linux/pkt_cls.h +++ b/include/linux/pkt_cls.h @@ -204,11 +204,36 @@ 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), }; diff --git a/lib/netdev-tc-offloads.c b/lib/netdev-tc-offloads.c index 2276a327a..62ae021e0 100644 --- a/lib/netdev-tc-offloads.c +++ b/lib/netdev-tc-offloads.c @@ -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); @@ -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); } @@ -1811,6 +1969,38 @@ nl_msg_put_masked_value(struct ofpbuf *request, uint16_t type, } 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) { ovs_be32 ipv4_src = flower->key.tunnel.ipv4.ipv4_src; @@ -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) \ @@ -123,6 +123,7 @@ struct tc_flower_key { ovs_be16 tp_src; ovs_be16 tp_dst; ovs_be64 id; + struct tun_metadata metadata; } tunnel; }; |