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;
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;
}
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),
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),
[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[],
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)
*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 &&
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,
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;
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;
}
}
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;
}
#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];
{
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 */
#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))