Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ipv6: some fixes for ipv6_dev_find()

This patch is to do 3 things for ipv6_dev_find():

As David A. noticed,

- rt6_lookup() is not really needed. Different from __ip_dev_find(),
ipv6_dev_find() doesn't have a compatibility problem, so remove it.

As Hideaki suggested,

- "valid" (non-tentative) check for the address is also needed.
ipv6_chk_addr() calls ipv6_chk_addr_and_flags(), which will
traverse the address hash list, but it's heavy to be called
inside ipv6_dev_find(). This patch is to reuse the code of
ipv6_chk_addr_and_flags() for ipv6_dev_find().

- dev parameter is passed into ipv6_dev_find(), as link-local
addresses from user space has sin6_scope_id set and the dev
lookup needs it.

Fixes: 81f6cb31222d ("ipv6: add ipv6_dev_find()")
Suggested-by: YOSHIFUJI Hideaki <hideaki.yoshifuji@miraclelinux.com>
Reported-by: David Ahern <dsahern@gmail.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Xin Long and committed by
David S. Miller
4ef1a7cb 0410d071

+28 -43
+2 -1
include/net/addrconf.h
··· 97 97 98 98 int ipv6_chk_prefix(const struct in6_addr *addr, struct net_device *dev); 99 99 100 - struct net_device *ipv6_dev_find(struct net *net, const struct in6_addr *addr); 100 + struct net_device *ipv6_dev_find(struct net *net, const struct in6_addr *addr, 101 + struct net_device *dev); 101 102 102 103 struct inet6_ifaddr *ipv6_get_ifaddr(struct net *net, 103 104 const struct in6_addr *addr,
+23 -37
net/ipv6/addrconf.c
··· 1893 1893 * 2. does the address exist on the specific device 1894 1894 * (skip_dev_check = false) 1895 1895 */ 1896 - int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, 1897 - const struct net_device *dev, bool skip_dev_check, 1898 - int strict, u32 banned_flags) 1896 + static struct net_device * 1897 + __ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, 1898 + const struct net_device *dev, bool skip_dev_check, 1899 + int strict, u32 banned_flags) 1899 1900 { 1900 1901 unsigned int hash = inet6_addr_hash(net, addr); 1901 - const struct net_device *l3mdev; 1902 + struct net_device *l3mdev, *ndev; 1902 1903 struct inet6_ifaddr *ifp; 1903 1904 u32 ifp_flags; 1904 1905 ··· 1910 1909 dev = NULL; 1911 1910 1912 1911 hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { 1913 - if (!net_eq(dev_net(ifp->idev->dev), net)) 1912 + ndev = ifp->idev->dev; 1913 + if (!net_eq(dev_net(ndev), net)) 1914 1914 continue; 1915 1915 1916 - if (l3mdev_master_dev_rcu(ifp->idev->dev) != l3mdev) 1916 + if (l3mdev_master_dev_rcu(ndev) != l3mdev) 1917 1917 continue; 1918 1918 1919 1919 /* Decouple optimistic from tentative for evaluation here. ··· 1925 1923 : ifp->flags; 1926 1924 if (ipv6_addr_equal(&ifp->addr, addr) && 1927 1925 !(ifp_flags&banned_flags) && 1928 - (!dev || ifp->idev->dev == dev || 1926 + (!dev || ndev == dev || 1929 1927 !(ifp->scope&(IFA_LINK|IFA_HOST) || strict))) { 1930 1928 rcu_read_unlock(); 1931 - return 1; 1929 + return ndev; 1932 1930 } 1933 1931 } 1934 1932 1935 1933 rcu_read_unlock(); 1936 - return 0; 1934 + return NULL; 1935 + } 1936 + 1937 + int ipv6_chk_addr_and_flags(struct net *net, const struct in6_addr *addr, 1938 + const struct net_device *dev, bool skip_dev_check, 1939 + int strict, u32 banned_flags) 1940 + { 1941 + return __ipv6_chk_addr_and_flags(net, addr, dev, skip_dev_check, 1942 + strict, banned_flags) ? 1 : 0; 1937 1943 } 1938 1944 EXPORT_SYMBOL(ipv6_chk_addr_and_flags); 1939 1945 ··· 2000 1990 * 2001 1991 * The caller should be protected by RCU, or RTNL. 2002 1992 */ 2003 - struct net_device *ipv6_dev_find(struct net *net, const struct in6_addr *addr) 1993 + struct net_device *ipv6_dev_find(struct net *net, const struct in6_addr *addr, 1994 + struct net_device *dev) 2004 1995 { 2005 - unsigned int hash = inet6_addr_hash(net, addr); 2006 - struct inet6_ifaddr *ifp, *result = NULL; 2007 - struct net_device *dev = NULL; 2008 - 2009 - rcu_read_lock(); 2010 - hlist_for_each_entry_rcu(ifp, &inet6_addr_lst[hash], addr_lst) { 2011 - if (net_eq(dev_net(ifp->idev->dev), net) && 2012 - ipv6_addr_equal(&ifp->addr, addr)) { 2013 - result = ifp; 2014 - break; 2015 - } 2016 - } 2017 - 2018 - if (!result) { 2019 - struct rt6_info *rt; 2020 - 2021 - rt = rt6_lookup(net, addr, NULL, 0, NULL, 0); 2022 - if (rt) { 2023 - dev = rt->dst.dev; 2024 - ip6_rt_put(rt); 2025 - } 2026 - } else { 2027 - dev = result->idev->dev; 2028 - } 2029 - rcu_read_unlock(); 2030 - 2031 - return dev; 1996 + return __ipv6_chk_addr_and_flags(net, addr, dev, !dev, 1, 1997 + IFA_F_TENTATIVE); 2032 1998 } 2033 1999 EXPORT_SYMBOL(ipv6_dev_find); 2034 2000
+3 -5
net/tipc/udp_media.c
··· 660 660 struct udp_tunnel_sock_cfg tuncfg = {NULL}; 661 661 struct nlattr *opts[TIPC_NLA_UDP_MAX + 1]; 662 662 u8 node_id[NODE_ID_LEN] = {0,}; 663 + struct net_device *dev; 663 664 int rmcast = 0; 664 665 665 666 ub = kzalloc(sizeof(*ub), GFP_ATOMIC); ··· 715 714 rcu_assign_pointer(ub->bearer, b); 716 715 tipc_udp_media_addr_set(&b->addr, &local); 717 716 if (local.proto == htons(ETH_P_IP)) { 718 - struct net_device *dev; 719 - 720 717 dev = __ip_dev_find(net, local.ipv4.s_addr, false); 721 718 if (!dev) { 722 719 err = -ENODEV; ··· 737 738 b->mtu = b->media->mtu; 738 739 #if IS_ENABLED(CONFIG_IPV6) 739 740 } else if (local.proto == htons(ETH_P_IPV6)) { 740 - struct net_device *dev; 741 - 742 - dev = ipv6_dev_find(net, &local.ipv6); 741 + dev = ub->ifindex ? __dev_get_by_index(net, ub->ifindex) : NULL; 742 + dev = ipv6_dev_find(net, &local.ipv6, dev); 743 743 if (!dev) { 744 744 err = -ENODEV; 745 745 goto err;