12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * NET3 IP device support routines. 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * Derived from the IP parts of dev.c 1.0.19 602c30a84SJesper Juhl * Authors: Ross Biro 71da177e4SLinus Torvalds * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> 81da177e4SLinus Torvalds * Mark Evans, <evansmp@uhura.aston.ac.uk> 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Additional Authors: 111da177e4SLinus Torvalds * Alan Cox, <gw4pts@gw4pts.ampr.org> 121da177e4SLinus Torvalds * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * Changes: 151da177e4SLinus Torvalds * Alexey Kuznetsov: pa_* fields are replaced with ifaddr 161da177e4SLinus Torvalds * lists. 171da177e4SLinus Torvalds * Cyrus Durgin: updated for kmod 181da177e4SLinus Torvalds * Matthias Andree: in devinet_ioctl, compare label and 191da177e4SLinus Torvalds * address (4.4BSD alias style support), 201da177e4SLinus Torvalds * fall back to comparing just the label 211da177e4SLinus Torvalds * if no match found. 221da177e4SLinus Torvalds */ 231da177e4SLinus Torvalds 241da177e4SLinus Torvalds 257c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 261da177e4SLinus Torvalds #include <linux/bitops.h> 274fc268d2SRandy Dunlap #include <linux/capability.h> 281da177e4SLinus Torvalds #include <linux/module.h> 291da177e4SLinus Torvalds #include <linux/types.h> 301da177e4SLinus Torvalds #include <linux/kernel.h> 31174cd4b1SIngo Molnar #include <linux/sched/signal.h> 321da177e4SLinus Torvalds #include <linux/string.h> 331da177e4SLinus Torvalds #include <linux/mm.h> 341da177e4SLinus Torvalds #include <linux/socket.h> 351da177e4SLinus Torvalds #include <linux/sockios.h> 361da177e4SLinus Torvalds #include <linux/in.h> 371da177e4SLinus Torvalds #include <linux/errno.h> 381da177e4SLinus Torvalds #include <linux/interrupt.h> 391823730fSThomas Graf #include <linux/if_addr.h> 401da177e4SLinus Torvalds #include <linux/if_ether.h> 411da177e4SLinus Torvalds #include <linux/inet.h> 421da177e4SLinus Torvalds #include <linux/netdevice.h> 431da177e4SLinus Torvalds #include <linux/etherdevice.h> 441da177e4SLinus Torvalds #include <linux/skbuff.h> 451da177e4SLinus Torvalds #include <linux/init.h> 461da177e4SLinus Torvalds #include <linux/notifier.h> 471da177e4SLinus Torvalds #include <linux/inetdevice.h> 481da177e4SLinus Torvalds #include <linux/igmp.h> 495a0e3ad6STejun Heo #include <linux/slab.h> 50fd23c3b3SDavid S. Miller #include <linux/hash.h> 511da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 521da177e4SLinus Torvalds #include <linux/sysctl.h> 531da177e4SLinus Torvalds #endif 541da177e4SLinus Torvalds #include <linux/kmod.h> 55edc9e748SNicolas Dichtel #include <linux/netconf.h> 561da177e4SLinus Torvalds 5714c85021SArnaldo Carvalho de Melo #include <net/arp.h> 581da177e4SLinus Torvalds #include <net/ip.h> 591da177e4SLinus Torvalds #include <net/route.h> 601da177e4SLinus Torvalds #include <net/ip_fib.h> 6163f3444fSThomas Graf #include <net/rtnetlink.h> 62752d14dcSPavel Emelyanov #include <net/net_namespace.h> 635c766d64SJiri Pirko #include <net/addrconf.h> 641da177e4SLinus Torvalds 652e605463SMatteo Croce #define IPV6ONLY_FLAGS \ 662e605463SMatteo Croce (IFA_F_NODAD | IFA_F_OPTIMISTIC | IFA_F_DADFAILED | \ 672e605463SMatteo Croce IFA_F_HOMEADDRESS | IFA_F_TENTATIVE | \ 682e605463SMatteo Croce IFA_F_MANAGETEMPADDR | IFA_F_STABLE_PRIVACY) 692e605463SMatteo Croce 700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 7142f811b8SHerbert Xu .data = { 7202291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7302291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7402291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7502291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 762690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 772690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 78fcdb44d0SJames Prestwood [IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1, 7942f811b8SHerbert Xu }, 801da177e4SLinus Torvalds }; 811da177e4SLinus Torvalds 821da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 8342f811b8SHerbert Xu .data = { 8402291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8502291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8602291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8702291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8802291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 892690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 902690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 91fcdb44d0SJames Prestwood [IPV4_DEVCONF_ARP_EVICT_NOCARRIER - 1] = 1, 9242f811b8SHerbert Xu }, 931da177e4SLinus Torvalds }; 941da177e4SLinus Torvalds 959355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 969355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9742f811b8SHerbert Xu 98ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 995c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 1005c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 1015c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 1025176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 1035c766d64SJiri Pirko [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 104ad6c8135SJiri Pirko [IFA_FLAGS] = { .type = NLA_U32 }, 105af4d768aSDavid Ahern [IFA_RT_PRIORITY] = { .type = NLA_U32 }, 106d3807145SChristian Brauner [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, 10747f0bd50SJacques de Laval [IFA_PROTO] = { .type = NLA_U8 }, 1085c753978SThomas Graf }; 1095c753978SThomas Graf 110978a46faSChristian Brauner struct inet_fill_args { 111978a46faSChristian Brauner u32 portid; 112978a46faSChristian Brauner u32 seq; 113978a46faSChristian Brauner int event; 114978a46faSChristian Brauner unsigned int flags; 115978a46faSChristian Brauner int netnsid; 1165fcd266aSDavid Ahern int ifindex; 117978a46faSChristian Brauner }; 118978a46faSChristian Brauner 11940384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT 8 12040384999SEric Dumazet #define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 12140384999SEric Dumazet 122fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 123fd23c3b3SDavid S. Miller 1246eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr) 125fd23c3b3SDavid S. Miller { 12640384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 127fd23c3b3SDavid S. Miller 12840384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 129fd23c3b3SDavid S. Miller } 130fd23c3b3SDavid S. Miller 131fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 132fd23c3b3SDavid S. Miller { 13340384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 134fd23c3b3SDavid S. Miller 13532a4be48SWANG Cong ASSERT_RTNL(); 136fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 137fd23c3b3SDavid S. Miller } 138fd23c3b3SDavid S. Miller 139fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 140fd23c3b3SDavid S. Miller { 14132a4be48SWANG Cong ASSERT_RTNL(); 142fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 143fd23c3b3SDavid S. Miller } 144fd23c3b3SDavid S. Miller 1459435eb1cSDavid S. Miller /** 1469435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1479435eb1cSDavid S. Miller * @net: the net namespace 1489435eb1cSDavid S. Miller * @addr: the source address 1499435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1509435eb1cSDavid S. Miller * 1519435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1529435eb1cSDavid S. Miller */ 1539435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1549435eb1cSDavid S. Miller { 1559435eb1cSDavid S. Miller struct net_device *result = NULL; 1569435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1579435eb1cSDavid S. Miller 1589435eb1cSDavid S. Miller rcu_read_lock(); 1596e617de8SPaolo Abeni ifa = inet_lookup_ifaddr_rcu(net, addr); 1606e617de8SPaolo Abeni if (!ifa) { 161406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 162406b6f97SDavid S. Miller struct fib_result res = { 0 }; 163406b6f97SDavid S. Miller struct fib_table *local; 164406b6f97SDavid S. Miller 165406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 166406b6f97SDavid S. Miller * over loopback subnets work. 167406b6f97SDavid S. Miller */ 168406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 169406b6f97SDavid S. Miller if (local && 170406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 171406b6f97SDavid S. Miller res.type == RTN_LOCAL) 172406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 1736e617de8SPaolo Abeni } else { 1746e617de8SPaolo Abeni result = ifa->ifa_dev->dev; 175406b6f97SDavid S. Miller } 1769435eb1cSDavid S. Miller if (result && devref) 1779435eb1cSDavid S. Miller dev_hold(result); 1789435eb1cSDavid S. Miller rcu_read_unlock(); 1799435eb1cSDavid S. Miller return result; 1809435eb1cSDavid S. Miller } 1819435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1829435eb1cSDavid S. Miller 1836e617de8SPaolo Abeni /* called under RCU lock */ 1846e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr) 1856e617de8SPaolo Abeni { 1866e617de8SPaolo Abeni u32 hash = inet_addr_hash(net, addr); 1876e617de8SPaolo Abeni struct in_ifaddr *ifa; 1886e617de8SPaolo Abeni 1896e617de8SPaolo Abeni hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) 1906e617de8SPaolo Abeni if (ifa->ifa_local == addr && 1916e617de8SPaolo Abeni net_eq(dev_net(ifa->ifa_dev->dev), net)) 1926e617de8SPaolo Abeni return ifa; 1936e617de8SPaolo Abeni 1946e617de8SPaolo Abeni return NULL; 1956e617de8SPaolo Abeni } 1966e617de8SPaolo Abeni 197d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1981da177e4SLinus Torvalds 199e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 2003ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain); 2012638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev, 2022638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap, 2031da177e4SLinus Torvalds int destroy); 2041da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 20520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev); 20651602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 20751602b2aSPavel Emelyanov #else 20820e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 20951602b2aSPavel Emelyanov { 21020e61da7SWANG Cong return 0; 21151602b2aSPavel Emelyanov } 21240384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 21351602b2aSPavel Emelyanov { 21451602b2aSPavel Emelyanov } 2151da177e4SLinus Torvalds #endif 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds /* Locks all the inet devices. */ 2181da177e4SLinus Torvalds 2191da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 2201da177e4SLinus Torvalds { 2216126891cSVasily Averin return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL_ACCOUNT); 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds 2241da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2251da177e4SLinus Torvalds { 2261da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2271da177e4SLinus Torvalds if (ifa->ifa_dev) 2281da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2291da177e4SLinus Torvalds kfree(ifa); 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds 23240384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2331da177e4SLinus Torvalds { 2341da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2351da177e4SLinus Torvalds } 2361da177e4SLinus Torvalds 2379d40c84cSEric Dumazet static void in_dev_free_rcu(struct rcu_head *head) 2389d40c84cSEric Dumazet { 2399d40c84cSEric Dumazet struct in_device *idev = container_of(head, struct in_device, rcu_head); 2409d40c84cSEric Dumazet 2419d40c84cSEric Dumazet kfree(rcu_dereference_protected(idev->mc_hash, 1)); 2429d40c84cSEric Dumazet kfree(idev); 2439d40c84cSEric Dumazet } 2449d40c84cSEric Dumazet 2451da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2461da177e4SLinus Torvalds { 2471da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2481da177e4SLinus Torvalds 249547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 250547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 2511da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 25291df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2531da177e4SLinus Torvalds #endif 254d62607c3SJakub Kicinski netdev_put(dev, &idev->dev_tracker); 2551da177e4SLinus Torvalds if (!idev->dead) 2569f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2579f9354b9SEric Dumazet else 2589d40c84cSEric Dumazet call_rcu(&idev->rcu_head, in_dev_free_rcu); 2591da177e4SLinus Torvalds } 2609f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2611da177e4SLinus Torvalds 26271e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2631da177e4SLinus Torvalds { 2641da177e4SLinus Torvalds struct in_device *in_dev; 26520e61da7SWANG Cong int err = -ENOMEM; 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds ASSERT_RTNL(); 2681da177e4SLinus Torvalds 2690da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2701da177e4SLinus Torvalds if (!in_dev) 2711da177e4SLinus Torvalds goto out; 272c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2739355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2741da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2751da177e4SLinus Torvalds in_dev->dev = dev; 2769f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2779f9354b9SEric Dumazet if (!in_dev->arp_parms) 2781da177e4SLinus Torvalds goto out_kfree; 2790187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2800187bdfbSBen Hutchings dev_disable_lro(dev); 2811da177e4SLinus Torvalds /* Reference in_dev->dev */ 282d62607c3SJakub Kicinski netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL); 28330c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2847658b36fSReshetova, Elena refcount_set(&in_dev->refcnt, 1); 2851da177e4SLinus Torvalds 28620e61da7SWANG Cong err = devinet_sysctl_register(in_dev); 28720e61da7SWANG Cong if (err) { 28820e61da7SWANG Cong in_dev->dead = 1; 2891b49cd71SYang Yingliang neigh_parms_release(&arp_tbl, in_dev->arp_parms); 29020e61da7SWANG Cong in_dev_put(in_dev); 29120e61da7SWANG Cong in_dev = NULL; 29220e61da7SWANG Cong goto out; 29320e61da7SWANG Cong } 2941da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2951da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2961da177e4SLinus Torvalds ip_mc_up(in_dev); 297483479ecSJarek Poplawski 29830c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 299cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 300483479ecSJarek Poplawski out: 30120e61da7SWANG Cong return in_dev ?: ERR_PTR(err); 3021da177e4SLinus Torvalds out_kfree: 3031da177e4SLinus Torvalds kfree(in_dev); 3041da177e4SLinus Torvalds in_dev = NULL; 3051da177e4SLinus Torvalds goto out; 3061da177e4SLinus Torvalds } 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 3091da177e4SLinus Torvalds { 3101da177e4SLinus Torvalds struct net_device *dev; 3112638eb8bSFlorian Westphal struct in_ifaddr *ifa; 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds ASSERT_RTNL(); 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds dev = in_dev->dev; 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds in_dev->dead = 1; 3181da177e4SLinus Torvalds 3191da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 3201da177e4SLinus Torvalds 3212638eb8bSFlorian Westphal while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) { 3221da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 3231da177e4SLinus Torvalds inet_free_ifa(ifa); 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds 326a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 3271da177e4SLinus Torvalds 32851602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 3291da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 3301da177e4SLinus Torvalds arp_ifdown(dev); 3311da177e4SLinus Torvalds 3329d40c84cSEric Dumazet in_dev_put(in_dev); 3331da177e4SLinus Torvalds } 3341da177e4SLinus Torvalds 335ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3361da177e4SLinus Torvalds { 337d519e870SFlorian Westphal const struct in_ifaddr *ifa; 338d519e870SFlorian Westphal 3391da177e4SLinus Torvalds rcu_read_lock(); 340d519e870SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 3411da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3421da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3431da177e4SLinus Torvalds rcu_read_unlock(); 3441da177e4SLinus Torvalds return 1; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds } 347d519e870SFlorian Westphal } 3481da177e4SLinus Torvalds rcu_read_unlock(); 3491da177e4SLinus Torvalds return 0; 3501da177e4SLinus Torvalds } 3511da177e4SLinus Torvalds 3522638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev, 3532638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap, 35415e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3551da177e4SLinus Torvalds { 3568f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3572638eb8bSFlorian Westphal struct in_ifaddr *ifa, *ifa1; 358ac28b1ecSLiu Jian struct in_ifaddr __rcu **last_prim; 3590ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3600ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3611da177e4SLinus Torvalds 3621da177e4SLinus Torvalds ASSERT_RTNL(); 3631da177e4SLinus Torvalds 3642638eb8bSFlorian Westphal ifa1 = rtnl_dereference(*ifap); 365ac28b1ecSLiu Jian last_prim = ifap; 366fbd40ea0SDavid S. Miller if (in_dev->dead) 367fbd40ea0SDavid S. Miller goto no_promotions; 368fbd40ea0SDavid S. Miller 3698f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3708f937c60SHarald Welte * unless alias promotion is set 3718f937c60SHarald Welte **/ 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3742638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next; 3751da177e4SLinus Torvalds 3762638eb8bSFlorian Westphal while ((ifa = rtnl_dereference(*ifap1)) != NULL) { 3770ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3780ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 379ac28b1ecSLiu Jian last_prim = &ifa->ifa_next; 3800ff60a45SJamal Hadi Salim 3811da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3821da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3831da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3841da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3850ff60a45SJamal Hadi Salim prev_prom = ifa; 3861da177e4SLinus Torvalds continue; 3871da177e4SLinus Torvalds } 3881da177e4SLinus Torvalds 3890ff60a45SJamal Hadi Salim if (!do_promote) { 390fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3911da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3921da177e4SLinus Torvalds 39315e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 394e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 395e041c683SAlan Stern NETDEV_DOWN, ifa); 3961da177e4SLinus Torvalds inet_free_ifa(ifa); 3978f937c60SHarald Welte } else { 3988f937c60SHarald Welte promote = ifa; 3998f937c60SHarald Welte break; 4008f937c60SHarald Welte } 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds } 4031da177e4SLinus Torvalds 4042d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 4052d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 4062d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 4072d230e2bSJulian Anastasov * while all addresses are on the device list. 4082d230e2bSJulian Anastasov */ 4092638eb8bSFlorian Westphal for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) { 4102d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 4112d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 4122d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 4132d230e2bSJulian Anastasov } 4142d230e2bSJulian Anastasov 415fbd40ea0SDavid S. Miller no_promotions: 4161da177e4SLinus Torvalds /* 2. Unlink it */ 4171da177e4SLinus Torvalds 4181da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 419fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 4201da177e4SLinus Torvalds 4211da177e4SLinus Torvalds /* 3. Announce address deletion */ 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds /* Send message first, then call notifier. 4241da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 4251da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 4261da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 4271da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 4281da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 4291da177e4SLinus Torvalds So that, this order is correct. 4301da177e4SLinus Torvalds */ 43115e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 432e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 4330ff60a45SJamal Hadi Salim 4340ff60a45SJamal Hadi Salim if (promote) { 4352638eb8bSFlorian Westphal struct in_ifaddr *next_sec; 4360ff60a45SJamal Hadi Salim 4372638eb8bSFlorian Westphal next_sec = rtnl_dereference(promote->ifa_next); 4380ff60a45SJamal Hadi Salim if (prev_prom) { 4392638eb8bSFlorian Westphal struct in_ifaddr *last_sec; 4402638eb8bSFlorian Westphal 4412638eb8bSFlorian Westphal rcu_assign_pointer(prev_prom->ifa_next, next_sec); 4426a9e9ceaSFlorian Westphal 443ac28b1ecSLiu Jian last_sec = rtnl_dereference(*last_prim); 4442638eb8bSFlorian Westphal rcu_assign_pointer(promote->ifa_next, last_sec); 445ac28b1ecSLiu Jian rcu_assign_pointer(*last_prim, promote); 4460ff60a45SJamal Hadi Salim } 4470ff60a45SJamal Hadi Salim 4480ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 44915e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 450e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 451e041c683SAlan Stern NETDEV_UP, promote); 4522638eb8bSFlorian Westphal for (ifa = next_sec; ifa; 4532638eb8bSFlorian Westphal ifa = rtnl_dereference(ifa->ifa_next)) { 4540ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4550ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4560ff60a45SJamal Hadi Salim continue; 4570ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4580ff60a45SJamal Hadi Salim } 4590ff60a45SJamal Hadi Salim 4600ff60a45SJamal Hadi Salim } 4616363097cSHerbert Xu if (destroy) 4621da177e4SLinus Torvalds inet_free_ifa(ifa1); 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds 4652638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev, 4662638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap, 467d6062cbbSThomas Graf int destroy) 468d6062cbbSThomas Graf { 469d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 470d6062cbbSThomas Graf } 471d6062cbbSThomas Graf 4725c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4735c766d64SJiri Pirko 4745c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4755c766d64SJiri Pirko 476d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 477de95e047SDavid Ahern u32 portid, struct netlink_ext_ack *extack) 4781da177e4SLinus Torvalds { 4792638eb8bSFlorian Westphal struct in_ifaddr __rcu **last_primary, **ifap; 4801da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4813ad7d246SKrister Johansen struct in_validator_info ivi; 4822638eb8bSFlorian Westphal struct in_ifaddr *ifa1; 4833ad7d246SKrister Johansen int ret; 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds ASSERT_RTNL(); 4861da177e4SLinus Torvalds 4871da177e4SLinus Torvalds if (!ifa->ifa_local) { 4881da177e4SLinus Torvalds inet_free_ifa(ifa); 4891da177e4SLinus Torvalds return 0; 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4931da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4941da177e4SLinus Torvalds 4952e605463SMatteo Croce /* Don't set IPv6 only flags to IPv4 addresses */ 4962e605463SMatteo Croce ifa->ifa_flags &= ~IPV6ONLY_FLAGS; 4972e605463SMatteo Croce 4982638eb8bSFlorian Westphal ifap = &in_dev->ifa_list; 4992638eb8bSFlorian Westphal ifa1 = rtnl_dereference(*ifap); 5002638eb8bSFlorian Westphal 5012638eb8bSFlorian Westphal while (ifa1) { 5021da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 5031da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 5041da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 5051da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 5061da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 5071da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 5081da177e4SLinus Torvalds inet_free_ifa(ifa); 5091da177e4SLinus Torvalds return -EEXIST; 5101da177e4SLinus Torvalds } 5111da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 512b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Invalid scope value"); 5131da177e4SLinus Torvalds inet_free_ifa(ifa); 5141da177e4SLinus Torvalds return -EINVAL; 5151da177e4SLinus Torvalds } 5161da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 5171da177e4SLinus Torvalds } 5182638eb8bSFlorian Westphal 5192638eb8bSFlorian Westphal ifap = &ifa1->ifa_next; 5202638eb8bSFlorian Westphal ifa1 = rtnl_dereference(*ifap); 5211da177e4SLinus Torvalds } 5221da177e4SLinus Torvalds 5233ad7d246SKrister Johansen /* Allow any devices that wish to register ifaddr validtors to weigh 5243ad7d246SKrister Johansen * in now, before changes are committed. The rntl lock is serializing 5253ad7d246SKrister Johansen * access here, so the state should not change between a validator call 5263ad7d246SKrister Johansen * and a final notify on commit. This isn't invoked on promotion under 5273ad7d246SKrister Johansen * the assumption that validators are checking the address itself, and 5283ad7d246SKrister Johansen * not the flags. 5293ad7d246SKrister Johansen */ 5303ad7d246SKrister Johansen ivi.ivi_addr = ifa->ifa_address; 5313ad7d246SKrister Johansen ivi.ivi_dev = ifa->ifa_dev; 532de95e047SDavid Ahern ivi.extack = extack; 5333ad7d246SKrister Johansen ret = blocking_notifier_call_chain(&inetaddr_validator_chain, 5343ad7d246SKrister Johansen NETDEV_UP, &ivi); 5353ad7d246SKrister Johansen ret = notifier_to_errno(ret); 5363ad7d246SKrister Johansen if (ret) { 5373ad7d246SKrister Johansen inet_free_ifa(ifa); 5383ad7d246SKrister Johansen return ret; 5393ad7d246SKrister Johansen } 5403ad7d246SKrister Johansen 541d4150779SJason A. Donenfeld if (!(ifa->ifa_flags & IFA_F_SECONDARY)) 5421da177e4SLinus Torvalds ifap = last_primary; 5431da177e4SLinus Torvalds 5442638eb8bSFlorian Westphal rcu_assign_pointer(ifa->ifa_next, *ifap); 5452638eb8bSFlorian Westphal rcu_assign_pointer(*ifap, ifa); 5461da177e4SLinus Torvalds 547fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 548fd23c3b3SDavid S. Miller 5495c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 550906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 5515c766d64SJiri Pirko 5521da177e4SLinus Torvalds /* Send message first, then call notifier. 5531da177e4SLinus Torvalds Notifier will trigger FIB update, so that 5541da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 55515e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 556e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 5571da177e4SLinus Torvalds 5581da177e4SLinus Torvalds return 0; 5591da177e4SLinus Torvalds } 5601da177e4SLinus Torvalds 561d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 562d6062cbbSThomas Graf { 563de95e047SDavid Ahern return __inet_insert_ifa(ifa, NULL, 0, NULL); 564d6062cbbSThomas Graf } 565d6062cbbSThomas Graf 5661da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 5671da177e4SLinus Torvalds { 568e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 5691da177e4SLinus Torvalds 5701da177e4SLinus Torvalds ASSERT_RTNL(); 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds if (!in_dev) { 5731da177e4SLinus Torvalds inet_free_ifa(ifa); 5741da177e4SLinus Torvalds return -ENOBUFS; 5751da177e4SLinus Torvalds } 57671e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 5771d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 5781da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 579547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 5801da177e4SLinus Torvalds in_dev_hold(in_dev); 5811da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5821da177e4SLinus Torvalds } 583f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5841da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5851da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5861da177e4SLinus Torvalds } 5871da177e4SLinus Torvalds 5888723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5898723e1b4SEric Dumazet * We dont take a reference on found in_device 5908723e1b4SEric Dumazet */ 5917fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5921da177e4SLinus Torvalds { 5931da177e4SLinus Torvalds struct net_device *dev; 5941da177e4SLinus Torvalds struct in_device *in_dev = NULL; 595c148fc2eSEric Dumazet 596c148fc2eSEric Dumazet rcu_read_lock(); 597c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5981da177e4SLinus Torvalds if (dev) 5998723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 600c148fc2eSEric Dumazet rcu_read_unlock(); 6011da177e4SLinus Torvalds return in_dev; 6021da177e4SLinus Torvalds } 6039f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 6041da177e4SLinus Torvalds 6051da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 6061da177e4SLinus Torvalds 60760cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 60860cad5daSAl Viro __be32 mask) 6091da177e4SLinus Torvalds { 610d519e870SFlorian Westphal struct in_ifaddr *ifa; 611d519e870SFlorian Westphal 6121da177e4SLinus Torvalds ASSERT_RTNL(); 6131da177e4SLinus Torvalds 614d519e870SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 6151da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 6161da177e4SLinus Torvalds return ifa; 617d519e870SFlorian Westphal } 6181da177e4SLinus Torvalds return NULL; 6191da177e4SLinus Torvalds } 6201da177e4SLinus Torvalds 621690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join, 622690cc863STaras Chornyi const struct in_ifaddr *ifa) 62393a714d6SMadhu Challa { 624690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST) 62593a714d6SMadhu Challa struct ip_mreqn mreq = { 62693a714d6SMadhu Challa .imr_multiaddr.s_addr = ifa->ifa_address, 62793a714d6SMadhu Challa .imr_ifindex = ifa->ifa_dev->dev->ifindex, 62893a714d6SMadhu Challa }; 629690cc863STaras Chornyi struct sock *sk = net->ipv4.mc_autojoin_sk; 63093a714d6SMadhu Challa int ret; 63193a714d6SMadhu Challa 63293a714d6SMadhu Challa ASSERT_RTNL(); 63393a714d6SMadhu Challa 63493a714d6SMadhu Challa lock_sock(sk); 63593a714d6SMadhu Challa if (join) 63654ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_join_group(sk, &mreq); 63793a714d6SMadhu Challa else 63854ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_leave_group(sk, &mreq); 63993a714d6SMadhu Challa release_sock(sk); 64093a714d6SMadhu Challa 64193a714d6SMadhu Challa return ret; 642690cc863STaras Chornyi #else 643690cc863STaras Chornyi return -EOPNOTSUPP; 644690cc863STaras Chornyi #endif 64593a714d6SMadhu Challa } 64693a714d6SMadhu Challa 647c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, 648c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 6491da177e4SLinus Torvalds { 6503b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 6512638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap; 652dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 6531da177e4SLinus Torvalds struct in_device *in_dev; 654dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 6552638eb8bSFlorian Westphal struct in_ifaddr *ifa; 65630e2379eSMenglong Dong int err; 6571da177e4SLinus Torvalds 6581da177e4SLinus Torvalds ASSERT_RTNL(); 6591da177e4SLinus Torvalds 6608cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 6618cb08174SJohannes Berg ifa_ipv4_policy, extack); 662dfdd5fd4SThomas Graf if (err < 0) 663dfdd5fd4SThomas Graf goto errout; 664dfdd5fd4SThomas Graf 665dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 6667fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 66751456b29SIan Morris if (!in_dev) { 668b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Device not found"); 669dfdd5fd4SThomas Graf err = -ENODEV; 670dfdd5fd4SThomas Graf goto errout; 671dfdd5fd4SThomas Graf } 672dfdd5fd4SThomas Graf 6732638eb8bSFlorian Westphal for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL; 6741da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 675dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 67667b61f6cSJiri Benc ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL])) 6771da177e4SLinus Torvalds continue; 678dfdd5fd4SThomas Graf 679dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 680dfdd5fd4SThomas Graf continue; 681dfdd5fd4SThomas Graf 682dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 683dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 68467b61f6cSJiri Benc !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa))) 685dfdd5fd4SThomas Graf continue; 686dfdd5fd4SThomas Graf 68793a714d6SMadhu Challa if (ipv4_is_multicast(ifa->ifa_address)) 688690cc863STaras Chornyi ip_mc_autojoin_config(net, false, ifa); 68915e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 6901da177e4SLinus Torvalds return 0; 6911da177e4SLinus Torvalds } 692dfdd5fd4SThomas Graf 693b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Address not found"); 694dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 695dfdd5fd4SThomas Graf errout: 696dfdd5fd4SThomas Graf return err; 6971da177e4SLinus Torvalds } 6981da177e4SLinus Torvalds 6995c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 7005c766d64SJiri Pirko 7015c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 7025c766d64SJiri Pirko { 7035c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 7045c766d64SJiri Pirko struct in_ifaddr *ifa; 705c988d1e8SJiri Pirko struct hlist_node *n; 7065c766d64SJiri Pirko int i; 7075c766d64SJiri Pirko 7085c766d64SJiri Pirko now = jiffies; 7095c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 7105c766d64SJiri Pirko 7115c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 712c988d1e8SJiri Pirko bool change_needed = false; 713c988d1e8SJiri Pirko 714c988d1e8SJiri Pirko rcu_read_lock(); 715b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 716*3cd3e72cSEric Dumazet unsigned long age, tstamp; 7175c766d64SJiri Pirko 7185c766d64SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 7195c766d64SJiri Pirko continue; 7205c766d64SJiri Pirko 721*3cd3e72cSEric Dumazet tstamp = READ_ONCE(ifa->ifa_tstamp); 7225c766d64SJiri Pirko /* We try to batch several events at once. */ 723*3cd3e72cSEric Dumazet age = (now - tstamp + 7245c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 7255c766d64SJiri Pirko 7265c766d64SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 7275c766d64SJiri Pirko age >= ifa->ifa_valid_lft) { 728c988d1e8SJiri Pirko change_needed = true; 729c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft == 730c988d1e8SJiri Pirko INFINITY_LIFE_TIME) { 731c988d1e8SJiri Pirko continue; 732c988d1e8SJiri Pirko } else if (age >= ifa->ifa_preferred_lft) { 733*3cd3e72cSEric Dumazet if (time_before(tstamp + 734c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ, next)) 735*3cd3e72cSEric Dumazet next = tstamp + 736c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ; 737c988d1e8SJiri Pirko 738c988d1e8SJiri Pirko if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) 739c988d1e8SJiri Pirko change_needed = true; 740*3cd3e72cSEric Dumazet } else if (time_before(tstamp + 741c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ, 742c988d1e8SJiri Pirko next)) { 743*3cd3e72cSEric Dumazet next = tstamp + 744c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ; 745c988d1e8SJiri Pirko } 746c988d1e8SJiri Pirko } 747c988d1e8SJiri Pirko rcu_read_unlock(); 748c988d1e8SJiri Pirko if (!change_needed) 749c988d1e8SJiri Pirko continue; 750c988d1e8SJiri Pirko rtnl_lock(); 751c988d1e8SJiri Pirko hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 752c988d1e8SJiri Pirko unsigned long age; 753c988d1e8SJiri Pirko 754c988d1e8SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 755c988d1e8SJiri Pirko continue; 756c988d1e8SJiri Pirko 757c988d1e8SJiri Pirko /* We try to batch several events at once. */ 758c988d1e8SJiri Pirko age = (now - ifa->ifa_tstamp + 759c988d1e8SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 760c988d1e8SJiri Pirko 761c988d1e8SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 762c988d1e8SJiri Pirko age >= ifa->ifa_valid_lft) { 7632638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap; 7642638eb8bSFlorian Westphal struct in_ifaddr *tmp; 7655c766d64SJiri Pirko 7662638eb8bSFlorian Westphal ifap = &ifa->ifa_dev->ifa_list; 7672638eb8bSFlorian Westphal tmp = rtnl_dereference(*ifap); 7682638eb8bSFlorian Westphal while (tmp) { 76940008e92SFlorian Westphal if (tmp == ifa) { 7705c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 7715c766d64SJiri Pirko ifap, 1); 772c988d1e8SJiri Pirko break; 7735c766d64SJiri Pirko } 7742638eb8bSFlorian Westphal ifap = &tmp->ifa_next; 7752638eb8bSFlorian Westphal tmp = rtnl_dereference(*ifap); 776c988d1e8SJiri Pirko } 777c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft != 778c988d1e8SJiri Pirko INFINITY_LIFE_TIME && 779c988d1e8SJiri Pirko age >= ifa->ifa_preferred_lft && 780c988d1e8SJiri Pirko !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 7815c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7825c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 7835c766d64SJiri Pirko } 7845c766d64SJiri Pirko } 785c988d1e8SJiri Pirko rtnl_unlock(); 7865c766d64SJiri Pirko } 7875c766d64SJiri Pirko 7885c766d64SJiri Pirko next_sec = round_jiffies_up(next); 7895c766d64SJiri Pirko next_sched = next; 7905c766d64SJiri Pirko 7915c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 7925c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 7935c766d64SJiri Pirko next_sched = next_sec; 7945c766d64SJiri Pirko 7955c766d64SJiri Pirko now = jiffies; 7965c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 7975c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 7985c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 7995c766d64SJiri Pirko 800906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 801906e073fSviresh kumar next_sched - now); 8025c766d64SJiri Pirko } 8035c766d64SJiri Pirko 8045c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 8055c766d64SJiri Pirko __u32 prefered_lft) 8065c766d64SJiri Pirko { 8075c766d64SJiri Pirko unsigned long timeout; 8085c766d64SJiri Pirko 8095c766d64SJiri Pirko ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 8105c766d64SJiri Pirko 8115c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 8125c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 8135c766d64SJiri Pirko ifa->ifa_valid_lft = timeout; 8145c766d64SJiri Pirko else 8155c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_PERMANENT; 8165c766d64SJiri Pirko 8175c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 8185c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 8195c766d64SJiri Pirko if (timeout == 0) 8205c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 8215c766d64SJiri Pirko ifa->ifa_preferred_lft = timeout; 8225c766d64SJiri Pirko } 823*3cd3e72cSEric Dumazet WRITE_ONCE(ifa->ifa_tstamp, jiffies); 8245c766d64SJiri Pirko if (!ifa->ifa_cstamp) 825*3cd3e72cSEric Dumazet WRITE_ONCE(ifa->ifa_cstamp, ifa->ifa_tstamp); 8265c766d64SJiri Pirko } 8275c766d64SJiri Pirko 8285c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 829dac9c979SDavid Ahern __u32 *pvalid_lft, __u32 *pprefered_lft, 830dac9c979SDavid Ahern struct netlink_ext_ack *extack) 8311da177e4SLinus Torvalds { 8325c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 8335c753978SThomas Graf struct in_ifaddr *ifa; 8345c753978SThomas Graf struct ifaddrmsg *ifm; 8351da177e4SLinus Torvalds struct net_device *dev; 8361da177e4SLinus Torvalds struct in_device *in_dev; 8377b218574SDenis V. Lunev int err; 8381da177e4SLinus Torvalds 8398cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 8408cb08174SJohannes Berg ifa_ipv4_policy, extack); 8415c753978SThomas Graf if (err < 0) 8425c753978SThomas Graf goto errout; 8431da177e4SLinus Torvalds 8445c753978SThomas Graf ifm = nlmsg_data(nlh); 845c4e38f41SEvgeniy Polyakov err = -EINVAL; 846b4672c73SHangbin Liu 847b4672c73SHangbin Liu if (ifm->ifa_prefixlen > 32) { 848b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Invalid prefix length"); 8495c753978SThomas Graf goto errout; 850b4672c73SHangbin Liu } 851b4672c73SHangbin Liu 852b4672c73SHangbin Liu if (!tb[IFA_LOCAL]) { 853b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Local address is not supplied"); 854b4672c73SHangbin Liu goto errout; 855b4672c73SHangbin Liu } 8561da177e4SLinus Torvalds 8574b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 8585c753978SThomas Graf err = -ENODEV; 859b4672c73SHangbin Liu if (!dev) { 860b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Device not found"); 8615c753978SThomas Graf goto errout; 862b4672c73SHangbin Liu } 8631da177e4SLinus Torvalds 8645c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 8655c753978SThomas Graf err = -ENOBUFS; 86651456b29SIan Morris if (!in_dev) 8675c753978SThomas Graf goto errout; 86871e27da9SHerbert Xu 8695c753978SThomas Graf ifa = inet_alloc_ifa(); 87051456b29SIan Morris if (!ifa) 8715c753978SThomas Graf /* 8725c753978SThomas Graf * A potential indev allocation can be left alive, it stays 8735c753978SThomas Graf * assigned to its device and is destroy with it. 8745c753978SThomas Graf */ 8755c753978SThomas Graf goto errout; 8765c753978SThomas Graf 877a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 8781d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 8795c753978SThomas Graf in_dev_hold(in_dev); 8805c753978SThomas Graf 88151456b29SIan Morris if (!tb[IFA_ADDRESS]) 8825c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 8835c753978SThomas Graf 884fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 8851da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 8861da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 887ad6c8135SJiri Pirko ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : 888ad6c8135SJiri Pirko ifm->ifa_flags; 8891da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 8901da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 8915c753978SThomas Graf 89267b61f6cSJiri Benc ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]); 89367b61f6cSJiri Benc ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]); 8945c753978SThomas Graf 8955c753978SThomas Graf if (tb[IFA_BROADCAST]) 89667b61f6cSJiri Benc ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]); 8975c753978SThomas Graf 8985c753978SThomas Graf if (tb[IFA_LABEL]) 899872f6903SFrancis Laniel nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 9001da177e4SLinus Torvalds else 9011da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 9021da177e4SLinus Torvalds 903af4d768aSDavid Ahern if (tb[IFA_RT_PRIORITY]) 904af4d768aSDavid Ahern ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 905af4d768aSDavid Ahern 90647f0bd50SJacques de Laval if (tb[IFA_PROTO]) 90747f0bd50SJacques de Laval ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]); 90847f0bd50SJacques de Laval 9095c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 9105c766d64SJiri Pirko struct ifa_cacheinfo *ci; 9115c766d64SJiri Pirko 9125c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 9135c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 914b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: address lifetime invalid"); 9155c766d64SJiri Pirko err = -EINVAL; 916446266b0SDaniel Borkmann goto errout_free; 9175c766d64SJiri Pirko } 9185c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 9195c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 9205c766d64SJiri Pirko } 9215c766d64SJiri Pirko 9225c753978SThomas Graf return ifa; 9235c753978SThomas Graf 924446266b0SDaniel Borkmann errout_free: 925446266b0SDaniel Borkmann inet_free_ifa(ifa); 9265c753978SThomas Graf errout: 9275c753978SThomas Graf return ERR_PTR(err); 9285c753978SThomas Graf } 9295c753978SThomas Graf 9305c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 9315c766d64SJiri Pirko { 9325c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 933ef11db33SFlorian Westphal struct in_ifaddr *ifa1; 9345c766d64SJiri Pirko 9355c766d64SJiri Pirko if (!ifa->ifa_local) 9365c766d64SJiri Pirko return NULL; 9375c766d64SJiri Pirko 938ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa1, in_dev) { 9395c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 9405c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 9415c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 9425c766d64SJiri Pirko return ifa1; 9435c766d64SJiri Pirko } 9445c766d64SJiri Pirko return NULL; 9455c766d64SJiri Pirko } 9465c766d64SJiri Pirko 947c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, 948c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 9495c753978SThomas Graf { 9503b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 9515c753978SThomas Graf struct in_ifaddr *ifa; 9525c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 9535c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 9545c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 9555c753978SThomas Graf 9565c753978SThomas Graf ASSERT_RTNL(); 9575c753978SThomas Graf 958dac9c979SDavid Ahern ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack); 9595c753978SThomas Graf if (IS_ERR(ifa)) 9605c753978SThomas Graf return PTR_ERR(ifa); 9615c753978SThomas Graf 9625c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 9635c766d64SJiri Pirko if (!ifa_existing) { 9645c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 965614d056cSstephen hemminger * userspace already relies on not having to provide this. 9665c766d64SJiri Pirko */ 9675c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 96893a714d6SMadhu Challa if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) { 969690cc863STaras Chornyi int ret = ip_mc_autojoin_config(net, true, ifa); 97093a714d6SMadhu Challa 97193a714d6SMadhu Challa if (ret < 0) { 972b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Multicast auto join failed"); 97393a714d6SMadhu Challa inet_free_ifa(ifa); 97493a714d6SMadhu Challa return ret; 97593a714d6SMadhu Challa } 97693a714d6SMadhu Challa } 977de95e047SDavid Ahern return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid, 978de95e047SDavid Ahern extack); 9795c766d64SJiri Pirko } else { 980af4d768aSDavid Ahern u32 new_metric = ifa->ifa_rt_priority; 9815c4a9aa8SPetr Machata u8 new_proto = ifa->ifa_proto; 982af4d768aSDavid Ahern 9835c766d64SJiri Pirko inet_free_ifa(ifa); 9845c766d64SJiri Pirko 9855c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 986b4672c73SHangbin Liu !(nlh->nlmsg_flags & NLM_F_REPLACE)) { 987b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Address already assigned"); 9885c766d64SJiri Pirko return -EEXIST; 989b4672c73SHangbin Liu } 99034e2ed34SJiri Pirko ifa = ifa_existing; 991af4d768aSDavid Ahern 992af4d768aSDavid Ahern if (ifa->ifa_rt_priority != new_metric) { 993af4d768aSDavid Ahern fib_modify_prefix_metric(ifa, new_metric); 994af4d768aSDavid Ahern ifa->ifa_rt_priority = new_metric; 995af4d768aSDavid Ahern } 996af4d768aSDavid Ahern 9975c4a9aa8SPetr Machata ifa->ifa_proto = new_proto; 9985c4a9aa8SPetr Machata 99934e2ed34SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 100005a324b9SJiri Pirko cancel_delayed_work(&check_lifetime_work); 1001906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, 1002906e073fSviresh kumar &check_lifetime_work, 0); 100334e2ed34SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 10045c766d64SJiri Pirko } 10055c766d64SJiri Pirko return 0; 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds 10081da177e4SLinus Torvalds /* 10091da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 10101da177e4SLinus Torvalds */ 10111da177e4SLinus Torvalds 101240384999SEric Dumazet static int inet_abc_len(__be32 addr) 10131da177e4SLinus Torvalds { 10141da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 10151da177e4SLinus Torvalds 101665cab850SDave Taht if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr)) 10171da177e4SLinus Torvalds rc = 0; 10181da177e4SLinus Torvalds else { 1019714e85beSAl Viro __u32 haddr = ntohl(addr); 1020714e85beSAl Viro if (IN_CLASSA(haddr)) 10211da177e4SLinus Torvalds rc = 8; 1022714e85beSAl Viro else if (IN_CLASSB(haddr)) 10231da177e4SLinus Torvalds rc = 16; 1024714e85beSAl Viro else if (IN_CLASSC(haddr)) 10251da177e4SLinus Torvalds rc = 24; 102665cab850SDave Taht else if (IN_CLASSE(haddr)) 102765cab850SDave Taht rc = 32; 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds 10301da177e4SLinus Torvalds return rc; 10311da177e4SLinus Torvalds } 10321da177e4SLinus Torvalds 10331da177e4SLinus Torvalds 103403aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr) 10351da177e4SLinus Torvalds { 10361da177e4SLinus Torvalds struct sockaddr_in sin_orig; 103703aef17bSAl Viro struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; 10382638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap = NULL; 10391da177e4SLinus Torvalds struct in_device *in_dev; 10401da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 10411da177e4SLinus Torvalds struct net_device *dev; 10421da177e4SLinus Torvalds char *colon; 10431da177e4SLinus Torvalds int ret = -EFAULT; 10441da177e4SLinus Torvalds int tryaddrmatch = 0; 10451da177e4SLinus Torvalds 104603aef17bSAl Viro ifr->ifr_name[IFNAMSIZ - 1] = 0; 10471da177e4SLinus Torvalds 10481da177e4SLinus Torvalds /* save original address for comparison */ 10491da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 10501da177e4SLinus Torvalds 105103aef17bSAl Viro colon = strchr(ifr->ifr_name, ':'); 10521da177e4SLinus Torvalds if (colon) 10531da177e4SLinus Torvalds *colon = 0; 10541da177e4SLinus Torvalds 105503aef17bSAl Viro dev_load(net, ifr->ifr_name); 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds switch (cmd) { 10581da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 10591da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 10601da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 10611da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 10621da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 10631da177e4SLinus Torvalds so that we do not impose a lock. 10641da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 10651da177e4SLinus Torvalds */ 10661da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 10671da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 10681da177e4SLinus Torvalds sin->sin_family = AF_INET; 10691da177e4SLinus Torvalds break; 10701da177e4SLinus Torvalds 10711da177e4SLinus Torvalds case SIOCSIFFLAGS: 1072bf5b30b8SZhao Hongjiang ret = -EPERM; 107352e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10741da177e4SLinus Torvalds goto out; 10751da177e4SLinus Torvalds break; 10761da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10771da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10781da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 10791da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 1080bf5b30b8SZhao Hongjiang ret = -EPERM; 108152e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10821da177e4SLinus Torvalds goto out; 10831da177e4SLinus Torvalds ret = -EINVAL; 10841da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 10851da177e4SLinus Torvalds goto out; 10861da177e4SLinus Torvalds break; 10871da177e4SLinus Torvalds default: 10881da177e4SLinus Torvalds ret = -EINVAL; 10891da177e4SLinus Torvalds goto out; 10901da177e4SLinus Torvalds } 10911da177e4SLinus Torvalds 10921da177e4SLinus Torvalds rtnl_lock(); 10931da177e4SLinus Torvalds 10941da177e4SLinus Torvalds ret = -ENODEV; 109503aef17bSAl Viro dev = __dev_get_by_name(net, ifr->ifr_name); 10969f9354b9SEric Dumazet if (!dev) 10971da177e4SLinus Torvalds goto done; 10981da177e4SLinus Torvalds 10991da177e4SLinus Torvalds if (colon) 11001da177e4SLinus Torvalds *colon = ':'; 11011da177e4SLinus Torvalds 11029f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 11039f9354b9SEric Dumazet if (in_dev) { 11041da177e4SLinus Torvalds if (tryaddrmatch) { 11051da177e4SLinus Torvalds /* Matthias Andree */ 11061da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 11071da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 11081da177e4SLinus Torvalds and only if the original address family was AF_INET. 11091da177e4SLinus Torvalds This is checked above. */ 11102638eb8bSFlorian Westphal 11112638eb8bSFlorian Westphal for (ifap = &in_dev->ifa_list; 11122638eb8bSFlorian Westphal (ifa = rtnl_dereference(*ifap)) != NULL; 11131da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 111403aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label) && 11151da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 11166c91afe1SDavid S. Miller ifa->ifa_local) { 11171da177e4SLinus Torvalds break; /* found */ 11181da177e4SLinus Torvalds } 11191da177e4SLinus Torvalds } 11201da177e4SLinus Torvalds } 11211da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 11221da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 11231da177e4SLinus Torvalds comparing just the label */ 11241da177e4SLinus Torvalds if (!ifa) { 11252638eb8bSFlorian Westphal for (ifap = &in_dev->ifa_list; 11262638eb8bSFlorian Westphal (ifa = rtnl_dereference(*ifap)) != NULL; 11271da177e4SLinus Torvalds ifap = &ifa->ifa_next) 112803aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label)) 11291da177e4SLinus Torvalds break; 11301da177e4SLinus Torvalds } 11311da177e4SLinus Torvalds } 11321da177e4SLinus Torvalds 11331da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 11341da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 11351da177e4SLinus Torvalds goto done; 11361da177e4SLinus Torvalds 11371da177e4SLinus Torvalds switch (cmd) { 11381da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 113930e948a3STonghao Zhang ret = 0; 11401da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 114103aef17bSAl Viro break; 11421da177e4SLinus Torvalds 11431da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 114430e948a3STonghao Zhang ret = 0; 11451da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 114603aef17bSAl Viro break; 11471da177e4SLinus Torvalds 11481da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 114930e948a3STonghao Zhang ret = 0; 11501da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 115103aef17bSAl Viro break; 11521da177e4SLinus Torvalds 11531da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 115430e948a3STonghao Zhang ret = 0; 11551da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 115603aef17bSAl Viro break; 11571da177e4SLinus Torvalds 11581da177e4SLinus Torvalds case SIOCSIFFLAGS: 11591da177e4SLinus Torvalds if (colon) { 11601da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 11611da177e4SLinus Torvalds if (!ifa) 11621da177e4SLinus Torvalds break; 11631da177e4SLinus Torvalds ret = 0; 116403aef17bSAl Viro if (!(ifr->ifr_flags & IFF_UP)) 11651da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 11661da177e4SLinus Torvalds break; 11671da177e4SLinus Torvalds } 1168567c5e13SPetr Machata ret = dev_change_flags(dev, ifr->ifr_flags, NULL); 11691da177e4SLinus Torvalds break; 11701da177e4SLinus Torvalds 11711da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 11721da177e4SLinus Torvalds ret = -EINVAL; 11731da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 11741da177e4SLinus Torvalds break; 11751da177e4SLinus Torvalds 11761da177e4SLinus Torvalds if (!ifa) { 11771da177e4SLinus Torvalds ret = -ENOBUFS; 11789f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 11799f9354b9SEric Dumazet if (!ifa) 11801da177e4SLinus Torvalds break; 1181c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 11821da177e4SLinus Torvalds if (colon) 118303aef17bSAl Viro memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ); 11841da177e4SLinus Torvalds else 11851da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11861da177e4SLinus Torvalds } else { 11871da177e4SLinus Torvalds ret = 0; 11881da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 11891da177e4SLinus Torvalds break; 11901da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11911da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 1192148f9729SBjorn Mork ifa->ifa_scope = 0; 11931da177e4SLinus Torvalds } 11941da177e4SLinus Torvalds 11951da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 11961da177e4SLinus Torvalds 11971da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 11981da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 11991da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 12001da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 12011da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 12021da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 12031da177e4SLinus Torvalds ~ifa->ifa_mask; 12041da177e4SLinus Torvalds } else { 12051da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 12061da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 12071da177e4SLinus Torvalds } 12085c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 12091da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 12101da177e4SLinus Torvalds break; 12111da177e4SLinus Torvalds 12121da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 12131da177e4SLinus Torvalds ret = 0; 12141da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 12151da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 12161da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 12171da177e4SLinus Torvalds inet_insert_ifa(ifa); 12181da177e4SLinus Torvalds } 12191da177e4SLinus Torvalds break; 12201da177e4SLinus Torvalds 12211da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 12221da177e4SLinus Torvalds ret = 0; 12231da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 12241da177e4SLinus Torvalds break; 12251da177e4SLinus Torvalds ret = -EINVAL; 12261da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 12271da177e4SLinus Torvalds break; 12281da177e4SLinus Torvalds ret = 0; 12291da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 12301da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 12311da177e4SLinus Torvalds inet_insert_ifa(ifa); 12321da177e4SLinus Torvalds break; 12331da177e4SLinus Torvalds 12341da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 12351da177e4SLinus Torvalds 12361da177e4SLinus Torvalds /* 12371da177e4SLinus Torvalds * The mask we set must be legal. 12381da177e4SLinus Torvalds */ 12391da177e4SLinus Torvalds ret = -EINVAL; 12401da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 12411da177e4SLinus Torvalds break; 12421da177e4SLinus Torvalds ret = 0; 12431da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1244a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 12451da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 12461da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 12471da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 12481da177e4SLinus Torvalds 12491da177e4SLinus Torvalds /* See if current broadcast address matches 12501da177e4SLinus Torvalds * with current netmask, then recalculate 12511da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 12521da177e4SLinus Torvalds * funny address, so don't touch it since 12531da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 12541da177e4SLinus Torvalds */ 12551da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 12561da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 12571da177e4SLinus Torvalds (ifa->ifa_broadcast == 1258dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 12591da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 12601da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 12611da177e4SLinus Torvalds } 12621da177e4SLinus Torvalds inet_insert_ifa(ifa); 12631da177e4SLinus Torvalds } 12641da177e4SLinus Torvalds break; 12651da177e4SLinus Torvalds } 12661da177e4SLinus Torvalds done: 12671da177e4SLinus Torvalds rtnl_unlock(); 12681da177e4SLinus Torvalds out: 12691da177e4SLinus Torvalds return ret; 12701da177e4SLinus Torvalds } 12711da177e4SLinus Torvalds 1272b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size) 12731da177e4SLinus Torvalds { 1274e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 1275ef11db33SFlorian Westphal const struct in_ifaddr *ifa; 12761da177e4SLinus Torvalds struct ifreq ifr; 12771da177e4SLinus Torvalds int done = 0; 12781da177e4SLinus Torvalds 127936fd633eSAl Viro if (WARN_ON(size > sizeof(struct ifreq))) 128036fd633eSAl Viro goto out; 128136fd633eSAl Viro 12829f9354b9SEric Dumazet if (!in_dev) 12831da177e4SLinus Torvalds goto out; 12841da177e4SLinus Torvalds 1285ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 12861da177e4SLinus Torvalds if (!buf) { 128736fd633eSAl Viro done += size; 12881da177e4SLinus Torvalds continue; 12891da177e4SLinus Torvalds } 129036fd633eSAl Viro if (len < size) 12911da177e4SLinus Torvalds break; 12921da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 12931da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 12941da177e4SLinus Torvalds 12951da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 12961da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 12971da177e4SLinus Torvalds ifa->ifa_local; 12981da177e4SLinus Torvalds 129936fd633eSAl Viro if (copy_to_user(buf + done, &ifr, size)) { 13001da177e4SLinus Torvalds done = -EFAULT; 13011da177e4SLinus Torvalds break; 13021da177e4SLinus Torvalds } 130336fd633eSAl Viro len -= size; 130436fd633eSAl Viro done += size; 13051da177e4SLinus Torvalds } 13061da177e4SLinus Torvalds out: 13071da177e4SLinus Torvalds return done; 13081da177e4SLinus Torvalds } 13091da177e4SLinus Torvalds 13108b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev, 13118b57fd1eSGao Feng int scope) 13128b57fd1eSGao Feng { 1313d519e870SFlorian Westphal const struct in_ifaddr *ifa; 1314d519e870SFlorian Westphal 1315d519e870SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 1316d519e870SFlorian Westphal if (ifa->ifa_flags & IFA_F_SECONDARY) 1317d519e870SFlorian Westphal continue; 13188b57fd1eSGao Feng if (ifa->ifa_scope != RT_SCOPE_LINK && 13198b57fd1eSGao Feng ifa->ifa_scope <= scope) 13208b57fd1eSGao Feng return ifa->ifa_local; 1321d519e870SFlorian Westphal } 13228b57fd1eSGao Feng 13238b57fd1eSGao Feng return 0; 13248b57fd1eSGao Feng } 13258b57fd1eSGao Feng 1326a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 13271da177e4SLinus Torvalds { 1328d519e870SFlorian Westphal const struct in_ifaddr *ifa; 1329a61ced5dSAl Viro __be32 addr = 0; 1330d8c444d5SShijie Luo unsigned char localnet_scope = RT_SCOPE_HOST; 13311da177e4SLinus Torvalds struct in_device *in_dev; 1332c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 13333f2fb9a8SDavid Ahern int master_idx; 13341da177e4SLinus Torvalds 13351da177e4SLinus Torvalds rcu_read_lock(); 1336e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 13371da177e4SLinus Torvalds if (!in_dev) 13381da177e4SLinus Torvalds goto no_in_dev; 13391da177e4SLinus Torvalds 1340d8c444d5SShijie Luo if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev))) 1341d8c444d5SShijie Luo localnet_scope = RT_SCOPE_LINK; 1342d8c444d5SShijie Luo 1343d519e870SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 1344d519e870SFlorian Westphal if (ifa->ifa_flags & IFA_F_SECONDARY) 1345d519e870SFlorian Westphal continue; 1346d8c444d5SShijie Luo if (min(ifa->ifa_scope, localnet_scope) > scope) 13471da177e4SLinus Torvalds continue; 13481da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 13491da177e4SLinus Torvalds addr = ifa->ifa_local; 13501da177e4SLinus Torvalds break; 13511da177e4SLinus Torvalds } 13521da177e4SLinus Torvalds if (!addr) 13531da177e4SLinus Torvalds addr = ifa->ifa_local; 1354d519e870SFlorian Westphal } 13551da177e4SLinus Torvalds 13561da177e4SLinus Torvalds if (addr) 1357c6d14c84SEric Dumazet goto out_unlock; 13589f9354b9SEric Dumazet no_in_dev: 13593f2fb9a8SDavid Ahern master_idx = l3mdev_master_ifindex_rcu(dev); 13601da177e4SLinus Torvalds 136117b693cdSDavid Lamparter /* For VRFs, the VRF device takes the place of the loopback device, 136217b693cdSDavid Lamparter * with addresses on it being preferred. Note in such cases the 136317b693cdSDavid Lamparter * loopback device will be among the devices that fail the master_idx 136417b693cdSDavid Lamparter * equality check in the loop below. 136517b693cdSDavid Lamparter */ 136617b693cdSDavid Lamparter if (master_idx && 136717b693cdSDavid Lamparter (dev = dev_get_by_index_rcu(net, master_idx)) && 136817b693cdSDavid Lamparter (in_dev = __in_dev_get_rcu(dev))) { 13698b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 13708b57fd1eSGao Feng if (addr) 137117b693cdSDavid Lamparter goto out_unlock; 137217b693cdSDavid Lamparter } 137317b693cdSDavid Lamparter 13741da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 1375ca9f1fd2SStephen Hemminger in this case. It is important that lo is the first interface 13761da177e4SLinus Torvalds in dev_base list. 13771da177e4SLinus Torvalds */ 1378c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 13793f2fb9a8SDavid Ahern if (l3mdev_master_ifindex_rcu(dev) != master_idx) 13803f2fb9a8SDavid Ahern continue; 13813f2fb9a8SDavid Ahern 13829f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 13839f9354b9SEric Dumazet if (!in_dev) 13841da177e4SLinus Torvalds continue; 13851da177e4SLinus Torvalds 13868b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 13878b57fd1eSGao Feng if (addr) 1388c6d14c84SEric Dumazet goto out_unlock; 13891da177e4SLinus Torvalds } 1390c6d14c84SEric Dumazet out_unlock: 13911da177e4SLinus Torvalds rcu_read_unlock(); 13921da177e4SLinus Torvalds return addr; 13931da177e4SLinus Torvalds } 13949f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 13951da177e4SLinus Torvalds 139660cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 139760cad5daSAl Viro __be32 local, int scope) 13981da177e4SLinus Torvalds { 1399650638a7SShijie Luo unsigned char localnet_scope = RT_SCOPE_HOST; 1400ef11db33SFlorian Westphal const struct in_ifaddr *ifa; 1401a144ea4bSAl Viro __be32 addr = 0; 1402ef11db33SFlorian Westphal int same = 0; 14031da177e4SLinus Torvalds 1404650638a7SShijie Luo if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev))) 1405650638a7SShijie Luo localnet_scope = RT_SCOPE_LINK; 1406650638a7SShijie Luo 1407ef11db33SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 1408650638a7SShijie Luo unsigned char min_scope = min(ifa->ifa_scope, localnet_scope); 1409650638a7SShijie Luo 14101da177e4SLinus Torvalds if (!addr && 14111da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 1412650638a7SShijie Luo min_scope <= scope) { 14131da177e4SLinus Torvalds addr = ifa->ifa_local; 14141da177e4SLinus Torvalds if (same) 14151da177e4SLinus Torvalds break; 14161da177e4SLinus Torvalds } 14171da177e4SLinus Torvalds if (!same) { 14181da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 14191da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 14201da177e4SLinus Torvalds if (same && addr) { 14211da177e4SLinus Torvalds if (local || !dst) 14221da177e4SLinus Torvalds break; 14231da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 14241da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 14251da177e4SLinus Torvalds break; 14261da177e4SLinus Torvalds /* No, then can we use new local src? */ 1427650638a7SShijie Luo if (min_scope <= scope) { 14281da177e4SLinus Torvalds addr = ifa->ifa_local; 14291da177e4SLinus Torvalds break; 14301da177e4SLinus Torvalds } 14311da177e4SLinus Torvalds /* search for large dst subnet for addr */ 14321da177e4SLinus Torvalds same = 0; 14331da177e4SLinus Torvalds } 14341da177e4SLinus Torvalds } 1435ef11db33SFlorian Westphal } 14361da177e4SLinus Torvalds 14371da177e4SLinus Torvalds return same ? addr : 0; 14381da177e4SLinus Torvalds } 14391da177e4SLinus Torvalds 14401da177e4SLinus Torvalds /* 14411da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 1442b601fa19SNicolas Dichtel * - net: netns to check, cannot be NULL 1443b601fa19SNicolas Dichtel * - in_dev: only on this interface, NULL=any interface 14441da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 14451da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 14461da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 14471da177e4SLinus Torvalds */ 1448b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, 14499bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 14501da177e4SLinus Torvalds { 145160cad5daSAl Viro __be32 addr = 0; 14529bd85e32SDenis V. Lunev struct net_device *dev; 14531da177e4SLinus Torvalds 145400db4124SIan Morris if (in_dev) 14559bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 14561da177e4SLinus Torvalds 14571da177e4SLinus Torvalds rcu_read_lock(); 1458c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 14599f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 14609f9354b9SEric Dumazet if (in_dev) { 14611da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 14621da177e4SLinus Torvalds if (addr) 14631da177e4SLinus Torvalds break; 14641da177e4SLinus Torvalds } 14651da177e4SLinus Torvalds } 14661da177e4SLinus Torvalds rcu_read_unlock(); 14671da177e4SLinus Torvalds 14681da177e4SLinus Torvalds return addr; 14691da177e4SLinus Torvalds } 1470eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 14711da177e4SLinus Torvalds 14721da177e4SLinus Torvalds /* 14731da177e4SLinus Torvalds * Device notifier 14741da177e4SLinus Torvalds */ 14751da177e4SLinus Torvalds 14761da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 14771da177e4SLinus Torvalds { 1478e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 14791da177e4SLinus Torvalds } 14809f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 14811da177e4SLinus Torvalds 14821da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 14831da177e4SLinus Torvalds { 1484e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 14851da177e4SLinus Torvalds } 14869f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 14871da177e4SLinus Torvalds 14883ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb) 14893ad7d246SKrister Johansen { 14903ad7d246SKrister Johansen return blocking_notifier_chain_register(&inetaddr_validator_chain, nb); 14913ad7d246SKrister Johansen } 14923ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier); 14933ad7d246SKrister Johansen 14943ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb) 14953ad7d246SKrister Johansen { 14963ad7d246SKrister Johansen return blocking_notifier_chain_unregister(&inetaddr_validator_chain, 14973ad7d246SKrister Johansen nb); 14983ad7d246SKrister Johansen } 14993ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier); 15003ad7d246SKrister Johansen 15019f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 15029f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 15031da177e4SLinus Torvalds */ 15041da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 15051da177e4SLinus Torvalds { 15061da177e4SLinus Torvalds struct in_ifaddr *ifa; 15071da177e4SLinus Torvalds int named = 0; 15081da177e4SLinus Torvalds 1509ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 15101da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 15111da177e4SLinus Torvalds 15121da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 15131da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 15141da177e4SLinus Torvalds if (named++ == 0) 1515573bf470SThomas Graf goto skip; 151644344b2aSMark McLoughlin dot = strchr(old, ':'); 151751456b29SIan Morris if (!dot) { 15181da177e4SLinus Torvalds sprintf(old, ":%d", named); 15191da177e4SLinus Torvalds dot = old; 15201da177e4SLinus Torvalds } 15219f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 15221da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 15239f9354b9SEric Dumazet else 15241da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1525573bf470SThomas Graf skip: 1526573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 15271da177e4SLinus Torvalds } 15281da177e4SLinus Torvalds } 15291da177e4SLinus Torvalds 1530d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1531d11327adSIan Campbell struct in_device *in_dev) 1532d11327adSIan Campbell 1533d11327adSIan Campbell { 1534ef11db33SFlorian Westphal const struct in_ifaddr *ifa; 1535d11327adSIan Campbell 1536ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 1537d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 15386c91afe1SDavid S. Miller ifa->ifa_local, dev, 15396c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1540d11327adSIan Campbell dev->dev_addr, NULL); 1541d11327adSIan Campbell } 1542b76d0789SZoltan Kiss } 1543d11327adSIan Campbell 15441da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 15451da177e4SLinus Torvalds 15461da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 15471da177e4SLinus Torvalds void *ptr) 15481da177e4SLinus Torvalds { 1549351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1550748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 15511da177e4SLinus Torvalds 15521da177e4SLinus Torvalds ASSERT_RTNL(); 15531da177e4SLinus Torvalds 15541da177e4SLinus Torvalds if (!in_dev) { 15558030f544SHerbert Xu if (event == NETDEV_REGISTER) { 15561da177e4SLinus Torvalds in_dev = inetdev_init(dev); 155720e61da7SWANG Cong if (IS_ERR(in_dev)) 155820e61da7SWANG Cong return notifier_from_errno(PTR_ERR(in_dev)); 15590cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 156042f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 156142f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 15621da177e4SLinus Torvalds } 156306770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 156406770843SBreno Leitao /* Re-enabling IP */ 156506770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 156606770843SBreno Leitao in_dev = inetdev_init(dev); 15678030f544SHerbert Xu } 15681da177e4SLinus Torvalds goto out; 15691da177e4SLinus Torvalds } 15701da177e4SLinus Torvalds 15711da177e4SLinus Torvalds switch (event) { 15721da177e4SLinus Torvalds case NETDEV_REGISTER: 157391df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1574a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 15751da177e4SLinus Torvalds break; 15761da177e4SLinus Torvalds case NETDEV_UP: 157706770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 15781da177e4SLinus Torvalds break; 15790cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 15809f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 15819f9354b9SEric Dumazet 15829f9354b9SEric Dumazet if (ifa) { 1583fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 15841da177e4SLinus Torvalds ifa->ifa_local = 15851da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 15861da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 15871da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 15881da177e4SLinus Torvalds in_dev_hold(in_dev); 15891da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 15901da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 15911da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 15925c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 15935c766d64SJiri Pirko INFINITY_LIFE_TIME); 1594dfd1582dSJiri Pirko ipv4_devconf_setall(in_dev); 1595dfd1582dSJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 15961da177e4SLinus Torvalds inet_insert_ifa(ifa); 15971da177e4SLinus Torvalds } 15981da177e4SLinus Torvalds } 15991da177e4SLinus Torvalds ip_mc_up(in_dev); 1600a8eceea8SJoe Perches fallthrough; 1601eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1602d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1603d11327adSIan Campbell break; 1604a8eceea8SJoe Perches fallthrough; 1605d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1606a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1607d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 16081da177e4SLinus Torvalds break; 16091da177e4SLinus Torvalds case NETDEV_DOWN: 16101da177e4SLinus Torvalds ip_mc_down(in_dev); 16111da177e4SLinus Torvalds break; 161293d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 161375c78500SMoni Shoua ip_mc_unmap(in_dev); 161475c78500SMoni Shoua break; 161593d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 161675c78500SMoni Shoua ip_mc_remap(in_dev); 161775c78500SMoni Shoua break; 16181da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 161906770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 16201da177e4SLinus Torvalds break; 162106770843SBreno Leitao /* disable IP when MTU is not enough */ 1622a8eceea8SJoe Perches fallthrough; 16231da177e4SLinus Torvalds case NETDEV_UNREGISTER: 16241da177e4SLinus Torvalds inetdev_destroy(in_dev); 16251da177e4SLinus Torvalds break; 16261da177e4SLinus Torvalds case NETDEV_CHANGENAME: 16271da177e4SLinus Torvalds /* Do not notify about label change, this event is 16281da177e4SLinus Torvalds * not interesting to applications using netlink. 16291da177e4SLinus Torvalds */ 16301da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 16311da177e4SLinus Torvalds 163251602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 163366f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 16341da177e4SLinus Torvalds break; 16351da177e4SLinus Torvalds } 16361da177e4SLinus Torvalds out: 16371da177e4SLinus Torvalds return NOTIFY_DONE; 16381da177e4SLinus Torvalds } 16391da177e4SLinus Torvalds 16401da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 16411da177e4SLinus Torvalds .notifier_call = inetdev_event, 16421da177e4SLinus Torvalds }; 16431da177e4SLinus Torvalds 164440384999SEric Dumazet static size_t inet_nlmsg_size(void) 1645339bf98fSThomas Graf { 1646339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1647339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1648339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1649339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1650ad6c8135SJiri Pirko + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ 165163b5f152SGeert Uytterhoeven + nla_total_size(4) /* IFA_FLAGS */ 165247f0bd50SJacques de Laval + nla_total_size(1) /* IFA_PROTO */ 1653af4d768aSDavid Ahern + nla_total_size(4) /* IFA_RT_PRIORITY */ 165463b5f152SGeert Uytterhoeven + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ 1655339bf98fSThomas Graf } 1656339bf98fSThomas Graf 16575c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 16585c766d64SJiri Pirko { 16595c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 16605c766d64SJiri Pirko } 16615c766d64SJiri Pirko 16625c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 16635c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 16645c766d64SJiri Pirko { 16655c766d64SJiri Pirko struct ifa_cacheinfo ci; 16665c766d64SJiri Pirko 16675c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 16685c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 16695c766d64SJiri Pirko ci.ifa_prefered = preferred; 16705c766d64SJiri Pirko ci.ifa_valid = valid; 16715c766d64SJiri Pirko 16725c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 16735c766d64SJiri Pirko } 16745c766d64SJiri Pirko 16751da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 1676978a46faSChristian Brauner struct inet_fill_args *args) 16771da177e4SLinus Torvalds { 16781da177e4SLinus Torvalds struct ifaddrmsg *ifm; 16791da177e4SLinus Torvalds struct nlmsghdr *nlh; 1680*3cd3e72cSEric Dumazet unsigned long tstamp; 16815c766d64SJiri Pirko u32 preferred, valid; 16821da177e4SLinus Torvalds 1683978a46faSChristian Brauner nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm), 1684978a46faSChristian Brauner args->flags); 168551456b29SIan Morris if (!nlh) 168626932566SPatrick McHardy return -EMSGSIZE; 168747f68512SThomas Graf 168847f68512SThomas Graf ifm = nlmsg_data(nlh); 16891da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 16901da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 16915c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 16921da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 16931da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 16941da177e4SLinus Torvalds 1695978a46faSChristian Brauner if (args->netnsid >= 0 && 1696978a46faSChristian Brauner nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) 1697d3807145SChristian Brauner goto nla_put_failure; 1698d3807145SChristian Brauner 1699*3cd3e72cSEric Dumazet tstamp = READ_ONCE(ifa->ifa_tstamp); 17005c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 17015c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 17025c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 17035c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 1704*3cd3e72cSEric Dumazet long tval = (jiffies - tstamp) / HZ; 17055c766d64SJiri Pirko 17065c766d64SJiri Pirko if (preferred > tval) 17075c766d64SJiri Pirko preferred -= tval; 17085c766d64SJiri Pirko else 17095c766d64SJiri Pirko preferred = 0; 17105c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 17115c766d64SJiri Pirko if (valid > tval) 17125c766d64SJiri Pirko valid -= tval; 17135c766d64SJiri Pirko else 17145c766d64SJiri Pirko valid = 0; 17155c766d64SJiri Pirko } 17165c766d64SJiri Pirko } 17175c766d64SJiri Pirko } else { 17185c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 17195c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 17205c766d64SJiri Pirko } 1721f3756b79SDavid S. Miller if ((ifa->ifa_address && 1722930345eaSJiri Benc nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) || 1723f3756b79SDavid S. Miller (ifa->ifa_local && 1724930345eaSJiri Benc nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) || 1725f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1726930345eaSJiri Benc nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1727f3756b79SDavid S. Miller (ifa->ifa_label[0] && 17285c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 172947f0bd50SJacques de Laval (ifa->ifa_proto && 173047f0bd50SJacques de Laval nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) || 1731ad6c8135SJiri Pirko nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || 1732af4d768aSDavid Ahern (ifa->ifa_rt_priority && 1733af4d768aSDavid Ahern nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) || 1734*3cd3e72cSEric Dumazet put_cacheinfo(skb, READ_ONCE(ifa->ifa_cstamp), tstamp, 17355c766d64SJiri Pirko preferred, valid)) 1736f3756b79SDavid S. Miller goto nla_put_failure; 173747f68512SThomas Graf 1738053c095aSJohannes Berg nlmsg_end(skb, nlh); 1739053c095aSJohannes Berg return 0; 174047f68512SThomas Graf 174147f68512SThomas Graf nla_put_failure: 174226932566SPatrick McHardy nlmsg_cancel(skb, nlh); 174326932566SPatrick McHardy return -EMSGSIZE; 17441da177e4SLinus Torvalds } 17451da177e4SLinus Torvalds 1746c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh, 1747c33078e3SDavid Ahern struct inet_fill_args *fillargs, 1748c33078e3SDavid Ahern struct net **tgt_net, struct sock *sk, 17495fcd266aSDavid Ahern struct netlink_callback *cb) 1750c33078e3SDavid Ahern { 17515fcd266aSDavid Ahern struct netlink_ext_ack *extack = cb->extack; 1752c33078e3SDavid Ahern struct nlattr *tb[IFA_MAX+1]; 1753c33078e3SDavid Ahern struct ifaddrmsg *ifm; 1754c33078e3SDavid Ahern int err, i; 1755c33078e3SDavid Ahern 1756c33078e3SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 1757c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request"); 1758c33078e3SDavid Ahern return -EINVAL; 1759c33078e3SDavid Ahern } 1760c33078e3SDavid Ahern 1761c33078e3SDavid Ahern ifm = nlmsg_data(nlh); 1762c33078e3SDavid Ahern if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { 1763c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request"); 1764c33078e3SDavid Ahern return -EINVAL; 1765c33078e3SDavid Ahern } 17665fcd266aSDavid Ahern 17675fcd266aSDavid Ahern fillargs->ifindex = ifm->ifa_index; 17685fcd266aSDavid Ahern if (fillargs->ifindex) { 17695fcd266aSDavid Ahern cb->answer_flags |= NLM_F_DUMP_FILTERED; 17705fcd266aSDavid Ahern fillargs->flags |= NLM_F_DUMP_FILTERED; 1771c33078e3SDavid Ahern } 1772c33078e3SDavid Ahern 17738cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX, 1774c33078e3SDavid Ahern ifa_ipv4_policy, extack); 1775c33078e3SDavid Ahern if (err < 0) 1776c33078e3SDavid Ahern return err; 1777c33078e3SDavid Ahern 1778c33078e3SDavid Ahern for (i = 0; i <= IFA_MAX; ++i) { 1779c33078e3SDavid Ahern if (!tb[i]) 1780c33078e3SDavid Ahern continue; 1781c33078e3SDavid Ahern 1782c33078e3SDavid Ahern if (i == IFA_TARGET_NETNSID) { 1783c33078e3SDavid Ahern struct net *net; 1784c33078e3SDavid Ahern 1785c33078e3SDavid Ahern fillargs->netnsid = nla_get_s32(tb[i]); 1786c33078e3SDavid Ahern 1787c33078e3SDavid Ahern net = rtnl_get_net_ns_capable(sk, fillargs->netnsid); 1788c33078e3SDavid Ahern if (IS_ERR(net)) { 1789bf4cc40eSBjørn Mork fillargs->netnsid = -1; 1790c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id"); 1791c33078e3SDavid Ahern return PTR_ERR(net); 1792c33078e3SDavid Ahern } 1793c33078e3SDavid Ahern *tgt_net = net; 1794c33078e3SDavid Ahern } else { 1795c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request"); 1796c33078e3SDavid Ahern return -EINVAL; 1797c33078e3SDavid Ahern } 1798c33078e3SDavid Ahern } 1799c33078e3SDavid Ahern 1800c33078e3SDavid Ahern return 0; 1801c33078e3SDavid Ahern } 1802c33078e3SDavid Ahern 18031c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb, 18041c98eca4SDavid Ahern struct netlink_callback *cb, int s_ip_idx, 18051c98eca4SDavid Ahern struct inet_fill_args *fillargs) 18061c98eca4SDavid Ahern { 18071c98eca4SDavid Ahern struct in_ifaddr *ifa; 18081c98eca4SDavid Ahern int ip_idx = 0; 18091c98eca4SDavid Ahern int err; 18101c98eca4SDavid Ahern 1811d3e6e285SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 1812ef11db33SFlorian Westphal if (ip_idx < s_ip_idx) { 1813ef11db33SFlorian Westphal ip_idx++; 18141c98eca4SDavid Ahern continue; 1815ef11db33SFlorian Westphal } 18161c98eca4SDavid Ahern err = inet_fill_ifaddr(skb, ifa, fillargs); 18171c98eca4SDavid Ahern if (err < 0) 18181c98eca4SDavid Ahern goto done; 18191c98eca4SDavid Ahern 18201c98eca4SDavid Ahern nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1821ef11db33SFlorian Westphal ip_idx++; 18221c98eca4SDavid Ahern } 18231c98eca4SDavid Ahern err = 0; 18241c98eca4SDavid Ahern 18251c98eca4SDavid Ahern done: 18261c98eca4SDavid Ahern cb->args[2] = ip_idx; 18271c98eca4SDavid Ahern 18281c98eca4SDavid Ahern return err; 18291c98eca4SDavid Ahern } 18301c98eca4SDavid Ahern 1831081a0e3bSEric Dumazet /* Combine dev_addr_genid and dev_base_seq to detect changes. 1832081a0e3bSEric Dumazet */ 1833081a0e3bSEric Dumazet static u32 inet_base_seq(const struct net *net) 1834081a0e3bSEric Dumazet { 1835081a0e3bSEric Dumazet u32 res = atomic_read(&net->ipv4.dev_addr_genid) + 1836081a0e3bSEric Dumazet net->dev_base_seq; 1837081a0e3bSEric Dumazet 1838081a0e3bSEric Dumazet /* Must not return 0 (see nl_dump_check_consistent()). 1839081a0e3bSEric Dumazet * Chose a value far away from 0. 1840081a0e3bSEric Dumazet */ 1841081a0e3bSEric Dumazet if (!res) 1842081a0e3bSEric Dumazet res = 0x80000000; 1843081a0e3bSEric Dumazet return res; 1844081a0e3bSEric Dumazet } 1845081a0e3bSEric Dumazet 18461da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 18471da177e4SLinus Torvalds { 1848c33078e3SDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 1849978a46faSChristian Brauner struct inet_fill_args fillargs = { 1850978a46faSChristian Brauner .portid = NETLINK_CB(cb->skb).portid, 1851c33078e3SDavid Ahern .seq = nlh->nlmsg_seq, 1852978a46faSChristian Brauner .event = RTM_NEWADDR, 1853978a46faSChristian Brauner .flags = NLM_F_MULTI, 1854978a46faSChristian Brauner .netnsid = -1, 1855978a46faSChristian Brauner }; 18563b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1857d3807145SChristian Brauner struct net *tgt_net = net; 1858eec4df98SEric Dumazet int h, s_h; 1859eec4df98SEric Dumazet int idx, s_idx; 18601c98eca4SDavid Ahern int s_ip_idx; 18611da177e4SLinus Torvalds struct net_device *dev; 18621da177e4SLinus Torvalds struct in_device *in_dev; 1863eec4df98SEric Dumazet struct hlist_head *head; 1864d7e38611SDavid Ahern int err = 0; 18651da177e4SLinus Torvalds 1866eec4df98SEric Dumazet s_h = cb->args[0]; 1867eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 18681c98eca4SDavid Ahern s_ip_idx = cb->args[2]; 1869eec4df98SEric Dumazet 1870c33078e3SDavid Ahern if (cb->strict_check) { 1871c33078e3SDavid Ahern err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 18725fcd266aSDavid Ahern skb->sk, cb); 1873c33078e3SDavid Ahern if (err < 0) 1874d7e38611SDavid Ahern goto put_tgt_net; 18755fcd266aSDavid Ahern 1876d7e38611SDavid Ahern err = 0; 18775fcd266aSDavid Ahern if (fillargs.ifindex) { 18785fcd266aSDavid Ahern dev = __dev_get_by_index(tgt_net, fillargs.ifindex); 1879d7e38611SDavid Ahern if (!dev) { 1880d7e38611SDavid Ahern err = -ENODEV; 1881d7e38611SDavid Ahern goto put_tgt_net; 1882d7e38611SDavid Ahern } 18835fcd266aSDavid Ahern 18845fcd266aSDavid Ahern in_dev = __in_dev_get_rtnl(dev); 18855fcd266aSDavid Ahern if (in_dev) { 18865fcd266aSDavid Ahern err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx, 18875fcd266aSDavid Ahern &fillargs); 18885fcd266aSDavid Ahern } 18895fcd266aSDavid Ahern goto put_tgt_net; 18905fcd266aSDavid Ahern } 1891d3807145SChristian Brauner } 1892d3807145SChristian Brauner 1893eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 18947562f876SPavel Emelianov idx = 0; 1895d3807145SChristian Brauner head = &tgt_net->dev_index_head[h]; 1896eec4df98SEric Dumazet rcu_read_lock(); 1897081a0e3bSEric Dumazet cb->seq = inet_base_seq(tgt_net); 1898b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 18991da177e4SLinus Torvalds if (idx < s_idx) 19007562f876SPavel Emelianov goto cont; 19014b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 19021da177e4SLinus Torvalds s_ip_idx = 0; 1903eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 19049f9354b9SEric Dumazet if (!in_dev) 19057562f876SPavel Emelianov goto cont; 19061da177e4SLinus Torvalds 19071c98eca4SDavid Ahern err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx, 19081c98eca4SDavid Ahern &fillargs); 19091c98eca4SDavid Ahern if (err < 0) { 1910eec4df98SEric Dumazet rcu_read_unlock(); 19111da177e4SLinus Torvalds goto done; 19121da177e4SLinus Torvalds } 19137562f876SPavel Emelianov cont: 19147562f876SPavel Emelianov idx++; 19151da177e4SLinus Torvalds } 1916eec4df98SEric Dumazet rcu_read_unlock(); 1917eec4df98SEric Dumazet } 19181da177e4SLinus Torvalds 19191da177e4SLinus Torvalds done: 1920eec4df98SEric Dumazet cb->args[0] = h; 1921eec4df98SEric Dumazet cb->args[1] = idx; 19225fcd266aSDavid Ahern put_tgt_net: 1923978a46faSChristian Brauner if (fillargs.netnsid >= 0) 1924d3807145SChristian Brauner put_net(tgt_net); 19251da177e4SLinus Torvalds 19267c1e8a38SArthur Gautier return skb->len ? : err; 19271da177e4SLinus Torvalds } 19281da177e4SLinus Torvalds 1929d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 193015e47304SEric W. Biederman u32 portid) 19311da177e4SLinus Torvalds { 1932978a46faSChristian Brauner struct inet_fill_args fillargs = { 1933978a46faSChristian Brauner .portid = portid, 1934978a46faSChristian Brauner .seq = nlh ? nlh->nlmsg_seq : 0, 1935978a46faSChristian Brauner .event = event, 1936978a46faSChristian Brauner .flags = 0, 1937978a46faSChristian Brauner .netnsid = -1, 1938978a46faSChristian Brauner }; 193947f68512SThomas Graf struct sk_buff *skb; 1940d6062cbbSThomas Graf int err = -ENOBUFS; 19414b8aa9abSDenis V. Lunev struct net *net; 19421da177e4SLinus Torvalds 1943c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1944339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 194551456b29SIan Morris if (!skb) 1946d6062cbbSThomas Graf goto errout; 1947d6062cbbSThomas Graf 1948978a46faSChristian Brauner err = inet_fill_ifaddr(skb, ifa, &fillargs); 194926932566SPatrick McHardy if (err < 0) { 195026932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 195126932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 195226932566SPatrick McHardy kfree_skb(skb); 195326932566SPatrick McHardy goto errout; 195426932566SPatrick McHardy } 195515e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 19561ce85fe4SPablo Neira Ayuso return; 1957d6062cbbSThomas Graf errout: 1958d6062cbbSThomas Graf if (err < 0) 19594b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 19601da177e4SLinus Torvalds } 19611da177e4SLinus Torvalds 1962b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev, 1963b1974ed0SArad, Ronen u32 ext_filter_mask) 19649f0f7272SThomas Graf { 19651fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 19669f0f7272SThomas Graf 19679f0f7272SThomas Graf if (!in_dev) 19689f0f7272SThomas Graf return 0; 19699f0f7272SThomas Graf 19709f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 19719f0f7272SThomas Graf } 19729f0f7272SThomas Graf 1973d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev, 1974d5566fd7SSowmini Varadhan u32 ext_filter_mask) 19759f0f7272SThomas Graf { 19761fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 19779f0f7272SThomas Graf struct nlattr *nla; 19789f0f7272SThomas Graf int i; 19799f0f7272SThomas Graf 19809f0f7272SThomas Graf if (!in_dev) 19819f0f7272SThomas Graf return -ENODATA; 19829f0f7272SThomas Graf 19839f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 198451456b29SIan Morris if (!nla) 19859f0f7272SThomas Graf return -EMSGSIZE; 19869f0f7272SThomas Graf 19879f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 19880598f8f3SEric Dumazet ((u32 *) nla_data(nla))[i] = READ_ONCE(in_dev->cnf.data[i]); 19899f0f7272SThomas Graf 19909f0f7272SThomas Graf return 0; 19919f0f7272SThomas Graf } 19929f0f7272SThomas Graf 19939f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 19949f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 19959f0f7272SThomas Graf }; 19969f0f7272SThomas Graf 1997cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 19988679c31eSRocco Yue const struct nlattr *nla, 19998679c31eSRocco Yue struct netlink_ext_ack *extack) 20009f0f7272SThomas Graf { 20019f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 20029f0f7272SThomas Graf int err, rem; 20039f0f7272SThomas Graf 2004a100243dSCong Wang if (dev && !__in_dev_get_rtnl(dev)) 2005cf7afbfeSThomas Graf return -EAFNOSUPPORT; 20069f0f7272SThomas Graf 20078cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, 20088679c31eSRocco Yue inet_af_policy, extack); 20099f0f7272SThomas Graf if (err < 0) 20109f0f7272SThomas Graf return err; 20119f0f7272SThomas Graf 20129f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 20139f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 20149f0f7272SThomas Graf int cfgid = nla_type(a); 20159f0f7272SThomas Graf 20169f0f7272SThomas Graf if (nla_len(a) < 4) 20179f0f7272SThomas Graf return -EINVAL; 20189f0f7272SThomas Graf 20199f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 20209f0f7272SThomas Graf return -EINVAL; 20219f0f7272SThomas Graf } 20229f0f7272SThomas Graf } 20239f0f7272SThomas Graf 2024cf7afbfeSThomas Graf return 0; 2025cf7afbfeSThomas Graf } 2026cf7afbfeSThomas Graf 20273583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla, 20283583a4e8SStephen Hemminger struct netlink_ext_ack *extack) 2029cf7afbfeSThomas Graf { 2030a100243dSCong Wang struct in_device *in_dev = __in_dev_get_rtnl(dev); 2031cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 2032cf7afbfeSThomas Graf int rem; 2033cf7afbfeSThomas Graf 2034cf7afbfeSThomas Graf if (!in_dev) 2035cf7afbfeSThomas Graf return -EAFNOSUPPORT; 2036cf7afbfeSThomas Graf 20378cb08174SJohannes Berg if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0) 20385ac6b198SZheng Yongjun return -EINVAL; 2039cf7afbfeSThomas Graf 20409f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 20419f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 20429f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 20439f0f7272SThomas Graf } 20449f0f7272SThomas Graf 20459f0f7272SThomas Graf return 0; 20469f0f7272SThomas Graf } 20479f0f7272SThomas Graf 2048edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 2049edc9e748SNicolas Dichtel { 2050edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 2051edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 2052136ba622SZhang Shengju bool all = false; 2053edc9e748SNicolas Dichtel 2054136ba622SZhang Shengju if (type == NETCONFA_ALL) 2055136ba622SZhang Shengju all = true; 2056136ba622SZhang Shengju 2057136ba622SZhang Shengju if (all || type == NETCONFA_FORWARDING) 2058edc9e748SNicolas Dichtel size += nla_total_size(4); 2059136ba622SZhang Shengju if (all || type == NETCONFA_RP_FILTER) 2060cc535dfbSNicolas Dichtel size += nla_total_size(4); 2061136ba622SZhang Shengju if (all || type == NETCONFA_MC_FORWARDING) 2062d67b8c61SNicolas Dichtel size += nla_total_size(4); 20635cbf777cSXin Long if (all || type == NETCONFA_BC_FORWARDING) 20645cbf777cSXin Long size += nla_total_size(4); 2065136ba622SZhang Shengju if (all || type == NETCONFA_PROXY_NEIGH) 2066f085ff1cSstephen hemminger size += nla_total_size(4); 2067136ba622SZhang Shengju if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) 2068974d7af5SAndy Gospodarek size += nla_total_size(4); 2069edc9e748SNicolas Dichtel 2070edc9e748SNicolas Dichtel return size; 2071edc9e748SNicolas Dichtel } 2072edc9e748SNicolas Dichtel 2073edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 20740598f8f3SEric Dumazet const struct ipv4_devconf *devconf, 20750598f8f3SEric Dumazet u32 portid, u32 seq, int event, 20760598f8f3SEric Dumazet unsigned int flags, int type) 2077edc9e748SNicolas Dichtel { 2078edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 2079edc9e748SNicolas Dichtel struct netconfmsg *ncm; 2080136ba622SZhang Shengju bool all = false; 2081edc9e748SNicolas Dichtel 2082edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 2083edc9e748SNicolas Dichtel flags); 208451456b29SIan Morris if (!nlh) 2085edc9e748SNicolas Dichtel return -EMSGSIZE; 2086edc9e748SNicolas Dichtel 2087136ba622SZhang Shengju if (type == NETCONFA_ALL) 2088136ba622SZhang Shengju all = true; 2089136ba622SZhang Shengju 2090edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 2091edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 2092edc9e748SNicolas Dichtel 2093edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 2094edc9e748SNicolas Dichtel goto nla_put_failure; 2095edc9e748SNicolas Dichtel 2096b5c9641dSDavid Ahern if (!devconf) 2097b5c9641dSDavid Ahern goto out; 2098b5c9641dSDavid Ahern 2099136ba622SZhang Shengju if ((all || type == NETCONFA_FORWARDING) && 2100edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 21010598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, FORWARDING)) < 0) 2102edc9e748SNicolas Dichtel goto nla_put_failure; 2103136ba622SZhang Shengju if ((all || type == NETCONFA_RP_FILTER) && 2104cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 21050598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, RP_FILTER)) < 0) 2106cc535dfbSNicolas Dichtel goto nla_put_failure; 2107136ba622SZhang Shengju if ((all || type == NETCONFA_MC_FORWARDING) && 2108d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 21090598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, MC_FORWARDING)) < 0) 2110d67b8c61SNicolas Dichtel goto nla_put_failure; 21115cbf777cSXin Long if ((all || type == NETCONFA_BC_FORWARDING) && 21125cbf777cSXin Long nla_put_s32(skb, NETCONFA_BC_FORWARDING, 21130598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, BC_FORWARDING)) < 0) 21145cbf777cSXin Long goto nla_put_failure; 2115136ba622SZhang Shengju if ((all || type == NETCONFA_PROXY_NEIGH) && 211609aea5dfSstephen hemminger nla_put_s32(skb, NETCONFA_PROXY_NEIGH, 21170598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, PROXY_ARP)) < 0) 2118f085ff1cSstephen hemminger goto nla_put_failure; 2119136ba622SZhang Shengju if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) && 2120974d7af5SAndy Gospodarek nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 21210598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, 21220598f8f3SEric Dumazet IGNORE_ROUTES_WITH_LINKDOWN)) < 0) 2123974d7af5SAndy Gospodarek goto nla_put_failure; 2124edc9e748SNicolas Dichtel 2125b5c9641dSDavid Ahern out: 2126053c095aSJohannes Berg nlmsg_end(skb, nlh); 2127053c095aSJohannes Berg return 0; 2128edc9e748SNicolas Dichtel 2129edc9e748SNicolas Dichtel nla_put_failure: 2130edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 2131edc9e748SNicolas Dichtel return -EMSGSIZE; 2132edc9e748SNicolas Dichtel } 2133edc9e748SNicolas Dichtel 21343b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type, 21353b022865SDavid Ahern int ifindex, struct ipv4_devconf *devconf) 2136edc9e748SNicolas Dichtel { 2137edc9e748SNicolas Dichtel struct sk_buff *skb; 2138edc9e748SNicolas Dichtel int err = -ENOBUFS; 2139edc9e748SNicolas Dichtel 2140fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL); 214151456b29SIan Morris if (!skb) 2142edc9e748SNicolas Dichtel goto errout; 2143edc9e748SNicolas Dichtel 2144edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 21453b022865SDavid Ahern event, 0, type); 2146edc9e748SNicolas Dichtel if (err < 0) { 2147edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 2148edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 2149edc9e748SNicolas Dichtel kfree_skb(skb); 2150edc9e748SNicolas Dichtel goto errout; 2151edc9e748SNicolas Dichtel } 2152fa17806cSEric Dumazet rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL); 2153edc9e748SNicolas Dichtel return; 2154edc9e748SNicolas Dichtel errout: 2155edc9e748SNicolas Dichtel if (err < 0) 2156edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 2157edc9e748SNicolas Dichtel } 2158edc9e748SNicolas Dichtel 21599e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 21609e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 21619e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 2162cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 216309aea5dfSstephen hemminger [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 2164974d7af5SAndy Gospodarek [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) }, 21659e551110SNicolas Dichtel }; 21669e551110SNicolas Dichtel 2167eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb, 2168eede370dSJakub Kicinski const struct nlmsghdr *nlh, 2169eede370dSJakub Kicinski struct nlattr **tb, 2170eede370dSJakub Kicinski struct netlink_ext_ack *extack) 2171eede370dSJakub Kicinski { 2172eede370dSJakub Kicinski int i, err; 2173eede370dSJakub Kicinski 2174eede370dSJakub Kicinski if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) { 2175eede370dSJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request"); 2176eede370dSJakub Kicinski return -EINVAL; 2177eede370dSJakub Kicinski } 2178eede370dSJakub Kicinski 2179eede370dSJakub Kicinski if (!netlink_strict_get_check(skb)) 21808cb08174SJohannes Berg return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg), 21818cb08174SJohannes Berg tb, NETCONFA_MAX, 21828cb08174SJohannes Berg devconf_ipv4_policy, extack); 2183eede370dSJakub Kicinski 21848cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg), 21858cb08174SJohannes Berg tb, NETCONFA_MAX, 21868cb08174SJohannes Berg devconf_ipv4_policy, extack); 2187eede370dSJakub Kicinski if (err) 2188eede370dSJakub Kicinski return err; 2189eede370dSJakub Kicinski 2190eede370dSJakub Kicinski for (i = 0; i <= NETCONFA_MAX; i++) { 2191eede370dSJakub Kicinski if (!tb[i]) 2192eede370dSJakub Kicinski continue; 2193eede370dSJakub Kicinski 2194eede370dSJakub Kicinski switch (i) { 2195eede370dSJakub Kicinski case NETCONFA_IFINDEX: 2196eede370dSJakub Kicinski break; 2197eede370dSJakub Kicinski default: 2198eede370dSJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request"); 2199eede370dSJakub Kicinski return -EINVAL; 2200eede370dSJakub Kicinski } 2201eede370dSJakub Kicinski } 2202eede370dSJakub Kicinski 2203eede370dSJakub Kicinski return 0; 2204eede370dSJakub Kicinski } 2205eede370dSJakub Kicinski 22069e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 2207c21ef3e3SDavid Ahern struct nlmsghdr *nlh, 2208c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 22099e551110SNicolas Dichtel { 22109e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 22119e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX + 1]; 2212bbcf9105SEric Dumazet const struct ipv4_devconf *devconf; 2213bbcf9105SEric Dumazet struct in_device *in_dev = NULL; 2214bbcf9105SEric Dumazet struct net_device *dev = NULL; 22159e551110SNicolas Dichtel struct sk_buff *skb; 22169e551110SNicolas Dichtel int ifindex; 22179e551110SNicolas Dichtel int err; 22189e551110SNicolas Dichtel 2219eede370dSJakub Kicinski err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack); 2220eede370dSJakub Kicinski if (err) 2221bbcf9105SEric Dumazet return err; 22229e551110SNicolas Dichtel 22239e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 2224bbcf9105SEric Dumazet return -EINVAL; 22259e551110SNicolas Dichtel 22269e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 22279e551110SNicolas Dichtel switch (ifindex) { 22289e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 22299e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 22309e551110SNicolas Dichtel break; 22319e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 22329e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 22339e551110SNicolas Dichtel break; 22349e551110SNicolas Dichtel default: 2235bbcf9105SEric Dumazet err = -ENODEV; 2236bbcf9105SEric Dumazet dev = dev_get_by_index(net, ifindex); 2237bbcf9105SEric Dumazet if (dev) 2238bbcf9105SEric Dumazet in_dev = in_dev_get(dev); 223951456b29SIan Morris if (!in_dev) 22409e551110SNicolas Dichtel goto errout; 22419e551110SNicolas Dichtel devconf = &in_dev->cnf; 22429e551110SNicolas Dichtel break; 22439e551110SNicolas Dichtel } 22449e551110SNicolas Dichtel 22459e551110SNicolas Dichtel err = -ENOBUFS; 2246fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 224751456b29SIan Morris if (!skb) 22489e551110SNicolas Dichtel goto errout; 22499e551110SNicolas Dichtel 22509e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 22519e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 22529e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 2253136ba622SZhang Shengju NETCONFA_ALL); 22549e551110SNicolas Dichtel if (err < 0) { 22559e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 22569e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 22579e551110SNicolas Dichtel kfree_skb(skb); 22589e551110SNicolas Dichtel goto errout; 22599e551110SNicolas Dichtel } 22609e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 22619e551110SNicolas Dichtel errout: 2262bbcf9105SEric Dumazet if (in_dev) 2263bbcf9105SEric Dumazet in_dev_put(in_dev); 2264bbcf9105SEric Dumazet dev_put(dev); 22659e551110SNicolas Dichtel return err; 22669e551110SNicolas Dichtel } 22679e551110SNicolas Dichtel 22687a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 22697a674200SNicolas Dichtel struct netlink_callback *cb) 22707a674200SNicolas Dichtel { 2271addd383fSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 22727a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 227316748707SEric Dumazet struct { 227416748707SEric Dumazet unsigned long ifindex; 227516748707SEric Dumazet unsigned int all_default; 227616748707SEric Dumazet } *ctx = (void *)cb->ctx; 227716748707SEric Dumazet const struct in_device *in_dev; 22787a674200SNicolas Dichtel struct net_device *dev; 227916748707SEric Dumazet int err = 0; 22807a674200SNicolas Dichtel 2281addd383fSDavid Ahern if (cb->strict_check) { 2282addd383fSDavid Ahern struct netlink_ext_ack *extack = cb->extack; 2283addd383fSDavid Ahern struct netconfmsg *ncm; 2284addd383fSDavid Ahern 2285addd383fSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) { 2286addd383fSDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request"); 2287addd383fSDavid Ahern return -EINVAL; 2288addd383fSDavid Ahern } 2289addd383fSDavid Ahern 2290addd383fSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ncm))) { 2291addd383fSDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request"); 2292addd383fSDavid Ahern return -EINVAL; 2293addd383fSDavid Ahern } 2294addd383fSDavid Ahern } 2295addd383fSDavid Ahern 22967a674200SNicolas Dichtel rcu_read_lock(); 229716748707SEric Dumazet for_each_netdev_dump(net, dev, ctx->ifindex) { 22987a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 22997a674200SNicolas Dichtel if (!in_dev) 230016748707SEric Dumazet continue; 230116748707SEric Dumazet err = inet_netconf_fill_devconf(skb, dev->ifindex, 23027a674200SNicolas Dichtel &in_dev->cnf, 23037a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2304addd383fSDavid Ahern nlh->nlmsg_seq, 230516748707SEric Dumazet RTM_NEWNETCONF, NLM_F_MULTI, 230616748707SEric Dumazet NETCONFA_ALL); 230716748707SEric Dumazet if (err < 0) 23087a674200SNicolas Dichtel goto done; 23097a674200SNicolas Dichtel } 231016748707SEric Dumazet if (ctx->all_default == 0) { 231116748707SEric Dumazet err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 23127a674200SNicolas Dichtel net->ipv4.devconf_all, 23137a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2314addd383fSDavid Ahern nlh->nlmsg_seq, 23157a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 231616748707SEric Dumazet NETCONFA_ALL); 231716748707SEric Dumazet if (err < 0) 23187a674200SNicolas Dichtel goto done; 231916748707SEric Dumazet ctx->all_default++; 23207a674200SNicolas Dichtel } 232116748707SEric Dumazet if (ctx->all_default == 1) { 232216748707SEric Dumazet err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 23237a674200SNicolas Dichtel net->ipv4.devconf_dflt, 23247a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2325addd383fSDavid Ahern nlh->nlmsg_seq, 23267a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 232716748707SEric Dumazet NETCONFA_ALL); 232816748707SEric Dumazet if (err < 0) 23297a674200SNicolas Dichtel goto done; 233016748707SEric Dumazet ctx->all_default++; 23317a674200SNicolas Dichtel } 23327a674200SNicolas Dichtel done: 233316748707SEric Dumazet if (err < 0 && likely(skb->len)) 233416748707SEric Dumazet err = skb->len; 233516748707SEric Dumazet rcu_read_unlock(); 233616748707SEric Dumazet return err; 23377a674200SNicolas Dichtel } 23387a674200SNicolas Dichtel 23391da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 23401da177e4SLinus Torvalds 2341c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 234231be3085SHerbert Xu { 234331be3085SHerbert Xu struct net_device *dev; 234431be3085SHerbert Xu 234531be3085SHerbert Xu rcu_read_lock(); 2346c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 2347c6d14c84SEric Dumazet struct in_device *in_dev; 2348c6d14c84SEric Dumazet 234931be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 235031be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 23519355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 2352c6d14c84SEric Dumazet } 235331be3085SHerbert Xu rcu_read_unlock(); 235431be3085SHerbert Xu } 235531be3085SHerbert Xu 2356c6d14c84SEric Dumazet /* called with RTNL locked */ 2357c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 235868dd299bSPavel Emelyanov { 235968dd299bSPavel Emelyanov struct net_device *dev; 2360586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 236168dd299bSPavel Emelyanov 2362586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 23639355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 23643b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23653b022865SDavid Ahern NETCONFA_FORWARDING, 2366edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 2367edc9e748SNicolas Dichtel net->ipv4.devconf_all); 23683b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23693b022865SDavid Ahern NETCONFA_FORWARDING, 2370edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2371edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 237268dd299bSPavel Emelyanov 2373c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 237468dd299bSPavel Emelyanov struct in_device *in_dev; 2375fa17806cSEric Dumazet 23760187bdfbSBen Hutchings if (on) 23770187bdfbSBen Hutchings dev_disable_lro(dev); 2378fa17806cSEric Dumazet 2379fa17806cSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 2380edc9e748SNicolas Dichtel if (in_dev) { 238168dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 23823b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23833b022865SDavid Ahern NETCONFA_FORWARDING, 2384edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 2385edc9e748SNicolas Dichtel } 238668dd299bSPavel Emelyanov } 238768dd299bSPavel Emelyanov } 238868dd299bSPavel Emelyanov 2389f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf) 2390f085ff1cSstephen hemminger { 2391f085ff1cSstephen hemminger if (cnf == net->ipv4.devconf_dflt) 2392f085ff1cSstephen hemminger return NETCONFA_IFINDEX_DEFAULT; 2393f085ff1cSstephen hemminger else if (cnf == net->ipv4.devconf_all) 2394f085ff1cSstephen hemminger return NETCONFA_IFINDEX_ALL; 2395f085ff1cSstephen hemminger else { 2396f085ff1cSstephen hemminger struct in_device *idev 2397f085ff1cSstephen hemminger = container_of(cnf, struct in_device, cnf); 2398f085ff1cSstephen hemminger return idev->dev->ifindex; 2399f085ff1cSstephen hemminger } 2400f085ff1cSstephen hemminger } 2401f085ff1cSstephen hemminger 2402fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write, 240332927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 240431be3085SHerbert Xu { 2405d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 24068d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 2407d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 240831be3085SHerbert Xu 240931be3085SHerbert Xu if (write) { 241031be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 2411c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 241231be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 2413f085ff1cSstephen hemminger int ifindex; 241431be3085SHerbert Xu 241531be3085SHerbert Xu set_bit(i, cnf->state); 241631be3085SHerbert Xu 24179355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 2418c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 2419d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 2420d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 2421d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 24224ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2423f085ff1cSstephen hemminger 24245cbf777cSXin Long if (i == IPV4_DEVCONF_BC_FORWARDING - 1 && 24255cbf777cSXin Long new_value != old_value) 24265cbf777cSXin Long rt_cache_flush(net); 24275cbf777cSXin Long 2428cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 2429cc535dfbSNicolas Dichtel new_value != old_value) { 2430f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 24313b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24323b022865SDavid Ahern NETCONFA_RP_FILTER, 2433cc535dfbSNicolas Dichtel ifindex, cnf); 2434cc535dfbSNicolas Dichtel } 2435f085ff1cSstephen hemminger if (i == IPV4_DEVCONF_PROXY_ARP - 1 && 2436f085ff1cSstephen hemminger new_value != old_value) { 2437f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 24383b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24393b022865SDavid Ahern NETCONFA_PROXY_NEIGH, 2440f085ff1cSstephen hemminger ifindex, cnf); 2441f085ff1cSstephen hemminger } 2442974d7af5SAndy Gospodarek if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 && 2443974d7af5SAndy Gospodarek new_value != old_value) { 2444974d7af5SAndy Gospodarek ifindex = devinet_conf_ifindex(net, cnf); 24453b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24463b022865SDavid Ahern NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 2447974d7af5SAndy Gospodarek ifindex, cnf); 2448974d7af5SAndy Gospodarek } 244931be3085SHerbert Xu } 245031be3085SHerbert Xu 245131be3085SHerbert Xu return ret; 245231be3085SHerbert Xu } 245331be3085SHerbert Xu 2454fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write, 245532927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 24561da177e4SLinus Torvalds { 24571da177e4SLinus Torvalds int *valp = ctl->data; 24581da177e4SLinus Torvalds int val = *valp; 245988af182eSEric W. Biederman loff_t pos = *ppos; 24608292d7f6SYang Yang struct net *net = ctl->extra2; 24618292d7f6SYang Yang int ret; 24628292d7f6SYang Yang 24638292d7f6SYang Yang if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN)) 24648292d7f6SYang Yang return -EPERM; 24658292d7f6SYang Yang 24668292d7f6SYang Yang ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 24671da177e4SLinus Torvalds 24681da177e4SLinus Torvalds if (write && *valp != val) { 24690187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 247088af182eSEric W. Biederman if (!rtnl_trylock()) { 247188af182eSEric W. Biederman /* Restore the original values before restarting */ 247288af182eSEric W. Biederman *valp = val; 247388af182eSEric W. Biederman *ppos = pos; 24749b8adb5eSEric W. Biederman return restart_syscall(); 247588af182eSEric W. Biederman } 24760187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2477c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2478edc9e748SNicolas Dichtel } else { 24790187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 24800187bdfbSBen Hutchings struct in_device *idev = 24810187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2482edc9e748SNicolas Dichtel if (*valp) 24830187bdfbSBen Hutchings dev_disable_lro(idev->dev); 24843b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 2485edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2486edc9e748SNicolas Dichtel idev->dev->ifindex, 2487edc9e748SNicolas Dichtel cnf); 24880187bdfbSBen Hutchings } 24890187bdfbSBen Hutchings rtnl_unlock(); 24904ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2491edc9e748SNicolas Dichtel } else 24923b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24933b022865SDavid Ahern NETCONFA_FORWARDING, 2494edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2495edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 24960187bdfbSBen Hutchings } 24971da177e4SLinus Torvalds 24981da177e4SLinus Torvalds return ret; 24991da177e4SLinus Torvalds } 25001da177e4SLinus Torvalds 2501fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 250232927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 25031da177e4SLinus Torvalds { 25041da177e4SLinus Torvalds int *valp = ctl->data; 25051da177e4SLinus Torvalds int val = *valp; 25068d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 250776e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 25081da177e4SLinus Torvalds 25091da177e4SLinus Torvalds if (write && *valp != val) 25104ccfe6d4SNicolas Dichtel rt_cache_flush(net); 25111da177e4SLinus Torvalds 25121da177e4SLinus Torvalds return ret; 25131da177e4SLinus Torvalds } 25141da177e4SLinus Torvalds 2515f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 251642f811b8SHerbert Xu { \ 251742f811b8SHerbert Xu .procname = name, \ 251842f811b8SHerbert Xu .data = ipv4_devconf.data + \ 251902291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 252042f811b8SHerbert Xu .maxlen = sizeof(int), \ 252142f811b8SHerbert Xu .mode = mval, \ 252242f811b8SHerbert Xu .proc_handler = proc, \ 252331be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 252442f811b8SHerbert Xu } 252542f811b8SHerbert Xu 252642f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2527f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 252842f811b8SHerbert Xu 252942f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2530f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 253142f811b8SHerbert Xu 2532f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2533f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 253442f811b8SHerbert Xu 253542f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2536f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 253742f811b8SHerbert Xu 25381da177e4SLinus Torvalds static struct devinet_sysctl_table { 25391da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 254002291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 25411da177e4SLinus Torvalds } devinet_sysctl = { 25421da177e4SLinus Torvalds .devinet_vars = { 254342f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2544f8572d8fSEric W. Biederman devinet_sysctl_forward), 254542f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 25465cbf777cSXin Long DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"), 254742f811b8SHerbert Xu 254842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 254942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 255042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 255142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 255242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 255342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 255442f811b8SHerbert Xu "accept_source_route"), 25558153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 255628f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 255742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 255842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 255942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 256042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 256142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 256242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 256342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 256442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 256542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2566eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 2567fcdb44d0SJames Prestwood DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER, 2568fcdb44d0SJames Prestwood "arp_evict_nocarrier"), 256965324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 25705c6fe01cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 25715c6fe01cSWilliam Manley "force_igmp_version"), 25722690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, 25732690048cSWilliam Manley "igmpv2_unsolicited_report_interval"), 25742690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, 25752690048cSWilliam Manley "igmpv3_unsolicited_report_interval"), 25760eeb075fSAndy Gospodarek DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN, 25770eeb075fSAndy Gospodarek "ignore_routes_with_linkdown"), 257897daf331SJohannes Berg DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP, 257997daf331SJohannes Berg "drop_gratuitous_arp"), 258042f811b8SHerbert Xu 258142f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 258242f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 258342f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 258442f811b8SHerbert Xu "promote_secondaries"), 2585d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2586d0daebc3SThomas Graf "route_localnet"), 258712b74dfaSJohannes Berg DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST, 258812b74dfaSJohannes Berg "drop_unicast_in_l2_multicast"), 25891da177e4SLinus Torvalds }, 25901da177e4SLinus Torvalds }; 25911da177e4SLinus Torvalds 2592ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 259329c994e3SNicolas Dichtel int ifindex, struct ipv4_devconf *p) 25941da177e4SLinus Torvalds { 25951da177e4SLinus Torvalds int i; 25969fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 25978607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2598bfada697SPavel Emelyanov 2599425b9c7fSVasily Averin t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT); 26001da177e4SLinus Torvalds if (!t) 26019fa89642SPavel Emelyanov goto out; 26029fa89642SPavel Emelyanov 26031da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 26041da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 260531be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2606c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 26071da177e4SLinus Torvalds } 26081da177e4SLinus Torvalds 26098607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 26101da177e4SLinus Torvalds 26118607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 26121da177e4SLinus Torvalds if (!t->sysctl_header) 26138607ddb8SEric W. Biederman goto free; 26141da177e4SLinus Torvalds 26151da177e4SLinus Torvalds p->sysctl = t; 261629c994e3SNicolas Dichtel 26173b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, 26183b022865SDavid Ahern ifindex, p); 2619ea40b324SPavel Emelyanov return 0; 26201da177e4SLinus Torvalds 26211da177e4SLinus Torvalds free: 26221da177e4SLinus Torvalds kfree(t); 26239fa89642SPavel Emelyanov out: 26246def4801Sliuguoqiang return -ENOMEM; 26251da177e4SLinus Torvalds } 26261da177e4SLinus Torvalds 2627b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net, 2628b5c9641dSDavid Ahern struct ipv4_devconf *cnf, int ifindex) 262966f27a52SPavel Emelyanov { 263051602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 263166f27a52SPavel Emelyanov 2632b5c9641dSDavid Ahern if (t) { 263351602b2aSPavel Emelyanov cnf->sysctl = NULL; 2634ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 26351da177e4SLinus Torvalds kfree(t); 26361da177e4SLinus Torvalds } 263751602b2aSPavel Emelyanov 2638b5c9641dSDavid Ahern inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); 2639b5c9641dSDavid Ahern } 2640b5c9641dSDavid Ahern 264120e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 264251602b2aSPavel Emelyanov { 264320e61da7SWANG Cong int err; 264420e61da7SWANG Cong 264520e61da7SWANG Cong if (!sysctl_dev_name_is_allowed(idev->dev->name)) 264620e61da7SWANG Cong return -EINVAL; 264720e61da7SWANG Cong 264820e61da7SWANG Cong err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); 264920e61da7SWANG Cong if (err) 265020e61da7SWANG Cong return err; 265120e61da7SWANG Cong err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 265229c994e3SNicolas Dichtel idev->dev->ifindex, &idev->cnf); 265320e61da7SWANG Cong if (err) 265420e61da7SWANG Cong neigh_sysctl_unregister(idev->arp_parms); 265520e61da7SWANG Cong return err; 265651602b2aSPavel Emelyanov } 265751602b2aSPavel Emelyanov 265851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 265951602b2aSPavel Emelyanov { 2660b5c9641dSDavid Ahern struct net *net = dev_net(idev->dev); 2661b5c9641dSDavid Ahern 2662b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex); 266351602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 26641da177e4SLinus Torvalds } 26651da177e4SLinus Torvalds 266668dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 266768dd299bSPavel Emelyanov { 266868dd299bSPavel Emelyanov .procname = "ip_forward", 266968dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 267002291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 267168dd299bSPavel Emelyanov .maxlen = sizeof(int), 267268dd299bSPavel Emelyanov .mode = 0644, 267368dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 267468dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2675c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 267668dd299bSPavel Emelyanov }, 267768dd299bSPavel Emelyanov { }, 267868dd299bSPavel Emelyanov }; 26792a75de0cSEric Dumazet #endif 268068dd299bSPavel Emelyanov 2681752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2682752d14dcSPavel Emelyanov { 2683752d14dcSPavel Emelyanov int err; 2684752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 26852a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2686856c395cSCong Wang struct ctl_table *tbl; 2687752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 26882a75de0cSEric Dumazet #endif 2689752d14dcSPavel Emelyanov 2690752d14dcSPavel Emelyanov err = -ENOMEM; 2691856c395cSCong Wang all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL); 269251456b29SIan Morris if (!all) 2693752d14dcSPavel Emelyanov goto err_alloc_all; 2694752d14dcSPavel Emelyanov 2695856c395cSCong Wang dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 269651456b29SIan Morris if (!dflt) 2697752d14dcSPavel Emelyanov goto err_alloc_dflt; 2698752d14dcSPavel Emelyanov 26992a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2700856c395cSCong Wang tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL); 270151456b29SIan Morris if (!tbl) 2702752d14dcSPavel Emelyanov goto err_alloc_ctl; 2703752d14dcSPavel Emelyanov 270402291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2705752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2706752d14dcSPavel Emelyanov tbl[0].extra2 = net; 27072a75de0cSEric Dumazet #endif 2708856c395cSCong Wang 27099efd6a3cSNicolas Dichtel if (!net_eq(net, &init_net)) { 2710a5612ca1SKuniyuki Iwashima switch (net_inherit_devconf()) { 2711a5612ca1SKuniyuki Iwashima case 3: 27129efd6a3cSNicolas Dichtel /* copy from the current netns */ 27139efd6a3cSNicolas Dichtel memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all, 27149efd6a3cSNicolas Dichtel sizeof(ipv4_devconf)); 27159efd6a3cSNicolas Dichtel memcpy(dflt, 27169efd6a3cSNicolas Dichtel current->nsproxy->net_ns->ipv4.devconf_dflt, 27179efd6a3cSNicolas Dichtel sizeof(ipv4_devconf_dflt)); 2718a5612ca1SKuniyuki Iwashima break; 2719a5612ca1SKuniyuki Iwashima case 0: 2720a5612ca1SKuniyuki Iwashima case 1: 2721a5612ca1SKuniyuki Iwashima /* copy from init_net */ 27229efd6a3cSNicolas Dichtel memcpy(all, init_net.ipv4.devconf_all, 27239efd6a3cSNicolas Dichtel sizeof(ipv4_devconf)); 27249efd6a3cSNicolas Dichtel memcpy(dflt, init_net.ipv4.devconf_dflt, 27259efd6a3cSNicolas Dichtel sizeof(ipv4_devconf_dflt)); 2726a5612ca1SKuniyuki Iwashima break; 2727a5612ca1SKuniyuki Iwashima case 2: 2728a5612ca1SKuniyuki Iwashima /* use compiled values */ 2729a5612ca1SKuniyuki Iwashima break; 27309efd6a3cSNicolas Dichtel } 2731752d14dcSPavel Emelyanov } 2732752d14dcSPavel Emelyanov 2733752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 273429c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all); 2735752d14dcSPavel Emelyanov if (err < 0) 2736752d14dcSPavel Emelyanov goto err_reg_all; 2737752d14dcSPavel Emelyanov 273829c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "default", 273929c994e3SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, dflt); 2740752d14dcSPavel Emelyanov if (err < 0) 2741752d14dcSPavel Emelyanov goto err_reg_dflt; 2742752d14dcSPavel Emelyanov 2743752d14dcSPavel Emelyanov err = -ENOMEM; 2744c899710fSJoel Granados forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl, 2745c899710fSJoel Granados ARRAY_SIZE(ctl_forward_entry)); 274651456b29SIan Morris if (!forw_hdr) 2747752d14dcSPavel Emelyanov goto err_reg_ctl; 27482a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2749752d14dcSPavel Emelyanov #endif 2750752d14dcSPavel Emelyanov 2751752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2752752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2753752d14dcSPavel Emelyanov return 0; 2754752d14dcSPavel Emelyanov 2755752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2756752d14dcSPavel Emelyanov err_reg_ctl: 2757b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT); 2758752d14dcSPavel Emelyanov err_reg_dflt: 2759b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); 2760752d14dcSPavel Emelyanov err_reg_all: 2761752d14dcSPavel Emelyanov kfree(tbl); 2762752d14dcSPavel Emelyanov err_alloc_ctl: 27632a75de0cSEric Dumazet #endif 2764752d14dcSPavel Emelyanov kfree(dflt); 2765752d14dcSPavel Emelyanov err_alloc_dflt: 2766752d14dcSPavel Emelyanov kfree(all); 2767752d14dcSPavel Emelyanov err_alloc_all: 2768752d14dcSPavel Emelyanov return err; 2769752d14dcSPavel Emelyanov } 2770752d14dcSPavel Emelyanov 2771752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2772752d14dcSPavel Emelyanov { 27732a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2774752d14dcSPavel Emelyanov struct ctl_table *tbl; 2775752d14dcSPavel Emelyanov 2776752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2777752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2778b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_dflt, 2779b5c9641dSDavid Ahern NETCONFA_IFINDEX_DEFAULT); 2780b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_all, 2781b5c9641dSDavid Ahern NETCONFA_IFINDEX_ALL); 2782752d14dcSPavel Emelyanov kfree(tbl); 27832a75de0cSEric Dumazet #endif 2784752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2785752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2786752d14dcSPavel Emelyanov } 2787752d14dcSPavel Emelyanov 2788752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2789752d14dcSPavel Emelyanov .init = devinet_init_net, 2790752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2791752d14dcSPavel Emelyanov }; 2792752d14dcSPavel Emelyanov 2793207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = { 27949f0f7272SThomas Graf .family = AF_INET, 27959f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 27969f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2797cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2798cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 27999f0f7272SThomas Graf }; 28009f0f7272SThomas Graf 28011da177e4SLinus Torvalds void __init devinet_init(void) 28021da177e4SLinus Torvalds { 2803fd23c3b3SDavid S. Miller int i; 2804fd23c3b3SDavid S. Miller 2805fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2806fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2807fd23c3b3SDavid S. Miller 2808752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 28091da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 281063f3444fSThomas Graf 2811906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 28125c766d64SJiri Pirko 28139f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 28149f0f7272SThomas Graf 2815b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0); 2816b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0); 2817b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0); 28189e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 2819bbcf9105SEric Dumazet inet_netconf_dump_devconf, 282016748707SEric Dumazet RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED); 28211da177e4SLinus Torvalds } 2822