diff options
| -rw-r--r-- | Documentation/netlink/specs/rt-link.yaml | 9 | ||||
| -rw-r--r-- | drivers/net/geneve.c | 167 | ||||
| -rw-r--r-- | include/uapi/linux/if_link.h | 2 |
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) |
