summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/netlink/specs/rt-link.yaml9
-rw-r--r--drivers/net/geneve.c167
-rw-r--r--include/uapi/linux/if_link.h2
3 files changed, 169 insertions, 9 deletions
diff --git a/Documentation/netlink/specs/rt-link.yaml b/Documentation/netlink/specs/rt-link.yaml
index a5c0297818c6..7f8e3ad3a405 100644
--- a/Documentation/netlink/specs/rt-link.yaml
+++ b/Documentation/netlink/specs/rt-link.yaml
@@ -1941,6 +1941,15 @@ attribute-sets:
-
name: gro-hint
type: flag
+ -
+ name: local
+ type: u32
+ byte-order: big-endian
+ display-hint: ipv4
+ -
+ name: local6
+ type: binary
+ display-hint: ipv6
-
name: linkinfo-hsr-attrs
name-prefix: ifla-hsr-
diff --git a/drivers/net/geneve.c b/drivers/net/geneve.c
index 3a62d132a8c4..da17b4f6fda5 100644
--- a/drivers/net/geneve.c
+++ b/drivers/net/geneve.c
@@ -775,9 +775,10 @@ static struct sock *geneve_create_sock(struct net *net,
udp_conf.family = AF_INET6;
udp_conf.ipv6_v6only = 1;
udp_conf.use_udp6_rx_checksums = geneve->cfg.use_udp6_rx_checksums;
+ udp_conf.local_ip6 = info->key.u.ipv6.src;
} else {
udp_conf.family = AF_INET;
- udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
+ udp_conf.local_ip.s_addr = info->key.u.ipv4.src;
}
udp_conf.local_udp_port = info->key.tp_dst;
@@ -1061,6 +1062,16 @@ static struct geneve_sock *geneve_find_sock(struct net *net,
if (gs->gro_hint != gro_hint)
continue;
+ if (family == AF_INET &&
+ inet_sk(gs->sk)->inet_saddr != info->key.u.ipv4.src)
+ continue;
+
+#if IS_ENABLED(CONFIG_IPV6)
+ if (family == AF_INET6 &&
+ !ipv6_addr_equal(&gs->sk->sk_v6_rcv_saddr, &info->key.u.ipv6.src))
+ continue;
+#endif
+
return gs;
}
@@ -1327,6 +1338,12 @@ static int geneve_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (IS_ERR(rt))
return PTR_ERR(rt);
+ if (geneve->cfg.info.key.u.ipv4.src &&
+ saddr != geneve->cfg.info.key.u.ipv4.src) {
+ dst_release(&rt->dst);
+ return -EADDRNOTAVAIL;
+ }
+
err = skb_tunnel_check_pmtu(skb, &rt->dst,
GENEVE_IPV4_HLEN + info->options_len +
geneve_build_gro_hint_opt(geneve, skb),
@@ -1438,6 +1455,12 @@ static int geneve6_xmit_skb(struct sk_buff *skb, struct net_device *dev,
if (IS_ERR(dst))
return PTR_ERR(dst);
+ if (!ipv6_addr_any(&geneve->cfg.info.key.u.ipv6.src) &&
+ !ipv6_addr_equal(&saddr, &geneve->cfg.info.key.u.ipv6.src)) {
+ dst_release(dst);
+ return -EADDRNOTAVAIL;
+ }
+
err = skb_tunnel_check_pmtu(skb, dst,
GENEVE_IPV6_HLEN + info->options_len +
geneve_build_gro_hint_opt(geneve, skb),
@@ -1729,6 +1752,8 @@ static const struct nla_policy geneve_policy[IFLA_GENEVE_MAX + 1] = {
[IFLA_GENEVE_INNER_PROTO_INHERIT] = { .type = NLA_FLAG },
[IFLA_GENEVE_PORT_RANGE] = NLA_POLICY_EXACT_LEN(sizeof(struct ifla_geneve_port_range)),
[IFLA_GENEVE_GRO_HINT] = { .type = NLA_FLAG },
+ [IFLA_GENEVE_LOCAL] = { .type = NLA_BE32 },
+ [IFLA_GENEVE_LOCAL6] = NLA_POLICY_EXACT_LEN(sizeof(struct in6_addr)),
};
static int geneve_validate(struct nlattr *tb[], struct nlattr *data[],
@@ -1788,7 +1813,45 @@ static int geneve_validate(struct nlattr *tb[], struct nlattr *data[],
return 0;
}
+static bool geneve_saddr_wildcard(const struct ip_tunnel_info *info)
+{
+ if (ip_tunnel_info_af(info) == AF_INET) {
+ if (!info->key.u.ipv4.src)
+ return true;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ if (ipv6_addr_any(&info->key.u.ipv6.src))
+ return true;
+#endif
+ }
+
+ return false;
+}
+
+static bool geneve_saddr_conflict(const struct ip_tunnel_info *a,
+ const struct ip_tunnel_info *b)
+{
+ if (ip_tunnel_info_af(a) != ip_tunnel_info_af(b))
+ return false;
+
+ if (geneve_saddr_wildcard(a) || geneve_saddr_wildcard(b))
+ return true;
+
+ if (ip_tunnel_info_af(a) == AF_INET) {
+ if (a->key.u.ipv4.src == b->key.u.ipv4.src)
+ return true;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ if (ipv6_addr_equal(&a->key.u.ipv6.src, &b->key.u.ipv6.src))
+ return true;
+#endif
+ }
+
+ return false;
+}
+
static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
+ const struct geneve_config *cfg,
const struct ip_tunnel_info *info,
bool *tun_on_same_port,
bool *tun_collect_md)
@@ -1798,8 +1861,10 @@ static struct geneve_dev *geneve_find_dev(struct geneve_net *gn,
*tun_on_same_port = false;
*tun_collect_md = false;
list_for_each_entry(geneve, &gn->geneve_list, next) {
- if (info->key.tp_dst == geneve->cfg.info.key.tp_dst) {
- *tun_collect_md = geneve->cfg.collect_md;
+ if (info->key.tp_dst == geneve->cfg.info.key.tp_dst &&
+ (cfg->dualstack || geneve->cfg.dualstack ||
+ geneve_saddr_conflict(info, &geneve->cfg.info))) {
+ *tun_collect_md |= geneve->cfg.collect_md;
*tun_on_same_port = true;
}
if (info->key.tun_id == geneve->cfg.info.key.tun_id &&
@@ -1815,7 +1880,12 @@ static bool is_tnl_info_zero(const struct ip_tunnel_info *info)
return !(info->key.tun_id || info->key.tos ||
!ip_tunnel_flags_empty(info->key.tun_flags) ||
info->key.ttl || info->key.label || info->key.tp_src ||
- memchr_inv(&info->key.u, 0, sizeof(info->key.u)));
+#if IS_ENABLED(CONFIG_IPV6)
+ (ip_tunnel_info_af(info) == AF_INET6 &&
+ !ipv6_addr_any(&info->key.u.ipv6.dst)) ||
+#endif
+ (ip_tunnel_info_af(info) == AF_INET &&
+ info->key.u.ipv4.dst));
}
static bool geneve_dst_addr_equal(struct ip_tunnel_info *a,
@@ -1846,7 +1916,7 @@ static int geneve_configure(struct net *net, struct net_device *dev,
geneve->net = net;
geneve->dev = dev;
- t = geneve_find_dev(gn, info, &tun_on_same_port, &tun_collect_md);
+ t = geneve_find_dev(gn, cfg, info, &tun_on_same_port, &tun_collect_md);
if (t)
return -EBUSY;
@@ -1864,13 +1934,13 @@ static int geneve_configure(struct net *net, struct net_device *dev,
if (cfg->collect_md) {
if (tun_on_same_port) {
NL_SET_ERR_MSG(extack,
- "There can be only one externally controlled device on a destination port");
+ "There can be only one externally controlled device on a destination port and a source address");
return -EPERM;
}
} else {
if (tun_collect_md) {
NL_SET_ERR_MSG(extack,
- "There already exists an externally controlled device on this destination port");
+ "There already exists an externally controlled device on this destination port and the source address");
return -EPERM;
}
}
@@ -1917,9 +1987,10 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[],
cfg->dualstack = true;
}
- if (data[IFLA_GENEVE_REMOTE] && data[IFLA_GENEVE_REMOTE6]) {
+ if ((data[IFLA_GENEVE_LOCAL] || data[IFLA_GENEVE_REMOTE]) &&
+ (data[IFLA_GENEVE_LOCAL6] || data[IFLA_GENEVE_REMOTE6])) {
NL_SET_ERR_MSG(extack,
- "Cannot specify both IPv4 and IPv6 Remote addresses");
+ "Cannot specify both IPv4/IPv6 Remote/Local addresses");
return -EINVAL;
}
@@ -1972,6 +2043,65 @@ static int geneve_nl2info(struct nlattr *tb[], struct nlattr *data[],
#endif
}
+ if (data[IFLA_GENEVE_LOCAL]) {
+ if (changelink) {
+ __be32 src = nla_get_in_addr(data[IFLA_GENEVE_LOCAL]);
+
+ if (ip_tunnel_info_af(info) == AF_INET6 ||
+ src != info->key.u.ipv4.src) {
+ attrtype = IFLA_GENEVE_LOCAL;
+ goto change_notsup;
+ }
+ } else {
+ info->key.u.ipv4.src = nla_get_in_addr(data[IFLA_GENEVE_LOCAL]);
+
+ if (ipv4_is_multicast(info->key.u.ipv4.src)) {
+ NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LOCAL],
+ "Local IPv4 address cannot be Multicast");
+ return -EINVAL;
+ }
+
+ cfg->dualstack = false;
+ }
+ }
+
+ if (data[IFLA_GENEVE_LOCAL6]) {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (changelink) {
+ struct in6_addr src = nla_get_in6_addr(data[IFLA_GENEVE_LOCAL6]);
+
+ if (ip_tunnel_info_af(info) == AF_INET ||
+ !ipv6_addr_equal(&src, &info->key.u.ipv6.src)) {
+ attrtype = IFLA_GENEVE_LOCAL6;
+ goto change_notsup;
+ }
+ } else {
+ int addr_type;
+
+ info->mode = IP_TUNNEL_INFO_IPV6;
+ info->key.u.ipv6.src = nla_get_in6_addr(data[IFLA_GENEVE_LOCAL6]);
+
+ addr_type = ipv6_addr_type(&info->key.u.ipv6.src);
+ if (addr_type & IPV6_ADDR_LINKLOCAL) {
+ NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LOCAL6],
+ "Local IPv6 address cannot be link-local");
+ return -EINVAL;
+ }
+ if (addr_type & IPV6_ADDR_MULTICAST) {
+ NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LOCAL6],
+ "Local IPv6 address cannot be Multicast");
+ return -EINVAL;
+ }
+
+ cfg->dualstack = false;
+ }
+#else
+ NL_SET_ERR_MSG_ATTR(extack, data[IFLA_GENEVE_LOCAL6],
+ "IPv6 support not enabled in the kernel");
+ return -EPFNOSUPPORT;
+#endif
+ }
+
if (data[IFLA_GENEVE_ID]) {
__u32 vni;
__u8 tvni[3];
@@ -2265,6 +2395,7 @@ static size_t geneve_get_size(const struct net_device *dev)
{
return nla_total_size(sizeof(__u32)) + /* IFLA_GENEVE_ID */
nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_REMOTE{6} */
+ nla_total_size(sizeof(struct in6_addr)) + /* IFLA_GENEVE_LOCAL{6} */
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TTL */
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_TOS */
nla_total_size(sizeof(__u8)) + /* IFLA_GENEVE_DF */
@@ -2320,6 +2451,24 @@ static int geneve_fill_info(struct sk_buff *skb, const struct net_device *dev)
#endif
}
+ if (!geneve->cfg.dualstack) {
+ if (ip_tunnel_info_af(info) == AF_INET) {
+ if ((info->key.u.ipv4.src ||
+ geneve->cfg.collect_md) &&
+ nla_put_in_addr(skb, IFLA_GENEVE_LOCAL,
+ info->key.u.ipv4.src))
+ goto nla_put_failure;
+#if IS_ENABLED(CONFIG_IPV6)
+ } else {
+ if ((!ipv6_addr_any(&info->key.u.ipv6.src) ||
+ geneve->cfg.collect_md) &&
+ nla_put_in6_addr(skb, IFLA_GENEVE_LOCAL6,
+ &info->key.u.ipv6.src))
+ goto nla_put_failure;
+#endif
+ }
+ }
+
if (nla_put_u8(skb, IFLA_GENEVE_TTL, info->key.ttl) ||
nla_put_u8(skb, IFLA_GENEVE_TOS, info->key.tos) ||
nla_put_be32(skb, IFLA_GENEVE_LABEL, info->key.label))
diff --git a/include/uapi/linux/if_link.h b/include/uapi/linux/if_link.h
index 46413392b402..363526549a01 100644
--- a/include/uapi/linux/if_link.h
+++ b/include/uapi/linux/if_link.h
@@ -1506,6 +1506,8 @@ enum {
IFLA_GENEVE_INNER_PROTO_INHERIT,
IFLA_GENEVE_PORT_RANGE,
IFLA_GENEVE_GRO_HINT,
+ IFLA_GENEVE_LOCAL,
+ IFLA_GENEVE_LOCAL6,
__IFLA_GENEVE_MAX
};
#define IFLA_GENEVE_MAX (__IFLA_GENEVE_MAX - 1)