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*/, 7842f811b8SHerbert Xu }, 791da177e4SLinus Torvalds }; 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 8242f811b8SHerbert Xu .data = { 8302291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8402291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8502291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8602291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8702291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 882690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 892690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 9042f811b8SHerbert Xu }, 911da177e4SLinus Torvalds }; 921da177e4SLinus Torvalds 939355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 949355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9542f811b8SHerbert Xu 96ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 975c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 985c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 995c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 1005176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 1015c766d64SJiri Pirko [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 102ad6c8135SJiri Pirko [IFA_FLAGS] = { .type = NLA_U32 }, 103af4d768aSDavid Ahern [IFA_RT_PRIORITY] = { .type = NLA_U32 }, 104d3807145SChristian Brauner [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, 1055c753978SThomas Graf }; 1065c753978SThomas Graf 107978a46faSChristian Brauner struct inet_fill_args { 108978a46faSChristian Brauner u32 portid; 109978a46faSChristian Brauner u32 seq; 110978a46faSChristian Brauner int event; 111978a46faSChristian Brauner unsigned int flags; 112978a46faSChristian Brauner int netnsid; 1135fcd266aSDavid Ahern int ifindex; 114978a46faSChristian Brauner }; 115978a46faSChristian Brauner 11640384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT 8 11740384999SEric Dumazet #define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 11840384999SEric Dumazet 119fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 120fd23c3b3SDavid S. Miller 1216eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr) 122fd23c3b3SDavid S. Miller { 12340384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 124fd23c3b3SDavid S. Miller 12540384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 126fd23c3b3SDavid S. Miller } 127fd23c3b3SDavid S. Miller 128fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 129fd23c3b3SDavid S. Miller { 13040384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 131fd23c3b3SDavid S. Miller 13232a4be48SWANG Cong ASSERT_RTNL(); 133fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 134fd23c3b3SDavid S. Miller } 135fd23c3b3SDavid S. Miller 136fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 137fd23c3b3SDavid S. Miller { 13832a4be48SWANG Cong ASSERT_RTNL(); 139fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 140fd23c3b3SDavid S. Miller } 141fd23c3b3SDavid S. Miller 1429435eb1cSDavid S. Miller /** 1439435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1449435eb1cSDavid S. Miller * @net: the net namespace 1459435eb1cSDavid S. Miller * @addr: the source address 1469435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1479435eb1cSDavid S. Miller * 1489435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1499435eb1cSDavid S. Miller */ 1509435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1519435eb1cSDavid S. Miller { 1529435eb1cSDavid S. Miller struct net_device *result = NULL; 1539435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1549435eb1cSDavid S. Miller 1559435eb1cSDavid S. Miller rcu_read_lock(); 1566e617de8SPaolo Abeni ifa = inet_lookup_ifaddr_rcu(net, addr); 1576e617de8SPaolo Abeni if (!ifa) { 158406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 159406b6f97SDavid S. Miller struct fib_result res = { 0 }; 160406b6f97SDavid S. Miller struct fib_table *local; 161406b6f97SDavid S. Miller 162406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 163406b6f97SDavid S. Miller * over loopback subnets work. 164406b6f97SDavid S. Miller */ 165406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 166406b6f97SDavid S. Miller if (local && 167406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 168406b6f97SDavid S. Miller res.type == RTN_LOCAL) 169406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 1706e617de8SPaolo Abeni } else { 1716e617de8SPaolo Abeni result = ifa->ifa_dev->dev; 172406b6f97SDavid S. Miller } 1739435eb1cSDavid S. Miller if (result && devref) 1749435eb1cSDavid S. Miller dev_hold(result); 1759435eb1cSDavid S. Miller rcu_read_unlock(); 1769435eb1cSDavid S. Miller return result; 1779435eb1cSDavid S. Miller } 1789435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1799435eb1cSDavid S. Miller 1806e617de8SPaolo Abeni /* called under RCU lock */ 1816e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr) 1826e617de8SPaolo Abeni { 1836e617de8SPaolo Abeni u32 hash = inet_addr_hash(net, addr); 1846e617de8SPaolo Abeni struct in_ifaddr *ifa; 1856e617de8SPaolo Abeni 1866e617de8SPaolo Abeni hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) 1876e617de8SPaolo Abeni if (ifa->ifa_local == addr && 1886e617de8SPaolo Abeni net_eq(dev_net(ifa->ifa_dev->dev), net)) 1896e617de8SPaolo Abeni return ifa; 1906e617de8SPaolo Abeni 1916e617de8SPaolo Abeni return NULL; 1926e617de8SPaolo Abeni } 1936e617de8SPaolo Abeni 194d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1951da177e4SLinus Torvalds 196e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1973ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain); 1982638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev, 1992638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap, 2001da177e4SLinus Torvalds int destroy); 2011da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 20220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev); 20351602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 20451602b2aSPavel Emelyanov #else 20520e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 20651602b2aSPavel Emelyanov { 20720e61da7SWANG Cong return 0; 20851602b2aSPavel Emelyanov } 20940384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 21051602b2aSPavel Emelyanov { 21151602b2aSPavel Emelyanov } 2121da177e4SLinus Torvalds #endif 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds /* Locks all the inet devices. */ 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 2171da177e4SLinus Torvalds { 218*6126891cSVasily Averin return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL_ACCOUNT); 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2221da177e4SLinus Torvalds { 2231da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2241da177e4SLinus Torvalds if (ifa->ifa_dev) 2251da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2261da177e4SLinus Torvalds kfree(ifa); 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 22940384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2301da177e4SLinus Torvalds { 2311da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2321da177e4SLinus Torvalds } 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2351da177e4SLinus Torvalds { 2361da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2371da177e4SLinus Torvalds 238547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 239547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 240e9897071SEric Dumazet kfree(rcu_dereference_protected(idev->mc_hash, 1)); 2411da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 24291df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2431da177e4SLinus Torvalds #endif 2441da177e4SLinus Torvalds dev_put(dev); 2451da177e4SLinus Torvalds if (!idev->dead) 2469f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2479f9354b9SEric Dumazet else 2481da177e4SLinus Torvalds kfree(idev); 2491da177e4SLinus Torvalds } 2509f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2511da177e4SLinus Torvalds 25271e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2531da177e4SLinus Torvalds { 2541da177e4SLinus Torvalds struct in_device *in_dev; 25520e61da7SWANG Cong int err = -ENOMEM; 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds ASSERT_RTNL(); 2581da177e4SLinus Torvalds 2590da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2601da177e4SLinus Torvalds if (!in_dev) 2611da177e4SLinus Torvalds goto out; 262c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2639355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2641da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2651da177e4SLinus Torvalds in_dev->dev = dev; 2669f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2679f9354b9SEric Dumazet if (!in_dev->arp_parms) 2681da177e4SLinus Torvalds goto out_kfree; 2690187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2700187bdfbSBen Hutchings dev_disable_lro(dev); 2711da177e4SLinus Torvalds /* Reference in_dev->dev */ 2721da177e4SLinus Torvalds dev_hold(dev); 27330c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2747658b36fSReshetova, Elena refcount_set(&in_dev->refcnt, 1); 2751da177e4SLinus Torvalds 27620e61da7SWANG Cong err = devinet_sysctl_register(in_dev); 27720e61da7SWANG Cong if (err) { 27820e61da7SWANG Cong in_dev->dead = 1; 2791b49cd71SYang Yingliang neigh_parms_release(&arp_tbl, in_dev->arp_parms); 28020e61da7SWANG Cong in_dev_put(in_dev); 28120e61da7SWANG Cong in_dev = NULL; 28220e61da7SWANG Cong goto out; 28320e61da7SWANG Cong } 2841da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2851da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2861da177e4SLinus Torvalds ip_mc_up(in_dev); 287483479ecSJarek Poplawski 28830c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 289cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 290483479ecSJarek Poplawski out: 29120e61da7SWANG Cong return in_dev ?: ERR_PTR(err); 2921da177e4SLinus Torvalds out_kfree: 2931da177e4SLinus Torvalds kfree(in_dev); 2941da177e4SLinus Torvalds in_dev = NULL; 2951da177e4SLinus Torvalds goto out; 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2991da177e4SLinus Torvalds { 3001da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 3011da177e4SLinus Torvalds in_dev_put(idev); 3021da177e4SLinus Torvalds } 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 3051da177e4SLinus Torvalds { 3061da177e4SLinus Torvalds struct net_device *dev; 3072638eb8bSFlorian Westphal struct in_ifaddr *ifa; 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds ASSERT_RTNL(); 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds dev = in_dev->dev; 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds in_dev->dead = 1; 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 3161da177e4SLinus Torvalds 3172638eb8bSFlorian Westphal while ((ifa = rtnl_dereference(in_dev->ifa_list)) != NULL) { 3181da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 3191da177e4SLinus Torvalds inet_free_ifa(ifa); 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds 322a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 3231da177e4SLinus Torvalds 32451602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 3251da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 3261da177e4SLinus Torvalds arp_ifdown(dev); 3271da177e4SLinus Torvalds 3281da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 3291da177e4SLinus Torvalds } 3301da177e4SLinus Torvalds 331ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3321da177e4SLinus Torvalds { 333d519e870SFlorian Westphal const struct in_ifaddr *ifa; 334d519e870SFlorian Westphal 3351da177e4SLinus Torvalds rcu_read_lock(); 336d519e870SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 3371da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3381da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3391da177e4SLinus Torvalds rcu_read_unlock(); 3401da177e4SLinus Torvalds return 1; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds } 343d519e870SFlorian Westphal } 3441da177e4SLinus Torvalds rcu_read_unlock(); 3451da177e4SLinus Torvalds return 0; 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds 3482638eb8bSFlorian Westphal static void __inet_del_ifa(struct in_device *in_dev, 3492638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap, 35015e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3511da177e4SLinus Torvalds { 3528f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3532638eb8bSFlorian Westphal struct in_ifaddr *ifa, *ifa1; 3542638eb8bSFlorian Westphal struct in_ifaddr *last_prim; 3550ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3560ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3571da177e4SLinus Torvalds 3581da177e4SLinus Torvalds ASSERT_RTNL(); 3591da177e4SLinus Torvalds 3602638eb8bSFlorian Westphal ifa1 = rtnl_dereference(*ifap); 3612638eb8bSFlorian Westphal last_prim = rtnl_dereference(in_dev->ifa_list); 362fbd40ea0SDavid S. Miller if (in_dev->dead) 363fbd40ea0SDavid S. Miller goto no_promotions; 364fbd40ea0SDavid S. Miller 3658f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3668f937c60SHarald Welte * unless alias promotion is set 3678f937c60SHarald Welte **/ 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3702638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap1 = &ifa1->ifa_next; 3711da177e4SLinus Torvalds 3722638eb8bSFlorian Westphal while ((ifa = rtnl_dereference(*ifap1)) != NULL) { 3730ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3740ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3750ff60a45SJamal Hadi Salim last_prim = ifa; 3760ff60a45SJamal Hadi Salim 3771da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3781da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3791da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3801da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3810ff60a45SJamal Hadi Salim prev_prom = ifa; 3821da177e4SLinus Torvalds continue; 3831da177e4SLinus Torvalds } 3841da177e4SLinus Torvalds 3850ff60a45SJamal Hadi Salim if (!do_promote) { 386fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3871da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3881da177e4SLinus Torvalds 38915e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 390e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 391e041c683SAlan Stern NETDEV_DOWN, ifa); 3921da177e4SLinus Torvalds inet_free_ifa(ifa); 3938f937c60SHarald Welte } else { 3948f937c60SHarald Welte promote = ifa; 3958f937c60SHarald Welte break; 3968f937c60SHarald Welte } 3971da177e4SLinus Torvalds } 3981da177e4SLinus Torvalds } 3991da177e4SLinus Torvalds 4002d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 4012d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 4022d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 4032d230e2bSJulian Anastasov * while all addresses are on the device list. 4042d230e2bSJulian Anastasov */ 4052638eb8bSFlorian Westphal for (ifa = promote; ifa; ifa = rtnl_dereference(ifa->ifa_next)) { 4062d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 4072d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 4082d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 4092d230e2bSJulian Anastasov } 4102d230e2bSJulian Anastasov 411fbd40ea0SDavid S. Miller no_promotions: 4121da177e4SLinus Torvalds /* 2. Unlink it */ 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 415fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 4161da177e4SLinus Torvalds 4171da177e4SLinus Torvalds /* 3. Announce address deletion */ 4181da177e4SLinus Torvalds 4191da177e4SLinus Torvalds /* Send message first, then call notifier. 4201da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 4211da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 4221da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 4231da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 4241da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 4251da177e4SLinus Torvalds So that, this order is correct. 4261da177e4SLinus Torvalds */ 42715e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 428e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 4290ff60a45SJamal Hadi Salim 4300ff60a45SJamal Hadi Salim if (promote) { 4312638eb8bSFlorian Westphal struct in_ifaddr *next_sec; 4320ff60a45SJamal Hadi Salim 4332638eb8bSFlorian Westphal next_sec = rtnl_dereference(promote->ifa_next); 4340ff60a45SJamal Hadi Salim if (prev_prom) { 4352638eb8bSFlorian Westphal struct in_ifaddr *last_sec; 4362638eb8bSFlorian Westphal 4372638eb8bSFlorian Westphal rcu_assign_pointer(prev_prom->ifa_next, next_sec); 4386a9e9ceaSFlorian Westphal 4396a9e9ceaSFlorian Westphal last_sec = rtnl_dereference(last_prim->ifa_next); 4402638eb8bSFlorian Westphal rcu_assign_pointer(promote->ifa_next, last_sec); 4412638eb8bSFlorian Westphal rcu_assign_pointer(last_prim->ifa_next, promote); 4420ff60a45SJamal Hadi Salim } 4430ff60a45SJamal Hadi Salim 4440ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 44515e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 446e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 447e041c683SAlan Stern NETDEV_UP, promote); 4482638eb8bSFlorian Westphal for (ifa = next_sec; ifa; 4492638eb8bSFlorian Westphal ifa = rtnl_dereference(ifa->ifa_next)) { 4500ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4510ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4520ff60a45SJamal Hadi Salim continue; 4530ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4540ff60a45SJamal Hadi Salim } 4550ff60a45SJamal Hadi Salim 4560ff60a45SJamal Hadi Salim } 4576363097cSHerbert Xu if (destroy) 4581da177e4SLinus Torvalds inet_free_ifa(ifa1); 4591da177e4SLinus Torvalds } 4601da177e4SLinus Torvalds 4612638eb8bSFlorian Westphal static void inet_del_ifa(struct in_device *in_dev, 4622638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap, 463d6062cbbSThomas Graf int destroy) 464d6062cbbSThomas Graf { 465d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 466d6062cbbSThomas Graf } 467d6062cbbSThomas Graf 4685c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4695c766d64SJiri Pirko 4705c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4715c766d64SJiri Pirko 472d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 473de95e047SDavid Ahern u32 portid, struct netlink_ext_ack *extack) 4741da177e4SLinus Torvalds { 4752638eb8bSFlorian Westphal struct in_ifaddr __rcu **last_primary, **ifap; 4761da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4773ad7d246SKrister Johansen struct in_validator_info ivi; 4782638eb8bSFlorian Westphal struct in_ifaddr *ifa1; 4793ad7d246SKrister Johansen int ret; 4801da177e4SLinus Torvalds 4811da177e4SLinus Torvalds ASSERT_RTNL(); 4821da177e4SLinus Torvalds 4831da177e4SLinus Torvalds if (!ifa->ifa_local) { 4841da177e4SLinus Torvalds inet_free_ifa(ifa); 4851da177e4SLinus Torvalds return 0; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 4881da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4891da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4901da177e4SLinus Torvalds 4912e605463SMatteo Croce /* Don't set IPv6 only flags to IPv4 addresses */ 4922e605463SMatteo Croce ifa->ifa_flags &= ~IPV6ONLY_FLAGS; 4932e605463SMatteo Croce 4942638eb8bSFlorian Westphal ifap = &in_dev->ifa_list; 4952638eb8bSFlorian Westphal ifa1 = rtnl_dereference(*ifap); 4962638eb8bSFlorian Westphal 4972638eb8bSFlorian Westphal while (ifa1) { 4981da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4991da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 5001da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 5011da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 5021da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 5031da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 5041da177e4SLinus Torvalds inet_free_ifa(ifa); 5051da177e4SLinus Torvalds return -EEXIST; 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 5081da177e4SLinus Torvalds inet_free_ifa(ifa); 5091da177e4SLinus Torvalds return -EINVAL; 5101da177e4SLinus Torvalds } 5111da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 5121da177e4SLinus Torvalds } 5132638eb8bSFlorian Westphal 5142638eb8bSFlorian Westphal ifap = &ifa1->ifa_next; 5152638eb8bSFlorian Westphal ifa1 = rtnl_dereference(*ifap); 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 5183ad7d246SKrister Johansen /* Allow any devices that wish to register ifaddr validtors to weigh 5193ad7d246SKrister Johansen * in now, before changes are committed. The rntl lock is serializing 5203ad7d246SKrister Johansen * access here, so the state should not change between a validator call 5213ad7d246SKrister Johansen * and a final notify on commit. This isn't invoked on promotion under 5223ad7d246SKrister Johansen * the assumption that validators are checking the address itself, and 5233ad7d246SKrister Johansen * not the flags. 5243ad7d246SKrister Johansen */ 5253ad7d246SKrister Johansen ivi.ivi_addr = ifa->ifa_address; 5263ad7d246SKrister Johansen ivi.ivi_dev = ifa->ifa_dev; 527de95e047SDavid Ahern ivi.extack = extack; 5283ad7d246SKrister Johansen ret = blocking_notifier_call_chain(&inetaddr_validator_chain, 5293ad7d246SKrister Johansen NETDEV_UP, &ivi); 5303ad7d246SKrister Johansen ret = notifier_to_errno(ret); 5313ad7d246SKrister Johansen if (ret) { 5323ad7d246SKrister Johansen inet_free_ifa(ifa); 5333ad7d246SKrister Johansen return ret; 5343ad7d246SKrister Johansen } 5353ad7d246SKrister Johansen 5361da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 53763862b5bSAruna-Hewapathirane prandom_seed((__force u32) ifa->ifa_local); 5381da177e4SLinus Torvalds ifap = last_primary; 5391da177e4SLinus Torvalds } 5401da177e4SLinus Torvalds 5412638eb8bSFlorian Westphal rcu_assign_pointer(ifa->ifa_next, *ifap); 5422638eb8bSFlorian Westphal rcu_assign_pointer(*ifap, ifa); 5431da177e4SLinus Torvalds 544fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 545fd23c3b3SDavid S. Miller 5465c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 547906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 5485c766d64SJiri Pirko 5491da177e4SLinus Torvalds /* Send message first, then call notifier. 5501da177e4SLinus Torvalds Notifier will trigger FIB update, so that 5511da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 55215e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 553e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 5541da177e4SLinus Torvalds 5551da177e4SLinus Torvalds return 0; 5561da177e4SLinus Torvalds } 5571da177e4SLinus Torvalds 558d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 559d6062cbbSThomas Graf { 560de95e047SDavid Ahern return __inet_insert_ifa(ifa, NULL, 0, NULL); 561d6062cbbSThomas Graf } 562d6062cbbSThomas Graf 5631da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 5641da177e4SLinus Torvalds { 565e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 5661da177e4SLinus Torvalds 5671da177e4SLinus Torvalds ASSERT_RTNL(); 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds if (!in_dev) { 5701da177e4SLinus Torvalds inet_free_ifa(ifa); 5711da177e4SLinus Torvalds return -ENOBUFS; 5721da177e4SLinus Torvalds } 57371e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 5741d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 5751da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 576547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 5771da177e4SLinus Torvalds in_dev_hold(in_dev); 5781da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5791da177e4SLinus Torvalds } 580f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5811da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5821da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds 5858723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5868723e1b4SEric Dumazet * We dont take a reference on found in_device 5878723e1b4SEric Dumazet */ 5887fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5891da177e4SLinus Torvalds { 5901da177e4SLinus Torvalds struct net_device *dev; 5911da177e4SLinus Torvalds struct in_device *in_dev = NULL; 592c148fc2eSEric Dumazet 593c148fc2eSEric Dumazet rcu_read_lock(); 594c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5951da177e4SLinus Torvalds if (dev) 5968723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 597c148fc2eSEric Dumazet rcu_read_unlock(); 5981da177e4SLinus Torvalds return in_dev; 5991da177e4SLinus Torvalds } 6009f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 6031da177e4SLinus Torvalds 60460cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 60560cad5daSAl Viro __be32 mask) 6061da177e4SLinus Torvalds { 607d519e870SFlorian Westphal struct in_ifaddr *ifa; 608d519e870SFlorian Westphal 6091da177e4SLinus Torvalds ASSERT_RTNL(); 6101da177e4SLinus Torvalds 611d519e870SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 6121da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 6131da177e4SLinus Torvalds return ifa; 614d519e870SFlorian Westphal } 6151da177e4SLinus Torvalds return NULL; 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds 618690cc863STaras Chornyi static int ip_mc_autojoin_config(struct net *net, bool join, 619690cc863STaras Chornyi const struct in_ifaddr *ifa) 62093a714d6SMadhu Challa { 621690cc863STaras Chornyi #if defined(CONFIG_IP_MULTICAST) 62293a714d6SMadhu Challa struct ip_mreqn mreq = { 62393a714d6SMadhu Challa .imr_multiaddr.s_addr = ifa->ifa_address, 62493a714d6SMadhu Challa .imr_ifindex = ifa->ifa_dev->dev->ifindex, 62593a714d6SMadhu Challa }; 626690cc863STaras Chornyi struct sock *sk = net->ipv4.mc_autojoin_sk; 62793a714d6SMadhu Challa int ret; 62893a714d6SMadhu Challa 62993a714d6SMadhu Challa ASSERT_RTNL(); 63093a714d6SMadhu Challa 63193a714d6SMadhu Challa lock_sock(sk); 63293a714d6SMadhu Challa if (join) 63354ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_join_group(sk, &mreq); 63493a714d6SMadhu Challa else 63554ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_leave_group(sk, &mreq); 63693a714d6SMadhu Challa release_sock(sk); 63793a714d6SMadhu Challa 63893a714d6SMadhu Challa return ret; 639690cc863STaras Chornyi #else 640690cc863STaras Chornyi return -EOPNOTSUPP; 641690cc863STaras Chornyi #endif 64293a714d6SMadhu Challa } 64393a714d6SMadhu Challa 644c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, 645c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 6461da177e4SLinus Torvalds { 6473b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 6482638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap; 649dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 6501da177e4SLinus Torvalds struct in_device *in_dev; 651dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 6522638eb8bSFlorian Westphal struct in_ifaddr *ifa; 65330e2379eSMenglong Dong int err; 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds ASSERT_RTNL(); 6561da177e4SLinus Torvalds 6578cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 6588cb08174SJohannes Berg ifa_ipv4_policy, extack); 659dfdd5fd4SThomas Graf if (err < 0) 660dfdd5fd4SThomas Graf goto errout; 661dfdd5fd4SThomas Graf 662dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 6637fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 66451456b29SIan Morris if (!in_dev) { 665dfdd5fd4SThomas Graf err = -ENODEV; 666dfdd5fd4SThomas Graf goto errout; 667dfdd5fd4SThomas Graf } 668dfdd5fd4SThomas Graf 6692638eb8bSFlorian Westphal for (ifap = &in_dev->ifa_list; (ifa = rtnl_dereference(*ifap)) != NULL; 6701da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 671dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 67267b61f6cSJiri Benc ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL])) 6731da177e4SLinus Torvalds continue; 674dfdd5fd4SThomas Graf 675dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 676dfdd5fd4SThomas Graf continue; 677dfdd5fd4SThomas Graf 678dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 679dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 68067b61f6cSJiri Benc !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa))) 681dfdd5fd4SThomas Graf continue; 682dfdd5fd4SThomas Graf 68393a714d6SMadhu Challa if (ipv4_is_multicast(ifa->ifa_address)) 684690cc863STaras Chornyi ip_mc_autojoin_config(net, false, ifa); 68515e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 6861da177e4SLinus Torvalds return 0; 6871da177e4SLinus Torvalds } 688dfdd5fd4SThomas Graf 689dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 690dfdd5fd4SThomas Graf errout: 691dfdd5fd4SThomas Graf return err; 6921da177e4SLinus Torvalds } 6931da177e4SLinus Torvalds 6945c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 6955c766d64SJiri Pirko 6965c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 6975c766d64SJiri Pirko { 6985c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 6995c766d64SJiri Pirko struct in_ifaddr *ifa; 700c988d1e8SJiri Pirko struct hlist_node *n; 7015c766d64SJiri Pirko int i; 7025c766d64SJiri Pirko 7035c766d64SJiri Pirko now = jiffies; 7045c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 7055c766d64SJiri Pirko 7065c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 707c988d1e8SJiri Pirko bool change_needed = false; 708c988d1e8SJiri Pirko 709c988d1e8SJiri Pirko rcu_read_lock(); 710b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 7115c766d64SJiri Pirko unsigned long age; 7125c766d64SJiri Pirko 7135c766d64SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 7145c766d64SJiri Pirko continue; 7155c766d64SJiri Pirko 7165c766d64SJiri Pirko /* We try to batch several events at once. */ 7175c766d64SJiri Pirko age = (now - ifa->ifa_tstamp + 7185c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 7195c766d64SJiri Pirko 7205c766d64SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 7215c766d64SJiri Pirko age >= ifa->ifa_valid_lft) { 722c988d1e8SJiri Pirko change_needed = true; 723c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft == 724c988d1e8SJiri Pirko INFINITY_LIFE_TIME) { 725c988d1e8SJiri Pirko continue; 726c988d1e8SJiri Pirko } else if (age >= ifa->ifa_preferred_lft) { 727c988d1e8SJiri Pirko if (time_before(ifa->ifa_tstamp + 728c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ, next)) 729c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 730c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ; 731c988d1e8SJiri Pirko 732c988d1e8SJiri Pirko if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) 733c988d1e8SJiri Pirko change_needed = true; 734c988d1e8SJiri Pirko } else if (time_before(ifa->ifa_tstamp + 735c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ, 736c988d1e8SJiri Pirko next)) { 737c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 738c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ; 739c988d1e8SJiri Pirko } 740c988d1e8SJiri Pirko } 741c988d1e8SJiri Pirko rcu_read_unlock(); 742c988d1e8SJiri Pirko if (!change_needed) 743c988d1e8SJiri Pirko continue; 744c988d1e8SJiri Pirko rtnl_lock(); 745c988d1e8SJiri Pirko hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 746c988d1e8SJiri Pirko unsigned long age; 747c988d1e8SJiri Pirko 748c988d1e8SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 749c988d1e8SJiri Pirko continue; 750c988d1e8SJiri Pirko 751c988d1e8SJiri Pirko /* We try to batch several events at once. */ 752c988d1e8SJiri Pirko age = (now - ifa->ifa_tstamp + 753c988d1e8SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 754c988d1e8SJiri Pirko 755c988d1e8SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 756c988d1e8SJiri Pirko age >= ifa->ifa_valid_lft) { 7572638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap; 7582638eb8bSFlorian Westphal struct in_ifaddr *tmp; 7595c766d64SJiri Pirko 7602638eb8bSFlorian Westphal ifap = &ifa->ifa_dev->ifa_list; 7612638eb8bSFlorian Westphal tmp = rtnl_dereference(*ifap); 7622638eb8bSFlorian Westphal while (tmp) { 76340008e92SFlorian Westphal if (tmp == ifa) { 7645c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 7655c766d64SJiri Pirko ifap, 1); 766c988d1e8SJiri Pirko break; 7675c766d64SJiri Pirko } 7682638eb8bSFlorian Westphal ifap = &tmp->ifa_next; 7692638eb8bSFlorian Westphal tmp = rtnl_dereference(*ifap); 770c988d1e8SJiri Pirko } 771c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft != 772c988d1e8SJiri Pirko INFINITY_LIFE_TIME && 773c988d1e8SJiri Pirko age >= ifa->ifa_preferred_lft && 774c988d1e8SJiri Pirko !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 7755c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7765c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 7775c766d64SJiri Pirko } 7785c766d64SJiri Pirko } 779c988d1e8SJiri Pirko rtnl_unlock(); 7805c766d64SJiri Pirko } 7815c766d64SJiri Pirko 7825c766d64SJiri Pirko next_sec = round_jiffies_up(next); 7835c766d64SJiri Pirko next_sched = next; 7845c766d64SJiri Pirko 7855c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 7865c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 7875c766d64SJiri Pirko next_sched = next_sec; 7885c766d64SJiri Pirko 7895c766d64SJiri Pirko now = jiffies; 7905c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 7915c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 7925c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 7935c766d64SJiri Pirko 794906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 795906e073fSviresh kumar next_sched - now); 7965c766d64SJiri Pirko } 7975c766d64SJiri Pirko 7985c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 7995c766d64SJiri Pirko __u32 prefered_lft) 8005c766d64SJiri Pirko { 8015c766d64SJiri Pirko unsigned long timeout; 8025c766d64SJiri Pirko 8035c766d64SJiri Pirko ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 8045c766d64SJiri Pirko 8055c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 8065c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 8075c766d64SJiri Pirko ifa->ifa_valid_lft = timeout; 8085c766d64SJiri Pirko else 8095c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_PERMANENT; 8105c766d64SJiri Pirko 8115c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 8125c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 8135c766d64SJiri Pirko if (timeout == 0) 8145c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 8155c766d64SJiri Pirko ifa->ifa_preferred_lft = timeout; 8165c766d64SJiri Pirko } 8175c766d64SJiri Pirko ifa->ifa_tstamp = jiffies; 8185c766d64SJiri Pirko if (!ifa->ifa_cstamp) 8195c766d64SJiri Pirko ifa->ifa_cstamp = ifa->ifa_tstamp; 8205c766d64SJiri Pirko } 8215c766d64SJiri Pirko 8225c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 823dac9c979SDavid Ahern __u32 *pvalid_lft, __u32 *pprefered_lft, 824dac9c979SDavid Ahern struct netlink_ext_ack *extack) 8251da177e4SLinus Torvalds { 8265c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 8275c753978SThomas Graf struct in_ifaddr *ifa; 8285c753978SThomas Graf struct ifaddrmsg *ifm; 8291da177e4SLinus Torvalds struct net_device *dev; 8301da177e4SLinus Torvalds struct in_device *in_dev; 8317b218574SDenis V. Lunev int err; 8321da177e4SLinus Torvalds 8338cb08174SJohannes Berg err = nlmsg_parse_deprecated(nlh, sizeof(*ifm), tb, IFA_MAX, 8348cb08174SJohannes Berg ifa_ipv4_policy, extack); 8355c753978SThomas Graf if (err < 0) 8365c753978SThomas Graf goto errout; 8371da177e4SLinus Torvalds 8385c753978SThomas Graf ifm = nlmsg_data(nlh); 839c4e38f41SEvgeniy Polyakov err = -EINVAL; 84051456b29SIan Morris if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL]) 8415c753978SThomas Graf goto errout; 8421da177e4SLinus Torvalds 8434b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 8445c753978SThomas Graf err = -ENODEV; 84551456b29SIan Morris if (!dev) 8465c753978SThomas Graf goto errout; 8471da177e4SLinus Torvalds 8485c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 8495c753978SThomas Graf err = -ENOBUFS; 85051456b29SIan Morris if (!in_dev) 8515c753978SThomas Graf goto errout; 85271e27da9SHerbert Xu 8535c753978SThomas Graf ifa = inet_alloc_ifa(); 85451456b29SIan Morris if (!ifa) 8555c753978SThomas Graf /* 8565c753978SThomas Graf * A potential indev allocation can be left alive, it stays 8575c753978SThomas Graf * assigned to its device and is destroy with it. 8585c753978SThomas Graf */ 8595c753978SThomas Graf goto errout; 8605c753978SThomas Graf 861a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 8621d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 8635c753978SThomas Graf in_dev_hold(in_dev); 8645c753978SThomas Graf 86551456b29SIan Morris if (!tb[IFA_ADDRESS]) 8665c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 8675c753978SThomas Graf 868fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 8691da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 8701da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 871ad6c8135SJiri Pirko ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : 872ad6c8135SJiri Pirko ifm->ifa_flags; 8731da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 8741da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 8755c753978SThomas Graf 87667b61f6cSJiri Benc ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]); 87767b61f6cSJiri Benc ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]); 8785c753978SThomas Graf 8795c753978SThomas Graf if (tb[IFA_BROADCAST]) 88067b61f6cSJiri Benc ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]); 8815c753978SThomas Graf 8825c753978SThomas Graf if (tb[IFA_LABEL]) 883872f6903SFrancis Laniel nla_strscpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 8841da177e4SLinus Torvalds else 8851da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 8861da177e4SLinus Torvalds 887af4d768aSDavid Ahern if (tb[IFA_RT_PRIORITY]) 888af4d768aSDavid Ahern ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 889af4d768aSDavid Ahern 8905c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 8915c766d64SJiri Pirko struct ifa_cacheinfo *ci; 8925c766d64SJiri Pirko 8935c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 8945c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 8955c766d64SJiri Pirko err = -EINVAL; 896446266b0SDaniel Borkmann goto errout_free; 8975c766d64SJiri Pirko } 8985c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 8995c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 9005c766d64SJiri Pirko } 9015c766d64SJiri Pirko 9025c753978SThomas Graf return ifa; 9035c753978SThomas Graf 904446266b0SDaniel Borkmann errout_free: 905446266b0SDaniel Borkmann inet_free_ifa(ifa); 9065c753978SThomas Graf errout: 9075c753978SThomas Graf return ERR_PTR(err); 9085c753978SThomas Graf } 9095c753978SThomas Graf 9105c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 9115c766d64SJiri Pirko { 9125c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 913ef11db33SFlorian Westphal struct in_ifaddr *ifa1; 9145c766d64SJiri Pirko 9155c766d64SJiri Pirko if (!ifa->ifa_local) 9165c766d64SJiri Pirko return NULL; 9175c766d64SJiri Pirko 918ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa1, in_dev) { 9195c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 9205c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 9215c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 9225c766d64SJiri Pirko return ifa1; 9235c766d64SJiri Pirko } 9245c766d64SJiri Pirko return NULL; 9255c766d64SJiri Pirko } 9265c766d64SJiri Pirko 927c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, 928c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 9295c753978SThomas Graf { 9303b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 9315c753978SThomas Graf struct in_ifaddr *ifa; 9325c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 9335c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 9345c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 9355c753978SThomas Graf 9365c753978SThomas Graf ASSERT_RTNL(); 9375c753978SThomas Graf 938dac9c979SDavid Ahern ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack); 9395c753978SThomas Graf if (IS_ERR(ifa)) 9405c753978SThomas Graf return PTR_ERR(ifa); 9415c753978SThomas Graf 9425c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 9435c766d64SJiri Pirko if (!ifa_existing) { 9445c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 945614d056cSstephen hemminger * userspace already relies on not having to provide this. 9465c766d64SJiri Pirko */ 9475c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 94893a714d6SMadhu Challa if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) { 949690cc863STaras Chornyi int ret = ip_mc_autojoin_config(net, true, ifa); 95093a714d6SMadhu Challa 95193a714d6SMadhu Challa if (ret < 0) { 95293a714d6SMadhu Challa inet_free_ifa(ifa); 95393a714d6SMadhu Challa return ret; 95493a714d6SMadhu Challa } 95593a714d6SMadhu Challa } 956de95e047SDavid Ahern return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid, 957de95e047SDavid Ahern extack); 9585c766d64SJiri Pirko } else { 959af4d768aSDavid Ahern u32 new_metric = ifa->ifa_rt_priority; 960af4d768aSDavid Ahern 9615c766d64SJiri Pirko inet_free_ifa(ifa); 9625c766d64SJiri Pirko 9635c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 9645c766d64SJiri Pirko !(nlh->nlmsg_flags & NLM_F_REPLACE)) 9655c766d64SJiri Pirko return -EEXIST; 96634e2ed34SJiri Pirko ifa = ifa_existing; 967af4d768aSDavid Ahern 968af4d768aSDavid Ahern if (ifa->ifa_rt_priority != new_metric) { 969af4d768aSDavid Ahern fib_modify_prefix_metric(ifa, new_metric); 970af4d768aSDavid Ahern ifa->ifa_rt_priority = new_metric; 971af4d768aSDavid Ahern } 972af4d768aSDavid Ahern 97334e2ed34SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 97405a324b9SJiri Pirko cancel_delayed_work(&check_lifetime_work); 975906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, 976906e073fSviresh kumar &check_lifetime_work, 0); 97734e2ed34SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 9785c766d64SJiri Pirko } 9795c766d64SJiri Pirko return 0; 9801da177e4SLinus Torvalds } 9811da177e4SLinus Torvalds 9821da177e4SLinus Torvalds /* 9831da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 9841da177e4SLinus Torvalds */ 9851da177e4SLinus Torvalds 98640384999SEric Dumazet static int inet_abc_len(__be32 addr) 9871da177e4SLinus Torvalds { 9881da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 9891da177e4SLinus Torvalds 99065cab850SDave Taht if (ipv4_is_zeronet(addr) || ipv4_is_lbcast(addr)) 9911da177e4SLinus Torvalds rc = 0; 9921da177e4SLinus Torvalds else { 993714e85beSAl Viro __u32 haddr = ntohl(addr); 994714e85beSAl Viro if (IN_CLASSA(haddr)) 9951da177e4SLinus Torvalds rc = 8; 996714e85beSAl Viro else if (IN_CLASSB(haddr)) 9971da177e4SLinus Torvalds rc = 16; 998714e85beSAl Viro else if (IN_CLASSC(haddr)) 9991da177e4SLinus Torvalds rc = 24; 100065cab850SDave Taht else if (IN_CLASSE(haddr)) 100165cab850SDave Taht rc = 32; 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds 10041da177e4SLinus Torvalds return rc; 10051da177e4SLinus Torvalds } 10061da177e4SLinus Torvalds 10071da177e4SLinus Torvalds 100803aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr) 10091da177e4SLinus Torvalds { 10101da177e4SLinus Torvalds struct sockaddr_in sin_orig; 101103aef17bSAl Viro struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; 10122638eb8bSFlorian Westphal struct in_ifaddr __rcu **ifap = NULL; 10131da177e4SLinus Torvalds struct in_device *in_dev; 10141da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 10151da177e4SLinus Torvalds struct net_device *dev; 10161da177e4SLinus Torvalds char *colon; 10171da177e4SLinus Torvalds int ret = -EFAULT; 10181da177e4SLinus Torvalds int tryaddrmatch = 0; 10191da177e4SLinus Torvalds 102003aef17bSAl Viro ifr->ifr_name[IFNAMSIZ - 1] = 0; 10211da177e4SLinus Torvalds 10221da177e4SLinus Torvalds /* save original address for comparison */ 10231da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 10241da177e4SLinus Torvalds 102503aef17bSAl Viro colon = strchr(ifr->ifr_name, ':'); 10261da177e4SLinus Torvalds if (colon) 10271da177e4SLinus Torvalds *colon = 0; 10281da177e4SLinus Torvalds 102903aef17bSAl Viro dev_load(net, ifr->ifr_name); 10301da177e4SLinus Torvalds 10311da177e4SLinus Torvalds switch (cmd) { 10321da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 10331da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 10341da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 10351da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 10361da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 10371da177e4SLinus Torvalds so that we do not impose a lock. 10381da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 10391da177e4SLinus Torvalds */ 10401da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 10411da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 10421da177e4SLinus Torvalds sin->sin_family = AF_INET; 10431da177e4SLinus Torvalds break; 10441da177e4SLinus Torvalds 10451da177e4SLinus Torvalds case SIOCSIFFLAGS: 1046bf5b30b8SZhao Hongjiang ret = -EPERM; 104752e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10481da177e4SLinus Torvalds goto out; 10491da177e4SLinus Torvalds break; 10501da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10511da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10521da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 10531da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 1054bf5b30b8SZhao Hongjiang ret = -EPERM; 105552e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10561da177e4SLinus Torvalds goto out; 10571da177e4SLinus Torvalds ret = -EINVAL; 10581da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 10591da177e4SLinus Torvalds goto out; 10601da177e4SLinus Torvalds break; 10611da177e4SLinus Torvalds default: 10621da177e4SLinus Torvalds ret = -EINVAL; 10631da177e4SLinus Torvalds goto out; 10641da177e4SLinus Torvalds } 10651da177e4SLinus Torvalds 10661da177e4SLinus Torvalds rtnl_lock(); 10671da177e4SLinus Torvalds 10681da177e4SLinus Torvalds ret = -ENODEV; 106903aef17bSAl Viro dev = __dev_get_by_name(net, ifr->ifr_name); 10709f9354b9SEric Dumazet if (!dev) 10711da177e4SLinus Torvalds goto done; 10721da177e4SLinus Torvalds 10731da177e4SLinus Torvalds if (colon) 10741da177e4SLinus Torvalds *colon = ':'; 10751da177e4SLinus Torvalds 10769f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 10779f9354b9SEric Dumazet if (in_dev) { 10781da177e4SLinus Torvalds if (tryaddrmatch) { 10791da177e4SLinus Torvalds /* Matthias Andree */ 10801da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 10811da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 10821da177e4SLinus Torvalds and only if the original address family was AF_INET. 10831da177e4SLinus Torvalds This is checked above. */ 10842638eb8bSFlorian Westphal 10852638eb8bSFlorian Westphal for (ifap = &in_dev->ifa_list; 10862638eb8bSFlorian Westphal (ifa = rtnl_dereference(*ifap)) != NULL; 10871da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 108803aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label) && 10891da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 10906c91afe1SDavid S. Miller ifa->ifa_local) { 10911da177e4SLinus Torvalds break; /* found */ 10921da177e4SLinus Torvalds } 10931da177e4SLinus Torvalds } 10941da177e4SLinus Torvalds } 10951da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 10961da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 10971da177e4SLinus Torvalds comparing just the label */ 10981da177e4SLinus Torvalds if (!ifa) { 10992638eb8bSFlorian Westphal for (ifap = &in_dev->ifa_list; 11002638eb8bSFlorian Westphal (ifa = rtnl_dereference(*ifap)) != NULL; 11011da177e4SLinus Torvalds ifap = &ifa->ifa_next) 110203aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label)) 11031da177e4SLinus Torvalds break; 11041da177e4SLinus Torvalds } 11051da177e4SLinus Torvalds } 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 11081da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 11091da177e4SLinus Torvalds goto done; 11101da177e4SLinus Torvalds 11111da177e4SLinus Torvalds switch (cmd) { 11121da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 111330e948a3STonghao Zhang ret = 0; 11141da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 111503aef17bSAl Viro break; 11161da177e4SLinus Torvalds 11171da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 111830e948a3STonghao Zhang ret = 0; 11191da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 112003aef17bSAl Viro break; 11211da177e4SLinus Torvalds 11221da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 112330e948a3STonghao Zhang ret = 0; 11241da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 112503aef17bSAl Viro break; 11261da177e4SLinus Torvalds 11271da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 112830e948a3STonghao Zhang ret = 0; 11291da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 113003aef17bSAl Viro break; 11311da177e4SLinus Torvalds 11321da177e4SLinus Torvalds case SIOCSIFFLAGS: 11331da177e4SLinus Torvalds if (colon) { 11341da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 11351da177e4SLinus Torvalds if (!ifa) 11361da177e4SLinus Torvalds break; 11371da177e4SLinus Torvalds ret = 0; 113803aef17bSAl Viro if (!(ifr->ifr_flags & IFF_UP)) 11391da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 11401da177e4SLinus Torvalds break; 11411da177e4SLinus Torvalds } 1142567c5e13SPetr Machata ret = dev_change_flags(dev, ifr->ifr_flags, NULL); 11431da177e4SLinus Torvalds break; 11441da177e4SLinus Torvalds 11451da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 11461da177e4SLinus Torvalds ret = -EINVAL; 11471da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 11481da177e4SLinus Torvalds break; 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds if (!ifa) { 11511da177e4SLinus Torvalds ret = -ENOBUFS; 11529f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 11539f9354b9SEric Dumazet if (!ifa) 11541da177e4SLinus Torvalds break; 1155c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 11561da177e4SLinus Torvalds if (colon) 115703aef17bSAl Viro memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ); 11581da177e4SLinus Torvalds else 11591da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11601da177e4SLinus Torvalds } else { 11611da177e4SLinus Torvalds ret = 0; 11621da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 11631da177e4SLinus Torvalds break; 11641da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11651da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 1166148f9729SBjorn Mork ifa->ifa_scope = 0; 11671da177e4SLinus Torvalds } 11681da177e4SLinus Torvalds 11691da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 11701da177e4SLinus Torvalds 11711da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 11721da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 11731da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 11741da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 11751da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 11761da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 11771da177e4SLinus Torvalds ~ifa->ifa_mask; 11781da177e4SLinus Torvalds } else { 11791da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 11801da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 11811da177e4SLinus Torvalds } 11825c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 11831da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 11841da177e4SLinus Torvalds break; 11851da177e4SLinus Torvalds 11861da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 11871da177e4SLinus Torvalds ret = 0; 11881da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 11891da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11901da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 11911da177e4SLinus Torvalds inet_insert_ifa(ifa); 11921da177e4SLinus Torvalds } 11931da177e4SLinus Torvalds break; 11941da177e4SLinus Torvalds 11951da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 11961da177e4SLinus Torvalds ret = 0; 11971da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 11981da177e4SLinus Torvalds break; 11991da177e4SLinus Torvalds ret = -EINVAL; 12001da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 12011da177e4SLinus Torvalds break; 12021da177e4SLinus Torvalds ret = 0; 12031da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 12041da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 12051da177e4SLinus Torvalds inet_insert_ifa(ifa); 12061da177e4SLinus Torvalds break; 12071da177e4SLinus Torvalds 12081da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 12091da177e4SLinus Torvalds 12101da177e4SLinus Torvalds /* 12111da177e4SLinus Torvalds * The mask we set must be legal. 12121da177e4SLinus Torvalds */ 12131da177e4SLinus Torvalds ret = -EINVAL; 12141da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 12151da177e4SLinus Torvalds break; 12161da177e4SLinus Torvalds ret = 0; 12171da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1218a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 12191da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 12201da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 12211da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 12221da177e4SLinus Torvalds 12231da177e4SLinus Torvalds /* See if current broadcast address matches 12241da177e4SLinus Torvalds * with current netmask, then recalculate 12251da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 12261da177e4SLinus Torvalds * funny address, so don't touch it since 12271da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 12281da177e4SLinus Torvalds */ 12291da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 12301da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 12311da177e4SLinus Torvalds (ifa->ifa_broadcast == 1232dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 12331da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 12341da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 12351da177e4SLinus Torvalds } 12361da177e4SLinus Torvalds inet_insert_ifa(ifa); 12371da177e4SLinus Torvalds } 12381da177e4SLinus Torvalds break; 12391da177e4SLinus Torvalds } 12401da177e4SLinus Torvalds done: 12411da177e4SLinus Torvalds rtnl_unlock(); 12421da177e4SLinus Torvalds out: 12431da177e4SLinus Torvalds return ret; 12441da177e4SLinus Torvalds } 12451da177e4SLinus Torvalds 124636fd633eSAl Viro static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size) 12471da177e4SLinus Torvalds { 1248e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 1249ef11db33SFlorian Westphal const struct in_ifaddr *ifa; 12501da177e4SLinus Torvalds struct ifreq ifr; 12511da177e4SLinus Torvalds int done = 0; 12521da177e4SLinus Torvalds 125336fd633eSAl Viro if (WARN_ON(size > sizeof(struct ifreq))) 125436fd633eSAl Viro goto out; 125536fd633eSAl Viro 12569f9354b9SEric Dumazet if (!in_dev) 12571da177e4SLinus Torvalds goto out; 12581da177e4SLinus Torvalds 1259ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 12601da177e4SLinus Torvalds if (!buf) { 126136fd633eSAl Viro done += size; 12621da177e4SLinus Torvalds continue; 12631da177e4SLinus Torvalds } 126436fd633eSAl Viro if (len < size) 12651da177e4SLinus Torvalds break; 12661da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 12671da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 12681da177e4SLinus Torvalds 12691da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 12701da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 12711da177e4SLinus Torvalds ifa->ifa_local; 12721da177e4SLinus Torvalds 127336fd633eSAl Viro if (copy_to_user(buf + done, &ifr, size)) { 12741da177e4SLinus Torvalds done = -EFAULT; 12751da177e4SLinus Torvalds break; 12761da177e4SLinus Torvalds } 127736fd633eSAl Viro len -= size; 127836fd633eSAl Viro done += size; 12791da177e4SLinus Torvalds } 12801da177e4SLinus Torvalds out: 12811da177e4SLinus Torvalds return done; 12821da177e4SLinus Torvalds } 12831da177e4SLinus Torvalds 12848b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev, 12858b57fd1eSGao Feng int scope) 12868b57fd1eSGao Feng { 1287d519e870SFlorian Westphal const struct in_ifaddr *ifa; 1288d519e870SFlorian Westphal 1289d519e870SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 1290d519e870SFlorian Westphal if (ifa->ifa_flags & IFA_F_SECONDARY) 1291d519e870SFlorian Westphal continue; 12928b57fd1eSGao Feng if (ifa->ifa_scope != RT_SCOPE_LINK && 12938b57fd1eSGao Feng ifa->ifa_scope <= scope) 12948b57fd1eSGao Feng return ifa->ifa_local; 1295d519e870SFlorian Westphal } 12968b57fd1eSGao Feng 12978b57fd1eSGao Feng return 0; 12988b57fd1eSGao Feng } 12998b57fd1eSGao Feng 1300a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 13011da177e4SLinus Torvalds { 1302d519e870SFlorian Westphal const struct in_ifaddr *ifa; 1303a61ced5dSAl Viro __be32 addr = 0; 1304d8c444d5SShijie Luo unsigned char localnet_scope = RT_SCOPE_HOST; 13051da177e4SLinus Torvalds struct in_device *in_dev; 1306c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 13073f2fb9a8SDavid Ahern int master_idx; 13081da177e4SLinus Torvalds 13091da177e4SLinus Torvalds rcu_read_lock(); 1310e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 13111da177e4SLinus Torvalds if (!in_dev) 13121da177e4SLinus Torvalds goto no_in_dev; 13131da177e4SLinus Torvalds 1314d8c444d5SShijie Luo if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev))) 1315d8c444d5SShijie Luo localnet_scope = RT_SCOPE_LINK; 1316d8c444d5SShijie Luo 1317d519e870SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 1318d519e870SFlorian Westphal if (ifa->ifa_flags & IFA_F_SECONDARY) 1319d519e870SFlorian Westphal continue; 1320d8c444d5SShijie Luo if (min(ifa->ifa_scope, localnet_scope) > scope) 13211da177e4SLinus Torvalds continue; 13221da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 13231da177e4SLinus Torvalds addr = ifa->ifa_local; 13241da177e4SLinus Torvalds break; 13251da177e4SLinus Torvalds } 13261da177e4SLinus Torvalds if (!addr) 13271da177e4SLinus Torvalds addr = ifa->ifa_local; 1328d519e870SFlorian Westphal } 13291da177e4SLinus Torvalds 13301da177e4SLinus Torvalds if (addr) 1331c6d14c84SEric Dumazet goto out_unlock; 13329f9354b9SEric Dumazet no_in_dev: 13333f2fb9a8SDavid Ahern master_idx = l3mdev_master_ifindex_rcu(dev); 13341da177e4SLinus Torvalds 133517b693cdSDavid Lamparter /* For VRFs, the VRF device takes the place of the loopback device, 133617b693cdSDavid Lamparter * with addresses on it being preferred. Note in such cases the 133717b693cdSDavid Lamparter * loopback device will be among the devices that fail the master_idx 133817b693cdSDavid Lamparter * equality check in the loop below. 133917b693cdSDavid Lamparter */ 134017b693cdSDavid Lamparter if (master_idx && 134117b693cdSDavid Lamparter (dev = dev_get_by_index_rcu(net, master_idx)) && 134217b693cdSDavid Lamparter (in_dev = __in_dev_get_rcu(dev))) { 13438b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 13448b57fd1eSGao Feng if (addr) 134517b693cdSDavid Lamparter goto out_unlock; 134617b693cdSDavid Lamparter } 134717b693cdSDavid Lamparter 13481da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 1349ca9f1fd2SStephen Hemminger in this case. It is important that lo is the first interface 13501da177e4SLinus Torvalds in dev_base list. 13511da177e4SLinus Torvalds */ 1352c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 13533f2fb9a8SDavid Ahern if (l3mdev_master_ifindex_rcu(dev) != master_idx) 13543f2fb9a8SDavid Ahern continue; 13553f2fb9a8SDavid Ahern 13569f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 13579f9354b9SEric Dumazet if (!in_dev) 13581da177e4SLinus Torvalds continue; 13591da177e4SLinus Torvalds 13608b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 13618b57fd1eSGao Feng if (addr) 1362c6d14c84SEric Dumazet goto out_unlock; 13631da177e4SLinus Torvalds } 1364c6d14c84SEric Dumazet out_unlock: 13651da177e4SLinus Torvalds rcu_read_unlock(); 13661da177e4SLinus Torvalds return addr; 13671da177e4SLinus Torvalds } 13689f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 13691da177e4SLinus Torvalds 137060cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 137160cad5daSAl Viro __be32 local, int scope) 13721da177e4SLinus Torvalds { 1373650638a7SShijie Luo unsigned char localnet_scope = RT_SCOPE_HOST; 1374ef11db33SFlorian Westphal const struct in_ifaddr *ifa; 1375a144ea4bSAl Viro __be32 addr = 0; 1376ef11db33SFlorian Westphal int same = 0; 13771da177e4SLinus Torvalds 1378650638a7SShijie Luo if (unlikely(IN_DEV_ROUTE_LOCALNET(in_dev))) 1379650638a7SShijie Luo localnet_scope = RT_SCOPE_LINK; 1380650638a7SShijie Luo 1381ef11db33SFlorian Westphal in_dev_for_each_ifa_rcu(ifa, in_dev) { 1382650638a7SShijie Luo unsigned char min_scope = min(ifa->ifa_scope, localnet_scope); 1383650638a7SShijie Luo 13841da177e4SLinus Torvalds if (!addr && 13851da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 1386650638a7SShijie Luo min_scope <= scope) { 13871da177e4SLinus Torvalds addr = ifa->ifa_local; 13881da177e4SLinus Torvalds if (same) 13891da177e4SLinus Torvalds break; 13901da177e4SLinus Torvalds } 13911da177e4SLinus Torvalds if (!same) { 13921da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 13931da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 13941da177e4SLinus Torvalds if (same && addr) { 13951da177e4SLinus Torvalds if (local || !dst) 13961da177e4SLinus Torvalds break; 13971da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 13981da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 13991da177e4SLinus Torvalds break; 14001da177e4SLinus Torvalds /* No, then can we use new local src? */ 1401650638a7SShijie Luo if (min_scope <= scope) { 14021da177e4SLinus Torvalds addr = ifa->ifa_local; 14031da177e4SLinus Torvalds break; 14041da177e4SLinus Torvalds } 14051da177e4SLinus Torvalds /* search for large dst subnet for addr */ 14061da177e4SLinus Torvalds same = 0; 14071da177e4SLinus Torvalds } 14081da177e4SLinus Torvalds } 1409ef11db33SFlorian Westphal } 14101da177e4SLinus Torvalds 14111da177e4SLinus Torvalds return same ? addr : 0; 14121da177e4SLinus Torvalds } 14131da177e4SLinus Torvalds 14141da177e4SLinus Torvalds /* 14151da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 1416b601fa19SNicolas Dichtel * - net: netns to check, cannot be NULL 1417b601fa19SNicolas Dichtel * - in_dev: only on this interface, NULL=any interface 14181da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 14191da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 14201da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 14211da177e4SLinus Torvalds */ 1422b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, 14239bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 14241da177e4SLinus Torvalds { 142560cad5daSAl Viro __be32 addr = 0; 14269bd85e32SDenis V. Lunev struct net_device *dev; 14271da177e4SLinus Torvalds 142800db4124SIan Morris if (in_dev) 14299bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 14301da177e4SLinus Torvalds 14311da177e4SLinus Torvalds rcu_read_lock(); 1432c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 14339f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 14349f9354b9SEric Dumazet if (in_dev) { 14351da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 14361da177e4SLinus Torvalds if (addr) 14371da177e4SLinus Torvalds break; 14381da177e4SLinus Torvalds } 14391da177e4SLinus Torvalds } 14401da177e4SLinus Torvalds rcu_read_unlock(); 14411da177e4SLinus Torvalds 14421da177e4SLinus Torvalds return addr; 14431da177e4SLinus Torvalds } 1444eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 14451da177e4SLinus Torvalds 14461da177e4SLinus Torvalds /* 14471da177e4SLinus Torvalds * Device notifier 14481da177e4SLinus Torvalds */ 14491da177e4SLinus Torvalds 14501da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 14511da177e4SLinus Torvalds { 1452e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 14531da177e4SLinus Torvalds } 14549f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 14551da177e4SLinus Torvalds 14561da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 14571da177e4SLinus Torvalds { 1458e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 14591da177e4SLinus Torvalds } 14609f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 14611da177e4SLinus Torvalds 14623ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb) 14633ad7d246SKrister Johansen { 14643ad7d246SKrister Johansen return blocking_notifier_chain_register(&inetaddr_validator_chain, nb); 14653ad7d246SKrister Johansen } 14663ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier); 14673ad7d246SKrister Johansen 14683ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb) 14693ad7d246SKrister Johansen { 14703ad7d246SKrister Johansen return blocking_notifier_chain_unregister(&inetaddr_validator_chain, 14713ad7d246SKrister Johansen nb); 14723ad7d246SKrister Johansen } 14733ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier); 14743ad7d246SKrister Johansen 14759f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 14769f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 14771da177e4SLinus Torvalds */ 14781da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 14791da177e4SLinus Torvalds { 14801da177e4SLinus Torvalds struct in_ifaddr *ifa; 14811da177e4SLinus Torvalds int named = 0; 14821da177e4SLinus Torvalds 1483ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 14841da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 14851da177e4SLinus Torvalds 14861da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 14871da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 14881da177e4SLinus Torvalds if (named++ == 0) 1489573bf470SThomas Graf goto skip; 149044344b2aSMark McLoughlin dot = strchr(old, ':'); 149151456b29SIan Morris if (!dot) { 14921da177e4SLinus Torvalds sprintf(old, ":%d", named); 14931da177e4SLinus Torvalds dot = old; 14941da177e4SLinus Torvalds } 14959f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 14961da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 14979f9354b9SEric Dumazet else 14981da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1499573bf470SThomas Graf skip: 1500573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 15011da177e4SLinus Torvalds } 15021da177e4SLinus Torvalds } 15031da177e4SLinus Torvalds 1504d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1505d11327adSIan Campbell struct in_device *in_dev) 1506d11327adSIan Campbell 1507d11327adSIan Campbell { 1508ef11db33SFlorian Westphal const struct in_ifaddr *ifa; 1509d11327adSIan Campbell 1510ef11db33SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 1511d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 15126c91afe1SDavid S. Miller ifa->ifa_local, dev, 15136c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1514d11327adSIan Campbell dev->dev_addr, NULL); 1515d11327adSIan Campbell } 1516b76d0789SZoltan Kiss } 1517d11327adSIan Campbell 15181da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 15191da177e4SLinus Torvalds 15201da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 15211da177e4SLinus Torvalds void *ptr) 15221da177e4SLinus Torvalds { 1523351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1524748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 15251da177e4SLinus Torvalds 15261da177e4SLinus Torvalds ASSERT_RTNL(); 15271da177e4SLinus Torvalds 15281da177e4SLinus Torvalds if (!in_dev) { 15298030f544SHerbert Xu if (event == NETDEV_REGISTER) { 15301da177e4SLinus Torvalds in_dev = inetdev_init(dev); 153120e61da7SWANG Cong if (IS_ERR(in_dev)) 153220e61da7SWANG Cong return notifier_from_errno(PTR_ERR(in_dev)); 15330cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 153442f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 153542f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 15361da177e4SLinus Torvalds } 153706770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 153806770843SBreno Leitao /* Re-enabling IP */ 153906770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 154006770843SBreno Leitao in_dev = inetdev_init(dev); 15418030f544SHerbert Xu } 15421da177e4SLinus Torvalds goto out; 15431da177e4SLinus Torvalds } 15441da177e4SLinus Torvalds 15451da177e4SLinus Torvalds switch (event) { 15461da177e4SLinus Torvalds case NETDEV_REGISTER: 154791df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1548a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 15491da177e4SLinus Torvalds break; 15501da177e4SLinus Torvalds case NETDEV_UP: 155106770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 15521da177e4SLinus Torvalds break; 15530cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 15549f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 15559f9354b9SEric Dumazet 15569f9354b9SEric Dumazet if (ifa) { 1557fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 15581da177e4SLinus Torvalds ifa->ifa_local = 15591da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 15601da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 15611da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 15621da177e4SLinus Torvalds in_dev_hold(in_dev); 15631da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 15641da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 15651da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 15665c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 15675c766d64SJiri Pirko INFINITY_LIFE_TIME); 1568dfd1582dSJiri Pirko ipv4_devconf_setall(in_dev); 1569dfd1582dSJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 15701da177e4SLinus Torvalds inet_insert_ifa(ifa); 15711da177e4SLinus Torvalds } 15721da177e4SLinus Torvalds } 15731da177e4SLinus Torvalds ip_mc_up(in_dev); 1574a8eceea8SJoe Perches fallthrough; 1575eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1576d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1577d11327adSIan Campbell break; 1578a8eceea8SJoe Perches fallthrough; 1579d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1580a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1581d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 15821da177e4SLinus Torvalds break; 15831da177e4SLinus Torvalds case NETDEV_DOWN: 15841da177e4SLinus Torvalds ip_mc_down(in_dev); 15851da177e4SLinus Torvalds break; 158693d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 158775c78500SMoni Shoua ip_mc_unmap(in_dev); 158875c78500SMoni Shoua break; 158993d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 159075c78500SMoni Shoua ip_mc_remap(in_dev); 159175c78500SMoni Shoua break; 15921da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 159306770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 15941da177e4SLinus Torvalds break; 159506770843SBreno Leitao /* disable IP when MTU is not enough */ 1596a8eceea8SJoe Perches fallthrough; 15971da177e4SLinus Torvalds case NETDEV_UNREGISTER: 15981da177e4SLinus Torvalds inetdev_destroy(in_dev); 15991da177e4SLinus Torvalds break; 16001da177e4SLinus Torvalds case NETDEV_CHANGENAME: 16011da177e4SLinus Torvalds /* Do not notify about label change, this event is 16021da177e4SLinus Torvalds * not interesting to applications using netlink. 16031da177e4SLinus Torvalds */ 16041da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 16051da177e4SLinus Torvalds 160651602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 160766f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 16081da177e4SLinus Torvalds break; 16091da177e4SLinus Torvalds } 16101da177e4SLinus Torvalds out: 16111da177e4SLinus Torvalds return NOTIFY_DONE; 16121da177e4SLinus Torvalds } 16131da177e4SLinus Torvalds 16141da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 16151da177e4SLinus Torvalds .notifier_call = inetdev_event, 16161da177e4SLinus Torvalds }; 16171da177e4SLinus Torvalds 161840384999SEric Dumazet static size_t inet_nlmsg_size(void) 1619339bf98fSThomas Graf { 1620339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1621339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1622339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1623339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1624ad6c8135SJiri Pirko + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ 162563b5f152SGeert Uytterhoeven + nla_total_size(4) /* IFA_FLAGS */ 1626af4d768aSDavid Ahern + nla_total_size(4) /* IFA_RT_PRIORITY */ 162763b5f152SGeert Uytterhoeven + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ 1628339bf98fSThomas Graf } 1629339bf98fSThomas Graf 16305c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 16315c766d64SJiri Pirko { 16325c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 16335c766d64SJiri Pirko } 16345c766d64SJiri Pirko 16355c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 16365c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 16375c766d64SJiri Pirko { 16385c766d64SJiri Pirko struct ifa_cacheinfo ci; 16395c766d64SJiri Pirko 16405c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 16415c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 16425c766d64SJiri Pirko ci.ifa_prefered = preferred; 16435c766d64SJiri Pirko ci.ifa_valid = valid; 16445c766d64SJiri Pirko 16455c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 16465c766d64SJiri Pirko } 16475c766d64SJiri Pirko 16481da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 1649978a46faSChristian Brauner struct inet_fill_args *args) 16501da177e4SLinus Torvalds { 16511da177e4SLinus Torvalds struct ifaddrmsg *ifm; 16521da177e4SLinus Torvalds struct nlmsghdr *nlh; 16535c766d64SJiri Pirko u32 preferred, valid; 16541da177e4SLinus Torvalds 1655978a46faSChristian Brauner nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm), 1656978a46faSChristian Brauner args->flags); 165751456b29SIan Morris if (!nlh) 165826932566SPatrick McHardy return -EMSGSIZE; 165947f68512SThomas Graf 166047f68512SThomas Graf ifm = nlmsg_data(nlh); 16611da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 16621da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 16635c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 16641da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 16651da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 16661da177e4SLinus Torvalds 1667978a46faSChristian Brauner if (args->netnsid >= 0 && 1668978a46faSChristian Brauner nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) 1669d3807145SChristian Brauner goto nla_put_failure; 1670d3807145SChristian Brauner 16715c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 16725c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 16735c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 16745c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 16755c766d64SJiri Pirko long tval = (jiffies - ifa->ifa_tstamp) / HZ; 16765c766d64SJiri Pirko 16775c766d64SJiri Pirko if (preferred > tval) 16785c766d64SJiri Pirko preferred -= tval; 16795c766d64SJiri Pirko else 16805c766d64SJiri Pirko preferred = 0; 16815c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 16825c766d64SJiri Pirko if (valid > tval) 16835c766d64SJiri Pirko valid -= tval; 16845c766d64SJiri Pirko else 16855c766d64SJiri Pirko valid = 0; 16865c766d64SJiri Pirko } 16875c766d64SJiri Pirko } 16885c766d64SJiri Pirko } else { 16895c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 16905c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 16915c766d64SJiri Pirko } 1692f3756b79SDavid S. Miller if ((ifa->ifa_address && 1693930345eaSJiri Benc nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) || 1694f3756b79SDavid S. Miller (ifa->ifa_local && 1695930345eaSJiri Benc nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) || 1696f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1697930345eaSJiri Benc nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1698f3756b79SDavid S. Miller (ifa->ifa_label[0] && 16995c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 1700ad6c8135SJiri Pirko nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || 1701af4d768aSDavid Ahern (ifa->ifa_rt_priority && 1702af4d768aSDavid Ahern nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) || 17035c766d64SJiri Pirko put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 17045c766d64SJiri Pirko preferred, valid)) 1705f3756b79SDavid S. Miller goto nla_put_failure; 170647f68512SThomas Graf 1707053c095aSJohannes Berg nlmsg_end(skb, nlh); 1708053c095aSJohannes Berg return 0; 170947f68512SThomas Graf 171047f68512SThomas Graf nla_put_failure: 171126932566SPatrick McHardy nlmsg_cancel(skb, nlh); 171226932566SPatrick McHardy return -EMSGSIZE; 17131da177e4SLinus Torvalds } 17141da177e4SLinus Torvalds 1715c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh, 1716c33078e3SDavid Ahern struct inet_fill_args *fillargs, 1717c33078e3SDavid Ahern struct net **tgt_net, struct sock *sk, 17185fcd266aSDavid Ahern struct netlink_callback *cb) 1719c33078e3SDavid Ahern { 17205fcd266aSDavid Ahern struct netlink_ext_ack *extack = cb->extack; 1721c33078e3SDavid Ahern struct nlattr *tb[IFA_MAX+1]; 1722c33078e3SDavid Ahern struct ifaddrmsg *ifm; 1723c33078e3SDavid Ahern int err, i; 1724c33078e3SDavid Ahern 1725c33078e3SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 1726c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request"); 1727c33078e3SDavid Ahern return -EINVAL; 1728c33078e3SDavid Ahern } 1729c33078e3SDavid Ahern 1730c33078e3SDavid Ahern ifm = nlmsg_data(nlh); 1731c33078e3SDavid Ahern if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { 1732c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request"); 1733c33078e3SDavid Ahern return -EINVAL; 1734c33078e3SDavid Ahern } 17355fcd266aSDavid Ahern 17365fcd266aSDavid Ahern fillargs->ifindex = ifm->ifa_index; 17375fcd266aSDavid Ahern if (fillargs->ifindex) { 17385fcd266aSDavid Ahern cb->answer_flags |= NLM_F_DUMP_FILTERED; 17395fcd266aSDavid Ahern fillargs->flags |= NLM_F_DUMP_FILTERED; 1740c33078e3SDavid Ahern } 1741c33078e3SDavid Ahern 17428cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(*ifm), tb, IFA_MAX, 1743c33078e3SDavid Ahern ifa_ipv4_policy, extack); 1744c33078e3SDavid Ahern if (err < 0) 1745c33078e3SDavid Ahern return err; 1746c33078e3SDavid Ahern 1747c33078e3SDavid Ahern for (i = 0; i <= IFA_MAX; ++i) { 1748c33078e3SDavid Ahern if (!tb[i]) 1749c33078e3SDavid Ahern continue; 1750c33078e3SDavid Ahern 1751c33078e3SDavid Ahern if (i == IFA_TARGET_NETNSID) { 1752c33078e3SDavid Ahern struct net *net; 1753c33078e3SDavid Ahern 1754c33078e3SDavid Ahern fillargs->netnsid = nla_get_s32(tb[i]); 1755c33078e3SDavid Ahern 1756c33078e3SDavid Ahern net = rtnl_get_net_ns_capable(sk, fillargs->netnsid); 1757c33078e3SDavid Ahern if (IS_ERR(net)) { 1758bf4cc40eSBjørn Mork fillargs->netnsid = -1; 1759c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id"); 1760c33078e3SDavid Ahern return PTR_ERR(net); 1761c33078e3SDavid Ahern } 1762c33078e3SDavid Ahern *tgt_net = net; 1763c33078e3SDavid Ahern } else { 1764c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request"); 1765c33078e3SDavid Ahern return -EINVAL; 1766c33078e3SDavid Ahern } 1767c33078e3SDavid Ahern } 1768c33078e3SDavid Ahern 1769c33078e3SDavid Ahern return 0; 1770c33078e3SDavid Ahern } 1771c33078e3SDavid Ahern 17721c98eca4SDavid Ahern static int in_dev_dump_addr(struct in_device *in_dev, struct sk_buff *skb, 17731c98eca4SDavid Ahern struct netlink_callback *cb, int s_ip_idx, 17741c98eca4SDavid Ahern struct inet_fill_args *fillargs) 17751c98eca4SDavid Ahern { 17761c98eca4SDavid Ahern struct in_ifaddr *ifa; 17771c98eca4SDavid Ahern int ip_idx = 0; 17781c98eca4SDavid Ahern int err; 17791c98eca4SDavid Ahern 1780d3e6e285SFlorian Westphal in_dev_for_each_ifa_rtnl(ifa, in_dev) { 1781ef11db33SFlorian Westphal if (ip_idx < s_ip_idx) { 1782ef11db33SFlorian Westphal ip_idx++; 17831c98eca4SDavid Ahern continue; 1784ef11db33SFlorian Westphal } 17851c98eca4SDavid Ahern err = inet_fill_ifaddr(skb, ifa, fillargs); 17861c98eca4SDavid Ahern if (err < 0) 17871c98eca4SDavid Ahern goto done; 17881c98eca4SDavid Ahern 17891c98eca4SDavid Ahern nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1790ef11db33SFlorian Westphal ip_idx++; 17911c98eca4SDavid Ahern } 17921c98eca4SDavid Ahern err = 0; 17931c98eca4SDavid Ahern 17941c98eca4SDavid Ahern done: 17951c98eca4SDavid Ahern cb->args[2] = ip_idx; 17961c98eca4SDavid Ahern 17971c98eca4SDavid Ahern return err; 17981c98eca4SDavid Ahern } 17991c98eca4SDavid Ahern 18001da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 18011da177e4SLinus Torvalds { 1802c33078e3SDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 1803978a46faSChristian Brauner struct inet_fill_args fillargs = { 1804978a46faSChristian Brauner .portid = NETLINK_CB(cb->skb).portid, 1805c33078e3SDavid Ahern .seq = nlh->nlmsg_seq, 1806978a46faSChristian Brauner .event = RTM_NEWADDR, 1807978a46faSChristian Brauner .flags = NLM_F_MULTI, 1808978a46faSChristian Brauner .netnsid = -1, 1809978a46faSChristian Brauner }; 18103b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1811d3807145SChristian Brauner struct net *tgt_net = net; 1812eec4df98SEric Dumazet int h, s_h; 1813eec4df98SEric Dumazet int idx, s_idx; 18141c98eca4SDavid Ahern int s_ip_idx; 18151da177e4SLinus Torvalds struct net_device *dev; 18161da177e4SLinus Torvalds struct in_device *in_dev; 1817eec4df98SEric Dumazet struct hlist_head *head; 1818d7e38611SDavid Ahern int err = 0; 18191da177e4SLinus Torvalds 1820eec4df98SEric Dumazet s_h = cb->args[0]; 1821eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 18221c98eca4SDavid Ahern s_ip_idx = cb->args[2]; 1823eec4df98SEric Dumazet 1824c33078e3SDavid Ahern if (cb->strict_check) { 1825c33078e3SDavid Ahern err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 18265fcd266aSDavid Ahern skb->sk, cb); 1827c33078e3SDavid Ahern if (err < 0) 1828d7e38611SDavid Ahern goto put_tgt_net; 18295fcd266aSDavid Ahern 1830d7e38611SDavid Ahern err = 0; 18315fcd266aSDavid Ahern if (fillargs.ifindex) { 18325fcd266aSDavid Ahern dev = __dev_get_by_index(tgt_net, fillargs.ifindex); 1833d7e38611SDavid Ahern if (!dev) { 1834d7e38611SDavid Ahern err = -ENODEV; 1835d7e38611SDavid Ahern goto put_tgt_net; 1836d7e38611SDavid Ahern } 18375fcd266aSDavid Ahern 18385fcd266aSDavid Ahern in_dev = __in_dev_get_rtnl(dev); 18395fcd266aSDavid Ahern if (in_dev) { 18405fcd266aSDavid Ahern err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx, 18415fcd266aSDavid Ahern &fillargs); 18425fcd266aSDavid Ahern } 18435fcd266aSDavid Ahern goto put_tgt_net; 18445fcd266aSDavid Ahern } 1845d3807145SChristian Brauner } 1846d3807145SChristian Brauner 1847eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 18487562f876SPavel Emelianov idx = 0; 1849d3807145SChristian Brauner head = &tgt_net->dev_index_head[h]; 1850eec4df98SEric Dumazet rcu_read_lock(); 1851d3807145SChristian Brauner cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^ 1852d3807145SChristian Brauner tgt_net->dev_base_seq; 1853b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 18541da177e4SLinus Torvalds if (idx < s_idx) 18557562f876SPavel Emelianov goto cont; 18564b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 18571da177e4SLinus Torvalds s_ip_idx = 0; 1858eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 18599f9354b9SEric Dumazet if (!in_dev) 18607562f876SPavel Emelianov goto cont; 18611da177e4SLinus Torvalds 18621c98eca4SDavid Ahern err = in_dev_dump_addr(in_dev, skb, cb, s_ip_idx, 18631c98eca4SDavid Ahern &fillargs); 18641c98eca4SDavid Ahern if (err < 0) { 1865eec4df98SEric Dumazet rcu_read_unlock(); 18661da177e4SLinus Torvalds goto done; 18671da177e4SLinus Torvalds } 18687562f876SPavel Emelianov cont: 18697562f876SPavel Emelianov idx++; 18701da177e4SLinus Torvalds } 1871eec4df98SEric Dumazet rcu_read_unlock(); 1872eec4df98SEric Dumazet } 18731da177e4SLinus Torvalds 18741da177e4SLinus Torvalds done: 1875eec4df98SEric Dumazet cb->args[0] = h; 1876eec4df98SEric Dumazet cb->args[1] = idx; 18775fcd266aSDavid Ahern put_tgt_net: 1878978a46faSChristian Brauner if (fillargs.netnsid >= 0) 1879d3807145SChristian Brauner put_net(tgt_net); 18801da177e4SLinus Torvalds 18817c1e8a38SArthur Gautier return skb->len ? : err; 18821da177e4SLinus Torvalds } 18831da177e4SLinus Torvalds 1884d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 188515e47304SEric W. Biederman u32 portid) 18861da177e4SLinus Torvalds { 1887978a46faSChristian Brauner struct inet_fill_args fillargs = { 1888978a46faSChristian Brauner .portid = portid, 1889978a46faSChristian Brauner .seq = nlh ? nlh->nlmsg_seq : 0, 1890978a46faSChristian Brauner .event = event, 1891978a46faSChristian Brauner .flags = 0, 1892978a46faSChristian Brauner .netnsid = -1, 1893978a46faSChristian Brauner }; 189447f68512SThomas Graf struct sk_buff *skb; 1895d6062cbbSThomas Graf int err = -ENOBUFS; 18964b8aa9abSDenis V. Lunev struct net *net; 18971da177e4SLinus Torvalds 1898c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1899339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 190051456b29SIan Morris if (!skb) 1901d6062cbbSThomas Graf goto errout; 1902d6062cbbSThomas Graf 1903978a46faSChristian Brauner err = inet_fill_ifaddr(skb, ifa, &fillargs); 190426932566SPatrick McHardy if (err < 0) { 190526932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 190626932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 190726932566SPatrick McHardy kfree_skb(skb); 190826932566SPatrick McHardy goto errout; 190926932566SPatrick McHardy } 191015e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 19111ce85fe4SPablo Neira Ayuso return; 1912d6062cbbSThomas Graf errout: 1913d6062cbbSThomas Graf if (err < 0) 19144b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 19151da177e4SLinus Torvalds } 19161da177e4SLinus Torvalds 1917b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev, 1918b1974ed0SArad, Ronen u32 ext_filter_mask) 19199f0f7272SThomas Graf { 19201fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 19219f0f7272SThomas Graf 19229f0f7272SThomas Graf if (!in_dev) 19239f0f7272SThomas Graf return 0; 19249f0f7272SThomas Graf 19259f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 19269f0f7272SThomas Graf } 19279f0f7272SThomas Graf 1928d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev, 1929d5566fd7SSowmini Varadhan u32 ext_filter_mask) 19309f0f7272SThomas Graf { 19311fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 19329f0f7272SThomas Graf struct nlattr *nla; 19339f0f7272SThomas Graf int i; 19349f0f7272SThomas Graf 19359f0f7272SThomas Graf if (!in_dev) 19369f0f7272SThomas Graf return -ENODATA; 19379f0f7272SThomas Graf 19389f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 193951456b29SIan Morris if (!nla) 19409f0f7272SThomas Graf return -EMSGSIZE; 19419f0f7272SThomas Graf 19429f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 19439f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 19449f0f7272SThomas Graf 19459f0f7272SThomas Graf return 0; 19469f0f7272SThomas Graf } 19479f0f7272SThomas Graf 19489f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 19499f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 19509f0f7272SThomas Graf }; 19519f0f7272SThomas Graf 1952cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1953cf7afbfeSThomas Graf const struct nlattr *nla) 19549f0f7272SThomas Graf { 19559f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 19569f0f7272SThomas Graf int err, rem; 19579f0f7272SThomas Graf 1958a100243dSCong Wang if (dev && !__in_dev_get_rtnl(dev)) 1959cf7afbfeSThomas Graf return -EAFNOSUPPORT; 19609f0f7272SThomas Graf 19618cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, 19628cb08174SJohannes Berg inet_af_policy, NULL); 19639f0f7272SThomas Graf if (err < 0) 19649f0f7272SThomas Graf return err; 19659f0f7272SThomas Graf 19669f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 19679f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 19689f0f7272SThomas Graf int cfgid = nla_type(a); 19699f0f7272SThomas Graf 19709f0f7272SThomas Graf if (nla_len(a) < 4) 19719f0f7272SThomas Graf return -EINVAL; 19729f0f7272SThomas Graf 19739f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 19749f0f7272SThomas Graf return -EINVAL; 19759f0f7272SThomas Graf } 19769f0f7272SThomas Graf } 19779f0f7272SThomas Graf 1978cf7afbfeSThomas Graf return 0; 1979cf7afbfeSThomas Graf } 1980cf7afbfeSThomas Graf 19813583a4e8SStephen Hemminger static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla, 19823583a4e8SStephen Hemminger struct netlink_ext_ack *extack) 1983cf7afbfeSThomas Graf { 1984a100243dSCong Wang struct in_device *in_dev = __in_dev_get_rtnl(dev); 1985cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1986cf7afbfeSThomas Graf int rem; 1987cf7afbfeSThomas Graf 1988cf7afbfeSThomas Graf if (!in_dev) 1989cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1990cf7afbfeSThomas Graf 19918cb08174SJohannes Berg if (nla_parse_nested_deprecated(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0) 19925ac6b198SZheng Yongjun return -EINVAL; 1993cf7afbfeSThomas Graf 19949f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 19959f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 19969f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 19979f0f7272SThomas Graf } 19989f0f7272SThomas Graf 19999f0f7272SThomas Graf return 0; 20009f0f7272SThomas Graf } 20019f0f7272SThomas Graf 2002edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 2003edc9e748SNicolas Dichtel { 2004edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 2005edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 2006136ba622SZhang Shengju bool all = false; 2007edc9e748SNicolas Dichtel 2008136ba622SZhang Shengju if (type == NETCONFA_ALL) 2009136ba622SZhang Shengju all = true; 2010136ba622SZhang Shengju 2011136ba622SZhang Shengju if (all || type == NETCONFA_FORWARDING) 2012edc9e748SNicolas Dichtel size += nla_total_size(4); 2013136ba622SZhang Shengju if (all || type == NETCONFA_RP_FILTER) 2014cc535dfbSNicolas Dichtel size += nla_total_size(4); 2015136ba622SZhang Shengju if (all || type == NETCONFA_MC_FORWARDING) 2016d67b8c61SNicolas Dichtel size += nla_total_size(4); 20175cbf777cSXin Long if (all || type == NETCONFA_BC_FORWARDING) 20185cbf777cSXin Long size += nla_total_size(4); 2019136ba622SZhang Shengju if (all || type == NETCONFA_PROXY_NEIGH) 2020f085ff1cSstephen hemminger size += nla_total_size(4); 2021136ba622SZhang Shengju if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) 2022974d7af5SAndy Gospodarek size += nla_total_size(4); 2023edc9e748SNicolas Dichtel 2024edc9e748SNicolas Dichtel return size; 2025edc9e748SNicolas Dichtel } 2026edc9e748SNicolas Dichtel 2027edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 2028edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 2029edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 2030edc9e748SNicolas Dichtel int type) 2031edc9e748SNicolas Dichtel { 2032edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 2033edc9e748SNicolas Dichtel struct netconfmsg *ncm; 2034136ba622SZhang Shengju bool all = false; 2035edc9e748SNicolas Dichtel 2036edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 2037edc9e748SNicolas Dichtel flags); 203851456b29SIan Morris if (!nlh) 2039edc9e748SNicolas Dichtel return -EMSGSIZE; 2040edc9e748SNicolas Dichtel 2041136ba622SZhang Shengju if (type == NETCONFA_ALL) 2042136ba622SZhang Shengju all = true; 2043136ba622SZhang Shengju 2044edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 2045edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 2046edc9e748SNicolas Dichtel 2047edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 2048edc9e748SNicolas Dichtel goto nla_put_failure; 2049edc9e748SNicolas Dichtel 2050b5c9641dSDavid Ahern if (!devconf) 2051b5c9641dSDavid Ahern goto out; 2052b5c9641dSDavid Ahern 2053136ba622SZhang Shengju if ((all || type == NETCONFA_FORWARDING) && 2054edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 2055edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 2056edc9e748SNicolas Dichtel goto nla_put_failure; 2057136ba622SZhang Shengju if ((all || type == NETCONFA_RP_FILTER) && 2058cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 2059cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 2060cc535dfbSNicolas Dichtel goto nla_put_failure; 2061136ba622SZhang Shengju if ((all || type == NETCONFA_MC_FORWARDING) && 2062d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 2063d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 2064d67b8c61SNicolas Dichtel goto nla_put_failure; 20655cbf777cSXin Long if ((all || type == NETCONFA_BC_FORWARDING) && 20665cbf777cSXin Long nla_put_s32(skb, NETCONFA_BC_FORWARDING, 20675cbf777cSXin Long IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0) 20685cbf777cSXin Long goto nla_put_failure; 2069136ba622SZhang Shengju if ((all || type == NETCONFA_PROXY_NEIGH) && 207009aea5dfSstephen hemminger nla_put_s32(skb, NETCONFA_PROXY_NEIGH, 2071f085ff1cSstephen hemminger IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0) 2072f085ff1cSstephen hemminger goto nla_put_failure; 2073136ba622SZhang Shengju if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) && 2074974d7af5SAndy Gospodarek nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 2075974d7af5SAndy Gospodarek IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0) 2076974d7af5SAndy Gospodarek goto nla_put_failure; 2077edc9e748SNicolas Dichtel 2078b5c9641dSDavid Ahern out: 2079053c095aSJohannes Berg nlmsg_end(skb, nlh); 2080053c095aSJohannes Berg return 0; 2081edc9e748SNicolas Dichtel 2082edc9e748SNicolas Dichtel nla_put_failure: 2083edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 2084edc9e748SNicolas Dichtel return -EMSGSIZE; 2085edc9e748SNicolas Dichtel } 2086edc9e748SNicolas Dichtel 20873b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type, 20883b022865SDavid Ahern int ifindex, struct ipv4_devconf *devconf) 2089edc9e748SNicolas Dichtel { 2090edc9e748SNicolas Dichtel struct sk_buff *skb; 2091edc9e748SNicolas Dichtel int err = -ENOBUFS; 2092edc9e748SNicolas Dichtel 2093fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL); 209451456b29SIan Morris if (!skb) 2095edc9e748SNicolas Dichtel goto errout; 2096edc9e748SNicolas Dichtel 2097edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 20983b022865SDavid Ahern event, 0, type); 2099edc9e748SNicolas Dichtel if (err < 0) { 2100edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 2101edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 2102edc9e748SNicolas Dichtel kfree_skb(skb); 2103edc9e748SNicolas Dichtel goto errout; 2104edc9e748SNicolas Dichtel } 2105fa17806cSEric Dumazet rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL); 2106edc9e748SNicolas Dichtel return; 2107edc9e748SNicolas Dichtel errout: 2108edc9e748SNicolas Dichtel if (err < 0) 2109edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 2110edc9e748SNicolas Dichtel } 2111edc9e748SNicolas Dichtel 21129e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 21139e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 21149e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 2115cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 211609aea5dfSstephen hemminger [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 2117974d7af5SAndy Gospodarek [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) }, 21189e551110SNicolas Dichtel }; 21199e551110SNicolas Dichtel 2120eede370dSJakub Kicinski static int inet_netconf_valid_get_req(struct sk_buff *skb, 2121eede370dSJakub Kicinski const struct nlmsghdr *nlh, 2122eede370dSJakub Kicinski struct nlattr **tb, 2123eede370dSJakub Kicinski struct netlink_ext_ack *extack) 2124eede370dSJakub Kicinski { 2125eede370dSJakub Kicinski int i, err; 2126eede370dSJakub Kicinski 2127eede370dSJakub Kicinski if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(struct netconfmsg))) { 2128eede370dSJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf get request"); 2129eede370dSJakub Kicinski return -EINVAL; 2130eede370dSJakub Kicinski } 2131eede370dSJakub Kicinski 2132eede370dSJakub Kicinski if (!netlink_strict_get_check(skb)) 21338cb08174SJohannes Berg return nlmsg_parse_deprecated(nlh, sizeof(struct netconfmsg), 21348cb08174SJohannes Berg tb, NETCONFA_MAX, 21358cb08174SJohannes Berg devconf_ipv4_policy, extack); 2136eede370dSJakub Kicinski 21378cb08174SJohannes Berg err = nlmsg_parse_deprecated_strict(nlh, sizeof(struct netconfmsg), 21388cb08174SJohannes Berg tb, NETCONFA_MAX, 21398cb08174SJohannes Berg devconf_ipv4_policy, extack); 2140eede370dSJakub Kicinski if (err) 2141eede370dSJakub Kicinski return err; 2142eede370dSJakub Kicinski 2143eede370dSJakub Kicinski for (i = 0; i <= NETCONFA_MAX; i++) { 2144eede370dSJakub Kicinski if (!tb[i]) 2145eede370dSJakub Kicinski continue; 2146eede370dSJakub Kicinski 2147eede370dSJakub Kicinski switch (i) { 2148eede370dSJakub Kicinski case NETCONFA_IFINDEX: 2149eede370dSJakub Kicinski break; 2150eede370dSJakub Kicinski default: 2151eede370dSJakub Kicinski NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in netconf get request"); 2152eede370dSJakub Kicinski return -EINVAL; 2153eede370dSJakub Kicinski } 2154eede370dSJakub Kicinski } 2155eede370dSJakub Kicinski 2156eede370dSJakub Kicinski return 0; 2157eede370dSJakub Kicinski } 2158eede370dSJakub Kicinski 21599e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 2160c21ef3e3SDavid Ahern struct nlmsghdr *nlh, 2161c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 21629e551110SNicolas Dichtel { 21639e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 21649e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 21659e551110SNicolas Dichtel struct sk_buff *skb; 21669e551110SNicolas Dichtel struct ipv4_devconf *devconf; 21679e551110SNicolas Dichtel struct in_device *in_dev; 21689e551110SNicolas Dichtel struct net_device *dev; 21699e551110SNicolas Dichtel int ifindex; 21709e551110SNicolas Dichtel int err; 21719e551110SNicolas Dichtel 2172eede370dSJakub Kicinski err = inet_netconf_valid_get_req(in_skb, nlh, tb, extack); 2173eede370dSJakub Kicinski if (err) 21749e551110SNicolas Dichtel goto errout; 21759e551110SNicolas Dichtel 2176a97eb33fSAnton Protopopov err = -EINVAL; 21779e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 21789e551110SNicolas Dichtel goto errout; 21799e551110SNicolas Dichtel 21809e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 21819e551110SNicolas Dichtel switch (ifindex) { 21829e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 21839e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 21849e551110SNicolas Dichtel break; 21859e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 21869e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 21879e551110SNicolas Dichtel break; 21889e551110SNicolas Dichtel default: 21899e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 219051456b29SIan Morris if (!dev) 21919e551110SNicolas Dichtel goto errout; 21929e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 219351456b29SIan Morris if (!in_dev) 21949e551110SNicolas Dichtel goto errout; 21959e551110SNicolas Dichtel devconf = &in_dev->cnf; 21969e551110SNicolas Dichtel break; 21979e551110SNicolas Dichtel } 21989e551110SNicolas Dichtel 21999e551110SNicolas Dichtel err = -ENOBUFS; 2200fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 220151456b29SIan Morris if (!skb) 22029e551110SNicolas Dichtel goto errout; 22039e551110SNicolas Dichtel 22049e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 22059e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 22069e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 2207136ba622SZhang Shengju NETCONFA_ALL); 22089e551110SNicolas Dichtel if (err < 0) { 22099e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 22109e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 22119e551110SNicolas Dichtel kfree_skb(skb); 22129e551110SNicolas Dichtel goto errout; 22139e551110SNicolas Dichtel } 22149e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 22159e551110SNicolas Dichtel errout: 22169e551110SNicolas Dichtel return err; 22179e551110SNicolas Dichtel } 22189e551110SNicolas Dichtel 22197a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 22207a674200SNicolas Dichtel struct netlink_callback *cb) 22217a674200SNicolas Dichtel { 2222addd383fSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 22237a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 22247a674200SNicolas Dichtel int h, s_h; 22257a674200SNicolas Dichtel int idx, s_idx; 22267a674200SNicolas Dichtel struct net_device *dev; 22277a674200SNicolas Dichtel struct in_device *in_dev; 22287a674200SNicolas Dichtel struct hlist_head *head; 22297a674200SNicolas Dichtel 2230addd383fSDavid Ahern if (cb->strict_check) { 2231addd383fSDavid Ahern struct netlink_ext_ack *extack = cb->extack; 2232addd383fSDavid Ahern struct netconfmsg *ncm; 2233addd383fSDavid Ahern 2234addd383fSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) { 2235addd383fSDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request"); 2236addd383fSDavid Ahern return -EINVAL; 2237addd383fSDavid Ahern } 2238addd383fSDavid Ahern 2239addd383fSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ncm))) { 2240addd383fSDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request"); 2241addd383fSDavid Ahern return -EINVAL; 2242addd383fSDavid Ahern } 2243addd383fSDavid Ahern } 2244addd383fSDavid Ahern 22457a674200SNicolas Dichtel s_h = cb->args[0]; 22467a674200SNicolas Dichtel s_idx = idx = cb->args[1]; 22477a674200SNicolas Dichtel 22487a674200SNicolas Dichtel for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 22497a674200SNicolas Dichtel idx = 0; 22507a674200SNicolas Dichtel head = &net->dev_index_head[h]; 22517a674200SNicolas Dichtel rcu_read_lock(); 22520465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 22530465277fSNicolas Dichtel net->dev_base_seq; 22547a674200SNicolas Dichtel hlist_for_each_entry_rcu(dev, head, index_hlist) { 22557a674200SNicolas Dichtel if (idx < s_idx) 22567a674200SNicolas Dichtel goto cont; 22577a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 22587a674200SNicolas Dichtel if (!in_dev) 22597a674200SNicolas Dichtel goto cont; 22607a674200SNicolas Dichtel 22617a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, dev->ifindex, 22627a674200SNicolas Dichtel &in_dev->cnf, 22637a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2264addd383fSDavid Ahern nlh->nlmsg_seq, 22657a674200SNicolas Dichtel RTM_NEWNETCONF, 22667a674200SNicolas Dichtel NLM_F_MULTI, 2267136ba622SZhang Shengju NETCONFA_ALL) < 0) { 22687a674200SNicolas Dichtel rcu_read_unlock(); 22697a674200SNicolas Dichtel goto done; 22707a674200SNicolas Dichtel } 22710465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 22727a674200SNicolas Dichtel cont: 22737a674200SNicolas Dichtel idx++; 22747a674200SNicolas Dichtel } 22757a674200SNicolas Dichtel rcu_read_unlock(); 22767a674200SNicolas Dichtel } 22777a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES) { 22787a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 22797a674200SNicolas Dichtel net->ipv4.devconf_all, 22807a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2281addd383fSDavid Ahern nlh->nlmsg_seq, 22827a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 2283136ba622SZhang Shengju NETCONFA_ALL) < 0) 22847a674200SNicolas Dichtel goto done; 22857a674200SNicolas Dichtel else 22867a674200SNicolas Dichtel h++; 22877a674200SNicolas Dichtel } 22887a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES + 1) { 22897a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 22907a674200SNicolas Dichtel net->ipv4.devconf_dflt, 22917a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2292addd383fSDavid Ahern nlh->nlmsg_seq, 22937a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 2294136ba622SZhang Shengju NETCONFA_ALL) < 0) 22957a674200SNicolas Dichtel goto done; 22967a674200SNicolas Dichtel else 22977a674200SNicolas Dichtel h++; 22987a674200SNicolas Dichtel } 22997a674200SNicolas Dichtel done: 23007a674200SNicolas Dichtel cb->args[0] = h; 23017a674200SNicolas Dichtel cb->args[1] = idx; 23027a674200SNicolas Dichtel 23037a674200SNicolas Dichtel return skb->len; 23047a674200SNicolas Dichtel } 23057a674200SNicolas Dichtel 23061da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 23071da177e4SLinus Torvalds 2308c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 230931be3085SHerbert Xu { 231031be3085SHerbert Xu struct net_device *dev; 231131be3085SHerbert Xu 231231be3085SHerbert Xu rcu_read_lock(); 2313c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 2314c6d14c84SEric Dumazet struct in_device *in_dev; 2315c6d14c84SEric Dumazet 231631be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 231731be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 23189355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 2319c6d14c84SEric Dumazet } 232031be3085SHerbert Xu rcu_read_unlock(); 232131be3085SHerbert Xu } 232231be3085SHerbert Xu 2323c6d14c84SEric Dumazet /* called with RTNL locked */ 2324c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 232568dd299bSPavel Emelyanov { 232668dd299bSPavel Emelyanov struct net_device *dev; 2327586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 232868dd299bSPavel Emelyanov 2329586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 23309355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 23313b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23323b022865SDavid Ahern NETCONFA_FORWARDING, 2333edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 2334edc9e748SNicolas Dichtel net->ipv4.devconf_all); 23353b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23363b022865SDavid Ahern NETCONFA_FORWARDING, 2337edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2338edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 233968dd299bSPavel Emelyanov 2340c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 234168dd299bSPavel Emelyanov struct in_device *in_dev; 2342fa17806cSEric Dumazet 23430187bdfbSBen Hutchings if (on) 23440187bdfbSBen Hutchings dev_disable_lro(dev); 2345fa17806cSEric Dumazet 2346fa17806cSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 2347edc9e748SNicolas Dichtel if (in_dev) { 234868dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 23493b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23503b022865SDavid Ahern NETCONFA_FORWARDING, 2351edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 2352edc9e748SNicolas Dichtel } 235368dd299bSPavel Emelyanov } 235468dd299bSPavel Emelyanov } 235568dd299bSPavel Emelyanov 2356f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf) 2357f085ff1cSstephen hemminger { 2358f085ff1cSstephen hemminger if (cnf == net->ipv4.devconf_dflt) 2359f085ff1cSstephen hemminger return NETCONFA_IFINDEX_DEFAULT; 2360f085ff1cSstephen hemminger else if (cnf == net->ipv4.devconf_all) 2361f085ff1cSstephen hemminger return NETCONFA_IFINDEX_ALL; 2362f085ff1cSstephen hemminger else { 2363f085ff1cSstephen hemminger struct in_device *idev 2364f085ff1cSstephen hemminger = container_of(cnf, struct in_device, cnf); 2365f085ff1cSstephen hemminger return idev->dev->ifindex; 2366f085ff1cSstephen hemminger } 2367f085ff1cSstephen hemminger } 2368f085ff1cSstephen hemminger 2369fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write, 237032927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 237131be3085SHerbert Xu { 2372d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 23738d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 2374d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 237531be3085SHerbert Xu 237631be3085SHerbert Xu if (write) { 237731be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 2378c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 237931be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 2380f085ff1cSstephen hemminger int ifindex; 238131be3085SHerbert Xu 238231be3085SHerbert Xu set_bit(i, cnf->state); 238331be3085SHerbert Xu 23849355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 2385c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 2386d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 2387d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 2388d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 23894ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2390f085ff1cSstephen hemminger 23915cbf777cSXin Long if (i == IPV4_DEVCONF_BC_FORWARDING - 1 && 23925cbf777cSXin Long new_value != old_value) 23935cbf777cSXin Long rt_cache_flush(net); 23945cbf777cSXin Long 2395cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 2396cc535dfbSNicolas Dichtel new_value != old_value) { 2397f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 23983b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23993b022865SDavid Ahern NETCONFA_RP_FILTER, 2400cc535dfbSNicolas Dichtel ifindex, cnf); 2401cc535dfbSNicolas Dichtel } 2402f085ff1cSstephen hemminger if (i == IPV4_DEVCONF_PROXY_ARP - 1 && 2403f085ff1cSstephen hemminger new_value != old_value) { 2404f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 24053b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24063b022865SDavid Ahern NETCONFA_PROXY_NEIGH, 2407f085ff1cSstephen hemminger ifindex, cnf); 2408f085ff1cSstephen hemminger } 2409974d7af5SAndy Gospodarek if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 && 2410974d7af5SAndy Gospodarek new_value != old_value) { 2411974d7af5SAndy Gospodarek ifindex = devinet_conf_ifindex(net, cnf); 24123b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24133b022865SDavid Ahern NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 2414974d7af5SAndy Gospodarek ifindex, cnf); 2415974d7af5SAndy Gospodarek } 241631be3085SHerbert Xu } 241731be3085SHerbert Xu 241831be3085SHerbert Xu return ret; 241931be3085SHerbert Xu } 242031be3085SHerbert Xu 2421fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write, 242232927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 24231da177e4SLinus Torvalds { 24241da177e4SLinus Torvalds int *valp = ctl->data; 24251da177e4SLinus Torvalds int val = *valp; 242688af182eSEric W. Biederman loff_t pos = *ppos; 24278d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 24281da177e4SLinus Torvalds 24291da177e4SLinus Torvalds if (write && *valp != val) { 2430c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 2431c0ce9fb3SPavel Emelyanov 24320187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 243388af182eSEric W. Biederman if (!rtnl_trylock()) { 243488af182eSEric W. Biederman /* Restore the original values before restarting */ 243588af182eSEric W. Biederman *valp = val; 243688af182eSEric W. Biederman *ppos = pos; 24379b8adb5eSEric W. Biederman return restart_syscall(); 243888af182eSEric W. Biederman } 24390187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2440c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2441edc9e748SNicolas Dichtel } else { 24420187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 24430187bdfbSBen Hutchings struct in_device *idev = 24440187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2445edc9e748SNicolas Dichtel if (*valp) 24460187bdfbSBen Hutchings dev_disable_lro(idev->dev); 24473b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 2448edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2449edc9e748SNicolas Dichtel idev->dev->ifindex, 2450edc9e748SNicolas Dichtel cnf); 24510187bdfbSBen Hutchings } 24520187bdfbSBen Hutchings rtnl_unlock(); 24534ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2454edc9e748SNicolas Dichtel } else 24553b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 24563b022865SDavid Ahern NETCONFA_FORWARDING, 2457edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2458edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 24590187bdfbSBen Hutchings } 24601da177e4SLinus Torvalds 24611da177e4SLinus Torvalds return ret; 24621da177e4SLinus Torvalds } 24631da177e4SLinus Torvalds 2464fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 246532927393SChristoph Hellwig void *buffer, size_t *lenp, loff_t *ppos) 24661da177e4SLinus Torvalds { 24671da177e4SLinus Torvalds int *valp = ctl->data; 24681da177e4SLinus Torvalds int val = *valp; 24698d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 247076e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 24711da177e4SLinus Torvalds 24721da177e4SLinus Torvalds if (write && *valp != val) 24734ccfe6d4SNicolas Dichtel rt_cache_flush(net); 24741da177e4SLinus Torvalds 24751da177e4SLinus Torvalds return ret; 24761da177e4SLinus Torvalds } 24771da177e4SLinus Torvalds 2478f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 247942f811b8SHerbert Xu { \ 248042f811b8SHerbert Xu .procname = name, \ 248142f811b8SHerbert Xu .data = ipv4_devconf.data + \ 248202291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 248342f811b8SHerbert Xu .maxlen = sizeof(int), \ 248442f811b8SHerbert Xu .mode = mval, \ 248542f811b8SHerbert Xu .proc_handler = proc, \ 248631be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 248742f811b8SHerbert Xu } 248842f811b8SHerbert Xu 248942f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2490f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 249142f811b8SHerbert Xu 249242f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2493f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 249442f811b8SHerbert Xu 2495f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2496f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 249742f811b8SHerbert Xu 249842f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2499f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 250042f811b8SHerbert Xu 25011da177e4SLinus Torvalds static struct devinet_sysctl_table { 25021da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 250302291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 25041da177e4SLinus Torvalds } devinet_sysctl = { 25051da177e4SLinus Torvalds .devinet_vars = { 250642f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2507f8572d8fSEric W. Biederman devinet_sysctl_forward), 250842f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 25095cbf777cSXin Long DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"), 251042f811b8SHerbert Xu 251142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 251242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 251342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 251442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 251542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 251642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 251742f811b8SHerbert Xu "accept_source_route"), 25188153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 251928f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 252042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 252142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 252242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 252342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 252442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 252542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 252642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 252742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 252842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2529eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 253065324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 25315c6fe01cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 25325c6fe01cSWilliam Manley "force_igmp_version"), 25332690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, 25342690048cSWilliam Manley "igmpv2_unsolicited_report_interval"), 25352690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, 25362690048cSWilliam Manley "igmpv3_unsolicited_report_interval"), 25370eeb075fSAndy Gospodarek DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN, 25380eeb075fSAndy Gospodarek "ignore_routes_with_linkdown"), 253997daf331SJohannes Berg DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP, 254097daf331SJohannes Berg "drop_gratuitous_arp"), 254142f811b8SHerbert Xu 254242f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 254342f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 254442f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 254542f811b8SHerbert Xu "promote_secondaries"), 2546d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2547d0daebc3SThomas Graf "route_localnet"), 254812b74dfaSJohannes Berg DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST, 254912b74dfaSJohannes Berg "drop_unicast_in_l2_multicast"), 25501da177e4SLinus Torvalds }, 25511da177e4SLinus Torvalds }; 25521da177e4SLinus Torvalds 2553ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 255429c994e3SNicolas Dichtel int ifindex, struct ipv4_devconf *p) 25551da177e4SLinus Torvalds { 25561da177e4SLinus Torvalds int i; 25579fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 25588607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2559bfada697SPavel Emelyanov 25609fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 25611da177e4SLinus Torvalds if (!t) 25629fa89642SPavel Emelyanov goto out; 25639fa89642SPavel Emelyanov 25641da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 25651da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 256631be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2567c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 25681da177e4SLinus Torvalds } 25691da177e4SLinus Torvalds 25708607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 25711da177e4SLinus Torvalds 25728607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 25731da177e4SLinus Torvalds if (!t->sysctl_header) 25748607ddb8SEric W. Biederman goto free; 25751da177e4SLinus Torvalds 25761da177e4SLinus Torvalds p->sysctl = t; 257729c994e3SNicolas Dichtel 25783b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, 25793b022865SDavid Ahern ifindex, p); 2580ea40b324SPavel Emelyanov return 0; 25811da177e4SLinus Torvalds 25821da177e4SLinus Torvalds free: 25831da177e4SLinus Torvalds kfree(t); 25849fa89642SPavel Emelyanov out: 2585ea40b324SPavel Emelyanov return -ENOBUFS; 25861da177e4SLinus Torvalds } 25871da177e4SLinus Torvalds 2588b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net, 2589b5c9641dSDavid Ahern struct ipv4_devconf *cnf, int ifindex) 259066f27a52SPavel Emelyanov { 259151602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 259266f27a52SPavel Emelyanov 2593b5c9641dSDavid Ahern if (t) { 259451602b2aSPavel Emelyanov cnf->sysctl = NULL; 2595ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 25961da177e4SLinus Torvalds kfree(t); 25971da177e4SLinus Torvalds } 259851602b2aSPavel Emelyanov 2599b5c9641dSDavid Ahern inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); 2600b5c9641dSDavid Ahern } 2601b5c9641dSDavid Ahern 260220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 260351602b2aSPavel Emelyanov { 260420e61da7SWANG Cong int err; 260520e61da7SWANG Cong 260620e61da7SWANG Cong if (!sysctl_dev_name_is_allowed(idev->dev->name)) 260720e61da7SWANG Cong return -EINVAL; 260820e61da7SWANG Cong 260920e61da7SWANG Cong err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); 261020e61da7SWANG Cong if (err) 261120e61da7SWANG Cong return err; 261220e61da7SWANG Cong err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 261329c994e3SNicolas Dichtel idev->dev->ifindex, &idev->cnf); 261420e61da7SWANG Cong if (err) 261520e61da7SWANG Cong neigh_sysctl_unregister(idev->arp_parms); 261620e61da7SWANG Cong return err; 261751602b2aSPavel Emelyanov } 261851602b2aSPavel Emelyanov 261951602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 262051602b2aSPavel Emelyanov { 2621b5c9641dSDavid Ahern struct net *net = dev_net(idev->dev); 2622b5c9641dSDavid Ahern 2623b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex); 262451602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 26251da177e4SLinus Torvalds } 26261da177e4SLinus Torvalds 262768dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 262868dd299bSPavel Emelyanov { 262968dd299bSPavel Emelyanov .procname = "ip_forward", 263068dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 263102291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 263268dd299bSPavel Emelyanov .maxlen = sizeof(int), 263368dd299bSPavel Emelyanov .mode = 0644, 263468dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 263568dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2636c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 263768dd299bSPavel Emelyanov }, 263868dd299bSPavel Emelyanov { }, 263968dd299bSPavel Emelyanov }; 26402a75de0cSEric Dumazet #endif 264168dd299bSPavel Emelyanov 2642752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2643752d14dcSPavel Emelyanov { 2644752d14dcSPavel Emelyanov int err; 2645752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 26462a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2647856c395cSCong Wang struct ctl_table *tbl; 2648752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 26492a75de0cSEric Dumazet #endif 2650752d14dcSPavel Emelyanov 2651752d14dcSPavel Emelyanov err = -ENOMEM; 2652856c395cSCong Wang all = kmemdup(&ipv4_devconf, sizeof(ipv4_devconf), GFP_KERNEL); 265351456b29SIan Morris if (!all) 2654752d14dcSPavel Emelyanov goto err_alloc_all; 2655752d14dcSPavel Emelyanov 2656856c395cSCong Wang dflt = kmemdup(&ipv4_devconf_dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 265751456b29SIan Morris if (!dflt) 2658752d14dcSPavel Emelyanov goto err_alloc_dflt; 2659752d14dcSPavel Emelyanov 26602a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2661856c395cSCong Wang tbl = kmemdup(ctl_forward_entry, sizeof(ctl_forward_entry), GFP_KERNEL); 266251456b29SIan Morris if (!tbl) 2663752d14dcSPavel Emelyanov goto err_alloc_ctl; 2664752d14dcSPavel Emelyanov 266502291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2666752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2667752d14dcSPavel Emelyanov tbl[0].extra2 = net; 26682a75de0cSEric Dumazet #endif 2669856c395cSCong Wang 26709efd6a3cSNicolas Dichtel if (!net_eq(net, &init_net)) { 26719efd6a3cSNicolas Dichtel if (IS_ENABLED(CONFIG_SYSCTL) && 26729efd6a3cSNicolas Dichtel sysctl_devconf_inherit_init_net == 3) { 26739efd6a3cSNicolas Dichtel /* copy from the current netns */ 26749efd6a3cSNicolas Dichtel memcpy(all, current->nsproxy->net_ns->ipv4.devconf_all, 26759efd6a3cSNicolas Dichtel sizeof(ipv4_devconf)); 26769efd6a3cSNicolas Dichtel memcpy(dflt, 26779efd6a3cSNicolas Dichtel current->nsproxy->net_ns->ipv4.devconf_dflt, 26789efd6a3cSNicolas Dichtel sizeof(ipv4_devconf_dflt)); 26799efd6a3cSNicolas Dichtel } else if (!IS_ENABLED(CONFIG_SYSCTL) || 26809efd6a3cSNicolas Dichtel sysctl_devconf_inherit_init_net != 2) { 26819efd6a3cSNicolas Dichtel /* inherit == 0 or 1: copy from init_net */ 26829efd6a3cSNicolas Dichtel memcpy(all, init_net.ipv4.devconf_all, 26839efd6a3cSNicolas Dichtel sizeof(ipv4_devconf)); 26849efd6a3cSNicolas Dichtel memcpy(dflt, init_net.ipv4.devconf_dflt, 26859efd6a3cSNicolas Dichtel sizeof(ipv4_devconf_dflt)); 26869efd6a3cSNicolas Dichtel } 26879efd6a3cSNicolas Dichtel /* else inherit == 2: use compiled values */ 2688752d14dcSPavel Emelyanov } 2689752d14dcSPavel Emelyanov 2690752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 269129c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all); 2692752d14dcSPavel Emelyanov if (err < 0) 2693752d14dcSPavel Emelyanov goto err_reg_all; 2694752d14dcSPavel Emelyanov 269529c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "default", 269629c994e3SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, dflt); 2697752d14dcSPavel Emelyanov if (err < 0) 2698752d14dcSPavel Emelyanov goto err_reg_dflt; 2699752d14dcSPavel Emelyanov 2700752d14dcSPavel Emelyanov err = -ENOMEM; 27018607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 270251456b29SIan Morris if (!forw_hdr) 2703752d14dcSPavel Emelyanov goto err_reg_ctl; 27042a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2705752d14dcSPavel Emelyanov #endif 2706752d14dcSPavel Emelyanov 2707752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2708752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2709752d14dcSPavel Emelyanov return 0; 2710752d14dcSPavel Emelyanov 2711752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2712752d14dcSPavel Emelyanov err_reg_ctl: 2713b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT); 2714752d14dcSPavel Emelyanov err_reg_dflt: 2715b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); 2716752d14dcSPavel Emelyanov err_reg_all: 2717752d14dcSPavel Emelyanov kfree(tbl); 2718752d14dcSPavel Emelyanov err_alloc_ctl: 27192a75de0cSEric Dumazet #endif 2720752d14dcSPavel Emelyanov kfree(dflt); 2721752d14dcSPavel Emelyanov err_alloc_dflt: 2722752d14dcSPavel Emelyanov kfree(all); 2723752d14dcSPavel Emelyanov err_alloc_all: 2724752d14dcSPavel Emelyanov return err; 2725752d14dcSPavel Emelyanov } 2726752d14dcSPavel Emelyanov 2727752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2728752d14dcSPavel Emelyanov { 27292a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2730752d14dcSPavel Emelyanov struct ctl_table *tbl; 2731752d14dcSPavel Emelyanov 2732752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2733752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2734b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_dflt, 2735b5c9641dSDavid Ahern NETCONFA_IFINDEX_DEFAULT); 2736b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_all, 2737b5c9641dSDavid Ahern NETCONFA_IFINDEX_ALL); 2738752d14dcSPavel Emelyanov kfree(tbl); 27392a75de0cSEric Dumazet #endif 2740752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2741752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2742752d14dcSPavel Emelyanov } 2743752d14dcSPavel Emelyanov 2744752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2745752d14dcSPavel Emelyanov .init = devinet_init_net, 2746752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2747752d14dcSPavel Emelyanov }; 2748752d14dcSPavel Emelyanov 2749207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = { 27509f0f7272SThomas Graf .family = AF_INET, 27519f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 27529f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2753cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2754cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 27559f0f7272SThomas Graf }; 27569f0f7272SThomas Graf 27571da177e4SLinus Torvalds void __init devinet_init(void) 27581da177e4SLinus Torvalds { 2759fd23c3b3SDavid S. Miller int i; 2760fd23c3b3SDavid S. Miller 2761fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2762fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2763fd23c3b3SDavid S. Miller 2764752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 2765752d14dcSPavel Emelyanov 27661da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 27671da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 276863f3444fSThomas Graf 2769906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 27705c766d64SJiri Pirko 27719f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 27729f0f7272SThomas Graf 2773b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0); 2774b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0); 2775b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0); 27769e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 2777b97bac64SFlorian Westphal inet_netconf_dump_devconf, 0); 27781da177e4SLinus Torvalds } 2779