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,
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);
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,
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);
.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
}
}
-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]);
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
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))) {
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);
}
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)
{
}
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) \