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 219*6e701eb9SKuniyuki Iwashima static struct in_ifaddr *inet_alloc_ifa(struct in_device *in_dev) 2201da177e4SLinus Torvalds { 221*6e701eb9SKuniyuki Iwashima struct in_ifaddr *ifa; 222*6e701eb9SKuniyuki Iwashima 223*6e701eb9SKuniyuki Iwashima ifa = kzalloc(sizeof(*ifa), GFP_KERNEL_ACCOUNT); 224*6e701eb9SKuniyuki Iwashima if (!ifa) 225*6e701eb9SKuniyuki Iwashima return NULL; 226*6e701eb9SKuniyuki Iwashima 227*6e701eb9SKuniyuki Iwashima in_dev_hold(in_dev); 228*6e701eb9SKuniyuki Iwashima ifa->ifa_dev = in_dev; 229*6e701eb9SKuniyuki Iwashima 230*6e701eb9SKuniyuki Iwashima return ifa; 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2341da177e4SLinus Torvalds { 2351da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 23661f5338dSEric Dumazet 2371da177e4SLinus Torvalds if (ifa->ifa_dev) 2381da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2391da177e4SLinus Torvalds kfree(ifa); 2401da177e4SLinus Torvalds } 2411da177e4SLinus Torvalds 24240384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2431da177e4SLinus Torvalds { 24461f5338dSEric Dumazet /* Our reference to ifa->ifa_dev must be freed ASAP 24561f5338dSEric Dumazet * to release the reference to the netdev the same way. 24661f5338dSEric Dumazet * in_dev_put() -> in_dev_finish_destroy() -> netdev_put() 24761f5338dSEric Dumazet */ 24861f5338dSEric Dumazet call_rcu_hurry(&ifa->rcu_head, inet_rcu_free_ifa); 2491da177e4SLinus Torvalds } 2501da177e4SLinus Torvalds 2519d40c84cSEric Dumazet static void in_dev_free_rcu(struct rcu_head *head) 2529d40c84cSEric Dumazet { 2539d40c84cSEric Dumazet struct in_device *idev = container_of(head, struct in_device, rcu_head); 2549d40c84cSEric Dumazet 2559d40c84cSEric Dumazet kfree(rcu_dereference_protected(idev->mc_hash, 1)); 2569d40c84cSEric Dumazet kfree(idev); 2579d40c84cSEric Dumazet } 2589d40c84cSEric Dumazet 2591da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2601da177e4SLinus Torvalds { 2611da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2621da177e4SLinus Torvalds 263547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 264547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 2651da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 26691df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2671da177e4SLinus Torvalds #endif 268d62607c3SJakub Kicinski netdev_put(dev, &idev->dev_tracker); 2691da177e4SLinus Torvalds if (!idev->dead) 2709f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2719f9354b9SEric Dumazet else 2729d40c84cSEric Dumazet call_rcu(&idev->rcu_head, in_dev_free_rcu); 2731da177e4SLinus Torvalds } 2749f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2751da177e4SLinus Torvalds 27671e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2771da177e4SLinus Torvalds { 2781da177e4SLinus Torvalds struct in_device *in_dev; 27920e61da7SWANG Cong int err = -ENOMEM; 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds ASSERT_RTNL(); 2821da177e4SLinus Torvalds 2830da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2841da177e4SLinus Torvalds if (!in_dev) 2851da177e4SLinus Torvalds goto out; 286c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2879355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2881da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2891da177e4SLinus Torvalds in_dev->dev = dev; 2909f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2919f9354b9SEric Dumazet if (!in_dev->arp_parms) 2921da177e4SLinus Torvalds goto out_kfree; 2930187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2940187bdfbSBen Hutchings dev_disable_lro(dev); 2951da177e4SLinus Torvalds /* Reference in_dev->dev */ 296d62607c3SJakub Kicinski netdev_hold(dev, &in_dev->dev_tracker, GFP_KERNEL); 29730c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2987658b36fSReshetova, Elena refcount_set(&in_dev->refcnt, 1); 2991da177e4SLinus Torvalds 30020e61da7SWANG Cong err = devinet_sysctl_register(in_dev); 30120e61da7SWANG Cong if (err) { 30220e61da7SWANG Cong in_dev->dead = 1; 3031b49cd71SYang Yingliang neigh_parms_release(&arp_tbl, in_dev->arp_parms); 30420e61da7SWANG Cong in_dev_put(in_dev); 30520e61da7SWANG Cong in_dev = NULL; 30620e61da7SWANG Cong goto out; 30720e61da7SWANG Cong } 3081da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 3091da177e4SLinus Torvalds if (dev->flags & IFF_UP) 3101da177e4SLinus Torvalds ip_mc_up(in_dev); 311483479ecSJarek Poplawski 31230c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 313cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 314483479ecSJarek Poplawski out: 31520e61da7SWANG Cong return in_dev ?: ERR_PTR(err); 3161da177e4SLinus Torvalds out_kfree: 3171da177e4SLinus Torvalds kfree(in_dev); 3181da177e4SLinus Torvalds in_dev = NULL; 3191da177e4SLinus Torvalds goto out; 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 3231da177e4SLinus Torvalds { 3241da177e4SLinus Torvalds struct net_device *dev; 3252638eb8bSFlorian Westphal struct in_ifaddr *ifa; 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds ASSERT_RTNL(); 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds dev = in_dev->dev; 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds in_dev->dead = 1; 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 3341da177e4SLinus Torvalds 3352638eb8bSFlorian Westphal while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) { 3361da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 3371da177e4SLinus Torvalds inet_free_ifa(ifa); 3381da177e4SLinus Torvalds } 3391da177e4SLinus Torvalds 340a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 3411da177e4SLinus Torvalds 34251602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 3431da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 3441da177e4SLinus Torvalds arp_ifdown(dev); 3451da177e4SLinus Torvalds 3469d40c84cSEric Dumazet in_dev_put(in_dev); 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds 349ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3501da177e4SLinus Torvalds { 351d519e870SFlorian Westphal const struct in_ifaddr *ifa; 352d519e870SFlorian Westphal 3531da177e4SLinus Torvalds rcu_read_lock(); 354d519e870SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 3551da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3561da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3571da177e4SLinus Torvalds rcu_read_unlock(); 3581da177e4SLinus Torvalds return 1; 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds } 361d519e870SFlorian Westphal } 3621da177e4SLinus Torvalds rcu_read_unlock(); 3631da177e4SLinus Torvalds return 0; 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds 3662638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev, 3672638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap, 36815e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3691da177e4SLinus Torvalds { 3708f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3712638eb8bSFlorian Westphal struct in_ifaddr *ifa, *ifa1; 372ac28b1ecSLiu Jian struct in_ifaddr __rcu **last_prim; 3730ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3740ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds ASSERT_RTNL(); 3771da177e4SLinus Torvalds 3782638eb8bSFlorian Westphal ifa1 = rtnl_dereference(*ifap); 379ac28b1ecSLiu Jian last_prim = ifap; 380fbd40ea0SDavid S. Miller if (in_dev->dead) 381fbd40ea0SDavid S. Miller goto no_promotions; 382fbd40ea0SDavid S. Miller 3838f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3848f937c60SHarald Welte * unless alias promotion is set 3858f937c60SHarald Welte **/ 3861da177e4SLinus Torvalds 3871da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3882638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next; 3891da177e4SLinus Torvalds 3902638eb8bSFlorian Westphal while ((ifa = rtnl_dereference(*ifap1)) != NULL) { 3910ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3920ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 393ac28b1ecSLiu Jian last_prim = &ifa->ifa_next; 3940ff60a45SJamal Hadi Salim 3951da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3961da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3971da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3981da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3990ff60a45SJamal Hadi Salim prev_prom = ifa; 4001da177e4SLinus Torvalds continue; 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds 4030ff60a45SJamal Hadi Salim if (!do_promote) { 404fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 4051da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 4061da177e4SLinus Torvalds 40715e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 408e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 409e041c683SAlan Stern NETDEV_DOWN, ifa); 4101da177e4SLinus Torvalds inet_free_ifa(ifa); 4118f937c60SHarald Welte } else { 4128f937c60SHarald Welte promote = ifa; 4138f937c60SHarald Welte break; 4148f937c60SHarald Welte } 4151da177e4SLinus Torvalds } 4161da177e4SLinus Torvalds } 4171da177e4SLinus Torvalds 4182d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 4192d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 4202d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 4212d230e2bSJulian Anastasov * while all addresses are on the device list. 4222d230e2bSJulian Anastasov */ 4232638eb8bSFlorian Westphal for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) { 4242d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 4252d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 4262d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 4272d230e2bSJulian Anastasov } 4282d230e2bSJulian Anastasov 429fbd40ea0SDavid S. Miller no_promotions: 4301da177e4SLinus Torvalds /* 2. Unlink it */ 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 433fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 4341da177e4SLinus Torvalds 4351da177e4SLinus Torvalds /* 3. Announce address deletion */ 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds /* Send message first, then call notifier. 4381da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 4391da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 4401da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 4411da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 4421da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 4431da177e4SLinus Torvalds So that, this order is correct. 4441da177e4SLinus Torvalds */ 44515e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 446e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 4470ff60a45SJamal Hadi Salim 4480ff60a45SJamal Hadi Salim if (promote) { 4492638eb8bSFlorian Westphal struct in_ifaddr *next_sec; 4500ff60a45SJamal Hadi Salim 4512638eb8bSFlorian Westphal next_sec = rtnl_dereference(promote->ifa_next); 4520ff60a45SJamal Hadi Salim if (prev_prom) { 4532638eb8bSFlorian Westphal struct in_ifaddr *last_sec; 4542638eb8bSFlorian Westphal 4552638eb8bSFlorian Westphal rcu_assign_pointer(prev_prom->ifa_next, next_sec); 4566a9e9ceaSFlorian Westphal 457ac28b1ecSLiu Jian last_sec = rtnl_dereference(*last_prim); 4582638eb8bSFlorian Westphal rcu_assign_pointer(promote->ifa_next, last_sec); 459ac28b1ecSLiu Jian rcu_assign_pointer(*last_prim, promote); 4600ff60a45SJamal Hadi Salim } 4610ff60a45SJamal Hadi Salim 4620ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 46315e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 464e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 465e041c683SAlan Stern NETDEV_UP, promote); 4662638eb8bSFlorian Westphal for (ifa = next_sec; ifa; 4672638eb8bSFlorian Westphal ifa = rtnl_dereference(ifa->ifa_next)) { 4680ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4690ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4700ff60a45SJamal Hadi Salim continue; 4710ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4720ff60a45SJamal Hadi Salim } 4730ff60a45SJamal Hadi Salim 4740ff60a45SJamal Hadi Salim } 4756363097cSHerbert Xu if (destroy) 4761da177e4SLinus Torvalds inet_free_ifa(ifa1); 4771da177e4SLinus Torvalds } 4781da177e4SLinus Torvalds 4792638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev, 4802638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap, 481d6062cbbSThomas Graf int destroy) 482d6062cbbSThomas Graf { 483d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 484d6062cbbSThomas Graf } 485d6062cbbSThomas Graf 4865c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4875c766d64SJiri Pirko 4885c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4895c766d64SJiri Pirko 490d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 491de95e047SDavid Ahern u32 portid, struct netlink_ext_ack *extack) 4921da177e4SLinus Torvalds { 4932638eb8bSFlorian Westphal struct in_ifaddr __rcu **last_primary, **ifap; 4941da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4953ad7d246SKrister Johansen struct in_validator_info ivi; 4962638eb8bSFlorian Westphal struct in_ifaddr *ifa1; 4973ad7d246SKrister Johansen int ret; 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds ASSERT_RTNL(); 5001da177e4SLinus Torvalds 5011da177e4SLinus Torvalds if (!ifa->ifa_local) { 5021da177e4SLinus Torvalds inet_free_ifa(ifa); 5031da177e4SLinus Torvalds return 0; 5041da177e4SLinus Torvalds } 5051da177e4SLinus Torvalds 5061da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 5071da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 5081da177e4SLinus Torvalds 5092e605463SMatteo Croce /* Don't set IPv6 only flags to IPv4 addresses */ 5102e605463SMatteo Croce ifa->ifa_flags &= ~IPV6ONLY_FLAGS; 5112e605463SMatteo Croce 5122638eb8bSFlorian Westphal ifap = &in_dev->ifa_list; 5132638eb8bSFlorian Westphal ifa1 = rtnl_dereference(*ifap); 5142638eb8bSFlorian Westphal 5152638eb8bSFlorian Westphal while (ifa1) { 5161da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 5171da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 5181da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 5191da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 5201da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 5211da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 5221da177e4SLinus Torvalds inet_free_ifa(ifa); 5231da177e4SLinus Torvalds return -EEXIST; 5241da177e4SLinus Torvalds } 5251da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 526b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Invalid scope value"); 5271da177e4SLinus Torvalds inet_free_ifa(ifa); 5281da177e4SLinus Torvalds return -EINVAL; 5291da177e4SLinus Torvalds } 5301da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 5311da177e4SLinus Torvalds } 5322638eb8bSFlorian Westphal 5332638eb8bSFlorian Westphal ifap = &ifa1->ifa_next; 5342638eb8bSFlorian Westphal ifa1 = rtnl_dereference(*ifap); 5351da177e4SLinus Torvalds } 5361da177e4SLinus Torvalds 5373ad7d246SKrister Johansen /* Allow any devices that wish to register ifaddr validtors to weigh 5383ad7d246SKrister Johansen * in now, before changes are committed. The rntl lock is serializing 5393ad7d246SKrister Johansen * access here, so the state should not change between a validator call 5403ad7d246SKrister Johansen * and a final notify on commit. This isn't invoked on promotion under 5413ad7d246SKrister Johansen * the assumption that validators are checking the address itself, and 5423ad7d246SKrister Johansen * not the flags. 5433ad7d246SKrister Johansen */ 5443ad7d246SKrister Johansen ivi.ivi_addr = ifa->ifa_address; 5453ad7d246SKrister Johansen ivi.ivi_dev = ifa->ifa_dev; 546de95e047SDavid Ahern ivi.extack = extack; 5473ad7d246SKrister Johansen ret = blocking_notifier_call_chain(&inetaddr_validator_chain, 5483ad7d246SKrister Johansen NETDEV_UP, &ivi); 5493ad7d246SKrister Johansen ret = notifier_to_errno(ret); 5503ad7d246SKrister Johansen if (ret) { 5513ad7d246SKrister Johansen inet_free_ifa(ifa); 5523ad7d246SKrister Johansen return ret; 5533ad7d246SKrister Johansen } 5543ad7d246SKrister Johansen 555d4150779SJason A. Donenfeld if (!(ifa->ifa_flags & IFA_F_SECONDARY)) 5561da177e4SLinus Torvalds ifap = last_primary; 5571da177e4SLinus Torvalds 5582638eb8bSFlorian Westphal rcu_assign_pointer(ifa->ifa_next, *ifap); 5592638eb8bSFlorian Westphal rcu_assign_pointer(*ifap, ifa); 5601da177e4SLinus Torvalds 561fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 562fd23c3b3SDavid S. Miller 5635c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 564906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 5655c766d64SJiri Pirko 5661da177e4SLinus Torvalds /* Send message first, then call notifier. 5671da177e4SLinus Torvalds Notifier will trigger FIB update, so that 5681da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 56915e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 570e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 5711da177e4SLinus Torvalds 5721da177e4SLinus Torvalds return 0; 5731da177e4SLinus Torvalds } 5741da177e4SLinus Torvalds 575d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 576d6062cbbSThomas Graf { 577de95e047SDavid Ahern return __inet_insert_ifa(ifa, NULL, 0, NULL); 578d6062cbbSThomas Graf } 579d6062cbbSThomas Graf 5801da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 5811da177e4SLinus Torvalds { 582e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 5831da177e4SLinus Torvalds 5841da177e4SLinus Torvalds ASSERT_RTNL(); 5851da177e4SLinus Torvalds 58671e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 5871d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 588*6e701eb9SKuniyuki Iwashima 589f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5901da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5911da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5921da177e4SLinus Torvalds } 5931da177e4SLinus Torvalds 5948723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5958723e1b4SEric Dumazet * We dont take a reference on found in_device 5968723e1b4SEric Dumazet */ 5977fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5981da177e4SLinus Torvalds { 5991da177e4SLinus Torvalds struct net_device *dev; 6001da177e4SLinus Torvalds struct in_device *in_dev = NULL; 601c148fc2eSEric Dumazet 602c148fc2eSEric Dumazet rcu_read_lock(); 603c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 6041da177e4SLinus Torvalds if (dev) 6058723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 606c148fc2eSEric Dumazet rcu_read_unlock(); 6071da177e4SLinus Torvalds return in_dev; 6081da177e4SLinus Torvalds } 6099f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 6101da177e4SLinus Torvalds 6111da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 6121da177e4SLinus Torvalds 61360cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 61460cad5daSAl Viro __be32 mask) 6151da177e4SLinus Torvalds { 616d519e870SFlorian Westphal struct in_ifaddr *ifa; 617d519e870SFlorian Westphal 6181da177e4SLinus Torvalds ASSERT_RTNL(); 6191da177e4SLinus Torvalds 620d519e870SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 6211da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 6221da177e4SLinus Torvalds return ifa; 623d519e870SFlorian Westphal } 6241da177e4SLinus Torvalds return NULL; 6251da177e4SLinus Torvalds } 6261da177e4SLinus Torvalds 627690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join, 628690cc863STaras Chornyi const struct in_ifaddr *ifa) 62993a714d6SMadhu Challa { 630690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST) 63193a714d6SMadhu Challa struct ip_mreqn mreq = { 63293a714d6SMadhu Challa .imr_multiaddr.s_addr = ifa->ifa_address, 63393a714d6SMadhu Challa .imr_ifindex = ifa->ifa_dev->dev->ifindex, 63493a714d6SMadhu Challa }; 635690cc863STaras Chornyi struct sock *sk = net->ipv4.mc_autojoin_sk; 63693a714d6SMadhu Challa int ret; 63793a714d6SMadhu Challa 63893a714d6SMadhu Challa ASSERT_RTNL(); 63993a714d6SMadhu Challa 64093a714d6SMadhu Challa lock_sock(sk); 64193a714d6SMadhu Challa if (join) 64254ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_join_group(sk, &mreq); 64393a714d6SMadhu Challa else 64454ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_leave_group(sk, &mreq); 64593a714d6SMadhu Challa release_sock(sk); 64693a714d6SMadhu Challa 64793a714d6SMadhu Challa return ret; 648690cc863STaras Chornyi #else 649690cc863STaras Chornyi return -EOPNOTSUPP; 650690cc863STaras Chornyi #endif 65193a714d6SMadhu Challa } 65293a714d6SMadhu Challa 653c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, 654c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 6551da177e4SLinus Torvalds { 6563b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 6572638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap; 658dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 6591da177e4SLinus Torvalds struct in_device *in_dev; 660dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 6612638eb8bSFlorian Westphal struct in_ifaddr *ifa; 66230e2379eSMenglong Dong int err; 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds ASSERT_RTNL(); 6651da177e4SLinus Torvalds 6668cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 6678cb08174SJohannes Berg ifa_ipv4_policy, extack); 668dfdd5fd4SThomas Graf if (err < 0) 669dfdd5fd4SThomas Graf goto errout; 670dfdd5fd4SThomas Graf 671dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 6727fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 67351456b29SIan Morris if (!in_dev) { 674b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Device not found"); 675dfdd5fd4SThomas Graf err = -ENODEV; 676dfdd5fd4SThomas Graf goto errout; 677dfdd5fd4SThomas Graf } 678dfdd5fd4SThomas Graf 6792638eb8bSFlorian Westphal for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL; 6801da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 681dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 68267b61f6cSJiri Benc ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL])) 6831da177e4SLinus Torvalds continue; 684dfdd5fd4SThomas Graf 685dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 686dfdd5fd4SThomas Graf continue; 687dfdd5fd4SThomas Graf 688dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 689dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 69067b61f6cSJiri Benc !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa))) 691dfdd5fd4SThomas Graf continue; 692dfdd5fd4SThomas Graf 69393a714d6SMadhu Challa if (ipv4_is_multicast(ifa->ifa_address)) 694690cc863STaras Chornyi ip_mc_autojoin_config(net, false, ifa); 69515e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 6961da177e4SLinus Torvalds return 0; 6971da177e4SLinus Torvalds } 698dfdd5fd4SThomas Graf 699b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Address not found"); 700dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 701dfdd5fd4SThomas Graf errout: 702dfdd5fd4SThomas Graf return err; 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds 7055c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 7065c766d64SJiri Pirko 7075c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 7085c766d64SJiri Pirko { 7095c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 7105c766d64SJiri Pirko struct in_ifaddr *ifa; 711c988d1e8SJiri Pirko struct hlist_node *n; 7125c766d64SJiri Pirko int i; 7135c766d64SJiri Pirko 7145c766d64SJiri Pirko now = jiffies; 7155c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 7165c766d64SJiri Pirko 7175c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 718c988d1e8SJiri Pirko bool change_needed = false; 719c988d1e8SJiri Pirko 720c988d1e8SJiri Pirko rcu_read_lock(); 721b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 7223cd3e72cSEric Dumazet unsigned long age, tstamp; 7239f6fa3c4SEric Dumazet u32 preferred_lft; 724a5fcf74dSEric Dumazet u32 valid_lft; 7253ddc2231SEric Dumazet u32 flags; 7265c766d64SJiri Pirko 7273ddc2231SEric Dumazet flags = READ_ONCE(ifa->ifa_flags); 7283ddc2231SEric Dumazet if (flags & IFA_F_PERMANENT) 7295c766d64SJiri Pirko continue; 7305c766d64SJiri Pirko 7319f6fa3c4SEric Dumazet preferred_lft = READ_ONCE(ifa->ifa_preferred_lft); 732a5fcf74dSEric Dumazet valid_lft = READ_ONCE(ifa->ifa_valid_lft); 7333cd3e72cSEric Dumazet tstamp = READ_ONCE(ifa->ifa_tstamp); 7345c766d64SJiri Pirko /* We try to batch several events at once. */ 7353cd3e72cSEric Dumazet age = (now - tstamp + 7365c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 7375c766d64SJiri Pirko 738a5fcf74dSEric Dumazet if (valid_lft != INFINITY_LIFE_TIME && 739a5fcf74dSEric Dumazet age >= valid_lft) { 740c988d1e8SJiri Pirko change_needed = true; 7419f6fa3c4SEric Dumazet } else if (preferred_lft == 742c988d1e8SJiri Pirko INFINITY_LIFE_TIME) { 743c988d1e8SJiri Pirko continue; 7449f6fa3c4SEric Dumazet } else if (age >= preferred_lft) { 745a5fcf74dSEric Dumazet if (time_before(tstamp + valid_lft * HZ, next)) 746a5fcf74dSEric Dumazet next = tstamp + valid_lft * HZ; 747c988d1e8SJiri Pirko 7483ddc2231SEric Dumazet if (!(flags & IFA_F_DEPRECATED)) 749c988d1e8SJiri Pirko change_needed = true; 7509f6fa3c4SEric Dumazet } else if (time_before(tstamp + preferred_lft * HZ, 751c988d1e8SJiri Pirko next)) { 7529f6fa3c4SEric Dumazet next = tstamp + preferred_lft * HZ; 753c988d1e8SJiri Pirko } 754c988d1e8SJiri Pirko } 755c988d1e8SJiri Pirko rcu_read_unlock(); 756c988d1e8SJiri Pirko if (!change_needed) 757c988d1e8SJiri Pirko continue; 758c988d1e8SJiri Pirko rtnl_lock(); 759c988d1e8SJiri Pirko hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 760c988d1e8SJiri Pirko unsigned long age; 761c988d1e8SJiri Pirko 762c988d1e8SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 763c988d1e8SJiri Pirko continue; 764c988d1e8SJiri Pirko 765c988d1e8SJiri Pirko /* We try to batch several events at once. */ 766c988d1e8SJiri Pirko age = (now - ifa->ifa_tstamp + 767c988d1e8SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 768c988d1e8SJiri Pirko 769c988d1e8SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 770c988d1e8SJiri Pirko age >= ifa->ifa_valid_lft) { 7712638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap; 7722638eb8bSFlorian Westphal struct in_ifaddr *tmp; 7735c766d64SJiri Pirko 7742638eb8bSFlorian Westphal ifap = &ifa->ifa_dev->ifa_list; 7752638eb8bSFlorian Westphal tmp = rtnl_dereference(*ifap); 7762638eb8bSFlorian Westphal while (tmp) { 77740008e92SFlorian Westphal if (tmp == ifa) { 7785c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 7795c766d64SJiri Pirko ifap, 1); 780c988d1e8SJiri Pirko break; 7815c766d64SJiri Pirko } 7822638eb8bSFlorian Westphal ifap = &tmp->ifa_next; 7832638eb8bSFlorian Westphal tmp = rtnl_dereference(*ifap); 784c988d1e8SJiri Pirko } 785c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft != 786c988d1e8SJiri Pirko INFINITY_LIFE_TIME && 787c988d1e8SJiri Pirko age >= ifa->ifa_preferred_lft && 788c988d1e8SJiri Pirko !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 7895c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7905c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 7915c766d64SJiri Pirko } 7925c766d64SJiri Pirko } 793c988d1e8SJiri Pirko rtnl_unlock(); 7945c766d64SJiri Pirko } 7955c766d64SJiri Pirko 7965c766d64SJiri Pirko next_sec = round_jiffies_up(next); 7975c766d64SJiri Pirko next_sched = next; 7985c766d64SJiri Pirko 7995c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 8005c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 8015c766d64SJiri Pirko next_sched = next_sec; 8025c766d64SJiri Pirko 8035c766d64SJiri Pirko now = jiffies; 8045c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 8055c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 8065c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 8075c766d64SJiri Pirko 808906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 809906e073fSviresh kumar next_sched - now); 8105c766d64SJiri Pirko } 8115c766d64SJiri Pirko 8125c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 8135c766d64SJiri Pirko __u32 prefered_lft) 8145c766d64SJiri Pirko { 8155c766d64SJiri Pirko unsigned long timeout; 8163ddc2231SEric Dumazet u32 flags; 8175c766d64SJiri Pirko 8183ddc2231SEric Dumazet flags = ifa->ifa_flags & ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 8195c766d64SJiri Pirko 8205c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 8215c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 822a5fcf74dSEric Dumazet WRITE_ONCE(ifa->ifa_valid_lft, timeout); 8235c766d64SJiri Pirko else 8243ddc2231SEric Dumazet flags |= IFA_F_PERMANENT; 8255c766d64SJiri Pirko 8265c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 8275c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 8285c766d64SJiri Pirko if (timeout == 0) 8293ddc2231SEric Dumazet flags |= IFA_F_DEPRECATED; 8309f6fa3c4SEric Dumazet WRITE_ONCE(ifa->ifa_preferred_lft, timeout); 8315c766d64SJiri Pirko } 8323ddc2231SEric Dumazet WRITE_ONCE(ifa->ifa_flags, flags); 8333cd3e72cSEric Dumazet WRITE_ONCE(ifa->ifa_tstamp, jiffies); 8345c766d64SJiri Pirko if (!ifa->ifa_cstamp) 8353cd3e72cSEric Dumazet WRITE_ONCE(ifa->ifa_cstamp, ifa->ifa_tstamp); 8365c766d64SJiri Pirko } 8375c766d64SJiri Pirko 8385c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 839dac9c979SDavid Ahern __u32 *pvalid_lft, __u32 *pprefered_lft, 840dac9c979SDavid Ahern struct netlink_ext_ack *extack) 8411da177e4SLinus Torvalds { 8425c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 8435c753978SThomas Graf struct in_ifaddr *ifa; 8445c753978SThomas Graf struct ifaddrmsg *ifm; 8451da177e4SLinus Torvalds struct net_device *dev; 8461da177e4SLinus Torvalds struct in_device *in_dev; 8477b218574SDenis V. Lunev int err; 8481da177e4SLinus Torvalds 8498cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 8508cb08174SJohannes Berg ifa_ipv4_policy, extack); 8515c753978SThomas Graf if (err < 0) 8525c753978SThomas Graf goto errout; 8531da177e4SLinus Torvalds 8545c753978SThomas Graf ifm = nlmsg_data(nlh); 855c4e38f41SEvgeniy Polyakov err = -EINVAL; 856b4672c73SHangbin Liu 857b4672c73SHangbin Liu if (ifm->ifa_prefixlen > 32) { 858b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Invalid prefix length"); 8595c753978SThomas Graf goto errout; 860b4672c73SHangbin Liu } 861b4672c73SHangbin Liu 862b4672c73SHangbin Liu if (!tb[IFA_LOCAL]) { 863b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Local address is not supplied"); 864b4672c73SHangbin Liu goto errout; 865b4672c73SHangbin Liu } 8661da177e4SLinus Torvalds 8674b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 8685c753978SThomas Graf err = -ENODEV; 869b4672c73SHangbin Liu if (!dev) { 870b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Device not found"); 8715c753978SThomas Graf goto errout; 872b4672c73SHangbin Liu } 8731da177e4SLinus Torvalds 8745c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 8755c753978SThomas Graf err = -ENOBUFS; 87651456b29SIan Morris if (!in_dev) 8775c753978SThomas Graf goto errout; 87871e27da9SHerbert Xu 879*6e701eb9SKuniyuki Iwashima ifa = inet_alloc_ifa(in_dev); 88051456b29SIan Morris if (!ifa) 8815c753978SThomas Graf /* 8825c753978SThomas Graf * A potential indev allocation can be left alive, it stays 8835c753978SThomas Graf * assigned to its device and is destroy with it. 8845c753978SThomas Graf */ 8855c753978SThomas Graf goto errout; 8865c753978SThomas Graf 887a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 8881d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 8895c753978SThomas Graf 89051456b29SIan Morris if (!tb[IFA_ADDRESS]) 8915c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 8925c753978SThomas Graf 893fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 8941da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 8951da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 896ad6c8135SJiri Pirko ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : 897ad6c8135SJiri Pirko ifm->ifa_flags; 8981da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 89967b61f6cSJiri Benc ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]); 90067b61f6cSJiri Benc ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]); 9015c753978SThomas Graf 9025c753978SThomas Graf if (tb[IFA_BROADCAST]) 90367b61f6cSJiri Benc ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]); 9045c753978SThomas Graf 9055c753978SThomas Graf if (tb[IFA_LABEL]) 906872f6903SFrancis Laniel nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 9071da177e4SLinus Torvalds else 9081da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 9091da177e4SLinus Torvalds 910af4d768aSDavid Ahern if (tb[IFA_RT_PRIORITY]) 911af4d768aSDavid Ahern ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 912af4d768aSDavid Ahern 91347f0bd50SJacques de Laval if (tb[IFA_PROTO]) 91447f0bd50SJacques de Laval ifa->ifa_proto = nla_get_u8(tb[IFA_PROTO]); 91547f0bd50SJacques de Laval 9165c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 9175c766d64SJiri Pirko struct ifa_cacheinfo *ci; 9185c766d64SJiri Pirko 9195c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 9205c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 921b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: address lifetime invalid"); 9225c766d64SJiri Pirko err = -EINVAL; 923446266b0SDaniel Borkmann goto errout_free; 9245c766d64SJiri Pirko } 9255c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 9265c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 9275c766d64SJiri Pirko } 9285c766d64SJiri Pirko 9295c753978SThomas Graf return ifa; 9305c753978SThomas Graf 931446266b0SDaniel Borkmann errout_free: 932446266b0SDaniel Borkmann inet_free_ifa(ifa); 9335c753978SThomas Graf errout: 9345c753978SThomas Graf return ERR_PTR(err); 9355c753978SThomas Graf } 9365c753978SThomas Graf 9375c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 9385c766d64SJiri Pirko { 9395c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 940ef11db33SFlorian Westphal struct in_ifaddr *ifa1; 9415c766d64SJiri Pirko 9425c766d64SJiri Pirko if (!ifa->ifa_local) 9435c766d64SJiri Pirko return NULL; 9445c766d64SJiri Pirko 945ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa1, in_dev) { 9465c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 9475c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 9485c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 9495c766d64SJiri Pirko return ifa1; 9505c766d64SJiri Pirko } 9515c766d64SJiri Pirko return NULL; 9525c766d64SJiri Pirko } 9535c766d64SJiri Pirko 954c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, 955c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 9565c753978SThomas Graf { 9573b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 9585c753978SThomas Graf struct in_ifaddr *ifa; 9595c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 9605c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 9615c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 9625c753978SThomas Graf 9635c753978SThomas Graf ASSERT_RTNL(); 9645c753978SThomas Graf 965dac9c979SDavid Ahern ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack); 9665c753978SThomas Graf if (IS_ERR(ifa)) 9675c753978SThomas Graf return PTR_ERR(ifa); 9685c753978SThomas Graf 9695c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 9705c766d64SJiri Pirko if (!ifa_existing) { 9715c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 972614d056cSstephen hemminger * userspace already relies on not having to provide this. 9735c766d64SJiri Pirko */ 9745c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 97593a714d6SMadhu Challa if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) { 976690cc863STaras Chornyi int ret = ip_mc_autojoin_config(net, true, ifa); 97793a714d6SMadhu Challa 97893a714d6SMadhu Challa if (ret < 0) { 979b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Multicast auto join failed"); 98093a714d6SMadhu Challa inet_free_ifa(ifa); 98193a714d6SMadhu Challa return ret; 98293a714d6SMadhu Challa } 98393a714d6SMadhu Challa } 984de95e047SDavid Ahern return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid, 985de95e047SDavid Ahern extack); 9865c766d64SJiri Pirko } else { 987af4d768aSDavid Ahern u32 new_metric = ifa->ifa_rt_priority; 9885c4a9aa8SPetr Machata u8 new_proto = ifa->ifa_proto; 989af4d768aSDavid Ahern 9905c766d64SJiri Pirko inet_free_ifa(ifa); 9915c766d64SJiri Pirko 9925c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 993b4672c73SHangbin Liu !(nlh->nlmsg_flags & NLM_F_REPLACE)) { 994b4672c73SHangbin Liu NL_SET_ERR_MSG(extack, "ipv4: Address already assigned"); 9955c766d64SJiri Pirko return -EEXIST; 996b4672c73SHangbin Liu } 99734e2ed34SJiri Pirko ifa = ifa_existing; 998af4d768aSDavid Ahern 999af4d768aSDavid Ahern if (ifa->ifa_rt_priority != new_metric) { 1000af4d768aSDavid Ahern fib_modify_prefix_metric(ifa, new_metric); 1001af4d768aSDavid Ahern ifa->ifa_rt_priority = new_metric; 1002af4d768aSDavid Ahern } 1003af4d768aSDavid Ahern 10045c4a9aa8SPetr Machata ifa->ifa_proto = new_proto; 10055c4a9aa8SPetr Machata 100634e2ed34SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 100705a324b9SJiri Pirko cancel_delayed_work(&check_lifetime_work); 1008906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, 1009906e073fSviresh kumar &check_lifetime_work, 0); 101034e2ed34SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 10115c766d64SJiri Pirko } 10125c766d64SJiri Pirko return 0; 10131da177e4SLinus Torvalds } 10141da177e4SLinus Torvalds 10151da177e4SLinus Torvalds /* 10161da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 10171da177e4SLinus Torvalds */ 10181da177e4SLinus Torvalds 101940384999SEric Dumazet static int inet_abc_len(__be32 addr) 10201da177e4SLinus Torvalds { 10211da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 10221da177e4SLinus Torvalds 102365cab850SDave Taht if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr)) 10241da177e4SLinus Torvalds rc = 0; 10251da177e4SLinus Torvalds else { 1026714e85beSAl Viro __u32 haddr = ntohl(addr); 1027714e85beSAl Viro if (IN_CLASSA(haddr)) 10281da177e4SLinus Torvalds rc = 8; 1029714e85beSAl Viro else if (IN_CLASSB(haddr)) 10301da177e4SLinus Torvalds rc = 16; 1031714e85beSAl Viro else if (IN_CLASSC(haddr)) 10321da177e4SLinus Torvalds rc = 24; 103365cab850SDave Taht else if (IN_CLASSE(haddr)) 103465cab850SDave Taht rc = 32; 10351da177e4SLinus Torvalds } 10361da177e4SLinus Torvalds 10371da177e4SLinus Torvalds return rc; 10381da177e4SLinus Torvalds } 10391da177e4SLinus Torvalds 10401da177e4SLinus Torvalds 104103aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr) 10421da177e4SLinus Torvalds { 10431da177e4SLinus Torvalds struct sockaddr_in sin_orig; 104403aef17bSAl Viro struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; 10452638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap = NULL; 10461da177e4SLinus Torvalds struct in_device *in_dev; 10471da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 10481da177e4SLinus Torvalds struct net_device *dev; 10491da177e4SLinus Torvalds char *colon; 10501da177e4SLinus Torvalds int ret = -EFAULT; 10511da177e4SLinus Torvalds int tryaddrmatch = 0; 10521da177e4SLinus Torvalds 105303aef17bSAl Viro ifr->ifr_name[IFNAMSIZ - 1] = 0; 10541da177e4SLinus Torvalds 10551da177e4SLinus Torvalds /* save original address for comparison */ 10561da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 10571da177e4SLinus Torvalds 105803aef17bSAl Viro colon = strchr(ifr->ifr_name, ':'); 10591da177e4SLinus Torvalds if (colon) 10601da177e4SLinus Torvalds *colon = 0; 10611da177e4SLinus Torvalds 106203aef17bSAl Viro dev_load(net, ifr->ifr_name); 10631da177e4SLinus Torvalds 10641da177e4SLinus Torvalds switch (cmd) { 10651da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 10661da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 10671da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 10681da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 10691da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 10701da177e4SLinus Torvalds so that we do not impose a lock. 10711da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 10721da177e4SLinus Torvalds */ 10731da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 10741da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 10751da177e4SLinus Torvalds sin->sin_family = AF_INET; 10761da177e4SLinus Torvalds break; 10771da177e4SLinus Torvalds 10781da177e4SLinus Torvalds case SIOCSIFFLAGS: 1079bf5b30b8SZhao Hongjiang ret = -EPERM; 108052e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10811da177e4SLinus Torvalds goto out; 10821da177e4SLinus Torvalds break; 10831da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10841da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10851da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 10861da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 1087bf5b30b8SZhao Hongjiang ret = -EPERM; 108852e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10891da177e4SLinus Torvalds goto out; 10901da177e4SLinus Torvalds ret = -EINVAL; 10911da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 10921da177e4SLinus Torvalds goto out; 10931da177e4SLinus Torvalds break; 10941da177e4SLinus Torvalds default: 10951da177e4SLinus Torvalds ret = -EINVAL; 10961da177e4SLinus Torvalds goto out; 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds 10991da177e4SLinus Torvalds rtnl_lock(); 11001da177e4SLinus Torvalds 11011da177e4SLinus Torvalds ret = -ENODEV; 110203aef17bSAl Viro dev = __dev_get_by_name(net, ifr->ifr_name); 11039f9354b9SEric Dumazet if (!dev) 11041da177e4SLinus Torvalds goto done; 11051da177e4SLinus Torvalds 11061da177e4SLinus Torvalds if (colon) 11071da177e4SLinus Torvalds *colon = ':'; 11081da177e4SLinus Torvalds 11099f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 11109f9354b9SEric Dumazet if (in_dev) { 11111da177e4SLinus Torvalds if (tryaddrmatch) { 11121da177e4SLinus Torvalds /* Matthias Andree */ 11131da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 11141da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 11151da177e4SLinus Torvalds and only if the original address family was AF_INET. 11161da177e4SLinus Torvalds This is checked above. */ 11172638eb8bSFlorian Westphal 11182638eb8bSFlorian Westphal for (ifap = &in_dev->ifa_list; 11192638eb8bSFlorian Westphal (ifa = rtnl_dereference(*ifap)) != NULL; 11201da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 112103aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label) && 11221da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 11236c91afe1SDavid S. Miller ifa->ifa_local) { 11241da177e4SLinus Torvalds break; /* found */ 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds } 11271da177e4SLinus Torvalds } 11281da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 11291da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 11301da177e4SLinus Torvalds comparing just the label */ 11311da177e4SLinus Torvalds if (!ifa) { 11322638eb8bSFlorian Westphal for (ifap = &in_dev->ifa_list; 11332638eb8bSFlorian Westphal (ifa = rtnl_dereference(*ifap)) != NULL; 11341da177e4SLinus Torvalds ifap = &ifa->ifa_next) 113503aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label)) 11361da177e4SLinus Torvalds break; 11371da177e4SLinus Torvalds } 11381da177e4SLinus Torvalds } 11391da177e4SLinus Torvalds 11401da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 11411da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 11421da177e4SLinus Torvalds goto done; 11431da177e4SLinus Torvalds 11441da177e4SLinus Torvalds switch (cmd) { 11451da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 114630e948a3STonghao Zhang ret = 0; 11471da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 114803aef17bSAl Viro break; 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 115130e948a3STonghao Zhang ret = 0; 11521da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 115303aef17bSAl Viro break; 11541da177e4SLinus Torvalds 11551da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 115630e948a3STonghao Zhang ret = 0; 11571da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 115803aef17bSAl Viro break; 11591da177e4SLinus Torvalds 11601da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 116130e948a3STonghao Zhang ret = 0; 11621da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 116303aef17bSAl Viro break; 11641da177e4SLinus Torvalds 11651da177e4SLinus Torvalds case SIOCSIFFLAGS: 11661da177e4SLinus Torvalds if (colon) { 11671da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 11681da177e4SLinus Torvalds if (!ifa) 11691da177e4SLinus Torvalds break; 11701da177e4SLinus Torvalds ret = 0; 117103aef17bSAl Viro if (!(ifr->ifr_flags & IFF_UP)) 11721da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 11731da177e4SLinus Torvalds break; 11741da177e4SLinus Torvalds } 1175567c5e13SPetr Machata ret = dev_change_flags(dev, ifr->ifr_flags, NULL); 11761da177e4SLinus Torvalds break; 11771da177e4SLinus Torvalds 11781da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 11791da177e4SLinus Torvalds ret = -EINVAL; 11801da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 11811da177e4SLinus Torvalds break; 11821da177e4SLinus Torvalds 11831da177e4SLinus Torvalds if (!ifa) { 11841da177e4SLinus Torvalds ret = -ENOBUFS; 1185e3af3d3cSKuniyuki Iwashima if (!in_dev) 1186e3af3d3cSKuniyuki Iwashima break; 1187*6e701eb9SKuniyuki Iwashima ifa = inet_alloc_ifa(in_dev); 11889f9354b9SEric Dumazet if (!ifa) 11891da177e4SLinus Torvalds break; 1190c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 11911da177e4SLinus Torvalds if (colon) 119203aef17bSAl Viro memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ); 11931da177e4SLinus Torvalds else 11941da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11951da177e4SLinus Torvalds } else { 11961da177e4SLinus Torvalds ret = 0; 11971da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 11981da177e4SLinus Torvalds break; 11991da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 12001da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 1201148f9729SBjorn Mork ifa->ifa_scope = 0; 12021da177e4SLinus Torvalds } 12031da177e4SLinus Torvalds 12041da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 12051da177e4SLinus Torvalds 12061da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 12071da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 12081da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 12091da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 12101da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 12111da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 12121da177e4SLinus Torvalds ~ifa->ifa_mask; 12131da177e4SLinus Torvalds } else { 12141da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 12151da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 12161da177e4SLinus Torvalds } 12175c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 12181da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 12191da177e4SLinus Torvalds break; 12201da177e4SLinus Torvalds 12211da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 12221da177e4SLinus Torvalds ret = 0; 12231da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 12241da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 12251da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 12261da177e4SLinus Torvalds inet_insert_ifa(ifa); 12271da177e4SLinus Torvalds } 12281da177e4SLinus Torvalds break; 12291da177e4SLinus Torvalds 12301da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 12311da177e4SLinus Torvalds ret = 0; 12321da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 12331da177e4SLinus Torvalds break; 12341da177e4SLinus Torvalds ret = -EINVAL; 12351da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 12361da177e4SLinus Torvalds break; 12371da177e4SLinus Torvalds ret = 0; 12381da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 12391da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 12401da177e4SLinus Torvalds inet_insert_ifa(ifa); 12411da177e4SLinus Torvalds break; 12421da177e4SLinus Torvalds 12431da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 12441da177e4SLinus Torvalds 12451da177e4SLinus Torvalds /* 12461da177e4SLinus Torvalds * The mask we set must be legal. 12471da177e4SLinus Torvalds */ 12481da177e4SLinus Torvalds ret = -EINVAL; 12491da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 12501da177e4SLinus Torvalds break; 12511da177e4SLinus Torvalds ret = 0; 12521da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1253a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 12541da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 12551da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 12561da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 12571da177e4SLinus Torvalds 12581da177e4SLinus Torvalds /* See if current broadcast address matches 12591da177e4SLinus Torvalds * with current netmask, then recalculate 12601da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 12611da177e4SLinus Torvalds * funny address, so don't touch it since 12621da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 12631da177e4SLinus Torvalds */ 12641da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 12651da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 12661da177e4SLinus Torvalds (ifa->ifa_broadcast == 1267dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 12681da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 12691da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 12701da177e4SLinus Torvalds } 12711da177e4SLinus Torvalds inet_insert_ifa(ifa); 12721da177e4SLinus Torvalds } 12731da177e4SLinus Torvalds break; 12741da177e4SLinus Torvalds } 12751da177e4SLinus Torvalds done: 12761da177e4SLinus Torvalds rtnl_unlock(); 12771da177e4SLinus Torvalds out: 12781da177e4SLinus Torvalds return ret; 12791da177e4SLinus Torvalds } 12801da177e4SLinus Torvalds 1281b0e99d03SArnd Bergmann int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size) 12821da177e4SLinus Torvalds { 1283e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 1284ef11db33SFlorian Westphal const struct in_ifaddr *ifa; 12851da177e4SLinus Torvalds struct ifreq ifr; 12861da177e4SLinus Torvalds int done = 0; 12871da177e4SLinus Torvalds 128836fd633eSAl Viro if (WARN_ON(size > sizeof(struct ifreq))) 128936fd633eSAl Viro goto out; 129036fd633eSAl Viro 12919f9354b9SEric Dumazet if (!in_dev) 12921da177e4SLinus Torvalds goto out; 12931da177e4SLinus Torvalds 1294ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 12951da177e4SLinus Torvalds if (!buf) { 129636fd633eSAl Viro done += size; 12971da177e4SLinus Torvalds continue; 12981da177e4SLinus Torvalds } 129936fd633eSAl Viro if (len < size) 13001da177e4SLinus Torvalds break; 13011da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 13021da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 13031da177e4SLinus Torvalds 13041da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 13051da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 13061da177e4SLinus Torvalds ifa->ifa_local; 13071da177e4SLinus Torvalds 130836fd633eSAl Viro if (copy_to_user(buf + done, &ifr, size)) { 13091da177e4SLinus Torvalds done = -EFAULT; 13101da177e4SLinus Torvalds break; 13111da177e4SLinus Torvalds } 131236fd633eSAl Viro len -= size; 131336fd633eSAl Viro done += size; 13141da177e4SLinus Torvalds } 13151da177e4SLinus Torvalds out: 13161da177e4SLinus Torvalds return done; 13171da177e4SLinus Torvalds } 13181da177e4SLinus Torvalds 13198b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev, 13208b57fd1eSGao Feng int scope) 13218b57fd1eSGao Feng { 1322d519e870SFlorian Westphal const struct in_ifaddr *ifa; 1323d519e870SFlorian Westphal 1324d519e870SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 13253ddc2231SEric Dumazet if (READ_ONCE(ifa->ifa_flags) & IFA_F_SECONDARY) 1326d519e870SFlorian Westphal continue; 13278b57fd1eSGao Feng if (ifa->ifa_scope != RT_SCOPE_LINK && 13288b57fd1eSGao Feng ifa->ifa_scope <= scope) 13298b57fd1eSGao Feng return ifa->ifa_local; 1330d519e870SFlorian Westphal } 13318b57fd1eSGao Feng 13328b57fd1eSGao Feng return 0; 13338b57fd1eSGao Feng } 13348b57fd1eSGao Feng 1335a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 13361da177e4SLinus Torvalds { 1337d519e870SFlorian Westphal const struct in_ifaddr *ifa; 1338a61ced5dSAl Viro __be32 addr = 0; 1339d8c444d5SShijie Luo unsigned char localnet_scope = RT_SCOPE_HOST; 13401da177e4SLinus Torvalds struct in_device *in_dev; 1341c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 13423f2fb9a8SDavid Ahern int master_idx; 13431da177e4SLinus Torvalds 13441da177e4SLinus Torvalds rcu_read_lock(); 1345e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 13461da177e4SLinus Torvalds if (!in_dev) 13471da177e4SLinus Torvalds goto no_in_dev; 13481da177e4SLinus Torvalds 1349d8c444d5SShijie Luo if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev))) 1350d8c444d5SShijie Luo localnet_scope = RT_SCOPE_LINK; 1351d8c444d5SShijie Luo 1352d519e870SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 13533ddc2231SEric Dumazet if (READ_ONCE(ifa->ifa_flags) & IFA_F_SECONDARY) 1354d519e870SFlorian Westphal continue; 1355d8c444d5SShijie Luo if (min(ifa->ifa_scope, localnet_scope) > scope) 13561da177e4SLinus Torvalds continue; 13571da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 13581da177e4SLinus Torvalds addr = ifa->ifa_local; 13591da177e4SLinus Torvalds break; 13601da177e4SLinus Torvalds } 13611da177e4SLinus Torvalds if (!addr) 13621da177e4SLinus Torvalds addr = ifa->ifa_local; 1363d519e870SFlorian Westphal } 13641da177e4SLinus Torvalds 13651da177e4SLinus Torvalds if (addr) 1366c6d14c84SEric Dumazet goto out_unlock; 13679f9354b9SEric Dumazet no_in_dev: 13683f2fb9a8SDavid Ahern master_idx = l3mdev_master_ifindex_rcu(dev); 13691da177e4SLinus Torvalds 137017b693cdSDavid Lamparter /* For VRFs, the VRF device takes the place of the loopback device, 137117b693cdSDavid Lamparter * with addresses on it being preferred. Note in such cases the 137217b693cdSDavid Lamparter * loopback device will be among the devices that fail the master_idx 137317b693cdSDavid Lamparter * equality check in the loop below. 137417b693cdSDavid Lamparter */ 137517b693cdSDavid Lamparter if (master_idx && 137617b693cdSDavid Lamparter (dev = dev_get_by_index_rcu(net, master_idx)) && 137717b693cdSDavid Lamparter (in_dev = __in_dev_get_rcu(dev))) { 13788b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 13798b57fd1eSGao Feng if (addr) 138017b693cdSDavid Lamparter goto out_unlock; 138117b693cdSDavid Lamparter } 138217b693cdSDavid Lamparter 13831da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 1384ca9f1fd2SStephen Hemminger in this case. It is important that lo is the first interface 13851da177e4SLinus Torvalds in dev_base list. 13861da177e4SLinus Torvalds */ 1387c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 13883f2fb9a8SDavid Ahern if (l3mdev_master_ifindex_rcu(dev) != master_idx) 13893f2fb9a8SDavid Ahern continue; 13903f2fb9a8SDavid Ahern 13919f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 13929f9354b9SEric Dumazet if (!in_dev) 13931da177e4SLinus Torvalds continue; 13941da177e4SLinus Torvalds 13958b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 13968b57fd1eSGao Feng if (addr) 1397c6d14c84SEric Dumazet goto out_unlock; 13981da177e4SLinus Torvalds } 1399c6d14c84SEric Dumazet out_unlock: 14001da177e4SLinus Torvalds rcu_read_unlock(); 14011da177e4SLinus Torvalds return addr; 14021da177e4SLinus Torvalds } 14039f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 14041da177e4SLinus Torvalds 140560cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 140660cad5daSAl Viro __be32 local, int scope) 14071da177e4SLinus Torvalds { 1408650638a7SShijie Luo unsigned char localnet_scope = RT_SCOPE_HOST; 1409ef11db33SFlorian Westphal const struct in_ifaddr *ifa; 1410a144ea4bSAl Viro __be32 addr = 0; 1411ef11db33SFlorian Westphal int same = 0; 14121da177e4SLinus Torvalds 1413650638a7SShijie Luo if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev))) 1414650638a7SShijie Luo localnet_scope = RT_SCOPE_LINK; 1415650638a7SShijie Luo 1416ef11db33SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 1417650638a7SShijie Luo unsigned char min_scope = min(ifa->ifa_scope, localnet_scope); 1418650638a7SShijie Luo 14191da177e4SLinus Torvalds if (!addr && 14201da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 1421650638a7SShijie Luo min_scope <= scope) { 14221da177e4SLinus Torvalds addr = ifa->ifa_local; 14231da177e4SLinus Torvalds if (same) 14241da177e4SLinus Torvalds break; 14251da177e4SLinus Torvalds } 14261da177e4SLinus Torvalds if (!same) { 14271da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 14281da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 14291da177e4SLinus Torvalds if (same && addr) { 14301da177e4SLinus Torvalds if (local || !dst) 14311da177e4SLinus Torvalds break; 14321da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 14331da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 14341da177e4SLinus Torvalds break; 14351da177e4SLinus Torvalds /* No, then can we use new local src? */ 1436650638a7SShijie Luo if (min_scope <= scope) { 14371da177e4SLinus Torvalds addr = ifa->ifa_local; 14381da177e4SLinus Torvalds break; 14391da177e4SLinus Torvalds } 14401da177e4SLinus Torvalds /* search for large dst subnet for addr */ 14411da177e4SLinus Torvalds same = 0; 14421da177e4SLinus Torvalds } 14431da177e4SLinus Torvalds } 1444ef11db33SFlorian Westphal } 14451da177e4SLinus Torvalds 14461da177e4SLinus Torvalds return same ? addr : 0; 14471da177e4SLinus Torvalds } 14481da177e4SLinus Torvalds 14491da177e4SLinus Torvalds /* 14501da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 1451b601fa19SNicolas Dichtel * - net: netns to check, cannot be NULL 1452b601fa19SNicolas Dichtel * - in_dev: only on this interface, NULL=any interface 14531da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 14541da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 14551da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 14561da177e4SLinus Torvalds */ 1457b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, 14589bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 14591da177e4SLinus Torvalds { 146060cad5daSAl Viro __be32 addr = 0; 14619bd85e32SDenis V. Lunev struct net_device *dev; 14621da177e4SLinus Torvalds 146300db4124SIan Morris if (in_dev) 14649bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 14651da177e4SLinus Torvalds 14661da177e4SLinus Torvalds rcu_read_lock(); 1467c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 14689f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 14699f9354b9SEric Dumazet if (in_dev) { 14701da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 14711da177e4SLinus Torvalds if (addr) 14721da177e4SLinus Torvalds break; 14731da177e4SLinus Torvalds } 14741da177e4SLinus Torvalds } 14751da177e4SLinus Torvalds rcu_read_unlock(); 14761da177e4SLinus Torvalds 14771da177e4SLinus Torvalds return addr; 14781da177e4SLinus Torvalds } 1479eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 14801da177e4SLinus Torvalds 14811da177e4SLinus Torvalds /* 14821da177e4SLinus Torvalds * Device notifier 14831da177e4SLinus Torvalds */ 14841da177e4SLinus Torvalds 14851da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 14861da177e4SLinus Torvalds { 1487e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 14881da177e4SLinus Torvalds } 14899f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 14901da177e4SLinus Torvalds 14911da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 14921da177e4SLinus Torvalds { 1493e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 14941da177e4SLinus Torvalds } 14959f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 14961da177e4SLinus Torvalds 14973ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb) 14983ad7d246SKrister Johansen { 14993ad7d246SKrister Johansen return blocking_notifier_chain_register(&inetaddr_validator_chain, nb); 15003ad7d246SKrister Johansen } 15013ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier); 15023ad7d246SKrister Johansen 15033ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb) 15043ad7d246SKrister Johansen { 15053ad7d246SKrister Johansen return blocking_notifier_chain_unregister(&inetaddr_validator_chain, 15063ad7d246SKrister Johansen nb); 15073ad7d246SKrister Johansen } 15083ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier); 15093ad7d246SKrister Johansen 15109f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 15119f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 15121da177e4SLinus Torvalds */ 15131da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 15141da177e4SLinus Torvalds { 15151da177e4SLinus Torvalds struct in_ifaddr *ifa; 15161da177e4SLinus Torvalds int named = 0; 15171da177e4SLinus Torvalds 1518ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 15191da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 15201da177e4SLinus Torvalds 15211da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 15221da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 15231da177e4SLinus Torvalds if (named++ == 0) 1524573bf470SThomas Graf goto skip; 152544344b2aSMark McLoughlin dot = strchr(old, ':'); 152651456b29SIan Morris if (!dot) { 15271da177e4SLinus Torvalds sprintf(old, ":%d", named); 15281da177e4SLinus Torvalds dot = old; 15291da177e4SLinus Torvalds } 15309f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 15311da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 15329f9354b9SEric Dumazet else 15331da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1534573bf470SThomas Graf skip: 1535573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 15361da177e4SLinus Torvalds } 15371da177e4SLinus Torvalds } 15381da177e4SLinus Torvalds 1539d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1540d11327adSIan Campbell struct in_device *in_dev) 1541d11327adSIan Campbell 1542d11327adSIan Campbell { 1543ef11db33SFlorian Westphal const struct in_ifaddr *ifa; 1544d11327adSIan Campbell 1545ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 1546d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 15476c91afe1SDavid S. Miller ifa->ifa_local, dev, 15486c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1549d11327adSIan Campbell dev->dev_addr, NULL); 1550d11327adSIan Campbell } 1551b76d0789SZoltan Kiss } 1552d11327adSIan Campbell 15531da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 15541da177e4SLinus Torvalds 15551da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 15561da177e4SLinus Torvalds void *ptr) 15571da177e4SLinus Torvalds { 1558351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1559748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 15601da177e4SLinus Torvalds 15611da177e4SLinus Torvalds ASSERT_RTNL(); 15621da177e4SLinus Torvalds 15631da177e4SLinus Torvalds if (!in_dev) { 15648030f544SHerbert Xu if (event == NETDEV_REGISTER) { 15651da177e4SLinus Torvalds in_dev = inetdev_init(dev); 156620e61da7SWANG Cong if (IS_ERR(in_dev)) 156720e61da7SWANG Cong return notifier_from_errno(PTR_ERR(in_dev)); 15680cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 156942f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 157042f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 15711da177e4SLinus Torvalds } 157206770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 157306770843SBreno Leitao /* Re-enabling IP */ 157406770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 157506770843SBreno Leitao in_dev = inetdev_init(dev); 15768030f544SHerbert Xu } 15771da177e4SLinus Torvalds goto out; 15781da177e4SLinus Torvalds } 15791da177e4SLinus Torvalds 15801da177e4SLinus Torvalds switch (event) { 15811da177e4SLinus Torvalds case NETDEV_REGISTER: 158291df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1583a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 15841da177e4SLinus Torvalds break; 15851da177e4SLinus Torvalds case NETDEV_UP: 158606770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 15871da177e4SLinus Torvalds break; 15880cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 1589*6e701eb9SKuniyuki Iwashima struct in_ifaddr *ifa = inet_alloc_ifa(in_dev); 15909f9354b9SEric Dumazet 15919f9354b9SEric Dumazet if (ifa) { 1592fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 15931da177e4SLinus Torvalds ifa->ifa_local = 15941da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 15951da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 15961da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 15971da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 15981da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 15995c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 16005c766d64SJiri Pirko INFINITY_LIFE_TIME); 1601dfd1582dSJiri Pirko ipv4_devconf_setall(in_dev); 1602dfd1582dSJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 16031da177e4SLinus Torvalds inet_insert_ifa(ifa); 16041da177e4SLinus Torvalds } 16051da177e4SLinus Torvalds } 16061da177e4SLinus Torvalds ip_mc_up(in_dev); 1607a8eceea8SJoe Perches fallthrough; 1608eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1609d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1610d11327adSIan Campbell break; 1611a8eceea8SJoe Perches fallthrough; 1612d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1613a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1614d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 16151da177e4SLinus Torvalds break; 16161da177e4SLinus Torvalds case NETDEV_DOWN: 16171da177e4SLinus Torvalds ip_mc_down(in_dev); 16181da177e4SLinus Torvalds break; 161993d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 162075c78500SMoni Shoua ip_mc_unmap(in_dev); 162175c78500SMoni Shoua break; 162293d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 162375c78500SMoni Shoua ip_mc_remap(in_dev); 162475c78500SMoni Shoua break; 16251da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 162606770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 16271da177e4SLinus Torvalds break; 162806770843SBreno Leitao /* disable IP when MTU is not enough */ 1629a8eceea8SJoe Perches fallthrough; 16301da177e4SLinus Torvalds case NETDEV_UNREGISTER: 16311da177e4SLinus Torvalds inetdev_destroy(in_dev); 16321da177e4SLinus Torvalds break; 16331da177e4SLinus Torvalds case NETDEV_CHANGENAME: 16341da177e4SLinus Torvalds /* Do not notify about label change, this event is 16351da177e4SLinus Torvalds * not interesting to applications using netlink. 16361da177e4SLinus Torvalds */ 16371da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 16381da177e4SLinus Torvalds 163951602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 164066f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 16411da177e4SLinus Torvalds break; 16421da177e4SLinus Torvalds } 16431da177e4SLinus Torvalds out: 16441da177e4SLinus Torvalds return NOTIFY_DONE; 16451da177e4SLinus Torvalds } 16461da177e4SLinus Torvalds 16471da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 16481da177e4SLinus Torvalds .notifier_call = inetdev_event, 16491da177e4SLinus Torvalds }; 16501da177e4SLinus Torvalds 165140384999SEric Dumazet static size_t inet_nlmsg_size(void) 1652339bf98fSThomas Graf { 1653339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1654339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1655339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1656339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1657ad6c8135SJiri Pirko + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ 165863b5f152SGeert Uytterhoeven + nla_total_size(4) /* IFA_FLAGS */ 165947f0bd50SJacques de Laval + nla_total_size(1) /* IFA_PROTO */ 1660af4d768aSDavid Ahern + nla_total_size(4) /* IFA_RT_PRIORITY */ 166163b5f152SGeert Uytterhoeven + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ 1662339bf98fSThomas Graf } 1663339bf98fSThomas Graf 16645c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 16655c766d64SJiri Pirko { 16665c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 16675c766d64SJiri Pirko } 16685c766d64SJiri Pirko 16695c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 16705c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 16715c766d64SJiri Pirko { 16725c766d64SJiri Pirko struct ifa_cacheinfo ci; 16735c766d64SJiri Pirko 16745c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 16755c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 16765c766d64SJiri Pirko ci.ifa_prefered = preferred; 16775c766d64SJiri Pirko ci.ifa_valid = valid; 16785c766d64SJiri Pirko 16795c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 16805c766d64SJiri Pirko } 16815c766d64SJiri Pirko 1682cdb2f80fSEric Dumazet static int inet_fill_ifaddr(struct sk_buff *skb, const struct in_ifaddr *ifa, 1683978a46faSChristian Brauner struct inet_fill_args *args) 16841da177e4SLinus Torvalds { 16851da177e4SLinus Torvalds struct ifaddrmsg *ifm; 16861da177e4SLinus Torvalds struct nlmsghdr *nlh; 16873cd3e72cSEric Dumazet unsigned long tstamp; 16885c766d64SJiri Pirko u32 preferred, valid; 16891af7f88aSEric Dumazet u32 flags; 16901da177e4SLinus Torvalds 1691978a46faSChristian Brauner nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm), 1692978a46faSChristian Brauner args->flags); 169351456b29SIan Morris if (!nlh) 169426932566SPatrick McHardy return -EMSGSIZE; 169547f68512SThomas Graf 169647f68512SThomas Graf ifm = nlmsg_data(nlh); 16971da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 16981da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 16991af7f88aSEric Dumazet 17001af7f88aSEric Dumazet flags = READ_ONCE(ifa->ifa_flags); 17011af7f88aSEric Dumazet /* Warning : ifm->ifa_flags is an __u8, it holds only 8 bits. 17021af7f88aSEric Dumazet * The 32bit value is given in IFA_FLAGS attribute. 17031af7f88aSEric Dumazet */ 17041af7f88aSEric Dumazet ifm->ifa_flags = (__u8)flags; 17051af7f88aSEric Dumazet 17061da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 17071da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 17081da177e4SLinus Torvalds 1709978a46faSChristian Brauner if (args->netnsid >= 0 && 1710978a46faSChristian Brauner nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) 1711d3807145SChristian Brauner goto nla_put_failure; 1712d3807145SChristian Brauner 17133cd3e72cSEric Dumazet tstamp = READ_ONCE(ifa->ifa_tstamp); 17141af7f88aSEric Dumazet if (!(flags & IFA_F_PERMANENT)) { 17159f6fa3c4SEric Dumazet preferred = READ_ONCE(ifa->ifa_preferred_lft); 1716a5fcf74dSEric Dumazet valid = READ_ONCE(ifa->ifa_valid_lft); 17175c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 17183cd3e72cSEric Dumazet long tval = (jiffies - tstamp) / HZ; 17195c766d64SJiri Pirko 17205c766d64SJiri Pirko if (preferred > tval) 17215c766d64SJiri Pirko preferred -= tval; 17225c766d64SJiri Pirko else 17235c766d64SJiri Pirko preferred = 0; 17245c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 17255c766d64SJiri Pirko if (valid > tval) 17265c766d64SJiri Pirko valid -= tval; 17275c766d64SJiri Pirko else 17285c766d64SJiri Pirko valid = 0; 17295c766d64SJiri Pirko } 17305c766d64SJiri Pirko } 17315c766d64SJiri Pirko } else { 17325c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 17335c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 17345c766d64SJiri Pirko } 1735f3756b79SDavid S. Miller if ((ifa->ifa_address && 1736930345eaSJiri Benc nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) || 1737f3756b79SDavid S. Miller (ifa->ifa_local && 1738930345eaSJiri Benc nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) || 1739f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1740930345eaSJiri Benc nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1741f3756b79SDavid S. Miller (ifa->ifa_label[0] && 17425c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 174347f0bd50SJacques de Laval (ifa->ifa_proto && 174447f0bd50SJacques de Laval nla_put_u8(skb, IFA_PROTO, ifa->ifa_proto)) || 17451af7f88aSEric Dumazet nla_put_u32(skb, IFA_FLAGS, flags) || 1746af4d768aSDavid Ahern (ifa->ifa_rt_priority && 1747af4d768aSDavid Ahern nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) || 17483cd3e72cSEric Dumazet put_cacheinfo(skb, READ_ONCE(ifa->ifa_cstamp), tstamp, 17495c766d64SJiri Pirko preferred, valid)) 1750f3756b79SDavid S. Miller goto nla_put_failure; 175147f68512SThomas Graf 1752053c095aSJohannes Berg nlmsg_end(skb, nlh); 1753053c095aSJohannes Berg return 0; 175447f68512SThomas Graf 175547f68512SThomas Graf nla_put_failure: 175626932566SPatrick McHardy nlmsg_cancel(skb, nlh); 175726932566SPatrick McHardy return -EMSGSIZE; 17581da177e4SLinus Torvalds } 17591da177e4SLinus Torvalds 1760c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh, 1761c33078e3SDavid Ahern struct inet_fill_args *fillargs, 1762c33078e3SDavid Ahern struct net **tgt_net, struct sock *sk, 17635fcd266aSDavid Ahern struct netlink_callback *cb) 1764c33078e3SDavid Ahern { 17655fcd266aSDavid Ahern struct netlink_ext_ack *extack = cb->extack; 1766c33078e3SDavid Ahern struct nlattr *tb[IFA_MAX+1]; 1767c33078e3SDavid Ahern struct ifaddrmsg *ifm; 1768c33078e3SDavid Ahern int err, i; 1769c33078e3SDavid Ahern 1770c33078e3SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 1771c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request"); 1772c33078e3SDavid Ahern return -EINVAL; 1773c33078e3SDavid Ahern } 1774c33078e3SDavid Ahern 1775c33078e3SDavid Ahern ifm = nlmsg_data(nlh); 1776c33078e3SDavid Ahern if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { 1777c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request"); 1778c33078e3SDavid Ahern return -EINVAL; 1779c33078e3SDavid Ahern } 17805fcd266aSDavid Ahern 17815fcd266aSDavid Ahern fillargs->ifindex = ifm->ifa_index; 17825fcd266aSDavid Ahern if (fillargs->ifindex) { 17835fcd266aSDavid Ahern cb->answer_flags |= NLM_F_DUMP_FILTERED; 17845fcd266aSDavid Ahern fillargs->flags |= NLM_F_DUMP_FILTERED; 1785c33078e3SDavid Ahern } 1786c33078e3SDavid Ahern 17878cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX, 1788c33078e3SDavid Ahern ifa_ipv4_policy, extack); 1789c33078e3SDavid Ahern if (err < 0) 1790c33078e3SDavid Ahern return err; 1791c33078e3SDavid Ahern 1792c33078e3SDavid Ahern for (i = 0; i <= IFA_MAX; ++i) { 1793c33078e3SDavid Ahern if (!tb[i]) 1794c33078e3SDavid Ahern continue; 1795c33078e3SDavid Ahern 1796c33078e3SDavid Ahern if (i == IFA_TARGET_NETNSID) { 1797c33078e3SDavid Ahern struct net *net; 1798c33078e3SDavid Ahern 1799c33078e3SDavid Ahern fillargs->netnsid = nla_get_s32(tb[i]); 1800c33078e3SDavid Ahern 1801c33078e3SDavid Ahern net = rtnl_get_net_ns_capable(sk, fillargs->netnsid); 1802c33078e3SDavid Ahern if (IS_ERR(net)) { 1803bf4cc40eSBjørn Mork fillargs->netnsid = -1; 1804c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id"); 1805c33078e3SDavid Ahern return PTR_ERR(net); 1806c33078e3SDavid Ahern } 1807c33078e3SDavid Ahern *tgt_net = net; 1808c33078e3SDavid Ahern } else { 1809c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request"); 1810c33078e3SDavid Ahern return -EINVAL; 1811c33078e3SDavid Ahern } 1812c33078e3SDavid Ahern } 1813c33078e3SDavid Ahern 1814c33078e3SDavid Ahern return 0; 1815c33078e3SDavid Ahern } 1816c33078e3SDavid Ahern 18171c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb, 1818cdb2f80fSEric Dumazet struct netlink_callback *cb, int *s_ip_idx, 18191c98eca4SDavid Ahern struct inet_fill_args *fillargs) 18201c98eca4SDavid Ahern { 18211c98eca4SDavid Ahern struct in_ifaddr *ifa; 18221c98eca4SDavid Ahern int ip_idx = 0; 18231c98eca4SDavid Ahern int err; 18241c98eca4SDavid Ahern 1825cdb2f80fSEric Dumazet in_dev_for_each_ifa_rcu(ifa, in_dev) { 1826cdb2f80fSEric Dumazet if (ip_idx < *s_ip_idx) { 1827ef11db33SFlorian Westphal ip_idx++; 18281c98eca4SDavid Ahern continue; 1829ef11db33SFlorian Westphal } 18301c98eca4SDavid Ahern err = inet_fill_ifaddr(skb, ifa, fillargs); 18311c98eca4SDavid Ahern if (err < 0) 18321c98eca4SDavid Ahern goto done; 18331c98eca4SDavid Ahern 18341c98eca4SDavid Ahern nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1835ef11db33SFlorian Westphal ip_idx++; 18361c98eca4SDavid Ahern } 18371c98eca4SDavid Ahern err = 0; 1838cdb2f80fSEric Dumazet ip_idx = 0; 18391c98eca4SDavid Ahern done: 1840cdb2f80fSEric Dumazet *s_ip_idx = ip_idx; 18411c98eca4SDavid Ahern 18421c98eca4SDavid Ahern return err; 18431c98eca4SDavid Ahern } 18441c98eca4SDavid Ahern 1845081a0e3bSEric Dumazet /* Combine dev_addr_genid and dev_base_seq to detect changes. 1846081a0e3bSEric Dumazet */ 1847081a0e3bSEric Dumazet static u32 inet_base_seq(const struct net *net) 1848081a0e3bSEric Dumazet { 1849081a0e3bSEric Dumazet u32 res = atomic_read(&net->ipv4.dev_addr_genid) + 1850590e92cdSEric Dumazet READ_ONCE(net->dev_base_seq); 1851081a0e3bSEric Dumazet 1852081a0e3bSEric Dumazet /* Must not return 0 (see nl_dump_check_consistent()). 1853081a0e3bSEric Dumazet * Chose a value far away from 0. 1854081a0e3bSEric Dumazet */ 1855081a0e3bSEric Dumazet if (!res) 1856081a0e3bSEric Dumazet res = 0x80000000; 1857081a0e3bSEric Dumazet return res; 1858081a0e3bSEric Dumazet } 1859081a0e3bSEric Dumazet 18601da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 18611da177e4SLinus Torvalds { 1862c33078e3SDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 1863978a46faSChristian Brauner struct inet_fill_args fillargs = { 1864978a46faSChristian Brauner .portid = NETLINK_CB(cb->skb).portid, 1865c33078e3SDavid Ahern .seq = nlh->nlmsg_seq, 1866978a46faSChristian Brauner .event = RTM_NEWADDR, 1867978a46faSChristian Brauner .flags = NLM_F_MULTI, 1868978a46faSChristian Brauner .netnsid = -1, 1869978a46faSChristian Brauner }; 18703b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1871d3807145SChristian Brauner struct net *tgt_net = net; 1872cdb2f80fSEric Dumazet struct { 1873cdb2f80fSEric Dumazet unsigned long ifindex; 1874cdb2f80fSEric Dumazet int ip_idx; 1875cdb2f80fSEric Dumazet } *ctx = (void *)cb->ctx; 18761da177e4SLinus Torvalds struct in_device *in_dev; 1877cdb2f80fSEric Dumazet struct net_device *dev; 1878d7e38611SDavid Ahern int err = 0; 18791da177e4SLinus Torvalds 1880cdb2f80fSEric Dumazet rcu_read_lock(); 1881c33078e3SDavid Ahern if (cb->strict_check) { 1882c33078e3SDavid Ahern err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 18835fcd266aSDavid Ahern skb->sk, cb); 1884c33078e3SDavid Ahern if (err < 0) 1885cdb2f80fSEric Dumazet goto done; 18865fcd266aSDavid Ahern 18875fcd266aSDavid Ahern if (fillargs.ifindex) { 1888cdb2f80fSEric Dumazet dev = dev_get_by_index_rcu(tgt_net, fillargs.ifindex); 18897b05ab85SIdo Schimmel if (!dev) { 18907b05ab85SIdo Schimmel err = -ENODEV; 1891cdb2f80fSEric Dumazet goto done; 18927b05ab85SIdo Schimmel } 1893eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 18949f9354b9SEric Dumazet if (!in_dev) 1895cdb2f80fSEric Dumazet goto done; 1896cdb2f80fSEric Dumazet err = in_dev_dump_addr(in_dev, skb, cb, &ctx->ip_idx, 18971c98eca4SDavid Ahern &fillargs); 18981da177e4SLinus Torvalds goto done; 18991da177e4SLinus Torvalds } 1900eec4df98SEric Dumazet } 19011da177e4SLinus Torvalds 1902cdb2f80fSEric Dumazet cb->seq = inet_base_seq(tgt_net); 1903cdb2f80fSEric Dumazet 1904b8c8abefSAlexander Mikhalitsyn for_each_netdev_dump(tgt_net, dev, ctx->ifindex) { 1905cdb2f80fSEric Dumazet in_dev = __in_dev_get_rcu(dev); 1906cdb2f80fSEric Dumazet if (!in_dev) 1907cdb2f80fSEric Dumazet continue; 1908cdb2f80fSEric Dumazet err = in_dev_dump_addr(in_dev, skb, cb, &ctx->ip_idx, 1909cdb2f80fSEric Dumazet &fillargs); 1910cdb2f80fSEric Dumazet if (err < 0) 1911cdb2f80fSEric Dumazet goto done; 1912cdb2f80fSEric Dumazet } 19131da177e4SLinus Torvalds done: 1914978a46faSChristian Brauner if (fillargs.netnsid >= 0) 1915d3807145SChristian Brauner put_net(tgt_net); 1916cdb2f80fSEric Dumazet rcu_read_unlock(); 1917cdb2f80fSEric Dumazet return err; 19181da177e4SLinus Torvalds } 19191da177e4SLinus Torvalds 1920d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 192115e47304SEric W. Biederman u32 portid) 19221da177e4SLinus Torvalds { 1923978a46faSChristian Brauner struct inet_fill_args fillargs = { 1924978a46faSChristian Brauner .portid = portid, 1925978a46faSChristian Brauner .seq = nlh ? nlh->nlmsg_seq : 0, 1926978a46faSChristian Brauner .event = event, 1927978a46faSChristian Brauner .flags = 0, 1928978a46faSChristian Brauner .netnsid = -1, 1929978a46faSChristian Brauner }; 193047f68512SThomas Graf struct sk_buff *skb; 1931d6062cbbSThomas Graf int err = -ENOBUFS; 19324b8aa9abSDenis V. Lunev struct net *net; 19331da177e4SLinus Torvalds 1934c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1935339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 193651456b29SIan Morris if (!skb) 1937d6062cbbSThomas Graf goto errout; 1938d6062cbbSThomas Graf 1939978a46faSChristian Brauner err = inet_fill_ifaddr(skb, ifa, &fillargs); 194026932566SPatrick McHardy if (err < 0) { 194126932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 194226932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 194326932566SPatrick McHardy kfree_skb(skb); 194426932566SPatrick McHardy goto errout; 194526932566SPatrick McHardy } 194615e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 19471ce85fe4SPablo Neira Ayuso return; 1948d6062cbbSThomas Graf errout: 1949d6062cbbSThomas Graf if (err < 0) 19504b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 19511da177e4SLinus Torvalds } 19521da177e4SLinus Torvalds 1953b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev, 1954b1974ed0SArad, Ronen u32 ext_filter_mask) 19559f0f7272SThomas Graf { 19561fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 19579f0f7272SThomas Graf 19589f0f7272SThomas Graf if (!in_dev) 19599f0f7272SThomas Graf return 0; 19609f0f7272SThomas Graf 19619f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 19629f0f7272SThomas Graf } 19639f0f7272SThomas Graf 1964d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev, 1965d5566fd7SSowmini Varadhan u32 ext_filter_mask) 19669f0f7272SThomas Graf { 19671fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 19689f0f7272SThomas Graf struct nlattr *nla; 19699f0f7272SThomas Graf int i; 19709f0f7272SThomas Graf 19719f0f7272SThomas Graf if (!in_dev) 19729f0f7272SThomas Graf return -ENODATA; 19739f0f7272SThomas Graf 19749f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 197551456b29SIan Morris if (!nla) 19769f0f7272SThomas Graf return -EMSGSIZE; 19779f0f7272SThomas Graf 19789f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 19790598f8f3SEric Dumazet ((u32 *) nla_data(nla))[i] = READ_ONCE(in_dev->cnf.data[i]); 19809f0f7272SThomas Graf 19819f0f7272SThomas Graf return 0; 19829f0f7272SThomas Graf } 19839f0f7272SThomas Graf 19849f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 19859f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 19869f0f7272SThomas Graf }; 19879f0f7272SThomas Graf 1988cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 19898679c31eSRocco Yue const struct nlattr *nla, 19908679c31eSRocco Yue struct netlink_ext_ack *extack) 19919f0f7272SThomas Graf { 19929f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 19939f0f7272SThomas Graf int err, rem; 19949f0f7272SThomas Graf 1995a100243dSCong Wang if (dev && !__in_dev_get_rtnl(dev)) 1996cf7afbfeSThomas Graf return -EAFNOSUPPORT; 19979f0f7272SThomas Graf 19988cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, 19998679c31eSRocco Yue inet_af_policy, extack); 20009f0f7272SThomas Graf if (err < 0) 20019f0f7272SThomas Graf return err; 20029f0f7272SThomas Graf 20039f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 20049f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 20059f0f7272SThomas Graf int cfgid = nla_type(a); 20069f0f7272SThomas Graf 20079f0f7272SThomas Graf if (nla_len(a) < 4) 20089f0f7272SThomas Graf return -EINVAL; 20099f0f7272SThomas Graf 20109f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 20119f0f7272SThomas Graf return -EINVAL; 20129f0f7272SThomas Graf } 20139f0f7272SThomas Graf } 20149f0f7272SThomas Graf 2015cf7afbfeSThomas Graf return 0; 2016cf7afbfeSThomas Graf } 2017cf7afbfeSThomas Graf 20183583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla, 20193583a4e8SStephen Hemminger struct netlink_ext_ack *extack) 2020cf7afbfeSThomas Graf { 2021a100243dSCong Wang struct in_device *in_dev = __in_dev_get_rtnl(dev); 2022cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 2023cf7afbfeSThomas Graf int rem; 2024cf7afbfeSThomas Graf 2025cf7afbfeSThomas Graf if (!in_dev) 2026cf7afbfeSThomas Graf return -EAFNOSUPPORT; 2027cf7afbfeSThomas Graf 20288cb08174SJohannes Berg if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0) 20295ac6b198SZheng Yongjun return -EINVAL; 2030cf7afbfeSThomas Graf 20319f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 20329f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 20339f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 20349f0f7272SThomas Graf } 20359f0f7272SThomas Graf 20369f0f7272SThomas Graf return 0; 20379f0f7272SThomas Graf } 20389f0f7272SThomas Graf 2039edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 2040edc9e748SNicolas Dichtel { 2041edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 2042edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 2043136ba622SZhang Shengju bool all = false; 2044edc9e748SNicolas Dichtel 2045136ba622SZhang Shengju if (type == NETCONFA_ALL) 2046136ba622SZhang Shengju all = true; 2047136ba622SZhang Shengju 2048136ba622SZhang Shengju if (all || type == NETCONFA_FORWARDING) 2049edc9e748SNicolas Dichtel size += nla_total_size(4); 2050136ba622SZhang Shengju if (all || type == NETCONFA_RP_FILTER) 2051cc535dfbSNicolas Dichtel size += nla_total_size(4); 2052136ba622SZhang Shengju if (all || type == NETCONFA_MC_FORWARDING) 2053d67b8c61SNicolas Dichtel size += nla_total_size(4); 20545cbf777cSXin Long if (all || type == NETCONFA_BC_FORWARDING) 20555cbf777cSXin Long size += nla_total_size(4); 2056136ba622SZhang Shengju if (all || type == NETCONFA_PROXY_NEIGH) 2057f085ff1cSstephen hemminger size += nla_total_size(4); 2058136ba622SZhang Shengju if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) 2059974d7af5SAndy Gospodarek size += nla_total_size(4); 2060edc9e748SNicolas Dichtel 2061edc9e748SNicolas Dichtel return size; 2062edc9e748SNicolas Dichtel } 2063edc9e748SNicolas Dichtel 2064edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 20650598f8f3SEric Dumazet const struct ipv4_devconf *devconf, 20660598f8f3SEric Dumazet u32 portid, u32 seq, int event, 20670598f8f3SEric Dumazet unsigned int flags, int type) 2068edc9e748SNicolas Dichtel { 2069edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 2070edc9e748SNicolas Dichtel struct netconfmsg *ncm; 2071136ba622SZhang Shengju bool all = false; 2072edc9e748SNicolas Dichtel 2073edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 2074edc9e748SNicolas Dichtel flags); 207551456b29SIan Morris if (!nlh) 2076edc9e748SNicolas Dichtel return -EMSGSIZE; 2077edc9e748SNicolas Dichtel 2078136ba622SZhang Shengju if (type == NETCONFA_ALL) 2079136ba622SZhang Shengju all = true; 2080136ba622SZhang Shengju 2081edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 2082edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 2083edc9e748SNicolas Dichtel 2084edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 2085edc9e748SNicolas Dichtel goto nla_put_failure; 2086edc9e748SNicolas Dichtel 2087b5c9641dSDavid Ahern if (!devconf) 2088b5c9641dSDavid Ahern goto out; 2089b5c9641dSDavid Ahern 2090136ba622SZhang Shengju if ((all || type == NETCONFA_FORWARDING) && 2091edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 20920598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, FORWARDING)) < 0) 2093edc9e748SNicolas Dichtel goto nla_put_failure; 2094136ba622SZhang Shengju if ((all || type == NETCONFA_RP_FILTER) && 2095cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 20960598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, RP_FILTER)) < 0) 2097cc535dfbSNicolas Dichtel goto nla_put_failure; 2098136ba622SZhang Shengju if ((all || type == NETCONFA_MC_FORWARDING) && 2099d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 21000598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, MC_FORWARDING)) < 0) 2101d67b8c61SNicolas Dichtel goto nla_put_failure; 21025cbf777cSXin Long if ((all || type == NETCONFA_BC_FORWARDING) && 21035cbf777cSXin Long nla_put_s32(skb, NETCONFA_BC_FORWARDING, 21040598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, BC_FORWARDING)) < 0) 21055cbf777cSXin Long goto nla_put_failure; 2106136ba622SZhang Shengju if ((all || type == NETCONFA_PROXY_NEIGH) && 210709aea5dfSstephen hemminger nla_put_s32(skb, NETCONFA_PROXY_NEIGH, 21080598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, PROXY_ARP)) < 0) 2109f085ff1cSstephen hemminger goto nla_put_failure; 2110136ba622SZhang Shengju if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) && 2111974d7af5SAndy Gospodarek nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 21120598f8f3SEric Dumazet IPV4_DEVCONF_RO(*devconf, 21130598f8f3SEric Dumazet IGNORE_ROUTES_WITH_LINKDOWN)) < 0) 2114974d7af5SAndy Gospodarek goto nla_put_failure; 2115edc9e748SNicolas Dichtel 2116b5c9641dSDavid Ahern out: 2117053c095aSJohannes Berg nlmsg_end(skb, nlh); 2118053c095aSJohannes Berg return 0; 2119edc9e748SNicolas Dichtel 2120edc9e748SNicolas Dichtel nla_put_failure: 2121edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 2122edc9e748SNicolas Dichtel return -EMSGSIZE; 2123edc9e748SNicolas Dichtel } 2124edc9e748SNicolas Dichtel 21253b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type, 21263b022865SDavid Ahern int ifindex, struct ipv4_devconf *devconf) 2127edc9e748SNicolas Dichtel { 2128edc9e748SNicolas Dichtel struct sk_buff *skb; 2129edc9e748SNicolas Dichtel int err = -ENOBUFS; 2130edc9e748SNicolas Dichtel 2131fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL); 213251456b29SIan Morris if (!skb) 2133edc9e748SNicolas Dichtel goto errout; 2134edc9e748SNicolas Dichtel 2135edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 21363b022865SDavid Ahern event, 0, type); 2137edc9e748SNicolas Dichtel if (err < 0) { 2138edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 2139edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 2140edc9e748SNicolas Dichtel kfree_skb(skb); 2141edc9e748SNicolas Dichtel goto errout; 2142edc9e748SNicolas Dichtel } 2143fa17806cSEric Dumazet rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL); 2144edc9e748SNicolas Dichtel return; 2145edc9e748SNicolas Dichtel errout: 2146edc9e748SNicolas Dichtel if (err < 0) 2147edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 2148edc9e748SNicolas Dichtel } 2149edc9e748SNicolas Dichtel 21509e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 21519e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 21529e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 2153cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 215409aea5dfSstephen hemminger [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 2155974d7af5SAndy Gospodarek [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) }, 21569e551110SNicolas Dichtel }; 21579e551110SNicolas Dichtel 2158eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb, 2159eede370dSJakub Kicinski const struct nlmsghdr *nlh, 2160eede370dSJakub Kicinski struct nlattr **tb, 2161eede370dSJakub Kicinski struct netlink_ext_ack *extack) 2162eede370dSJakub Kicinski { 2163eede370dSJakub Kicinski int i, err; 2164eede370dSJakub Kicinski 2165eede370dSJakub Kicinski if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) { 2166eede370dSJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request"); 2167eede370dSJakub Kicinski return -EINVAL; 2168eede370dSJakub Kicinski } 2169eede370dSJakub Kicinski 2170eede370dSJakub Kicinski if (!netlink_strict_get_check(skb)) 21718cb08174SJohannes Berg return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg), 21728cb08174SJohannes Berg tb, NETCONFA_MAX, 21738cb08174SJohannes Berg devconf_ipv4_policy, extack); 2174eede370dSJakub Kicinski 21758cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg), 21768cb08174SJohannes Berg tb, NETCONFA_MAX, 21778cb08174SJohannes Berg devconf_ipv4_policy, extack); 2178eede370dSJakub Kicinski if (err) 2179eede370dSJakub Kicinski return err; 2180eede370dSJakub Kicinski 2181eede370dSJakub Kicinski for (i = 0; i <= NETCONFA_MAX; i++) { 2182eede370dSJakub Kicinski if (!tb[i]) 2183eede370dSJakub Kicinski continue; 2184eede370dSJakub Kicinski 2185eede370dSJakub Kicinski switch (i) { 2186eede370dSJakub Kicinski case NETCONFA_IFINDEX: 2187eede370dSJakub Kicinski break; 2188eede370dSJakub Kicinski default: 2189eede370dSJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request"); 2190eede370dSJakub Kicinski return -EINVAL; 2191eede370dSJakub Kicinski } 2192eede370dSJakub Kicinski } 2193eede370dSJakub Kicinski 2194eede370dSJakub Kicinski return 0; 2195eede370dSJakub Kicinski } 2196eede370dSJakub Kicinski 21979e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 2198c21ef3e3SDavid Ahern struct nlmsghdr *nlh, 2199c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 22009e551110SNicolas Dichtel { 22019e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 22029e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX + 1]; 2203bbcf9105SEric Dumazet const struct ipv4_devconf *devconf; 2204bbcf9105SEric Dumazet struct in_device *in_dev = NULL; 2205bbcf9105SEric Dumazet struct net_device *dev = NULL; 22069e551110SNicolas Dichtel struct sk_buff *skb; 22079e551110SNicolas Dichtel int ifindex; 22089e551110SNicolas Dichtel int err; 22099e551110SNicolas Dichtel 2210eede370dSJakub Kicinski err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack); 2211eede370dSJakub Kicinski if (err) 2212bbcf9105SEric Dumazet return err; 22139e551110SNicolas Dichtel 22149e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 2215bbcf9105SEric Dumazet return -EINVAL; 22169e551110SNicolas Dichtel 22179e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 22189e551110SNicolas Dichtel switch (ifindex) { 22199e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 22209e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 22219e551110SNicolas Dichtel break; 22229e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 22239e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 22249e551110SNicolas Dichtel break; 22259e551110SNicolas Dichtel default: 2226bbcf9105SEric Dumazet err = -ENODEV; 2227bbcf9105SEric Dumazet dev = dev_get_by_index(net, ifindex); 2228bbcf9105SEric Dumazet if (dev) 2229bbcf9105SEric Dumazet in_dev = in_dev_get(dev); 223051456b29SIan Morris if (!in_dev) 22319e551110SNicolas Dichtel goto errout; 22329e551110SNicolas Dichtel devconf = &in_dev->cnf; 22339e551110SNicolas Dichtel break; 22349e551110SNicolas Dichtel } 22359e551110SNicolas Dichtel 22369e551110SNicolas Dichtel err = -ENOBUFS; 2237fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 223851456b29SIan Morris if (!skb) 22399e551110SNicolas Dichtel goto errout; 22409e551110SNicolas Dichtel 22419e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 22429e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 22439e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 2244136ba622SZhang Shengju NETCONFA_ALL); 22459e551110SNicolas Dichtel if (err < 0) { 22469e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 22479e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 22489e551110SNicolas Dichtel kfree_skb(skb); 22499e551110SNicolas Dichtel goto errout; 22509e551110SNicolas Dichtel } 22519e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 22529e551110SNicolas Dichtel errout: 2253bbcf9105SEric Dumazet if (in_dev) 2254bbcf9105SEric Dumazet in_dev_put(in_dev); 2255bbcf9105SEric Dumazet dev_put(dev); 22569e551110SNicolas Dichtel return err; 22579e551110SNicolas Dichtel } 22589e551110SNicolas Dichtel 22597a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 22607a674200SNicolas Dichtel struct netlink_callback *cb) 22617a674200SNicolas Dichtel { 2262addd383fSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 22637a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 226416748707SEric Dumazet struct { 226516748707SEric Dumazet unsigned long ifindex; 226616748707SEric Dumazet unsigned int all_default; 226716748707SEric Dumazet } *ctx = (void *)cb->ctx; 226816748707SEric Dumazet const struct in_device *in_dev; 22697a674200SNicolas Dichtel struct net_device *dev; 227016748707SEric Dumazet int err = 0; 22717a674200SNicolas Dichtel 2272addd383fSDavid Ahern if (cb->strict_check) { 2273addd383fSDavid Ahern struct netlink_ext_ack *extack = cb->extack; 2274addd383fSDavid Ahern struct netconfmsg *ncm; 2275addd383fSDavid Ahern 2276addd383fSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) { 2277addd383fSDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request"); 2278addd383fSDavid Ahern return -EINVAL; 2279addd383fSDavid Ahern } 2280addd383fSDavid Ahern 2281addd383fSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ncm))) { 2282addd383fSDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request"); 2283addd383fSDavid Ahern return -EINVAL; 2284addd383fSDavid Ahern } 2285addd383fSDavid Ahern } 2286addd383fSDavid Ahern 22877a674200SNicolas Dichtel rcu_read_lock(); 228816748707SEric Dumazet for_each_netdev_dump(net, dev, ctx->ifindex) { 22897a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 22907a674200SNicolas Dichtel if (!in_dev) 229116748707SEric Dumazet continue; 229216748707SEric Dumazet err = inet_netconf_fill_devconf(skb, dev->ifindex, 22937a674200SNicolas Dichtel &in_dev->cnf, 22947a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2295addd383fSDavid Ahern nlh->nlmsg_seq, 229616748707SEric Dumazet RTM_NEWNETCONF, NLM_F_MULTI, 229716748707SEric Dumazet NETCONFA_ALL); 229816748707SEric Dumazet if (err < 0) 22997a674200SNicolas Dichtel goto done; 23007a674200SNicolas Dichtel } 230116748707SEric Dumazet if (ctx->all_default == 0) { 230216748707SEric Dumazet err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 23037a674200SNicolas Dichtel net->ipv4.devconf_all, 23047a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2305addd383fSDavid Ahern nlh->nlmsg_seq, 23067a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 230716748707SEric Dumazet NETCONFA_ALL); 230816748707SEric Dumazet if (err < 0) 23097a674200SNicolas Dichtel goto done; 231016748707SEric Dumazet ctx->all_default++; 23117a674200SNicolas Dichtel } 231216748707SEric Dumazet if (ctx->all_default == 1) { 231316748707SEric Dumazet err = inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 23147a674200SNicolas Dichtel net->ipv4.devconf_dflt, 23157a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2316addd383fSDavid Ahern nlh->nlmsg_seq, 23177a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 231816748707SEric Dumazet NETCONFA_ALL); 231916748707SEric Dumazet if (err < 0) 23207a674200SNicolas Dichtel goto done; 232116748707SEric Dumazet ctx->all_default++; 23227a674200SNicolas Dichtel } 23237a674200SNicolas Dichtel done: 232416748707SEric Dumazet rcu_read_unlock(); 232516748707SEric Dumazet return err; 23267a674200SNicolas Dichtel } 23277a674200SNicolas Dichtel 23281da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 23291da177e4SLinus Torvalds 2330c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 233131be3085SHerbert Xu { 233231be3085SHerbert Xu struct net_device *dev; 233331be3085SHerbert Xu 233431be3085SHerbert Xu rcu_read_lock(); 2335c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 2336c6d14c84SEric Dumazet struct in_device *in_dev; 2337c6d14c84SEric Dumazet 233831be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 233931be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 23409355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 2341c6d14c84SEric Dumazet } 234231be3085SHerbert Xu rcu_read_unlock(); 234331be3085SHerbert Xu } 234431be3085SHerbert Xu 2345c6d14c84SEric Dumazet /* called with RTNL locked */ 2346c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 234768dd299bSPavel Emelyanov { 234868dd299bSPavel Emelyanov struct net_device *dev; 2349586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 235068dd299bSPavel Emelyanov 2351586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 23529355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 23533b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23543b022865SDavid Ahern NETCONFA_FORWARDING, 2355edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 2356edc9e748SNicolas Dichtel net->ipv4.devconf_all); 23573b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23583b022865SDavid Ahern NETCONFA_FORWARDING, 2359edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2360edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 236168dd299bSPavel Emelyanov 2362c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 236368dd299bSPavel Emelyanov struct in_device *in_dev; 2364fa17806cSEric Dumazet 23650187bdfbSBen Hutchings if (on) 23660187bdfbSBen Hutchings dev_disable_lro(dev); 2367fa17806cSEric Dumazet 2368fa17806cSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 2369edc9e748SNicolas Dichtel if (in_dev) { 237068dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 23713b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23723b022865SDavid Ahern NETCONFA_FORWARDING, 2373edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 2374edc9e748SNicolas Dichtel } 237568dd299bSPavel Emelyanov } 237668dd299bSPavel Emelyanov } 237768dd299bSPavel Emelyanov 2378f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf) 2379f085ff1cSstephen hemminger { 2380f085ff1cSstephen hemminger if (cnf == net->ipv4.devconf_dflt) 2381f085ff1cSstephen hemminger return NETCONFA_IFINDEX_DEFAULT; 2382f085ff1cSstephen hemminger else if (cnf == net->ipv4.devconf_all) 2383f085ff1cSstephen hemminger return NETCONFA_IFINDEX_ALL; 2384f085ff1cSstephen hemminger else { 2385f085ff1cSstephen hemminger struct in_device *idev 2386f085ff1cSstephen hemminger = container_of(cnf, struct in_device, cnf); 2387f085ff1cSstephen hemminger return idev->dev->ifindex; 2388f085ff1cSstephen hemminger } 2389f085ff1cSstephen hemminger } 2390f085ff1cSstephen hemminger 239178eb4ea2SJoel Granados static int devinet_conf_proc(const struct ctl_table *ctl, int write, 239232927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 239331be3085SHerbert Xu { 2394d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 23958d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 2396d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 239731be3085SHerbert Xu 239831be3085SHerbert Xu if (write) { 239931be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 2400c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 240131be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 2402f085ff1cSstephen hemminger int ifindex; 240331be3085SHerbert Xu 240431be3085SHerbert Xu set_bit(i, cnf->state); 240531be3085SHerbert Xu 24069355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 2407c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 2408d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 2409d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 2410d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 24114ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2412f085ff1cSstephen hemminger 24135cbf777cSXin Long if (i == IPV4_DEVCONF_BC_FORWARDING - 1 && 24145cbf777cSXin Long new_value != old_value) 24155cbf777cSXin Long rt_cache_flush(net); 24165cbf777cSXin Long 2417cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 2418cc535dfbSNicolas Dichtel new_value != old_value) { 2419f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 24203b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24213b022865SDavid Ahern NETCONFA_RP_FILTER, 2422cc535dfbSNicolas Dichtel ifindex, cnf); 2423cc535dfbSNicolas Dichtel } 2424f085ff1cSstephen hemminger if (i == IPV4_DEVCONF_PROXY_ARP - 1 && 2425f085ff1cSstephen hemminger new_value != old_value) { 2426f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 24273b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24283b022865SDavid Ahern NETCONFA_PROXY_NEIGH, 2429f085ff1cSstephen hemminger ifindex, cnf); 2430f085ff1cSstephen hemminger } 2431974d7af5SAndy Gospodarek if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 && 2432974d7af5SAndy Gospodarek new_value != old_value) { 2433974d7af5SAndy Gospodarek ifindex = devinet_conf_ifindex(net, cnf); 24343b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24353b022865SDavid Ahern NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 2436974d7af5SAndy Gospodarek ifindex, cnf); 2437974d7af5SAndy Gospodarek } 243831be3085SHerbert Xu } 243931be3085SHerbert Xu 244031be3085SHerbert Xu return ret; 244131be3085SHerbert Xu } 244231be3085SHerbert Xu 244378eb4ea2SJoel Granados static int devinet_sysctl_forward(const struct ctl_table *ctl, int write, 244432927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 24451da177e4SLinus Torvalds { 24461da177e4SLinus Torvalds int *valp = ctl->data; 24471da177e4SLinus Torvalds int val = *valp; 244888af182eSEric W. Biederman loff_t pos = *ppos; 24498292d7f6SYang Yang struct net *net = ctl->extra2; 24508292d7f6SYang Yang int ret; 24518292d7f6SYang Yang 24528292d7f6SYang Yang if (write && !ns_capable(net->user_ns, CAP_NET_ADMIN)) 24538292d7f6SYang Yang return -EPERM; 24548292d7f6SYang Yang 24558292d7f6SYang Yang ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 24561da177e4SLinus Torvalds 24571da177e4SLinus Torvalds if (write && *valp != val) { 24580187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 245988af182eSEric W. Biederman if (!rtnl_trylock()) { 246088af182eSEric W. Biederman /* Restore the original values before restarting */ 246188af182eSEric W. Biederman *valp = val; 246288af182eSEric W. Biederman *ppos = pos; 24639b8adb5eSEric W. Biederman return restart_syscall(); 246488af182eSEric W. Biederman } 24650187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2466c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2467edc9e748SNicolas Dichtel } else { 24680187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 24690187bdfbSBen Hutchings struct in_device *idev = 24700187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2471edc9e748SNicolas Dichtel if (*valp) 24720187bdfbSBen Hutchings dev_disable_lro(idev->dev); 24733b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 2474edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2475edc9e748SNicolas Dichtel idev->dev->ifindex, 2476edc9e748SNicolas Dichtel cnf); 24770187bdfbSBen Hutchings } 24780187bdfbSBen Hutchings rtnl_unlock(); 24794ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2480edc9e748SNicolas Dichtel } else 24813b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24823b022865SDavid Ahern NETCONFA_FORWARDING, 2483edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2484edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 24850187bdfbSBen Hutchings } 24861da177e4SLinus Torvalds 24871da177e4SLinus Torvalds return ret; 24881da177e4SLinus Torvalds } 24891da177e4SLinus Torvalds 249078eb4ea2SJoel Granados static int ipv4_doint_and_flush(const struct ctl_table *ctl, int write, 249132927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 24921da177e4SLinus Torvalds { 24931da177e4SLinus Torvalds int *valp = ctl->data; 24941da177e4SLinus Torvalds int val = *valp; 24958d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 249676e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 24971da177e4SLinus Torvalds 24981da177e4SLinus Torvalds if (write && *valp != val) 24994ccfe6d4SNicolas Dichtel rt_cache_flush(net); 25001da177e4SLinus Torvalds 25011da177e4SLinus Torvalds return ret; 25021da177e4SLinus Torvalds } 25031da177e4SLinus Torvalds 2504f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 250542f811b8SHerbert Xu { \ 250642f811b8SHerbert Xu .procname = name, \ 250742f811b8SHerbert Xu .data = ipv4_devconf.data + \ 250802291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 250942f811b8SHerbert Xu .maxlen = sizeof(int), \ 251042f811b8SHerbert Xu .mode = mval, \ 251142f811b8SHerbert Xu .proc_handler = proc, \ 251231be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 251342f811b8SHerbert Xu } 251442f811b8SHerbert Xu 251542f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2516f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 251742f811b8SHerbert Xu 251842f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2519f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 252042f811b8SHerbert Xu 2521f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2522f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 252342f811b8SHerbert Xu 252442f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2525f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 252642f811b8SHerbert Xu 25271da177e4SLinus Torvalds static struct devinet_sysctl_table { 25281da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 25291c106eb0SJoel Granados struct ctl_table devinet_vars[IPV4_DEVCONF_MAX]; 25301da177e4SLinus Torvalds } devinet_sysctl = { 25311da177e4SLinus Torvalds .devinet_vars = { 253242f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2533f8572d8fSEric W. Biederman devinet_sysctl_forward), 253442f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 25355cbf777cSXin Long DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"), 253642f811b8SHerbert Xu 253742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 253842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 253942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 254042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 254142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 254242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 254342f811b8SHerbert Xu "accept_source_route"), 25448153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 254528f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 254642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 254742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 254842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 254942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 255042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 255142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 255242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 255342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 255442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2555eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 2556fcdb44d0SJames Prestwood DEVINET_SYSCTL_RW_ENTRY(ARP_EVICT_NOCARRIER, 2557fcdb44d0SJames Prestwood "arp_evict_nocarrier"), 255865324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 25595c6fe01cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 25605c6fe01cSWilliam Manley "force_igmp_version"), 25612690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, 25622690048cSWilliam Manley "igmpv2_unsolicited_report_interval"), 25632690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, 25642690048cSWilliam Manley "igmpv3_unsolicited_report_interval"), 25650eeb075fSAndy Gospodarek DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN, 25660eeb075fSAndy Gospodarek "ignore_routes_with_linkdown"), 256797daf331SJohannes Berg DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP, 256897daf331SJohannes Berg "drop_gratuitous_arp"), 256942f811b8SHerbert Xu 257042f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 257142f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 257242f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 257342f811b8SHerbert Xu "promote_secondaries"), 2574d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2575d0daebc3SThomas Graf "route_localnet"), 257612b74dfaSJohannes Berg DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST, 257712b74dfaSJohannes Berg "drop_unicast_in_l2_multicast"), 25781da177e4SLinus Torvalds }, 25791da177e4SLinus Torvalds }; 25801da177e4SLinus Torvalds 2581ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 258229c994e3SNicolas Dichtel int ifindex, struct ipv4_devconf *p) 25831da177e4SLinus Torvalds { 25841da177e4SLinus Torvalds int i; 25859fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 25868607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2587bfada697SPavel Emelyanov 2588425b9c7fSVasily Averin t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL_ACCOUNT); 25891da177e4SLinus Torvalds if (!t) 25909fa89642SPavel Emelyanov goto out; 25919fa89642SPavel Emelyanov 25921c106eb0SJoel Granados for (i = 0; i < ARRAY_SIZE(t->devinet_vars); i++) { 25931da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 259431be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2595c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 25961da177e4SLinus Torvalds } 25971da177e4SLinus Torvalds 25988607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 25991da177e4SLinus Torvalds 26008607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 26011da177e4SLinus Torvalds if (!t->sysctl_header) 26028607ddb8SEric W. Biederman goto free; 26031da177e4SLinus Torvalds 26041da177e4SLinus Torvalds p->sysctl = t; 260529c994e3SNicolas Dichtel 26063b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, 26073b022865SDavid Ahern ifindex, p); 2608ea40b324SPavel Emelyanov return 0; 26091da177e4SLinus Torvalds 26101da177e4SLinus Torvalds free: 26111da177e4SLinus Torvalds kfree(t); 26129fa89642SPavel Emelyanov out: 26136def4801Sliuguoqiang return -ENOMEM; 26141da177e4SLinus Torvalds } 26151da177e4SLinus Torvalds 2616b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net, 2617b5c9641dSDavid Ahern struct ipv4_devconf *cnf, int ifindex) 261866f27a52SPavel Emelyanov { 261951602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 262066f27a52SPavel Emelyanov 2621b5c9641dSDavid Ahern if (t) { 262251602b2aSPavel Emelyanov cnf->sysctl = NULL; 2623ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 26241da177e4SLinus Torvalds kfree(t); 26251da177e4SLinus Torvalds } 262651602b2aSPavel Emelyanov 2627b5c9641dSDavid Ahern inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); 2628b5c9641dSDavid Ahern } 2629b5c9641dSDavid Ahern 263020e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 263151602b2aSPavel Emelyanov { 263220e61da7SWANG Cong int err; 263320e61da7SWANG Cong 263420e61da7SWANG Cong if (!sysctl_dev_name_is_allowed(idev->dev->name)) 263520e61da7SWANG Cong return -EINVAL; 263620e61da7SWANG Cong 263720e61da7SWANG Cong err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); 263820e61da7SWANG Cong if (err) 263920e61da7SWANG Cong return err; 264020e61da7SWANG Cong err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 264129c994e3SNicolas Dichtel idev->dev->ifindex, &idev->cnf); 264220e61da7SWANG Cong if (err) 264320e61da7SWANG Cong neigh_sysctl_unregister(idev->arp_parms); 264420e61da7SWANG Cong return err; 264551602b2aSPavel Emelyanov } 264651602b2aSPavel Emelyanov 264751602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 264851602b2aSPavel Emelyanov { 2649b5c9641dSDavid Ahern struct net *net = dev_net(idev->dev); 2650b5c9641dSDavid Ahern 2651b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex); 265251602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 26531da177e4SLinus Torvalds } 26541da177e4SLinus Torvalds 265568dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 265668dd299bSPavel Emelyanov { 265768dd299bSPavel Emelyanov .procname = "ip_forward", 265868dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 265902291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 266068dd299bSPavel Emelyanov .maxlen = sizeof(int), 266168dd299bSPavel Emelyanov .mode = 0644, 266268dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 266368dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2664c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 266568dd299bSPavel Emelyanov }, 266668dd299bSPavel Emelyanov }; 26672a75de0cSEric Dumazet #endif 266868dd299bSPavel Emelyanov 2669752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2670752d14dcSPavel Emelyanov { 2671752d14dcSPavel Emelyanov int err; 2672752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 26732a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2674856c395cSCong Wang struct ctl_table *tbl; 2675752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 26762a75de0cSEric Dumazet #endif 2677752d14dcSPavel Emelyanov 2678752d14dcSPavel Emelyanov err = -ENOMEM; 2679856c395cSCong Wang all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL); 268051456b29SIan Morris if (!all) 2681752d14dcSPavel Emelyanov goto err_alloc_all; 2682752d14dcSPavel Emelyanov 2683856c395cSCong Wang dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 268451456b29SIan Morris if (!dflt) 2685752d14dcSPavel Emelyanov goto err_alloc_dflt; 2686752d14dcSPavel Emelyanov 26872a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2688856c395cSCong Wang tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL); 268951456b29SIan Morris if (!tbl) 2690752d14dcSPavel Emelyanov goto err_alloc_ctl; 2691752d14dcSPavel Emelyanov 269202291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2693752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2694752d14dcSPavel Emelyanov tbl[0].extra2 = net; 26952a75de0cSEric Dumazet #endif 2696856c395cSCong Wang 26979efd6a3cSNicolas Dichtel if (!net_eq(net, &init_net)) { 2698a5612ca1SKuniyuki Iwashima switch (net_inherit_devconf()) { 2699a5612ca1SKuniyuki Iwashima case 3: 27009efd6a3cSNicolas Dichtel /* copy from the current netns */ 27019efd6a3cSNicolas Dichtel memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all, 27029efd6a3cSNicolas Dichtel sizeof(ipv4_devconf)); 27039efd6a3cSNicolas Dichtel memcpy(dflt, 27049efd6a3cSNicolas Dichtel current->nsproxy->net_ns->ipv4.devconf_dflt, 27059efd6a3cSNicolas Dichtel sizeof(ipv4_devconf_dflt)); 2706a5612ca1SKuniyuki Iwashima break; 2707a5612ca1SKuniyuki Iwashima case 0: 2708a5612ca1SKuniyuki Iwashima case 1: 2709a5612ca1SKuniyuki Iwashima /* copy from init_net */ 27109efd6a3cSNicolas Dichtel memcpy(all, init_net.ipv4.devconf_all, 27119efd6a3cSNicolas Dichtel sizeof(ipv4_devconf)); 27129efd6a3cSNicolas Dichtel memcpy(dflt, init_net.ipv4.devconf_dflt, 27139efd6a3cSNicolas Dichtel sizeof(ipv4_devconf_dflt)); 2714a5612ca1SKuniyuki Iwashima break; 2715a5612ca1SKuniyuki Iwashima case 2: 2716a5612ca1SKuniyuki Iwashima /* use compiled values */ 2717a5612ca1SKuniyuki Iwashima break; 27189efd6a3cSNicolas Dichtel } 2719752d14dcSPavel Emelyanov } 2720752d14dcSPavel Emelyanov 2721752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 272229c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all); 2723752d14dcSPavel Emelyanov if (err < 0) 2724752d14dcSPavel Emelyanov goto err_reg_all; 2725752d14dcSPavel Emelyanov 272629c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "default", 272729c994e3SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, dflt); 2728752d14dcSPavel Emelyanov if (err < 0) 2729752d14dcSPavel Emelyanov goto err_reg_dflt; 2730752d14dcSPavel Emelyanov 2731752d14dcSPavel Emelyanov err = -ENOMEM; 2732c899710fSJoel Granados forw_hdr = register_net_sysctl_sz(net, "net/ipv4", tbl, 2733c899710fSJoel Granados ARRAY_SIZE(ctl_forward_entry)); 273451456b29SIan Morris if (!forw_hdr) 2735752d14dcSPavel Emelyanov goto err_reg_ctl; 27362a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2737752d14dcSPavel Emelyanov #endif 2738752d14dcSPavel Emelyanov 2739752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2740752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2741752d14dcSPavel Emelyanov return 0; 2742752d14dcSPavel Emelyanov 2743752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2744752d14dcSPavel Emelyanov err_reg_ctl: 2745b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT); 2746752d14dcSPavel Emelyanov err_reg_dflt: 2747b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); 2748752d14dcSPavel Emelyanov err_reg_all: 2749752d14dcSPavel Emelyanov kfree(tbl); 2750752d14dcSPavel Emelyanov err_alloc_ctl: 27512a75de0cSEric Dumazet #endif 2752752d14dcSPavel Emelyanov kfree(dflt); 2753752d14dcSPavel Emelyanov err_alloc_dflt: 2754752d14dcSPavel Emelyanov kfree(all); 2755752d14dcSPavel Emelyanov err_alloc_all: 2756752d14dcSPavel Emelyanov return err; 2757752d14dcSPavel Emelyanov } 2758752d14dcSPavel Emelyanov 2759752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2760752d14dcSPavel Emelyanov { 27612a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2762bfa858f2SThomas Weißschuh const struct ctl_table *tbl; 2763752d14dcSPavel Emelyanov 2764752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2765752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2766b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_dflt, 2767b5c9641dSDavid Ahern NETCONFA_IFINDEX_DEFAULT); 2768b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_all, 2769b5c9641dSDavid Ahern NETCONFA_IFINDEX_ALL); 2770752d14dcSPavel Emelyanov kfree(tbl); 27712a75de0cSEric Dumazet #endif 2772752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2773752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2774752d14dcSPavel Emelyanov } 2775752d14dcSPavel Emelyanov 2776752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2777752d14dcSPavel Emelyanov .init = devinet_init_net, 2778752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2779752d14dcSPavel Emelyanov }; 2780752d14dcSPavel Emelyanov 2781207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = { 27829f0f7272SThomas Graf .family = AF_INET, 27839f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 27849f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2785cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2786cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 27879f0f7272SThomas Graf }; 27889f0f7272SThomas Graf 27891da177e4SLinus Torvalds void __init devinet_init(void) 27901da177e4SLinus Torvalds { 2791fd23c3b3SDavid S. Miller int i; 2792fd23c3b3SDavid S. Miller 2793fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2794fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2795fd23c3b3SDavid S. Miller 2796752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 27971da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 279863f3444fSThomas Graf 2799906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 28005c766d64SJiri Pirko 28019f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 28029f0f7272SThomas Graf 2803b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0); 2804b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0); 2805cdb2f80fSEric Dumazet rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 28065b4b62a1SJakub Kicinski RTNL_FLAG_DUMP_UNLOCKED | RTNL_FLAG_DUMP_SPLIT_NLM_DONE); 28079e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 2808bbcf9105SEric Dumazet inet_netconf_dump_devconf, 280916748707SEric Dumazet RTNL_FLAG_DOIT_UNLOCKED | RTNL_FLAG_DUMP_UNLOCKED); 28101da177e4SLinus Torvalds } 2811