|
@@ -13,6 +13,7 @@
|
|
|
#include <linux/kernel.h>
|
|
|
#include <linux/skbuff.h>
|
|
|
#include <linux/rtnetlink.h>
|
|
|
+#include <net/geneve.h>
|
|
|
#include <net/netlink.h>
|
|
|
#include <net/pkt_sched.h>
|
|
|
#include <net/dst.h>
|
|
@@ -57,6 +58,135 @@ static int tunnel_key_act(struct sk_buff *skb, const struct tc_action *a,
|
|
|
return action;
|
|
|
}
|
|
|
|
|
|
+static const struct nla_policy
|
|
|
+enc_opts_policy[TCA_TUNNEL_KEY_ENC_OPTS_MAX + 1] = {
|
|
|
+ [TCA_TUNNEL_KEY_ENC_OPTS_GENEVE] = { .type = NLA_NESTED },
|
|
|
+};
|
|
|
+
|
|
|
+static const struct nla_policy
|
|
|
+geneve_opt_policy[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1] = {
|
|
|
+ [TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS] = { .type = NLA_U16 },
|
|
|
+ [TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE] = { .type = NLA_U8 },
|
|
|
+ [TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA] = { .type = NLA_BINARY,
|
|
|
+ .len = 128 },
|
|
|
+};
|
|
|
+
|
|
|
+static int
|
|
|
+tunnel_key_copy_geneve_opt(const struct nlattr *nla, void *dst, int dst_len,
|
|
|
+ struct netlink_ext_ack *extack)
|
|
|
+{
|
|
|
+ struct nlattr *tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1];
|
|
|
+ int err, data_len, opt_len;
|
|
|
+ u8 *data;
|
|
|
+
|
|
|
+ err = nla_parse_nested(tb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX,
|
|
|
+ nla, geneve_opt_policy, extack);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ if (!tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS] ||
|
|
|
+ !tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE] ||
|
|
|
+ !tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]) {
|
|
|
+ NL_SET_ERR_MSG(extack, "Missing tunnel key geneve option class, type or data");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ data = nla_data(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]);
|
|
|
+ data_len = nla_len(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]);
|
|
|
+ if (data_len < 4) {
|
|
|
+ NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4 bytes long");
|
|
|
+ return -ERANGE;
|
|
|
+ }
|
|
|
+ if (data_len % 4) {
|
|
|
+ NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a multiple of 4 bytes long");
|
|
|
+ return -ERANGE;
|
|
|
+ }
|
|
|
+
|
|
|
+ opt_len = sizeof(struct geneve_opt) + data_len;
|
|
|
+ if (dst) {
|
|
|
+ struct geneve_opt *opt = dst;
|
|
|
+
|
|
|
+ WARN_ON(dst_len < opt_len);
|
|
|
+
|
|
|
+ opt->opt_class =
|
|
|
+ nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS]);
|
|
|
+ opt->type = nla_get_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE]);
|
|
|
+ opt->length = data_len / 4; /* length is in units of 4 bytes */
|
|
|
+ opt->r1 = 0;
|
|
|
+ opt->r2 = 0;
|
|
|
+ opt->r3 = 0;
|
|
|
+
|
|
|
+ memcpy(opt + 1, data, data_len);
|
|
|
+ }
|
|
|
+
|
|
|
+ return opt_len;
|
|
|
+}
|
|
|
+
|
|
|
+static int tunnel_key_copy_opts(const struct nlattr *nla, u8 *dst,
|
|
|
+ int dst_len, struct netlink_ext_ack *extack)
|
|
|
+{
|
|
|
+ int err, rem, opt_len, len = nla_len(nla), opts_len = 0;
|
|
|
+ const struct nlattr *attr, *head = nla_data(nla);
|
|
|
+
|
|
|
+ err = nla_validate(head, len, TCA_TUNNEL_KEY_ENC_OPTS_MAX,
|
|
|
+ enc_opts_policy, extack);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ nla_for_each_attr(attr, head, len, rem) {
|
|
|
+ switch (nla_type(attr)) {
|
|
|
+ case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE:
|
|
|
+ opt_len = tunnel_key_copy_geneve_opt(attr, dst,
|
|
|
+ dst_len, extack);
|
|
|
+ if (opt_len < 0)
|
|
|
+ return opt_len;
|
|
|
+ opts_len += opt_len;
|
|
|
+ if (dst) {
|
|
|
+ dst_len -= opt_len;
|
|
|
+ dst += opt_len;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!opts_len) {
|
|
|
+ NL_SET_ERR_MSG(extack, "Empty list of tunnel options");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (rem > 0) {
|
|
|
+ NL_SET_ERR_MSG(extack, "Trailing data after parsing tunnel key options attributes");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ return opts_len;
|
|
|
+}
|
|
|
+
|
|
|
+static int tunnel_key_get_opts_len(struct nlattr *nla,
|
|
|
+ struct netlink_ext_ack *extack)
|
|
|
+{
|
|
|
+ return tunnel_key_copy_opts(nla, NULL, 0, extack);
|
|
|
+}
|
|
|
+
|
|
|
+static int tunnel_key_opts_set(struct nlattr *nla, struct ip_tunnel_info *info,
|
|
|
+ int opts_len, struct netlink_ext_ack *extack)
|
|
|
+{
|
|
|
+ info->options_len = opts_len;
|
|
|
+ switch (nla_type(nla_data(nla))) {
|
|
|
+ case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE:
|
|
|
+#if IS_ENABLED(CONFIG_INET)
|
|
|
+ info->key.tun_flags |= TUNNEL_GENEVE_OPT;
|
|
|
+ return tunnel_key_copy_opts(nla, ip_tunnel_info_opts(info),
|
|
|
+ opts_len, extack);
|
|
|
+#else
|
|
|
+ return -EAFNOSUPPORT;
|
|
|
+#endif
|
|
|
+ default:
|
|
|
+ NL_SET_ERR_MSG(extack, "Cannot set tunnel options for unknown tunnel type");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = {
|
|
|
[TCA_TUNNEL_KEY_PARMS] = { .len = sizeof(struct tc_tunnel_key) },
|
|
|
[TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 },
|
|
@@ -66,6 +196,7 @@ static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = {
|
|
|
[TCA_TUNNEL_KEY_ENC_KEY_ID] = { .type = NLA_U32 },
|
|
|
[TCA_TUNNEL_KEY_ENC_DST_PORT] = {.type = NLA_U16},
|
|
|
[TCA_TUNNEL_KEY_NO_CSUM] = { .type = NLA_U8 },
|
|
|
+ [TCA_TUNNEL_KEY_ENC_OPTS] = { .type = NLA_NESTED },
|
|
|
};
|
|
|
|
|
|
static int tunnel_key_init(struct net *net, struct nlattr *nla,
|
|
@@ -81,6 +212,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
|
|
|
struct tcf_tunnel_key *t;
|
|
|
bool exists = false;
|
|
|
__be16 dst_port = 0;
|
|
|
+ int opts_len = 0;
|
|
|
__be64 key_id;
|
|
|
__be16 flags;
|
|
|
int ret = 0;
|
|
@@ -128,6 +260,15 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
|
|
|
if (tb[TCA_TUNNEL_KEY_ENC_DST_PORT])
|
|
|
dst_port = nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_DST_PORT]);
|
|
|
|
|
|
+ if (tb[TCA_TUNNEL_KEY_ENC_OPTS]) {
|
|
|
+ opts_len = tunnel_key_get_opts_len(tb[TCA_TUNNEL_KEY_ENC_OPTS],
|
|
|
+ extack);
|
|
|
+ if (opts_len < 0) {
|
|
|
+ ret = opts_len;
|
|
|
+ goto err_out;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (tb[TCA_TUNNEL_KEY_ENC_IPV4_SRC] &&
|
|
|
tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]) {
|
|
|
__be32 saddr;
|
|
@@ -138,7 +279,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
|
|
|
|
|
|
metadata = __ip_tun_set_dst(saddr, daddr, 0, 0,
|
|
|
dst_port, flags,
|
|
|
- key_id, 0);
|
|
|
+ key_id, opts_len);
|
|
|
} else if (tb[TCA_TUNNEL_KEY_ENC_IPV6_SRC] &&
|
|
|
tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]) {
|
|
|
struct in6_addr saddr;
|
|
@@ -162,6 +303,14 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla,
|
|
|
goto err_out;
|
|
|
}
|
|
|
|
|
|
+ if (opts_len) {
|
|
|
+ ret = tunnel_key_opts_set(tb[TCA_TUNNEL_KEY_ENC_OPTS],
|
|
|
+ &metadata->u.tun_info,
|
|
|
+ opts_len, extack);
|
|
|
+ if (ret < 0)
|
|
|
+ goto err_out;
|
|
|
+ }
|
|
|
+
|
|
|
metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX;
|
|
|
break;
|
|
|
default:
|
|
@@ -234,6 +383,61 @@ static void tunnel_key_release(struct tc_action *a)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static int tunnel_key_geneve_opts_dump(struct sk_buff *skb,
|
|
|
+ const struct ip_tunnel_info *info)
|
|
|
+{
|
|
|
+ int len = info->options_len;
|
|
|
+ u8 *src = (u8 *)(info + 1);
|
|
|
+ struct nlattr *start;
|
|
|
+
|
|
|
+ start = nla_nest_start(skb, TCA_TUNNEL_KEY_ENC_OPTS_GENEVE);
|
|
|
+ if (!start)
|
|
|
+ return -EMSGSIZE;
|
|
|
+
|
|
|
+ while (len > 0) {
|
|
|
+ struct geneve_opt *opt = (struct geneve_opt *)src;
|
|
|
+
|
|
|
+ if (nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS,
|
|
|
+ opt->opt_class) ||
|
|
|
+ nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE,
|
|
|
+ opt->type) ||
|
|
|
+ nla_put(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA,
|
|
|
+ opt->length * 4, opt + 1))
|
|
|
+ return -EMSGSIZE;
|
|
|
+
|
|
|
+ len -= sizeof(struct geneve_opt) + opt->length * 4;
|
|
|
+ src += sizeof(struct geneve_opt) + opt->length * 4;
|
|
|
+ }
|
|
|
+
|
|
|
+ nla_nest_end(skb, start);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int tunnel_key_opts_dump(struct sk_buff *skb,
|
|
|
+ const struct ip_tunnel_info *info)
|
|
|
+{
|
|
|
+ struct nlattr *start;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!info->options_len)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ start = nla_nest_start(skb, TCA_TUNNEL_KEY_ENC_OPTS);
|
|
|
+ if (!start)
|
|
|
+ return -EMSGSIZE;
|
|
|
+
|
|
|
+ if (info->key.tun_flags & TUNNEL_GENEVE_OPT) {
|
|
|
+ err = tunnel_key_geneve_opts_dump(skb, info);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ } else {
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ nla_nest_end(skb, start);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int tunnel_key_dump_addresses(struct sk_buff *skb,
|
|
|
const struct ip_tunnel_info *info)
|
|
|
{
|
|
@@ -284,8 +488,9 @@ static int tunnel_key_dump(struct sk_buff *skb, struct tc_action *a,
|
|
|
goto nla_put_failure;
|
|
|
|
|
|
if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET) {
|
|
|
- struct ip_tunnel_key *key =
|
|
|
- ¶ms->tcft_enc_metadata->u.tun_info.key;
|
|
|
+ struct ip_tunnel_info *info =
|
|
|
+ ¶ms->tcft_enc_metadata->u.tun_info;
|
|
|
+ struct ip_tunnel_key *key = &info->key;
|
|
|
__be32 key_id = tunnel_id_to_key32(key->tun_id);
|
|
|
|
|
|
if (nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id) ||
|
|
@@ -293,7 +498,8 @@ static int tunnel_key_dump(struct sk_buff *skb, struct tc_action *a,
|
|
|
¶ms->tcft_enc_metadata->u.tun_info) ||
|
|
|
nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT, key->tp_dst) ||
|
|
|
nla_put_u8(skb, TCA_TUNNEL_KEY_NO_CSUM,
|
|
|
- !(key->tun_flags & TUNNEL_CSUM)))
|
|
|
+ !(key->tun_flags & TUNNEL_CSUM)) ||
|
|
|
+ tunnel_key_opts_dump(skb, info))
|
|
|
goto nla_put_failure;
|
|
|
}
|
|
|
|