11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * NET3 IP device support routines. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 51da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 61da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 71da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Derived from the IP parts of dev.c 1.0.19 1002c30a84SJesper Juhl * Authors: Ross Biro 111da177e4SLinus Torvalds * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> 121da177e4SLinus Torvalds * Mark Evans, <evansmp@uhura.aston.ac.uk> 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * Additional Authors: 151da177e4SLinus Torvalds * Alan Cox, <gw4pts@gw4pts.ampr.org> 161da177e4SLinus Torvalds * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * Changes: 191da177e4SLinus Torvalds * Alexey Kuznetsov: pa_* fields are replaced with ifaddr 201da177e4SLinus Torvalds * lists. 211da177e4SLinus Torvalds * Cyrus Durgin: updated for kmod 221da177e4SLinus Torvalds * Matthias Andree: in devinet_ioctl, compare label and 231da177e4SLinus Torvalds * address (4.4BSD alias style support), 241da177e4SLinus Torvalds * fall back to comparing just the label 251da177e4SLinus Torvalds * if no match found. 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds 297c0f6ba6SLinus Torvalds #include <linux/uaccess.h> 301da177e4SLinus Torvalds #include <linux/bitops.h> 314fc268d2SRandy Dunlap #include <linux/capability.h> 321da177e4SLinus Torvalds #include <linux/module.h> 331da177e4SLinus Torvalds #include <linux/types.h> 341da177e4SLinus Torvalds #include <linux/kernel.h> 35174cd4b1SIngo Molnar #include <linux/sched/signal.h> 361da177e4SLinus Torvalds #include <linux/string.h> 371da177e4SLinus Torvalds #include <linux/mm.h> 381da177e4SLinus Torvalds #include <linux/socket.h> 391da177e4SLinus Torvalds #include <linux/sockios.h> 401da177e4SLinus Torvalds #include <linux/in.h> 411da177e4SLinus Torvalds #include <linux/errno.h> 421da177e4SLinus Torvalds #include <linux/interrupt.h> 431823730fSThomas Graf #include <linux/if_addr.h> 441da177e4SLinus Torvalds #include <linux/if_ether.h> 451da177e4SLinus Torvalds #include <linux/inet.h> 461da177e4SLinus Torvalds #include <linux/netdevice.h> 471da177e4SLinus Torvalds #include <linux/etherdevice.h> 481da177e4SLinus Torvalds #include <linux/skbuff.h> 491da177e4SLinus Torvalds #include <linux/init.h> 501da177e4SLinus Torvalds #include <linux/notifier.h> 511da177e4SLinus Torvalds #include <linux/inetdevice.h> 521da177e4SLinus Torvalds #include <linux/igmp.h> 535a0e3ad6STejun Heo #include <linux/slab.h> 54fd23c3b3SDavid S. Miller #include <linux/hash.h> 551da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 561da177e4SLinus Torvalds #include <linux/sysctl.h> 571da177e4SLinus Torvalds #endif 581da177e4SLinus Torvalds #include <linux/kmod.h> 59edc9e748SNicolas Dichtel #include <linux/netconf.h> 601da177e4SLinus Torvalds 6114c85021SArnaldo Carvalho de Melo #include <net/arp.h> 621da177e4SLinus Torvalds #include <net/ip.h> 631da177e4SLinus Torvalds #include <net/route.h> 641da177e4SLinus Torvalds #include <net/ip_fib.h> 6563f3444fSThomas Graf #include <net/rtnetlink.h> 66752d14dcSPavel Emelyanov #include <net/net_namespace.h> 675c766d64SJiri Pirko #include <net/addrconf.h> 681da177e4SLinus Torvalds 690027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 7042f811b8SHerbert Xu .data = { 7102291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7202291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7302291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7402291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 752690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 762690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 7742f811b8SHerbert Xu }, 781da177e4SLinus Torvalds }; 791da177e4SLinus Torvalds 801da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 8142f811b8SHerbert Xu .data = { 8202291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8302291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8402291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8502291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8602291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 872690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 882690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 8942f811b8SHerbert Xu }, 901da177e4SLinus Torvalds }; 911da177e4SLinus Torvalds 929355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 939355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9442f811b8SHerbert Xu 95ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 965c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 975c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 985c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 995176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 1005c766d64SJiri Pirko [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 101ad6c8135SJiri Pirko [IFA_FLAGS] = { .type = NLA_U32 }, 102af4d768aSDavid Ahern [IFA_RT_PRIORITY] = { .type = NLA_U32 }, 103d3807145SChristian Brauner [IFA_TARGET_NETNSID] = { .type = NLA_S32 }, 1045c753978SThomas Graf }; 1055c753978SThomas Graf 106978a46faSChristian Brauner struct inet_fill_args { 107978a46faSChristian Brauner u32 portid; 108978a46faSChristian Brauner u32 seq; 109978a46faSChristian Brauner int event; 110978a46faSChristian Brauner unsigned int flags; 111978a46faSChristian Brauner int netnsid; 112978a46faSChristian Brauner }; 113978a46faSChristian Brauner 11440384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT 8 11540384999SEric Dumazet #define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 11640384999SEric Dumazet 117fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 118fd23c3b3SDavid S. Miller 1196eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr) 120fd23c3b3SDavid S. Miller { 12140384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 122fd23c3b3SDavid S. Miller 12340384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 124fd23c3b3SDavid S. Miller } 125fd23c3b3SDavid S. Miller 126fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 127fd23c3b3SDavid S. Miller { 12840384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 129fd23c3b3SDavid S. Miller 13032a4be48SWANG Cong ASSERT_RTNL(); 131fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 132fd23c3b3SDavid S. Miller } 133fd23c3b3SDavid S. Miller 134fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 135fd23c3b3SDavid S. Miller { 13632a4be48SWANG Cong ASSERT_RTNL(); 137fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 138fd23c3b3SDavid S. Miller } 139fd23c3b3SDavid S. Miller 1409435eb1cSDavid S. Miller /** 1419435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1429435eb1cSDavid S. Miller * @net: the net namespace 1439435eb1cSDavid S. Miller * @addr: the source address 1449435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1459435eb1cSDavid S. Miller * 1469435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1479435eb1cSDavid S. Miller */ 1489435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1499435eb1cSDavid S. Miller { 1509435eb1cSDavid S. Miller struct net_device *result = NULL; 1519435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1529435eb1cSDavid S. Miller 1539435eb1cSDavid S. Miller rcu_read_lock(); 1546e617de8SPaolo Abeni ifa = inet_lookup_ifaddr_rcu(net, addr); 1556e617de8SPaolo Abeni if (!ifa) { 156406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 157406b6f97SDavid S. Miller struct fib_result res = { 0 }; 158406b6f97SDavid S. Miller struct fib_table *local; 159406b6f97SDavid S. Miller 160406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 161406b6f97SDavid S. Miller * over loopback subnets work. 162406b6f97SDavid S. Miller */ 163406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 164406b6f97SDavid S. Miller if (local && 165406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 166406b6f97SDavid S. Miller res.type == RTN_LOCAL) 167406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 1686e617de8SPaolo Abeni } else { 1696e617de8SPaolo Abeni result = ifa->ifa_dev->dev; 170406b6f97SDavid S. Miller } 1719435eb1cSDavid S. Miller if (result && devref) 1729435eb1cSDavid S. Miller dev_hold(result); 1739435eb1cSDavid S. Miller rcu_read_unlock(); 1749435eb1cSDavid S. Miller return result; 1759435eb1cSDavid S. Miller } 1769435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1779435eb1cSDavid S. Miller 1786e617de8SPaolo Abeni /* called under RCU lock */ 1796e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr) 1806e617de8SPaolo Abeni { 1816e617de8SPaolo Abeni u32 hash = inet_addr_hash(net, addr); 1826e617de8SPaolo Abeni struct in_ifaddr *ifa; 1836e617de8SPaolo Abeni 1846e617de8SPaolo Abeni hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) 1856e617de8SPaolo Abeni if (ifa->ifa_local == addr && 1866e617de8SPaolo Abeni net_eq(dev_net(ifa->ifa_dev->dev), net)) 1876e617de8SPaolo Abeni return ifa; 1886e617de8SPaolo Abeni 1896e617de8SPaolo Abeni return NULL; 1906e617de8SPaolo Abeni } 1916e617de8SPaolo Abeni 192d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1931da177e4SLinus Torvalds 194e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1953ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain); 1961da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1971da177e4SLinus Torvalds int destroy); 1981da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 19920e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev); 20051602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 20151602b2aSPavel Emelyanov #else 20220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 20351602b2aSPavel Emelyanov { 20420e61da7SWANG Cong return 0; 20551602b2aSPavel Emelyanov } 20640384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 20751602b2aSPavel Emelyanov { 20851602b2aSPavel Emelyanov } 2091da177e4SLinus Torvalds #endif 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds /* Locks all the inet devices. */ 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 2141da177e4SLinus Torvalds { 21593adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2191da177e4SLinus Torvalds { 2201da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2211da177e4SLinus Torvalds if (ifa->ifa_dev) 2221da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2231da177e4SLinus Torvalds kfree(ifa); 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds 22640384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2271da177e4SLinus Torvalds { 2281da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2291da177e4SLinus Torvalds } 2301da177e4SLinus Torvalds 2311da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2321da177e4SLinus Torvalds { 2331da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2341da177e4SLinus Torvalds 235547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 236547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 237e9897071SEric Dumazet kfree(rcu_dereference_protected(idev->mc_hash, 1)); 2381da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 23991df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2401da177e4SLinus Torvalds #endif 2411da177e4SLinus Torvalds dev_put(dev); 2421da177e4SLinus Torvalds if (!idev->dead) 2439f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2449f9354b9SEric Dumazet else 2451da177e4SLinus Torvalds kfree(idev); 2461da177e4SLinus Torvalds } 2479f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2481da177e4SLinus Torvalds 24971e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2501da177e4SLinus Torvalds { 2511da177e4SLinus Torvalds struct in_device *in_dev; 25220e61da7SWANG Cong int err = -ENOMEM; 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds ASSERT_RTNL(); 2551da177e4SLinus Torvalds 2560da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2571da177e4SLinus Torvalds if (!in_dev) 2581da177e4SLinus Torvalds goto out; 259c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2609355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2611da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2621da177e4SLinus Torvalds in_dev->dev = dev; 2639f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2649f9354b9SEric Dumazet if (!in_dev->arp_parms) 2651da177e4SLinus Torvalds goto out_kfree; 2660187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2670187bdfbSBen Hutchings dev_disable_lro(dev); 2681da177e4SLinus Torvalds /* Reference in_dev->dev */ 2691da177e4SLinus Torvalds dev_hold(dev); 27030c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2717658b36fSReshetova, Elena refcount_set(&in_dev->refcnt, 1); 2721da177e4SLinus Torvalds 27320e61da7SWANG Cong err = devinet_sysctl_register(in_dev); 27420e61da7SWANG Cong if (err) { 27520e61da7SWANG Cong in_dev->dead = 1; 27620e61da7SWANG Cong in_dev_put(in_dev); 27720e61da7SWANG Cong in_dev = NULL; 27820e61da7SWANG Cong goto out; 27920e61da7SWANG Cong } 2801da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2811da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2821da177e4SLinus Torvalds ip_mc_up(in_dev); 283483479ecSJarek Poplawski 28430c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 285cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 286483479ecSJarek Poplawski out: 28720e61da7SWANG Cong return in_dev ?: ERR_PTR(err); 2881da177e4SLinus Torvalds out_kfree: 2891da177e4SLinus Torvalds kfree(in_dev); 2901da177e4SLinus Torvalds in_dev = NULL; 2911da177e4SLinus Torvalds goto out; 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds 2941da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2951da177e4SLinus Torvalds { 2961da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2971da177e4SLinus Torvalds in_dev_put(idev); 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds struct in_ifaddr *ifa; 3031da177e4SLinus Torvalds struct net_device *dev; 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds ASSERT_RTNL(); 3061da177e4SLinus Torvalds 3071da177e4SLinus Torvalds dev = in_dev->dev; 3081da177e4SLinus Torvalds 3091da177e4SLinus Torvalds in_dev->dead = 1; 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 3141da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 3151da177e4SLinus Torvalds inet_free_ifa(ifa); 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 318a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 3191da177e4SLinus Torvalds 32051602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 3211da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 3221da177e4SLinus Torvalds arp_ifdown(dev); 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 3251da177e4SLinus Torvalds } 3261da177e4SLinus Torvalds 327ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3281da177e4SLinus Torvalds { 3291da177e4SLinus Torvalds rcu_read_lock(); 3301da177e4SLinus Torvalds for_primary_ifa(in_dev) { 3311da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3321da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3331da177e4SLinus Torvalds rcu_read_unlock(); 3341da177e4SLinus Torvalds return 1; 3351da177e4SLinus Torvalds } 3361da177e4SLinus Torvalds } 3371da177e4SLinus Torvalds } endfor_ifa(in_dev); 3381da177e4SLinus Torvalds rcu_read_unlock(); 3391da177e4SLinus Torvalds return 0; 3401da177e4SLinus Torvalds } 3411da177e4SLinus Torvalds 342d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 34315e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3441da177e4SLinus Torvalds { 3458f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3460ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3470ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3480ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3490ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds ASSERT_RTNL(); 3521da177e4SLinus Torvalds 353fbd40ea0SDavid S. Miller if (in_dev->dead) 354fbd40ea0SDavid S. Miller goto no_promotions; 355fbd40ea0SDavid S. Miller 3568f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3578f937c60SHarald Welte * unless alias promotion is set 3588f937c60SHarald Welte **/ 3591da177e4SLinus Torvalds 3601da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3611da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3621da177e4SLinus Torvalds 3631da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3640ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3650ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3660ff60a45SJamal Hadi Salim last_prim = ifa; 3670ff60a45SJamal Hadi Salim 3681da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3691da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3701da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3711da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3720ff60a45SJamal Hadi Salim prev_prom = ifa; 3731da177e4SLinus Torvalds continue; 3741da177e4SLinus Torvalds } 3751da177e4SLinus Torvalds 3760ff60a45SJamal Hadi Salim if (!do_promote) { 377fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3781da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3791da177e4SLinus Torvalds 38015e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 381e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 382e041c683SAlan Stern NETDEV_DOWN, ifa); 3831da177e4SLinus Torvalds inet_free_ifa(ifa); 3848f937c60SHarald Welte } else { 3858f937c60SHarald Welte promote = ifa; 3868f937c60SHarald Welte break; 3878f937c60SHarald Welte } 3881da177e4SLinus Torvalds } 3891da177e4SLinus Torvalds } 3901da177e4SLinus Torvalds 3912d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 3922d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 3932d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 3942d230e2bSJulian Anastasov * while all addresses are on the device list. 3952d230e2bSJulian Anastasov */ 3962d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 3972d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 3982d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 3992d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 4002d230e2bSJulian Anastasov } 4012d230e2bSJulian Anastasov 402fbd40ea0SDavid S. Miller no_promotions: 4031da177e4SLinus Torvalds /* 2. Unlink it */ 4041da177e4SLinus Torvalds 4051da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 406fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds /* 3. Announce address deletion */ 4091da177e4SLinus Torvalds 4101da177e4SLinus Torvalds /* Send message first, then call notifier. 4111da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 4121da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 4131da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 4141da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 4151da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 4161da177e4SLinus Torvalds So that, this order is correct. 4171da177e4SLinus Torvalds */ 41815e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 419e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 4200ff60a45SJamal Hadi Salim 4210ff60a45SJamal Hadi Salim if (promote) { 42204024b93SJulian Anastasov struct in_ifaddr *next_sec = promote->ifa_next; 4230ff60a45SJamal Hadi Salim 4240ff60a45SJamal Hadi Salim if (prev_prom) { 4250ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 4260ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 4270ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 4280ff60a45SJamal Hadi Salim } 4290ff60a45SJamal Hadi Salim 4300ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 43115e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 432e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 433e041c683SAlan Stern NETDEV_UP, promote); 43404024b93SJulian Anastasov for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { 4350ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4360ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4370ff60a45SJamal Hadi Salim continue; 4380ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4390ff60a45SJamal Hadi Salim } 4400ff60a45SJamal Hadi Salim 4410ff60a45SJamal Hadi Salim } 4426363097cSHerbert Xu if (destroy) 4431da177e4SLinus Torvalds inet_free_ifa(ifa1); 4441da177e4SLinus Torvalds } 4451da177e4SLinus Torvalds 446d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 447d6062cbbSThomas Graf int destroy) 448d6062cbbSThomas Graf { 449d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 450d6062cbbSThomas Graf } 451d6062cbbSThomas Graf 4525c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4535c766d64SJiri Pirko 4545c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4555c766d64SJiri Pirko 456d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 457de95e047SDavid Ahern u32 portid, struct netlink_ext_ack *extack) 4581da177e4SLinus Torvalds { 4591da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4601da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4613ad7d246SKrister Johansen struct in_validator_info ivi; 4623ad7d246SKrister Johansen int ret; 4631da177e4SLinus Torvalds 4641da177e4SLinus Torvalds ASSERT_RTNL(); 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds if (!ifa->ifa_local) { 4671da177e4SLinus Torvalds inet_free_ifa(ifa); 4681da177e4SLinus Torvalds return 0; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4721da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4731da177e4SLinus Torvalds 4741da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4751da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4761da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4771da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4781da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4791da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4801da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4811da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4821da177e4SLinus Torvalds inet_free_ifa(ifa); 4831da177e4SLinus Torvalds return -EEXIST; 4841da177e4SLinus Torvalds } 4851da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4861da177e4SLinus Torvalds inet_free_ifa(ifa); 4871da177e4SLinus Torvalds return -EINVAL; 4881da177e4SLinus Torvalds } 4891da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4901da177e4SLinus Torvalds } 4911da177e4SLinus Torvalds } 4921da177e4SLinus Torvalds 4933ad7d246SKrister Johansen /* Allow any devices that wish to register ifaddr validtors to weigh 4943ad7d246SKrister Johansen * in now, before changes are committed. The rntl lock is serializing 4953ad7d246SKrister Johansen * access here, so the state should not change between a validator call 4963ad7d246SKrister Johansen * and a final notify on commit. This isn't invoked on promotion under 4973ad7d246SKrister Johansen * the assumption that validators are checking the address itself, and 4983ad7d246SKrister Johansen * not the flags. 4993ad7d246SKrister Johansen */ 5003ad7d246SKrister Johansen ivi.ivi_addr = ifa->ifa_address; 5013ad7d246SKrister Johansen ivi.ivi_dev = ifa->ifa_dev; 502de95e047SDavid Ahern ivi.extack = extack; 5033ad7d246SKrister Johansen ret = blocking_notifier_call_chain(&inetaddr_validator_chain, 5043ad7d246SKrister Johansen NETDEV_UP, &ivi); 5053ad7d246SKrister Johansen ret = notifier_to_errno(ret); 5063ad7d246SKrister Johansen if (ret) { 5073ad7d246SKrister Johansen inet_free_ifa(ifa); 5083ad7d246SKrister Johansen return ret; 5093ad7d246SKrister Johansen } 5103ad7d246SKrister Johansen 5111da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 51263862b5bSAruna-Hewapathirane prandom_seed((__force u32) ifa->ifa_local); 5131da177e4SLinus Torvalds ifap = last_primary; 5141da177e4SLinus Torvalds } 5151da177e4SLinus Torvalds 5161da177e4SLinus Torvalds ifa->ifa_next = *ifap; 5171da177e4SLinus Torvalds *ifap = ifa; 5181da177e4SLinus Torvalds 519fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 520fd23c3b3SDavid S. Miller 5215c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 522906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 5235c766d64SJiri Pirko 5241da177e4SLinus Torvalds /* Send message first, then call notifier. 5251da177e4SLinus Torvalds Notifier will trigger FIB update, so that 5261da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 52715e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 528e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds return 0; 5311da177e4SLinus Torvalds } 5321da177e4SLinus Torvalds 533d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 534d6062cbbSThomas Graf { 535de95e047SDavid Ahern return __inet_insert_ifa(ifa, NULL, 0, NULL); 536d6062cbbSThomas Graf } 537d6062cbbSThomas Graf 5381da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 5391da177e4SLinus Torvalds { 540e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds ASSERT_RTNL(); 5431da177e4SLinus Torvalds 5441da177e4SLinus Torvalds if (!in_dev) { 5451da177e4SLinus Torvalds inet_free_ifa(ifa); 5461da177e4SLinus Torvalds return -ENOBUFS; 5471da177e4SLinus Torvalds } 54871e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 5491d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 5501da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 551547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 5521da177e4SLinus Torvalds in_dev_hold(in_dev); 5531da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5541da177e4SLinus Torvalds } 555f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5561da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5571da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5581da177e4SLinus Torvalds } 5591da177e4SLinus Torvalds 5608723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5618723e1b4SEric Dumazet * We dont take a reference on found in_device 5628723e1b4SEric Dumazet */ 5637fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5641da177e4SLinus Torvalds { 5651da177e4SLinus Torvalds struct net_device *dev; 5661da177e4SLinus Torvalds struct in_device *in_dev = NULL; 567c148fc2eSEric Dumazet 568c148fc2eSEric Dumazet rcu_read_lock(); 569c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5701da177e4SLinus Torvalds if (dev) 5718723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 572c148fc2eSEric Dumazet rcu_read_unlock(); 5731da177e4SLinus Torvalds return in_dev; 5741da177e4SLinus Torvalds } 5759f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5761da177e4SLinus Torvalds 5771da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5781da177e4SLinus Torvalds 57960cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 58060cad5daSAl Viro __be32 mask) 5811da177e4SLinus Torvalds { 5821da177e4SLinus Torvalds ASSERT_RTNL(); 5831da177e4SLinus Torvalds 5841da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5851da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5861da177e4SLinus Torvalds return ifa; 5871da177e4SLinus Torvalds } endfor_ifa(in_dev); 5881da177e4SLinus Torvalds return NULL; 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds 59193a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa) 59293a714d6SMadhu Challa { 59393a714d6SMadhu Challa struct ip_mreqn mreq = { 59493a714d6SMadhu Challa .imr_multiaddr.s_addr = ifa->ifa_address, 59593a714d6SMadhu Challa .imr_ifindex = ifa->ifa_dev->dev->ifindex, 59693a714d6SMadhu Challa }; 59793a714d6SMadhu Challa int ret; 59893a714d6SMadhu Challa 59993a714d6SMadhu Challa ASSERT_RTNL(); 60093a714d6SMadhu Challa 60193a714d6SMadhu Challa lock_sock(sk); 60293a714d6SMadhu Challa if (join) 60354ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_join_group(sk, &mreq); 60493a714d6SMadhu Challa else 60554ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_leave_group(sk, &mreq); 60693a714d6SMadhu Challa release_sock(sk); 60793a714d6SMadhu Challa 60893a714d6SMadhu Challa return ret; 60993a714d6SMadhu Challa } 61093a714d6SMadhu Challa 611c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, 612c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 6131da177e4SLinus Torvalds { 6143b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 615dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 6161da177e4SLinus Torvalds struct in_device *in_dev; 617dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 6181da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 619dfdd5fd4SThomas Graf int err = -EINVAL; 6201da177e4SLinus Torvalds 6211da177e4SLinus Torvalds ASSERT_RTNL(); 6221da177e4SLinus Torvalds 623fceb6435SJohannes Berg err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy, 624c21ef3e3SDavid Ahern extack); 625dfdd5fd4SThomas Graf if (err < 0) 626dfdd5fd4SThomas Graf goto errout; 627dfdd5fd4SThomas Graf 628dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 6297fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 63051456b29SIan Morris if (!in_dev) { 631dfdd5fd4SThomas Graf err = -ENODEV; 632dfdd5fd4SThomas Graf goto errout; 633dfdd5fd4SThomas Graf } 634dfdd5fd4SThomas Graf 6351da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 6361da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 637dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 63867b61f6cSJiri Benc ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL])) 6391da177e4SLinus Torvalds continue; 640dfdd5fd4SThomas Graf 641dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 642dfdd5fd4SThomas Graf continue; 643dfdd5fd4SThomas Graf 644dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 645dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 64667b61f6cSJiri Benc !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa))) 647dfdd5fd4SThomas Graf continue; 648dfdd5fd4SThomas Graf 64993a714d6SMadhu Challa if (ipv4_is_multicast(ifa->ifa_address)) 65093a714d6SMadhu Challa ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa); 65115e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 6521da177e4SLinus Torvalds return 0; 6531da177e4SLinus Torvalds } 654dfdd5fd4SThomas Graf 655dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 656dfdd5fd4SThomas Graf errout: 657dfdd5fd4SThomas Graf return err; 6581da177e4SLinus Torvalds } 6591da177e4SLinus Torvalds 6605c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 6615c766d64SJiri Pirko 6625c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 6635c766d64SJiri Pirko { 6645c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 6655c766d64SJiri Pirko struct in_ifaddr *ifa; 666c988d1e8SJiri Pirko struct hlist_node *n; 6675c766d64SJiri Pirko int i; 6685c766d64SJiri Pirko 6695c766d64SJiri Pirko now = jiffies; 6705c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 6715c766d64SJiri Pirko 6725c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 673c988d1e8SJiri Pirko bool change_needed = false; 674c988d1e8SJiri Pirko 675c988d1e8SJiri Pirko rcu_read_lock(); 676b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 6775c766d64SJiri Pirko unsigned long age; 6785c766d64SJiri Pirko 6795c766d64SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 6805c766d64SJiri Pirko continue; 6815c766d64SJiri Pirko 6825c766d64SJiri Pirko /* We try to batch several events at once. */ 6835c766d64SJiri Pirko age = (now - ifa->ifa_tstamp + 6845c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 6855c766d64SJiri Pirko 6865c766d64SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 6875c766d64SJiri Pirko age >= ifa->ifa_valid_lft) { 688c988d1e8SJiri Pirko change_needed = true; 689c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft == 690c988d1e8SJiri Pirko INFINITY_LIFE_TIME) { 691c988d1e8SJiri Pirko continue; 692c988d1e8SJiri Pirko } else if (age >= ifa->ifa_preferred_lft) { 693c988d1e8SJiri Pirko if (time_before(ifa->ifa_tstamp + 694c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ, next)) 695c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 696c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ; 697c988d1e8SJiri Pirko 698c988d1e8SJiri Pirko if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) 699c988d1e8SJiri Pirko change_needed = true; 700c988d1e8SJiri Pirko } else if (time_before(ifa->ifa_tstamp + 701c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ, 702c988d1e8SJiri Pirko next)) { 703c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 704c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ; 705c988d1e8SJiri Pirko } 706c988d1e8SJiri Pirko } 707c988d1e8SJiri Pirko rcu_read_unlock(); 708c988d1e8SJiri Pirko if (!change_needed) 709c988d1e8SJiri Pirko continue; 710c988d1e8SJiri Pirko rtnl_lock(); 711c988d1e8SJiri Pirko hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 712c988d1e8SJiri Pirko unsigned long age; 713c988d1e8SJiri Pirko 714c988d1e8SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 715c988d1e8SJiri Pirko continue; 716c988d1e8SJiri Pirko 717c988d1e8SJiri Pirko /* We try to batch several events at once. */ 718c988d1e8SJiri Pirko age = (now - ifa->ifa_tstamp + 719c988d1e8SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 720c988d1e8SJiri Pirko 721c988d1e8SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 722c988d1e8SJiri Pirko age >= ifa->ifa_valid_lft) { 7235c766d64SJiri Pirko struct in_ifaddr **ifap; 7245c766d64SJiri Pirko 7255c766d64SJiri Pirko for (ifap = &ifa->ifa_dev->ifa_list; 726c988d1e8SJiri Pirko *ifap != NULL; ifap = &(*ifap)->ifa_next) { 727c988d1e8SJiri Pirko if (*ifap == ifa) { 7285c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 7295c766d64SJiri Pirko ifap, 1); 730c988d1e8SJiri Pirko break; 7315c766d64SJiri Pirko } 732c988d1e8SJiri Pirko } 733c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft != 734c988d1e8SJiri Pirko INFINITY_LIFE_TIME && 735c988d1e8SJiri Pirko age >= ifa->ifa_preferred_lft && 736c988d1e8SJiri Pirko !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 7375c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7385c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 7395c766d64SJiri Pirko } 7405c766d64SJiri Pirko } 741c988d1e8SJiri Pirko rtnl_unlock(); 7425c766d64SJiri Pirko } 7435c766d64SJiri Pirko 7445c766d64SJiri Pirko next_sec = round_jiffies_up(next); 7455c766d64SJiri Pirko next_sched = next; 7465c766d64SJiri Pirko 7475c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 7485c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 7495c766d64SJiri Pirko next_sched = next_sec; 7505c766d64SJiri Pirko 7515c766d64SJiri Pirko now = jiffies; 7525c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 7535c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 7545c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 7555c766d64SJiri Pirko 756906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 757906e073fSviresh kumar next_sched - now); 7585c766d64SJiri Pirko } 7595c766d64SJiri Pirko 7605c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 7615c766d64SJiri Pirko __u32 prefered_lft) 7625c766d64SJiri Pirko { 7635c766d64SJiri Pirko unsigned long timeout; 7645c766d64SJiri Pirko 7655c766d64SJiri Pirko ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 7665c766d64SJiri Pirko 7675c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 7685c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 7695c766d64SJiri Pirko ifa->ifa_valid_lft = timeout; 7705c766d64SJiri Pirko else 7715c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_PERMANENT; 7725c766d64SJiri Pirko 7735c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 7745c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 7755c766d64SJiri Pirko if (timeout == 0) 7765c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7775c766d64SJiri Pirko ifa->ifa_preferred_lft = timeout; 7785c766d64SJiri Pirko } 7795c766d64SJiri Pirko ifa->ifa_tstamp = jiffies; 7805c766d64SJiri Pirko if (!ifa->ifa_cstamp) 7815c766d64SJiri Pirko ifa->ifa_cstamp = ifa->ifa_tstamp; 7825c766d64SJiri Pirko } 7835c766d64SJiri Pirko 7845c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 785dac9c979SDavid Ahern __u32 *pvalid_lft, __u32 *pprefered_lft, 786dac9c979SDavid Ahern struct netlink_ext_ack *extack) 7871da177e4SLinus Torvalds { 7885c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 7895c753978SThomas Graf struct in_ifaddr *ifa; 7905c753978SThomas Graf struct ifaddrmsg *ifm; 7911da177e4SLinus Torvalds struct net_device *dev; 7921da177e4SLinus Torvalds struct in_device *in_dev; 7937b218574SDenis V. Lunev int err; 7941da177e4SLinus Torvalds 795fceb6435SJohannes Berg err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy, 796dac9c979SDavid Ahern extack); 7975c753978SThomas Graf if (err < 0) 7985c753978SThomas Graf goto errout; 7991da177e4SLinus Torvalds 8005c753978SThomas Graf ifm = nlmsg_data(nlh); 801c4e38f41SEvgeniy Polyakov err = -EINVAL; 80251456b29SIan Morris if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL]) 8035c753978SThomas Graf goto errout; 8041da177e4SLinus Torvalds 8054b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 8065c753978SThomas Graf err = -ENODEV; 80751456b29SIan Morris if (!dev) 8085c753978SThomas Graf goto errout; 8091da177e4SLinus Torvalds 8105c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 8115c753978SThomas Graf err = -ENOBUFS; 81251456b29SIan Morris if (!in_dev) 8135c753978SThomas Graf goto errout; 81471e27da9SHerbert Xu 8155c753978SThomas Graf ifa = inet_alloc_ifa(); 81651456b29SIan Morris if (!ifa) 8175c753978SThomas Graf /* 8185c753978SThomas Graf * A potential indev allocation can be left alive, it stays 8195c753978SThomas Graf * assigned to its device and is destroy with it. 8205c753978SThomas Graf */ 8215c753978SThomas Graf goto errout; 8225c753978SThomas Graf 823a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 8241d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 8255c753978SThomas Graf in_dev_hold(in_dev); 8265c753978SThomas Graf 82751456b29SIan Morris if (!tb[IFA_ADDRESS]) 8285c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 8295c753978SThomas Graf 830fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 8311da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 8321da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 833ad6c8135SJiri Pirko ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : 834ad6c8135SJiri Pirko ifm->ifa_flags; 8351da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 8361da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 8375c753978SThomas Graf 83867b61f6cSJiri Benc ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]); 83967b61f6cSJiri Benc ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]); 8405c753978SThomas Graf 8415c753978SThomas Graf if (tb[IFA_BROADCAST]) 84267b61f6cSJiri Benc ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]); 8435c753978SThomas Graf 8445c753978SThomas Graf if (tb[IFA_LABEL]) 8455c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 8461da177e4SLinus Torvalds else 8471da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 8481da177e4SLinus Torvalds 849af4d768aSDavid Ahern if (tb[IFA_RT_PRIORITY]) 850af4d768aSDavid Ahern ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 851af4d768aSDavid Ahern 8525c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 8535c766d64SJiri Pirko struct ifa_cacheinfo *ci; 8545c766d64SJiri Pirko 8555c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 8565c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 8575c766d64SJiri Pirko err = -EINVAL; 858446266b0SDaniel Borkmann goto errout_free; 8595c766d64SJiri Pirko } 8605c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 8615c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 8625c766d64SJiri Pirko } 8635c766d64SJiri Pirko 8645c753978SThomas Graf return ifa; 8655c753978SThomas Graf 866446266b0SDaniel Borkmann errout_free: 867446266b0SDaniel Borkmann inet_free_ifa(ifa); 8685c753978SThomas Graf errout: 8695c753978SThomas Graf return ERR_PTR(err); 8705c753978SThomas Graf } 8715c753978SThomas Graf 8725c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 8735c766d64SJiri Pirko { 8745c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 8755c766d64SJiri Pirko struct in_ifaddr *ifa1, **ifap; 8765c766d64SJiri Pirko 8775c766d64SJiri Pirko if (!ifa->ifa_local) 8785c766d64SJiri Pirko return NULL; 8795c766d64SJiri Pirko 8805c766d64SJiri Pirko for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 8815c766d64SJiri Pirko ifap = &ifa1->ifa_next) { 8825c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 8835c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 8845c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 8855c766d64SJiri Pirko return ifa1; 8865c766d64SJiri Pirko } 8875c766d64SJiri Pirko return NULL; 8885c766d64SJiri Pirko } 8895c766d64SJiri Pirko 890c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, 891c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 8925c753978SThomas Graf { 8933b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 8945c753978SThomas Graf struct in_ifaddr *ifa; 8955c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 8965c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 8975c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 8985c753978SThomas Graf 8995c753978SThomas Graf ASSERT_RTNL(); 9005c753978SThomas Graf 901dac9c979SDavid Ahern ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft, extack); 9025c753978SThomas Graf if (IS_ERR(ifa)) 9035c753978SThomas Graf return PTR_ERR(ifa); 9045c753978SThomas Graf 9055c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 9065c766d64SJiri Pirko if (!ifa_existing) { 9075c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 908614d056cSstephen hemminger * userspace already relies on not having to provide this. 9095c766d64SJiri Pirko */ 9105c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 91193a714d6SMadhu Challa if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) { 91293a714d6SMadhu Challa int ret = ip_mc_config(net->ipv4.mc_autojoin_sk, 91393a714d6SMadhu Challa true, ifa); 91493a714d6SMadhu Challa 91593a714d6SMadhu Challa if (ret < 0) { 91693a714d6SMadhu Challa inet_free_ifa(ifa); 91793a714d6SMadhu Challa return ret; 91893a714d6SMadhu Challa } 91993a714d6SMadhu Challa } 920de95e047SDavid Ahern return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid, 921de95e047SDavid Ahern extack); 9225c766d64SJiri Pirko } else { 923af4d768aSDavid Ahern u32 new_metric = ifa->ifa_rt_priority; 924af4d768aSDavid Ahern 9255c766d64SJiri Pirko inet_free_ifa(ifa); 9265c766d64SJiri Pirko 9275c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 9285c766d64SJiri Pirko !(nlh->nlmsg_flags & NLM_F_REPLACE)) 9295c766d64SJiri Pirko return -EEXIST; 93034e2ed34SJiri Pirko ifa = ifa_existing; 931af4d768aSDavid Ahern 932af4d768aSDavid Ahern if (ifa->ifa_rt_priority != new_metric) { 933af4d768aSDavid Ahern fib_modify_prefix_metric(ifa, new_metric); 934af4d768aSDavid Ahern ifa->ifa_rt_priority = new_metric; 935af4d768aSDavid Ahern } 936af4d768aSDavid Ahern 93734e2ed34SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 93805a324b9SJiri Pirko cancel_delayed_work(&check_lifetime_work); 939906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, 940906e073fSviresh kumar &check_lifetime_work, 0); 94134e2ed34SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 9425c766d64SJiri Pirko } 9435c766d64SJiri Pirko return 0; 9441da177e4SLinus Torvalds } 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds /* 9471da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 9481da177e4SLinus Torvalds */ 9491da177e4SLinus Torvalds 95040384999SEric Dumazet static int inet_abc_len(__be32 addr) 9511da177e4SLinus Torvalds { 9521da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 9531da177e4SLinus Torvalds 954f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 9551da177e4SLinus Torvalds rc = 0; 9561da177e4SLinus Torvalds else { 957714e85beSAl Viro __u32 haddr = ntohl(addr); 9581da177e4SLinus Torvalds 959714e85beSAl Viro if (IN_CLASSA(haddr)) 9601da177e4SLinus Torvalds rc = 8; 961714e85beSAl Viro else if (IN_CLASSB(haddr)) 9621da177e4SLinus Torvalds rc = 16; 963714e85beSAl Viro else if (IN_CLASSC(haddr)) 9641da177e4SLinus Torvalds rc = 24; 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds 9671da177e4SLinus Torvalds return rc; 9681da177e4SLinus Torvalds } 9691da177e4SLinus Torvalds 9701da177e4SLinus Torvalds 97103aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr) 9721da177e4SLinus Torvalds { 9731da177e4SLinus Torvalds struct sockaddr_in sin_orig; 97403aef17bSAl Viro struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; 9751da177e4SLinus Torvalds struct in_device *in_dev; 9761da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 9771da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 9781da177e4SLinus Torvalds struct net_device *dev; 9791da177e4SLinus Torvalds char *colon; 9801da177e4SLinus Torvalds int ret = -EFAULT; 9811da177e4SLinus Torvalds int tryaddrmatch = 0; 9821da177e4SLinus Torvalds 98303aef17bSAl Viro ifr->ifr_name[IFNAMSIZ - 1] = 0; 9841da177e4SLinus Torvalds 9851da177e4SLinus Torvalds /* save original address for comparison */ 9861da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 9871da177e4SLinus Torvalds 98803aef17bSAl Viro colon = strchr(ifr->ifr_name, ':'); 9891da177e4SLinus Torvalds if (colon) 9901da177e4SLinus Torvalds *colon = 0; 9911da177e4SLinus Torvalds 99203aef17bSAl Viro dev_load(net, ifr->ifr_name); 9931da177e4SLinus Torvalds 9941da177e4SLinus Torvalds switch (cmd) { 9951da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9961da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9971da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9981da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 9991da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 10001da177e4SLinus Torvalds so that we do not impose a lock. 10011da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 10021da177e4SLinus Torvalds */ 10031da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 10041da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 10051da177e4SLinus Torvalds sin->sin_family = AF_INET; 10061da177e4SLinus Torvalds break; 10071da177e4SLinus Torvalds 10081da177e4SLinus Torvalds case SIOCSIFFLAGS: 1009bf5b30b8SZhao Hongjiang ret = -EPERM; 101052e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10111da177e4SLinus Torvalds goto out; 10121da177e4SLinus Torvalds break; 10131da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10141da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10151da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 10161da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 1017bf5b30b8SZhao Hongjiang ret = -EPERM; 101852e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10191da177e4SLinus Torvalds goto out; 10201da177e4SLinus Torvalds ret = -EINVAL; 10211da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 10221da177e4SLinus Torvalds goto out; 10231da177e4SLinus Torvalds break; 10241da177e4SLinus Torvalds default: 10251da177e4SLinus Torvalds ret = -EINVAL; 10261da177e4SLinus Torvalds goto out; 10271da177e4SLinus Torvalds } 10281da177e4SLinus Torvalds 10291da177e4SLinus Torvalds rtnl_lock(); 10301da177e4SLinus Torvalds 10311da177e4SLinus Torvalds ret = -ENODEV; 103203aef17bSAl Viro dev = __dev_get_by_name(net, ifr->ifr_name); 10339f9354b9SEric Dumazet if (!dev) 10341da177e4SLinus Torvalds goto done; 10351da177e4SLinus Torvalds 10361da177e4SLinus Torvalds if (colon) 10371da177e4SLinus Torvalds *colon = ':'; 10381da177e4SLinus Torvalds 10399f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 10409f9354b9SEric Dumazet if (in_dev) { 10411da177e4SLinus Torvalds if (tryaddrmatch) { 10421da177e4SLinus Torvalds /* Matthias Andree */ 10431da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 10441da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 10451da177e4SLinus Torvalds and only if the original address family was AF_INET. 10461da177e4SLinus Torvalds This is checked above. */ 10471da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 10481da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 104903aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label) && 10501da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 10516c91afe1SDavid S. Miller ifa->ifa_local) { 10521da177e4SLinus Torvalds break; /* found */ 10531da177e4SLinus Torvalds } 10541da177e4SLinus Torvalds } 10551da177e4SLinus Torvalds } 10561da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 10571da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 10581da177e4SLinus Torvalds comparing just the label */ 10591da177e4SLinus Torvalds if (!ifa) { 10601da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 10611da177e4SLinus Torvalds ifap = &ifa->ifa_next) 106203aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label)) 10631da177e4SLinus Torvalds break; 10641da177e4SLinus Torvalds } 10651da177e4SLinus Torvalds } 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 10681da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 10691da177e4SLinus Torvalds goto done; 10701da177e4SLinus Torvalds 10711da177e4SLinus Torvalds switch (cmd) { 10721da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 107330e948a3STonghao Zhang ret = 0; 10741da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 107503aef17bSAl Viro break; 10761da177e4SLinus Torvalds 10771da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 107830e948a3STonghao Zhang ret = 0; 10791da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 108003aef17bSAl Viro break; 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 108330e948a3STonghao Zhang ret = 0; 10841da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 108503aef17bSAl Viro break; 10861da177e4SLinus Torvalds 10871da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 108830e948a3STonghao Zhang ret = 0; 10891da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 109003aef17bSAl Viro break; 10911da177e4SLinus Torvalds 10921da177e4SLinus Torvalds case SIOCSIFFLAGS: 10931da177e4SLinus Torvalds if (colon) { 10941da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 10951da177e4SLinus Torvalds if (!ifa) 10961da177e4SLinus Torvalds break; 10971da177e4SLinus Torvalds ret = 0; 109803aef17bSAl Viro if (!(ifr->ifr_flags & IFF_UP)) 10991da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 11001da177e4SLinus Torvalds break; 11011da177e4SLinus Torvalds } 110203aef17bSAl Viro ret = dev_change_flags(dev, ifr->ifr_flags); 11031da177e4SLinus Torvalds break; 11041da177e4SLinus Torvalds 11051da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 11061da177e4SLinus Torvalds ret = -EINVAL; 11071da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 11081da177e4SLinus Torvalds break; 11091da177e4SLinus Torvalds 11101da177e4SLinus Torvalds if (!ifa) { 11111da177e4SLinus Torvalds ret = -ENOBUFS; 11129f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 11139f9354b9SEric Dumazet if (!ifa) 11141da177e4SLinus Torvalds break; 1115c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 11161da177e4SLinus Torvalds if (colon) 111703aef17bSAl Viro memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ); 11181da177e4SLinus Torvalds else 11191da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11201da177e4SLinus Torvalds } else { 11211da177e4SLinus Torvalds ret = 0; 11221da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 11231da177e4SLinus Torvalds break; 11241da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11251da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 1126148f9729SBjorn Mork ifa->ifa_scope = 0; 11271da177e4SLinus Torvalds } 11281da177e4SLinus Torvalds 11291da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 11321da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 11331da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 11341da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 11351da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 11361da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 11371da177e4SLinus Torvalds ~ifa->ifa_mask; 11381da177e4SLinus Torvalds } else { 11391da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 11401da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 11411da177e4SLinus Torvalds } 11425c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 11431da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 11441da177e4SLinus Torvalds break; 11451da177e4SLinus Torvalds 11461da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 11471da177e4SLinus Torvalds ret = 0; 11481da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 11491da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11501da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 11511da177e4SLinus Torvalds inet_insert_ifa(ifa); 11521da177e4SLinus Torvalds } 11531da177e4SLinus Torvalds break; 11541da177e4SLinus Torvalds 11551da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 11561da177e4SLinus Torvalds ret = 0; 11571da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 11581da177e4SLinus Torvalds break; 11591da177e4SLinus Torvalds ret = -EINVAL; 11601da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 11611da177e4SLinus Torvalds break; 11621da177e4SLinus Torvalds ret = 0; 11631da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11641da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 11651da177e4SLinus Torvalds inet_insert_ifa(ifa); 11661da177e4SLinus Torvalds break; 11671da177e4SLinus Torvalds 11681da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 11691da177e4SLinus Torvalds 11701da177e4SLinus Torvalds /* 11711da177e4SLinus Torvalds * The mask we set must be legal. 11721da177e4SLinus Torvalds */ 11731da177e4SLinus Torvalds ret = -EINVAL; 11741da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 11751da177e4SLinus Torvalds break; 11761da177e4SLinus Torvalds ret = 0; 11771da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1178a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 11791da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11801da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 11811da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 11821da177e4SLinus Torvalds 11831da177e4SLinus Torvalds /* See if current broadcast address matches 11841da177e4SLinus Torvalds * with current netmask, then recalculate 11851da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 11861da177e4SLinus Torvalds * funny address, so don't touch it since 11871da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 11881da177e4SLinus Torvalds */ 11891da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 11901da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 11911da177e4SLinus Torvalds (ifa->ifa_broadcast == 1192dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 11931da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 11941da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds inet_insert_ifa(ifa); 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds break; 11991da177e4SLinus Torvalds } 12001da177e4SLinus Torvalds done: 12011da177e4SLinus Torvalds rtnl_unlock(); 12021da177e4SLinus Torvalds out: 12031da177e4SLinus Torvalds return ret; 12041da177e4SLinus Torvalds } 12051da177e4SLinus Torvalds 120636fd633eSAl Viro static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size) 12071da177e4SLinus Torvalds { 1208e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 12091da177e4SLinus Torvalds struct in_ifaddr *ifa; 12101da177e4SLinus Torvalds struct ifreq ifr; 12111da177e4SLinus Torvalds int done = 0; 12121da177e4SLinus Torvalds 121336fd633eSAl Viro if (WARN_ON(size > sizeof(struct ifreq))) 121436fd633eSAl Viro goto out; 121536fd633eSAl Viro 12169f9354b9SEric Dumazet if (!in_dev) 12171da177e4SLinus Torvalds goto out; 12181da177e4SLinus Torvalds 12199f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 12201da177e4SLinus Torvalds if (!buf) { 122136fd633eSAl Viro done += size; 12221da177e4SLinus Torvalds continue; 12231da177e4SLinus Torvalds } 122436fd633eSAl Viro if (len < size) 12251da177e4SLinus Torvalds break; 12261da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 12271da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 12281da177e4SLinus Torvalds 12291da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 12301da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 12311da177e4SLinus Torvalds ifa->ifa_local; 12321da177e4SLinus Torvalds 123336fd633eSAl Viro if (copy_to_user(buf + done, &ifr, size)) { 12341da177e4SLinus Torvalds done = -EFAULT; 12351da177e4SLinus Torvalds break; 12361da177e4SLinus Torvalds } 123736fd633eSAl Viro len -= size; 123836fd633eSAl Viro done += size; 12391da177e4SLinus Torvalds } 12401da177e4SLinus Torvalds out: 12411da177e4SLinus Torvalds return done; 12421da177e4SLinus Torvalds } 12431da177e4SLinus Torvalds 12448b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev, 12458b57fd1eSGao Feng int scope) 12468b57fd1eSGao Feng { 12478b57fd1eSGao Feng for_primary_ifa(in_dev) { 12488b57fd1eSGao Feng if (ifa->ifa_scope != RT_SCOPE_LINK && 12498b57fd1eSGao Feng ifa->ifa_scope <= scope) 12508b57fd1eSGao Feng return ifa->ifa_local; 12518b57fd1eSGao Feng } endfor_ifa(in_dev); 12528b57fd1eSGao Feng 12538b57fd1eSGao Feng return 0; 12548b57fd1eSGao Feng } 12558b57fd1eSGao Feng 1256a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 12571da177e4SLinus Torvalds { 1258a61ced5dSAl Viro __be32 addr = 0; 12591da177e4SLinus Torvalds struct in_device *in_dev; 1260c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 12613f2fb9a8SDavid Ahern int master_idx; 12621da177e4SLinus Torvalds 12631da177e4SLinus Torvalds rcu_read_lock(); 1264e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 12651da177e4SLinus Torvalds if (!in_dev) 12661da177e4SLinus Torvalds goto no_in_dev; 12671da177e4SLinus Torvalds 12681da177e4SLinus Torvalds for_primary_ifa(in_dev) { 12691da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 12701da177e4SLinus Torvalds continue; 12711da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 12721da177e4SLinus Torvalds addr = ifa->ifa_local; 12731da177e4SLinus Torvalds break; 12741da177e4SLinus Torvalds } 12751da177e4SLinus Torvalds if (!addr) 12761da177e4SLinus Torvalds addr = ifa->ifa_local; 12771da177e4SLinus Torvalds } endfor_ifa(in_dev); 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds if (addr) 1280c6d14c84SEric Dumazet goto out_unlock; 12819f9354b9SEric Dumazet no_in_dev: 12823f2fb9a8SDavid Ahern master_idx = l3mdev_master_ifindex_rcu(dev); 12831da177e4SLinus Torvalds 128417b693cdSDavid Lamparter /* For VRFs, the VRF device takes the place of the loopback device, 128517b693cdSDavid Lamparter * with addresses on it being preferred. Note in such cases the 128617b693cdSDavid Lamparter * loopback device will be among the devices that fail the master_idx 128717b693cdSDavid Lamparter * equality check in the loop below. 128817b693cdSDavid Lamparter */ 128917b693cdSDavid Lamparter if (master_idx && 129017b693cdSDavid Lamparter (dev = dev_get_by_index_rcu(net, master_idx)) && 129117b693cdSDavid Lamparter (in_dev = __in_dev_get_rcu(dev))) { 12928b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 12938b57fd1eSGao Feng if (addr) 129417b693cdSDavid Lamparter goto out_unlock; 129517b693cdSDavid Lamparter } 129617b693cdSDavid Lamparter 12971da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 1298ca9f1fd2SStephen Hemminger in this case. It is important that lo is the first interface 12991da177e4SLinus Torvalds in dev_base list. 13001da177e4SLinus Torvalds */ 1301c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 13023f2fb9a8SDavid Ahern if (l3mdev_master_ifindex_rcu(dev) != master_idx) 13033f2fb9a8SDavid Ahern continue; 13043f2fb9a8SDavid Ahern 13059f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 13069f9354b9SEric Dumazet if (!in_dev) 13071da177e4SLinus Torvalds continue; 13081da177e4SLinus Torvalds 13098b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 13108b57fd1eSGao Feng if (addr) 1311c6d14c84SEric Dumazet goto out_unlock; 13121da177e4SLinus Torvalds } 1313c6d14c84SEric Dumazet out_unlock: 13141da177e4SLinus Torvalds rcu_read_unlock(); 13151da177e4SLinus Torvalds return addr; 13161da177e4SLinus Torvalds } 13179f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 13181da177e4SLinus Torvalds 131960cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 132060cad5daSAl Viro __be32 local, int scope) 13211da177e4SLinus Torvalds { 13221da177e4SLinus Torvalds int same = 0; 1323a144ea4bSAl Viro __be32 addr = 0; 13241da177e4SLinus Torvalds 13251da177e4SLinus Torvalds for_ifa(in_dev) { 13261da177e4SLinus Torvalds if (!addr && 13271da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 13281da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 13291da177e4SLinus Torvalds addr = ifa->ifa_local; 13301da177e4SLinus Torvalds if (same) 13311da177e4SLinus Torvalds break; 13321da177e4SLinus Torvalds } 13331da177e4SLinus Torvalds if (!same) { 13341da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 13351da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 13361da177e4SLinus Torvalds if (same && addr) { 13371da177e4SLinus Torvalds if (local || !dst) 13381da177e4SLinus Torvalds break; 13391da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 13401da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 13411da177e4SLinus Torvalds break; 13421da177e4SLinus Torvalds /* No, then can we use new local src? */ 13431da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 13441da177e4SLinus Torvalds addr = ifa->ifa_local; 13451da177e4SLinus Torvalds break; 13461da177e4SLinus Torvalds } 13471da177e4SLinus Torvalds /* search for large dst subnet for addr */ 13481da177e4SLinus Torvalds same = 0; 13491da177e4SLinus Torvalds } 13501da177e4SLinus Torvalds } 13511da177e4SLinus Torvalds } endfor_ifa(in_dev); 13521da177e4SLinus Torvalds 13531da177e4SLinus Torvalds return same ? addr : 0; 13541da177e4SLinus Torvalds } 13551da177e4SLinus Torvalds 13561da177e4SLinus Torvalds /* 13571da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 1358b601fa19SNicolas Dichtel * - net: netns to check, cannot be NULL 1359b601fa19SNicolas Dichtel * - in_dev: only on this interface, NULL=any interface 13601da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 13611da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 13621da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 13631da177e4SLinus Torvalds */ 1364b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, 13659bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 13661da177e4SLinus Torvalds { 136760cad5daSAl Viro __be32 addr = 0; 13689bd85e32SDenis V. Lunev struct net_device *dev; 13691da177e4SLinus Torvalds 137000db4124SIan Morris if (in_dev) 13719bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 13721da177e4SLinus Torvalds 13731da177e4SLinus Torvalds rcu_read_lock(); 1374c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 13759f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 13769f9354b9SEric Dumazet if (in_dev) { 13771da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 13781da177e4SLinus Torvalds if (addr) 13791da177e4SLinus Torvalds break; 13801da177e4SLinus Torvalds } 13811da177e4SLinus Torvalds } 13821da177e4SLinus Torvalds rcu_read_unlock(); 13831da177e4SLinus Torvalds 13841da177e4SLinus Torvalds return addr; 13851da177e4SLinus Torvalds } 1386eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 13871da177e4SLinus Torvalds 13881da177e4SLinus Torvalds /* 13891da177e4SLinus Torvalds * Device notifier 13901da177e4SLinus Torvalds */ 13911da177e4SLinus Torvalds 13921da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 13931da177e4SLinus Torvalds { 1394e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 13951da177e4SLinus Torvalds } 13969f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 13971da177e4SLinus Torvalds 13981da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 13991da177e4SLinus Torvalds { 1400e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 14011da177e4SLinus Torvalds } 14029f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 14031da177e4SLinus Torvalds 14043ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb) 14053ad7d246SKrister Johansen { 14063ad7d246SKrister Johansen return blocking_notifier_chain_register(&inetaddr_validator_chain, nb); 14073ad7d246SKrister Johansen } 14083ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier); 14093ad7d246SKrister Johansen 14103ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb) 14113ad7d246SKrister Johansen { 14123ad7d246SKrister Johansen return blocking_notifier_chain_unregister(&inetaddr_validator_chain, 14133ad7d246SKrister Johansen nb); 14143ad7d246SKrister Johansen } 14153ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier); 14163ad7d246SKrister Johansen 14179f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 14189f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 14191da177e4SLinus Torvalds */ 14201da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 14211da177e4SLinus Torvalds { 14221da177e4SLinus Torvalds struct in_ifaddr *ifa; 14231da177e4SLinus Torvalds int named = 0; 14241da177e4SLinus Torvalds 14251da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 14261da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 14271da177e4SLinus Torvalds 14281da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 14291da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 14301da177e4SLinus Torvalds if (named++ == 0) 1431573bf470SThomas Graf goto skip; 143244344b2aSMark McLoughlin dot = strchr(old, ':'); 143351456b29SIan Morris if (!dot) { 14341da177e4SLinus Torvalds sprintf(old, ":%d", named); 14351da177e4SLinus Torvalds dot = old; 14361da177e4SLinus Torvalds } 14379f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 14381da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 14399f9354b9SEric Dumazet else 14401da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1441573bf470SThomas Graf skip: 1442573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 14431da177e4SLinus Torvalds } 14441da177e4SLinus Torvalds } 14451da177e4SLinus Torvalds 144640384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu) 144706770843SBreno Leitao { 1448b5476022SEric Dumazet return mtu >= IPV4_MIN_MTU; 144906770843SBreno Leitao } 145006770843SBreno Leitao 1451d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1452d11327adSIan Campbell struct in_device *in_dev) 1453d11327adSIan Campbell 1454d11327adSIan Campbell { 1455b76d0789SZoltan Kiss struct in_ifaddr *ifa; 1456d11327adSIan Campbell 1457b76d0789SZoltan Kiss for (ifa = in_dev->ifa_list; ifa; 1458b76d0789SZoltan Kiss ifa = ifa->ifa_next) { 1459d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 14606c91afe1SDavid S. Miller ifa->ifa_local, dev, 14616c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1462d11327adSIan Campbell dev->dev_addr, NULL); 1463d11327adSIan Campbell } 1464b76d0789SZoltan Kiss } 1465d11327adSIan Campbell 14661da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 14671da177e4SLinus Torvalds 14681da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 14691da177e4SLinus Torvalds void *ptr) 14701da177e4SLinus Torvalds { 1471351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1472748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 14731da177e4SLinus Torvalds 14741da177e4SLinus Torvalds ASSERT_RTNL(); 14751da177e4SLinus Torvalds 14761da177e4SLinus Torvalds if (!in_dev) { 14778030f544SHerbert Xu if (event == NETDEV_REGISTER) { 14781da177e4SLinus Torvalds in_dev = inetdev_init(dev); 147920e61da7SWANG Cong if (IS_ERR(in_dev)) 148020e61da7SWANG Cong return notifier_from_errno(PTR_ERR(in_dev)); 14810cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 148242f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 148342f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 14841da177e4SLinus Torvalds } 148506770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 148606770843SBreno Leitao /* Re-enabling IP */ 148706770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 148806770843SBreno Leitao in_dev = inetdev_init(dev); 14898030f544SHerbert Xu } 14901da177e4SLinus Torvalds goto out; 14911da177e4SLinus Torvalds } 14921da177e4SLinus Torvalds 14931da177e4SLinus Torvalds switch (event) { 14941da177e4SLinus Torvalds case NETDEV_REGISTER: 149591df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1496a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 14971da177e4SLinus Torvalds break; 14981da177e4SLinus Torvalds case NETDEV_UP: 149906770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 15001da177e4SLinus Torvalds break; 15010cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 15029f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 15039f9354b9SEric Dumazet 15049f9354b9SEric Dumazet if (ifa) { 1505fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 15061da177e4SLinus Torvalds ifa->ifa_local = 15071da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 15081da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 15091da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 15101da177e4SLinus Torvalds in_dev_hold(in_dev); 15111da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 15121da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 15131da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 15145c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 15155c766d64SJiri Pirko INFINITY_LIFE_TIME); 1516dfd1582dSJiri Pirko ipv4_devconf_setall(in_dev); 1517dfd1582dSJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 15181da177e4SLinus Torvalds inet_insert_ifa(ifa); 15191da177e4SLinus Torvalds } 15201da177e4SLinus Torvalds } 15211da177e4SLinus Torvalds ip_mc_up(in_dev); 1522eefef1cfSStephen Hemminger /* fall through */ 1523eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1524d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1525d11327adSIan Campbell break; 1526d11327adSIan Campbell /* fall through */ 1527d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1528a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1529d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 15301da177e4SLinus Torvalds break; 15311da177e4SLinus Torvalds case NETDEV_DOWN: 15321da177e4SLinus Torvalds ip_mc_down(in_dev); 15331da177e4SLinus Torvalds break; 153493d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 153575c78500SMoni Shoua ip_mc_unmap(in_dev); 153675c78500SMoni Shoua break; 153793d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 153875c78500SMoni Shoua ip_mc_remap(in_dev); 153975c78500SMoni Shoua break; 15401da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 154106770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 15421da177e4SLinus Torvalds break; 154306770843SBreno Leitao /* disable IP when MTU is not enough */ 1544fcfd6dfaSGustavo A. R. Silva /* fall through */ 15451da177e4SLinus Torvalds case NETDEV_UNREGISTER: 15461da177e4SLinus Torvalds inetdev_destroy(in_dev); 15471da177e4SLinus Torvalds break; 15481da177e4SLinus Torvalds case NETDEV_CHANGENAME: 15491da177e4SLinus Torvalds /* Do not notify about label change, this event is 15501da177e4SLinus Torvalds * not interesting to applications using netlink. 15511da177e4SLinus Torvalds */ 15521da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 15531da177e4SLinus Torvalds 155451602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 155566f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 15561da177e4SLinus Torvalds break; 15571da177e4SLinus Torvalds } 15581da177e4SLinus Torvalds out: 15591da177e4SLinus Torvalds return NOTIFY_DONE; 15601da177e4SLinus Torvalds } 15611da177e4SLinus Torvalds 15621da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 15631da177e4SLinus Torvalds .notifier_call = inetdev_event, 15641da177e4SLinus Torvalds }; 15651da177e4SLinus Torvalds 156640384999SEric Dumazet static size_t inet_nlmsg_size(void) 1567339bf98fSThomas Graf { 1568339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1569339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1570339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1571339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1572ad6c8135SJiri Pirko + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ 157363b5f152SGeert Uytterhoeven + nla_total_size(4) /* IFA_FLAGS */ 1574af4d768aSDavid Ahern + nla_total_size(4) /* IFA_RT_PRIORITY */ 157563b5f152SGeert Uytterhoeven + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ 1576339bf98fSThomas Graf } 1577339bf98fSThomas Graf 15785c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 15795c766d64SJiri Pirko { 15805c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 15815c766d64SJiri Pirko } 15825c766d64SJiri Pirko 15835c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 15845c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 15855c766d64SJiri Pirko { 15865c766d64SJiri Pirko struct ifa_cacheinfo ci; 15875c766d64SJiri Pirko 15885c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 15895c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 15905c766d64SJiri Pirko ci.ifa_prefered = preferred; 15915c766d64SJiri Pirko ci.ifa_valid = valid; 15925c766d64SJiri Pirko 15935c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 15945c766d64SJiri Pirko } 15955c766d64SJiri Pirko 15961da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 1597978a46faSChristian Brauner struct inet_fill_args *args) 15981da177e4SLinus Torvalds { 15991da177e4SLinus Torvalds struct ifaddrmsg *ifm; 16001da177e4SLinus Torvalds struct nlmsghdr *nlh; 16015c766d64SJiri Pirko u32 preferred, valid; 16021da177e4SLinus Torvalds 1603978a46faSChristian Brauner nlh = nlmsg_put(skb, args->portid, args->seq, args->event, sizeof(*ifm), 1604978a46faSChristian Brauner args->flags); 160551456b29SIan Morris if (!nlh) 160626932566SPatrick McHardy return -EMSGSIZE; 160747f68512SThomas Graf 160847f68512SThomas Graf ifm = nlmsg_data(nlh); 16091da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 16101da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 16115c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 16121da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 16131da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 16141da177e4SLinus Torvalds 1615978a46faSChristian Brauner if (args->netnsid >= 0 && 1616978a46faSChristian Brauner nla_put_s32(skb, IFA_TARGET_NETNSID, args->netnsid)) 1617d3807145SChristian Brauner goto nla_put_failure; 1618d3807145SChristian Brauner 16195c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 16205c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 16215c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 16225c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 16235c766d64SJiri Pirko long tval = (jiffies - ifa->ifa_tstamp) / HZ; 16245c766d64SJiri Pirko 16255c766d64SJiri Pirko if (preferred > tval) 16265c766d64SJiri Pirko preferred -= tval; 16275c766d64SJiri Pirko else 16285c766d64SJiri Pirko preferred = 0; 16295c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 16305c766d64SJiri Pirko if (valid > tval) 16315c766d64SJiri Pirko valid -= tval; 16325c766d64SJiri Pirko else 16335c766d64SJiri Pirko valid = 0; 16345c766d64SJiri Pirko } 16355c766d64SJiri Pirko } 16365c766d64SJiri Pirko } else { 16375c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 16385c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 16395c766d64SJiri Pirko } 1640f3756b79SDavid S. Miller if ((ifa->ifa_address && 1641930345eaSJiri Benc nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) || 1642f3756b79SDavid S. Miller (ifa->ifa_local && 1643930345eaSJiri Benc nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) || 1644f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1645930345eaSJiri Benc nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1646f3756b79SDavid S. Miller (ifa->ifa_label[0] && 16475c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 1648ad6c8135SJiri Pirko nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || 1649af4d768aSDavid Ahern (ifa->ifa_rt_priority && 1650af4d768aSDavid Ahern nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) || 16515c766d64SJiri Pirko put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 16525c766d64SJiri Pirko preferred, valid)) 1653f3756b79SDavid S. Miller goto nla_put_failure; 165447f68512SThomas Graf 1655053c095aSJohannes Berg nlmsg_end(skb, nlh); 1656053c095aSJohannes Berg return 0; 165747f68512SThomas Graf 165847f68512SThomas Graf nla_put_failure: 165926932566SPatrick McHardy nlmsg_cancel(skb, nlh); 166026932566SPatrick McHardy return -EMSGSIZE; 16611da177e4SLinus Torvalds } 16621da177e4SLinus Torvalds 1663c33078e3SDavid Ahern static int inet_valid_dump_ifaddr_req(const struct nlmsghdr *nlh, 1664c33078e3SDavid Ahern struct inet_fill_args *fillargs, 1665c33078e3SDavid Ahern struct net **tgt_net, struct sock *sk, 1666c33078e3SDavid Ahern struct netlink_ext_ack *extack) 1667c33078e3SDavid Ahern { 1668c33078e3SDavid Ahern struct nlattr *tb[IFA_MAX+1]; 1669c33078e3SDavid Ahern struct ifaddrmsg *ifm; 1670c33078e3SDavid Ahern int err, i; 1671c33078e3SDavid Ahern 1672c33078e3SDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ifm))) { 1673c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for address dump request"); 1674c33078e3SDavid Ahern return -EINVAL; 1675c33078e3SDavid Ahern } 1676c33078e3SDavid Ahern 1677c33078e3SDavid Ahern ifm = nlmsg_data(nlh); 1678c33078e3SDavid Ahern if (ifm->ifa_prefixlen || ifm->ifa_flags || ifm->ifa_scope) { 1679c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid values in header for address dump request"); 1680c33078e3SDavid Ahern return -EINVAL; 1681c33078e3SDavid Ahern } 1682c33078e3SDavid Ahern if (ifm->ifa_index) { 1683c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Filter by device index not supported for address dump"); 1684c33078e3SDavid Ahern return -EINVAL; 1685c33078e3SDavid Ahern } 1686c33078e3SDavid Ahern 1687c33078e3SDavid Ahern err = nlmsg_parse_strict(nlh, sizeof(*ifm), tb, IFA_MAX, 1688c33078e3SDavid Ahern ifa_ipv4_policy, extack); 1689c33078e3SDavid Ahern if (err < 0) 1690c33078e3SDavid Ahern return err; 1691c33078e3SDavid Ahern 1692c33078e3SDavid Ahern for (i = 0; i <= IFA_MAX; ++i) { 1693c33078e3SDavid Ahern if (!tb[i]) 1694c33078e3SDavid Ahern continue; 1695c33078e3SDavid Ahern 1696c33078e3SDavid Ahern if (i == IFA_TARGET_NETNSID) { 1697c33078e3SDavid Ahern struct net *net; 1698c33078e3SDavid Ahern 1699c33078e3SDavid Ahern fillargs->netnsid = nla_get_s32(tb[i]); 1700c33078e3SDavid Ahern 1701c33078e3SDavid Ahern net = rtnl_get_net_ns_capable(sk, fillargs->netnsid); 1702c33078e3SDavid Ahern if (IS_ERR(net)) { 1703c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid target network namespace id"); 1704c33078e3SDavid Ahern return PTR_ERR(net); 1705c33078e3SDavid Ahern } 1706c33078e3SDavid Ahern *tgt_net = net; 1707c33078e3SDavid Ahern } else { 1708c33078e3SDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Unsupported attribute in dump request"); 1709c33078e3SDavid Ahern return -EINVAL; 1710c33078e3SDavid Ahern } 1711c33078e3SDavid Ahern } 1712c33078e3SDavid Ahern 1713c33078e3SDavid Ahern return 0; 1714c33078e3SDavid Ahern } 1715c33078e3SDavid Ahern 17161da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 17171da177e4SLinus Torvalds { 1718c33078e3SDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 1719978a46faSChristian Brauner struct inet_fill_args fillargs = { 1720978a46faSChristian Brauner .portid = NETLINK_CB(cb->skb).portid, 1721c33078e3SDavid Ahern .seq = nlh->nlmsg_seq, 1722978a46faSChristian Brauner .event = RTM_NEWADDR, 1723978a46faSChristian Brauner .flags = NLM_F_MULTI, 1724978a46faSChristian Brauner .netnsid = -1, 1725978a46faSChristian Brauner }; 17263b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1727d3807145SChristian Brauner struct net *tgt_net = net; 1728eec4df98SEric Dumazet int h, s_h; 1729eec4df98SEric Dumazet int idx, s_idx; 1730eec4df98SEric Dumazet int ip_idx, s_ip_idx; 17311da177e4SLinus Torvalds struct net_device *dev; 17321da177e4SLinus Torvalds struct in_device *in_dev; 17331da177e4SLinus Torvalds struct in_ifaddr *ifa; 1734eec4df98SEric Dumazet struct hlist_head *head; 17351da177e4SLinus Torvalds 1736eec4df98SEric Dumazet s_h = cb->args[0]; 1737eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1738eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1739eec4df98SEric Dumazet 1740c33078e3SDavid Ahern if (cb->strict_check) { 1741c33078e3SDavid Ahern int err; 1742d3807145SChristian Brauner 1743c33078e3SDavid Ahern err = inet_valid_dump_ifaddr_req(nlh, &fillargs, &tgt_net, 1744c33078e3SDavid Ahern skb->sk, cb->extack); 1745c33078e3SDavid Ahern if (err < 0) 1746c33078e3SDavid Ahern return err; 1747d3807145SChristian Brauner } 1748d3807145SChristian Brauner 1749eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 17507562f876SPavel Emelianov idx = 0; 1751d3807145SChristian Brauner head = &tgt_net->dev_index_head[h]; 1752eec4df98SEric Dumazet rcu_read_lock(); 1753d3807145SChristian Brauner cb->seq = atomic_read(&tgt_net->ipv4.dev_addr_genid) ^ 1754d3807145SChristian Brauner tgt_net->dev_base_seq; 1755b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 17561da177e4SLinus Torvalds if (idx < s_idx) 17577562f876SPavel Emelianov goto cont; 17584b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 17591da177e4SLinus Torvalds s_ip_idx = 0; 1760eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 17619f9354b9SEric Dumazet if (!in_dev) 17627562f876SPavel Emelianov goto cont; 17631da177e4SLinus Torvalds 17641da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 17651da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 17661da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1767596e4150SStephen Hemminger continue; 1768978a46faSChristian Brauner if (inet_fill_ifaddr(skb, ifa, &fillargs) < 0) { 1769eec4df98SEric Dumazet rcu_read_unlock(); 17701da177e4SLinus Torvalds goto done; 17711da177e4SLinus Torvalds } 17720465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1773eec4df98SEric Dumazet } 17747562f876SPavel Emelianov cont: 17757562f876SPavel Emelianov idx++; 17761da177e4SLinus Torvalds } 1777eec4df98SEric Dumazet rcu_read_unlock(); 1778eec4df98SEric Dumazet } 17791da177e4SLinus Torvalds 17801da177e4SLinus Torvalds done: 1781eec4df98SEric Dumazet cb->args[0] = h; 1782eec4df98SEric Dumazet cb->args[1] = idx; 1783eec4df98SEric Dumazet cb->args[2] = ip_idx; 1784978a46faSChristian Brauner if (fillargs.netnsid >= 0) 1785d3807145SChristian Brauner put_net(tgt_net); 17861da177e4SLinus Torvalds 17871da177e4SLinus Torvalds return skb->len; 17881da177e4SLinus Torvalds } 17891da177e4SLinus Torvalds 1790d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 179115e47304SEric W. Biederman u32 portid) 17921da177e4SLinus Torvalds { 1793978a46faSChristian Brauner struct inet_fill_args fillargs = { 1794978a46faSChristian Brauner .portid = portid, 1795978a46faSChristian Brauner .seq = nlh ? nlh->nlmsg_seq : 0, 1796978a46faSChristian Brauner .event = event, 1797978a46faSChristian Brauner .flags = 0, 1798978a46faSChristian Brauner .netnsid = -1, 1799978a46faSChristian Brauner }; 180047f68512SThomas Graf struct sk_buff *skb; 1801d6062cbbSThomas Graf int err = -ENOBUFS; 18024b8aa9abSDenis V. Lunev struct net *net; 18031da177e4SLinus Torvalds 1804c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1805339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 180651456b29SIan Morris if (!skb) 1807d6062cbbSThomas Graf goto errout; 1808d6062cbbSThomas Graf 1809978a46faSChristian Brauner err = inet_fill_ifaddr(skb, ifa, &fillargs); 181026932566SPatrick McHardy if (err < 0) { 181126932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 181226932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 181326932566SPatrick McHardy kfree_skb(skb); 181426932566SPatrick McHardy goto errout; 181526932566SPatrick McHardy } 181615e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 18171ce85fe4SPablo Neira Ayuso return; 1818d6062cbbSThomas Graf errout: 1819d6062cbbSThomas Graf if (err < 0) 18204b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 18211da177e4SLinus Torvalds } 18221da177e4SLinus Torvalds 1823b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev, 1824b1974ed0SArad, Ronen u32 ext_filter_mask) 18259f0f7272SThomas Graf { 18261fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 18279f0f7272SThomas Graf 18289f0f7272SThomas Graf if (!in_dev) 18299f0f7272SThomas Graf return 0; 18309f0f7272SThomas Graf 18319f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 18329f0f7272SThomas Graf } 18339f0f7272SThomas Graf 1834d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev, 1835d5566fd7SSowmini Varadhan u32 ext_filter_mask) 18369f0f7272SThomas Graf { 18371fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 18389f0f7272SThomas Graf struct nlattr *nla; 18399f0f7272SThomas Graf int i; 18409f0f7272SThomas Graf 18419f0f7272SThomas Graf if (!in_dev) 18429f0f7272SThomas Graf return -ENODATA; 18439f0f7272SThomas Graf 18449f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 184551456b29SIan Morris if (!nla) 18469f0f7272SThomas Graf return -EMSGSIZE; 18479f0f7272SThomas Graf 18489f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 18499f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 18509f0f7272SThomas Graf 18519f0f7272SThomas Graf return 0; 18529f0f7272SThomas Graf } 18539f0f7272SThomas Graf 18549f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 18559f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 18569f0f7272SThomas Graf }; 18579f0f7272SThomas Graf 1858cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1859cf7afbfeSThomas Graf const struct nlattr *nla) 18609f0f7272SThomas Graf { 18619f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 18629f0f7272SThomas Graf int err, rem; 18639f0f7272SThomas Graf 18645fa85a09SFlorian Westphal if (dev && !__in_dev_get_rcu(dev)) 1865cf7afbfeSThomas Graf return -EAFNOSUPPORT; 18669f0f7272SThomas Graf 1867fceb6435SJohannes Berg err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy, NULL); 18689f0f7272SThomas Graf if (err < 0) 18699f0f7272SThomas Graf return err; 18709f0f7272SThomas Graf 18719f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 18729f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 18739f0f7272SThomas Graf int cfgid = nla_type(a); 18749f0f7272SThomas Graf 18759f0f7272SThomas Graf if (nla_len(a) < 4) 18769f0f7272SThomas Graf return -EINVAL; 18779f0f7272SThomas Graf 18789f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 18799f0f7272SThomas Graf return -EINVAL; 18809f0f7272SThomas Graf } 18819f0f7272SThomas Graf } 18829f0f7272SThomas Graf 1883cf7afbfeSThomas Graf return 0; 1884cf7afbfeSThomas Graf } 1885cf7afbfeSThomas Graf 1886cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1887cf7afbfeSThomas Graf { 18885fa85a09SFlorian Westphal struct in_device *in_dev = __in_dev_get_rcu(dev); 1889cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1890cf7afbfeSThomas Graf int rem; 1891cf7afbfeSThomas Graf 1892cf7afbfeSThomas Graf if (!in_dev) 1893cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1894cf7afbfeSThomas Graf 1895fceb6435SJohannes Berg if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0) 1896cf7afbfeSThomas Graf BUG(); 1897cf7afbfeSThomas Graf 18989f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 18999f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 19009f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 19019f0f7272SThomas Graf } 19029f0f7272SThomas Graf 19039f0f7272SThomas Graf return 0; 19049f0f7272SThomas Graf } 19059f0f7272SThomas Graf 1906edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 1907edc9e748SNicolas Dichtel { 1908edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 1909edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 1910136ba622SZhang Shengju bool all = false; 1911edc9e748SNicolas Dichtel 1912136ba622SZhang Shengju if (type == NETCONFA_ALL) 1913136ba622SZhang Shengju all = true; 1914136ba622SZhang Shengju 1915136ba622SZhang Shengju if (all || type == NETCONFA_FORWARDING) 1916edc9e748SNicolas Dichtel size += nla_total_size(4); 1917136ba622SZhang Shengju if (all || type == NETCONFA_RP_FILTER) 1918cc535dfbSNicolas Dichtel size += nla_total_size(4); 1919136ba622SZhang Shengju if (all || type == NETCONFA_MC_FORWARDING) 1920d67b8c61SNicolas Dichtel size += nla_total_size(4); 19215cbf777cSXin Long if (all || type == NETCONFA_BC_FORWARDING) 19225cbf777cSXin Long size += nla_total_size(4); 1923136ba622SZhang Shengju if (all || type == NETCONFA_PROXY_NEIGH) 1924f085ff1cSstephen hemminger size += nla_total_size(4); 1925136ba622SZhang Shengju if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) 1926974d7af5SAndy Gospodarek size += nla_total_size(4); 1927edc9e748SNicolas Dichtel 1928edc9e748SNicolas Dichtel return size; 1929edc9e748SNicolas Dichtel } 1930edc9e748SNicolas Dichtel 1931edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 1932edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 1933edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 1934edc9e748SNicolas Dichtel int type) 1935edc9e748SNicolas Dichtel { 1936edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 1937edc9e748SNicolas Dichtel struct netconfmsg *ncm; 1938136ba622SZhang Shengju bool all = false; 1939edc9e748SNicolas Dichtel 1940edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 1941edc9e748SNicolas Dichtel flags); 194251456b29SIan Morris if (!nlh) 1943edc9e748SNicolas Dichtel return -EMSGSIZE; 1944edc9e748SNicolas Dichtel 1945136ba622SZhang Shengju if (type == NETCONFA_ALL) 1946136ba622SZhang Shengju all = true; 1947136ba622SZhang Shengju 1948edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 1949edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 1950edc9e748SNicolas Dichtel 1951edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 1952edc9e748SNicolas Dichtel goto nla_put_failure; 1953edc9e748SNicolas Dichtel 1954b5c9641dSDavid Ahern if (!devconf) 1955b5c9641dSDavid Ahern goto out; 1956b5c9641dSDavid Ahern 1957136ba622SZhang Shengju if ((all || type == NETCONFA_FORWARDING) && 1958edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 1959edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 1960edc9e748SNicolas Dichtel goto nla_put_failure; 1961136ba622SZhang Shengju if ((all || type == NETCONFA_RP_FILTER) && 1962cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 1963cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 1964cc535dfbSNicolas Dichtel goto nla_put_failure; 1965136ba622SZhang Shengju if ((all || type == NETCONFA_MC_FORWARDING) && 1966d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 1967d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 1968d67b8c61SNicolas Dichtel goto nla_put_failure; 19695cbf777cSXin Long if ((all || type == NETCONFA_BC_FORWARDING) && 19705cbf777cSXin Long nla_put_s32(skb, NETCONFA_BC_FORWARDING, 19715cbf777cSXin Long IPV4_DEVCONF(*devconf, BC_FORWARDING)) < 0) 19725cbf777cSXin Long goto nla_put_failure; 1973136ba622SZhang Shengju if ((all || type == NETCONFA_PROXY_NEIGH) && 197409aea5dfSstephen hemminger nla_put_s32(skb, NETCONFA_PROXY_NEIGH, 1975f085ff1cSstephen hemminger IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0) 1976f085ff1cSstephen hemminger goto nla_put_failure; 1977136ba622SZhang Shengju if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) && 1978974d7af5SAndy Gospodarek nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 1979974d7af5SAndy Gospodarek IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0) 1980974d7af5SAndy Gospodarek goto nla_put_failure; 1981edc9e748SNicolas Dichtel 1982b5c9641dSDavid Ahern out: 1983053c095aSJohannes Berg nlmsg_end(skb, nlh); 1984053c095aSJohannes Berg return 0; 1985edc9e748SNicolas Dichtel 1986edc9e748SNicolas Dichtel nla_put_failure: 1987edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 1988edc9e748SNicolas Dichtel return -EMSGSIZE; 1989edc9e748SNicolas Dichtel } 1990edc9e748SNicolas Dichtel 19913b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type, 19923b022865SDavid Ahern int ifindex, struct ipv4_devconf *devconf) 1993edc9e748SNicolas Dichtel { 1994edc9e748SNicolas Dichtel struct sk_buff *skb; 1995edc9e748SNicolas Dichtel int err = -ENOBUFS; 1996edc9e748SNicolas Dichtel 1997fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL); 199851456b29SIan Morris if (!skb) 1999edc9e748SNicolas Dichtel goto errout; 2000edc9e748SNicolas Dichtel 2001edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 20023b022865SDavid Ahern event, 0, type); 2003edc9e748SNicolas Dichtel if (err < 0) { 2004edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 2005edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 2006edc9e748SNicolas Dichtel kfree_skb(skb); 2007edc9e748SNicolas Dichtel goto errout; 2008edc9e748SNicolas Dichtel } 2009fa17806cSEric Dumazet rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL); 2010edc9e748SNicolas Dichtel return; 2011edc9e748SNicolas Dichtel errout: 2012edc9e748SNicolas Dichtel if (err < 0) 2013edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 2014edc9e748SNicolas Dichtel } 2015edc9e748SNicolas Dichtel 20169e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 20179e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 20189e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 2019cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 202009aea5dfSstephen hemminger [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 2021974d7af5SAndy Gospodarek [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) }, 20229e551110SNicolas Dichtel }; 20239e551110SNicolas Dichtel 20249e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 2025c21ef3e3SDavid Ahern struct nlmsghdr *nlh, 2026c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 20279e551110SNicolas Dichtel { 20289e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 20299e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 20309e551110SNicolas Dichtel struct netconfmsg *ncm; 20319e551110SNicolas Dichtel struct sk_buff *skb; 20329e551110SNicolas Dichtel struct ipv4_devconf *devconf; 20339e551110SNicolas Dichtel struct in_device *in_dev; 20349e551110SNicolas Dichtel struct net_device *dev; 20359e551110SNicolas Dichtel int ifindex; 20369e551110SNicolas Dichtel int err; 20379e551110SNicolas Dichtel 20389e551110SNicolas Dichtel err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 2039c21ef3e3SDavid Ahern devconf_ipv4_policy, extack); 20409e551110SNicolas Dichtel if (err < 0) 20419e551110SNicolas Dichtel goto errout; 20429e551110SNicolas Dichtel 2043a97eb33fSAnton Protopopov err = -EINVAL; 20449e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 20459e551110SNicolas Dichtel goto errout; 20469e551110SNicolas Dichtel 20479e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 20489e551110SNicolas Dichtel switch (ifindex) { 20499e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 20509e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 20519e551110SNicolas Dichtel break; 20529e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 20539e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 20549e551110SNicolas Dichtel break; 20559e551110SNicolas Dichtel default: 20569e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 205751456b29SIan Morris if (!dev) 20589e551110SNicolas Dichtel goto errout; 20599e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 206051456b29SIan Morris if (!in_dev) 20619e551110SNicolas Dichtel goto errout; 20629e551110SNicolas Dichtel devconf = &in_dev->cnf; 20639e551110SNicolas Dichtel break; 20649e551110SNicolas Dichtel } 20659e551110SNicolas Dichtel 20669e551110SNicolas Dichtel err = -ENOBUFS; 2067fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 206851456b29SIan Morris if (!skb) 20699e551110SNicolas Dichtel goto errout; 20709e551110SNicolas Dichtel 20719e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 20729e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 20739e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 2074136ba622SZhang Shengju NETCONFA_ALL); 20759e551110SNicolas Dichtel if (err < 0) { 20769e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 20779e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 20789e551110SNicolas Dichtel kfree_skb(skb); 20799e551110SNicolas Dichtel goto errout; 20809e551110SNicolas Dichtel } 20819e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 20829e551110SNicolas Dichtel errout: 20839e551110SNicolas Dichtel return err; 20849e551110SNicolas Dichtel } 20859e551110SNicolas Dichtel 20867a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 20877a674200SNicolas Dichtel struct netlink_callback *cb) 20887a674200SNicolas Dichtel { 2089addd383fSDavid Ahern const struct nlmsghdr *nlh = cb->nlh; 20907a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 20917a674200SNicolas Dichtel int h, s_h; 20927a674200SNicolas Dichtel int idx, s_idx; 20937a674200SNicolas Dichtel struct net_device *dev; 20947a674200SNicolas Dichtel struct in_device *in_dev; 20957a674200SNicolas Dichtel struct hlist_head *head; 20967a674200SNicolas Dichtel 2097addd383fSDavid Ahern if (cb->strict_check) { 2098addd383fSDavid Ahern struct netlink_ext_ack *extack = cb->extack; 2099addd383fSDavid Ahern struct netconfmsg *ncm; 2100addd383fSDavid Ahern 2101addd383fSDavid Ahern if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*ncm))) { 2102addd383fSDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid header for netconf dump request"); 2103addd383fSDavid Ahern return -EINVAL; 2104addd383fSDavid Ahern } 2105addd383fSDavid Ahern 2106addd383fSDavid Ahern if (nlmsg_attrlen(nlh, sizeof(*ncm))) { 2107addd383fSDavid Ahern NL_SET_ERR_MSG(extack, "ipv4: Invalid data after header in netconf dump request"); 2108addd383fSDavid Ahern return -EINVAL; 2109addd383fSDavid Ahern } 2110addd383fSDavid Ahern } 2111addd383fSDavid Ahern 21127a674200SNicolas Dichtel s_h = cb->args[0]; 21137a674200SNicolas Dichtel s_idx = idx = cb->args[1]; 21147a674200SNicolas Dichtel 21157a674200SNicolas Dichtel for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 21167a674200SNicolas Dichtel idx = 0; 21177a674200SNicolas Dichtel head = &net->dev_index_head[h]; 21187a674200SNicolas Dichtel rcu_read_lock(); 21190465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 21200465277fSNicolas Dichtel net->dev_base_seq; 21217a674200SNicolas Dichtel hlist_for_each_entry_rcu(dev, head, index_hlist) { 21227a674200SNicolas Dichtel if (idx < s_idx) 21237a674200SNicolas Dichtel goto cont; 21247a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 21257a674200SNicolas Dichtel if (!in_dev) 21267a674200SNicolas Dichtel goto cont; 21277a674200SNicolas Dichtel 21287a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, dev->ifindex, 21297a674200SNicolas Dichtel &in_dev->cnf, 21307a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2131addd383fSDavid Ahern nlh->nlmsg_seq, 21327a674200SNicolas Dichtel RTM_NEWNETCONF, 21337a674200SNicolas Dichtel NLM_F_MULTI, 2134136ba622SZhang Shengju NETCONFA_ALL) < 0) { 21357a674200SNicolas Dichtel rcu_read_unlock(); 21367a674200SNicolas Dichtel goto done; 21377a674200SNicolas Dichtel } 21380465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 21397a674200SNicolas Dichtel cont: 21407a674200SNicolas Dichtel idx++; 21417a674200SNicolas Dichtel } 21427a674200SNicolas Dichtel rcu_read_unlock(); 21437a674200SNicolas Dichtel } 21447a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES) { 21457a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 21467a674200SNicolas Dichtel net->ipv4.devconf_all, 21477a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2148addd383fSDavid Ahern nlh->nlmsg_seq, 21497a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 2150136ba622SZhang Shengju NETCONFA_ALL) < 0) 21517a674200SNicolas Dichtel goto done; 21527a674200SNicolas Dichtel else 21537a674200SNicolas Dichtel h++; 21547a674200SNicolas Dichtel } 21557a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES + 1) { 21567a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 21577a674200SNicolas Dichtel net->ipv4.devconf_dflt, 21587a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 2159addd383fSDavid Ahern nlh->nlmsg_seq, 21607a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 2161136ba622SZhang Shengju NETCONFA_ALL) < 0) 21627a674200SNicolas Dichtel goto done; 21637a674200SNicolas Dichtel else 21647a674200SNicolas Dichtel h++; 21657a674200SNicolas Dichtel } 21667a674200SNicolas Dichtel done: 21677a674200SNicolas Dichtel cb->args[0] = h; 21687a674200SNicolas Dichtel cb->args[1] = idx; 21697a674200SNicolas Dichtel 21707a674200SNicolas Dichtel return skb->len; 21717a674200SNicolas Dichtel } 21727a674200SNicolas Dichtel 21731da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 21741da177e4SLinus Torvalds 2175c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 217631be3085SHerbert Xu { 217731be3085SHerbert Xu struct net_device *dev; 217831be3085SHerbert Xu 217931be3085SHerbert Xu rcu_read_lock(); 2180c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 2181c6d14c84SEric Dumazet struct in_device *in_dev; 2182c6d14c84SEric Dumazet 218331be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 218431be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 21859355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 2186c6d14c84SEric Dumazet } 218731be3085SHerbert Xu rcu_read_unlock(); 218831be3085SHerbert Xu } 218931be3085SHerbert Xu 2190c6d14c84SEric Dumazet /* called with RTNL locked */ 2191c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 219268dd299bSPavel Emelyanov { 219368dd299bSPavel Emelyanov struct net_device *dev; 2194586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 219568dd299bSPavel Emelyanov 2196586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 21979355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 21983b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 21993b022865SDavid Ahern NETCONFA_FORWARDING, 2200edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 2201edc9e748SNicolas Dichtel net->ipv4.devconf_all); 22023b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 22033b022865SDavid Ahern NETCONFA_FORWARDING, 2204edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2205edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 220668dd299bSPavel Emelyanov 2207c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 220868dd299bSPavel Emelyanov struct in_device *in_dev; 2209fa17806cSEric Dumazet 22100187bdfbSBen Hutchings if (on) 22110187bdfbSBen Hutchings dev_disable_lro(dev); 2212fa17806cSEric Dumazet 2213fa17806cSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 2214edc9e748SNicolas Dichtel if (in_dev) { 221568dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 22163b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 22173b022865SDavid Ahern NETCONFA_FORWARDING, 2218edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 2219edc9e748SNicolas Dichtel } 222068dd299bSPavel Emelyanov } 222168dd299bSPavel Emelyanov } 222268dd299bSPavel Emelyanov 2223f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf) 2224f085ff1cSstephen hemminger { 2225f085ff1cSstephen hemminger if (cnf == net->ipv4.devconf_dflt) 2226f085ff1cSstephen hemminger return NETCONFA_IFINDEX_DEFAULT; 2227f085ff1cSstephen hemminger else if (cnf == net->ipv4.devconf_all) 2228f085ff1cSstephen hemminger return NETCONFA_IFINDEX_ALL; 2229f085ff1cSstephen hemminger else { 2230f085ff1cSstephen hemminger struct in_device *idev 2231f085ff1cSstephen hemminger = container_of(cnf, struct in_device, cnf); 2232f085ff1cSstephen hemminger return idev->dev->ifindex; 2233f085ff1cSstephen hemminger } 2234f085ff1cSstephen hemminger } 2235f085ff1cSstephen hemminger 2236fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write, 22378d65af78SAlexey Dobriyan void __user *buffer, 223831be3085SHerbert Xu size_t *lenp, loff_t *ppos) 223931be3085SHerbert Xu { 2240d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 22418d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 2242d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 224331be3085SHerbert Xu 224431be3085SHerbert Xu if (write) { 224531be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 2246c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 224731be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 2248f085ff1cSstephen hemminger int ifindex; 224931be3085SHerbert Xu 225031be3085SHerbert Xu set_bit(i, cnf->state); 225131be3085SHerbert Xu 22529355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 2253c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 2254d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 2255d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 2256d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 22574ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2258f085ff1cSstephen hemminger 22595cbf777cSXin Long if (i == IPV4_DEVCONF_BC_FORWARDING - 1 && 22605cbf777cSXin Long new_value != old_value) 22615cbf777cSXin Long rt_cache_flush(net); 22625cbf777cSXin Long 2263cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 2264cc535dfbSNicolas Dichtel new_value != old_value) { 2265f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 22663b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 22673b022865SDavid Ahern NETCONFA_RP_FILTER, 2268cc535dfbSNicolas Dichtel ifindex, cnf); 2269cc535dfbSNicolas Dichtel } 2270f085ff1cSstephen hemminger if (i == IPV4_DEVCONF_PROXY_ARP - 1 && 2271f085ff1cSstephen hemminger new_value != old_value) { 2272f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 22733b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 22743b022865SDavid Ahern NETCONFA_PROXY_NEIGH, 2275f085ff1cSstephen hemminger ifindex, cnf); 2276f085ff1cSstephen hemminger } 2277974d7af5SAndy Gospodarek if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 && 2278974d7af5SAndy Gospodarek new_value != old_value) { 2279974d7af5SAndy Gospodarek ifindex = devinet_conf_ifindex(net, cnf); 22803b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 22813b022865SDavid Ahern NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 2282974d7af5SAndy Gospodarek ifindex, cnf); 2283974d7af5SAndy Gospodarek } 228431be3085SHerbert Xu } 228531be3085SHerbert Xu 228631be3085SHerbert Xu return ret; 228731be3085SHerbert Xu } 228831be3085SHerbert Xu 2289fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write, 22908d65af78SAlexey Dobriyan void __user *buffer, 22911da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 22921da177e4SLinus Torvalds { 22931da177e4SLinus Torvalds int *valp = ctl->data; 22941da177e4SLinus Torvalds int val = *valp; 229588af182eSEric W. Biederman loff_t pos = *ppos; 22968d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 22971da177e4SLinus Torvalds 22981da177e4SLinus Torvalds if (write && *valp != val) { 2299c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 2300c0ce9fb3SPavel Emelyanov 23010187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 230288af182eSEric W. Biederman if (!rtnl_trylock()) { 230388af182eSEric W. Biederman /* Restore the original values before restarting */ 230488af182eSEric W. Biederman *valp = val; 230588af182eSEric W. Biederman *ppos = pos; 23069b8adb5eSEric W. Biederman return restart_syscall(); 230788af182eSEric W. Biederman } 23080187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2309c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2310edc9e748SNicolas Dichtel } else { 23110187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 23120187bdfbSBen Hutchings struct in_device *idev = 23130187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2314edc9e748SNicolas Dichtel if (*valp) 23150187bdfbSBen Hutchings dev_disable_lro(idev->dev); 23163b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 2317edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2318edc9e748SNicolas Dichtel idev->dev->ifindex, 2319edc9e748SNicolas Dichtel cnf); 23200187bdfbSBen Hutchings } 23210187bdfbSBen Hutchings rtnl_unlock(); 23224ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2323edc9e748SNicolas Dichtel } else 23243b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 23253b022865SDavid Ahern NETCONFA_FORWARDING, 2326edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2327edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 23280187bdfbSBen Hutchings } 23291da177e4SLinus Torvalds 23301da177e4SLinus Torvalds return ret; 23311da177e4SLinus Torvalds } 23321da177e4SLinus Torvalds 2333fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 23348d65af78SAlexey Dobriyan void __user *buffer, 23351da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 23361da177e4SLinus Torvalds { 23371da177e4SLinus Torvalds int *valp = ctl->data; 23381da177e4SLinus Torvalds int val = *valp; 23398d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 234076e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 23411da177e4SLinus Torvalds 23421da177e4SLinus Torvalds if (write && *valp != val) 23434ccfe6d4SNicolas Dichtel rt_cache_flush(net); 23441da177e4SLinus Torvalds 23451da177e4SLinus Torvalds return ret; 23461da177e4SLinus Torvalds } 23471da177e4SLinus Torvalds 2348f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 234942f811b8SHerbert Xu { \ 235042f811b8SHerbert Xu .procname = name, \ 235142f811b8SHerbert Xu .data = ipv4_devconf.data + \ 235202291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 235342f811b8SHerbert Xu .maxlen = sizeof(int), \ 235442f811b8SHerbert Xu .mode = mval, \ 235542f811b8SHerbert Xu .proc_handler = proc, \ 235631be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 235742f811b8SHerbert Xu } 235842f811b8SHerbert Xu 235942f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2360f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 236142f811b8SHerbert Xu 236242f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2363f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 236442f811b8SHerbert Xu 2365f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2366f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 236742f811b8SHerbert Xu 236842f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2369f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 237042f811b8SHerbert Xu 23711da177e4SLinus Torvalds static struct devinet_sysctl_table { 23721da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 237302291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 23741da177e4SLinus Torvalds } devinet_sysctl = { 23751da177e4SLinus Torvalds .devinet_vars = { 237642f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2377f8572d8fSEric W. Biederman devinet_sysctl_forward), 237842f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 23795cbf777cSXin Long DEVINET_SYSCTL_RW_ENTRY(BC_FORWARDING, "bc_forwarding"), 238042f811b8SHerbert Xu 238142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 238242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 238342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 238442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 238542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 238642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 238742f811b8SHerbert Xu "accept_source_route"), 23888153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 238928f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 239042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 239142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 239242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 239342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 239442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 239542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 239642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 239742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 239842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2399eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 240065324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 24015c6fe01cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 24025c6fe01cSWilliam Manley "force_igmp_version"), 24032690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, 24042690048cSWilliam Manley "igmpv2_unsolicited_report_interval"), 24052690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, 24062690048cSWilliam Manley "igmpv3_unsolicited_report_interval"), 24070eeb075fSAndy Gospodarek DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN, 24080eeb075fSAndy Gospodarek "ignore_routes_with_linkdown"), 240997daf331SJohannes Berg DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP, 241097daf331SJohannes Berg "drop_gratuitous_arp"), 241142f811b8SHerbert Xu 241242f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 241342f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 241442f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 241542f811b8SHerbert Xu "promote_secondaries"), 2416d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2417d0daebc3SThomas Graf "route_localnet"), 241812b74dfaSJohannes Berg DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST, 241912b74dfaSJohannes Berg "drop_unicast_in_l2_multicast"), 24201da177e4SLinus Torvalds }, 24211da177e4SLinus Torvalds }; 24221da177e4SLinus Torvalds 2423ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 242429c994e3SNicolas Dichtel int ifindex, struct ipv4_devconf *p) 24251da177e4SLinus Torvalds { 24261da177e4SLinus Torvalds int i; 24279fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 24288607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2429bfada697SPavel Emelyanov 24309fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 24311da177e4SLinus Torvalds if (!t) 24329fa89642SPavel Emelyanov goto out; 24339fa89642SPavel Emelyanov 24341da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 24351da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 243631be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2437c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 24381da177e4SLinus Torvalds } 24391da177e4SLinus Torvalds 24408607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 24411da177e4SLinus Torvalds 24428607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 24431da177e4SLinus Torvalds if (!t->sysctl_header) 24448607ddb8SEric W. Biederman goto free; 24451da177e4SLinus Torvalds 24461da177e4SLinus Torvalds p->sysctl = t; 244729c994e3SNicolas Dichtel 24483b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, 24493b022865SDavid Ahern ifindex, p); 2450ea40b324SPavel Emelyanov return 0; 24511da177e4SLinus Torvalds 24521da177e4SLinus Torvalds free: 24531da177e4SLinus Torvalds kfree(t); 24549fa89642SPavel Emelyanov out: 2455ea40b324SPavel Emelyanov return -ENOBUFS; 24561da177e4SLinus Torvalds } 24571da177e4SLinus Torvalds 2458b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net, 2459b5c9641dSDavid Ahern struct ipv4_devconf *cnf, int ifindex) 246066f27a52SPavel Emelyanov { 246151602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 246266f27a52SPavel Emelyanov 2463b5c9641dSDavid Ahern if (t) { 246451602b2aSPavel Emelyanov cnf->sysctl = NULL; 2465ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 24661da177e4SLinus Torvalds kfree(t); 24671da177e4SLinus Torvalds } 246851602b2aSPavel Emelyanov 2469b5c9641dSDavid Ahern inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); 2470b5c9641dSDavid Ahern } 2471b5c9641dSDavid Ahern 247220e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 247351602b2aSPavel Emelyanov { 247420e61da7SWANG Cong int err; 247520e61da7SWANG Cong 247620e61da7SWANG Cong if (!sysctl_dev_name_is_allowed(idev->dev->name)) 247720e61da7SWANG Cong return -EINVAL; 247820e61da7SWANG Cong 247920e61da7SWANG Cong err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); 248020e61da7SWANG Cong if (err) 248120e61da7SWANG Cong return err; 248220e61da7SWANG Cong err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 248329c994e3SNicolas Dichtel idev->dev->ifindex, &idev->cnf); 248420e61da7SWANG Cong if (err) 248520e61da7SWANG Cong neigh_sysctl_unregister(idev->arp_parms); 248620e61da7SWANG Cong return err; 248751602b2aSPavel Emelyanov } 248851602b2aSPavel Emelyanov 248951602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 249051602b2aSPavel Emelyanov { 2491b5c9641dSDavid Ahern struct net *net = dev_net(idev->dev); 2492b5c9641dSDavid Ahern 2493b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex); 249451602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 24951da177e4SLinus Torvalds } 24961da177e4SLinus Torvalds 249768dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 249868dd299bSPavel Emelyanov { 249968dd299bSPavel Emelyanov .procname = "ip_forward", 250068dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 250102291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 250268dd299bSPavel Emelyanov .maxlen = sizeof(int), 250368dd299bSPavel Emelyanov .mode = 0644, 250468dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 250568dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2506c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 250768dd299bSPavel Emelyanov }, 250868dd299bSPavel Emelyanov { }, 250968dd299bSPavel Emelyanov }; 25102a75de0cSEric Dumazet #endif 251168dd299bSPavel Emelyanov 2512752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2513752d14dcSPavel Emelyanov { 2514752d14dcSPavel Emelyanov int err; 2515752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 25162a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 25172a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 2518752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 25192a75de0cSEric Dumazet #endif 2520752d14dcSPavel Emelyanov 2521752d14dcSPavel Emelyanov err = -ENOMEM; 2522752d14dcSPavel Emelyanov all = &ipv4_devconf; 2523752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 2524752d14dcSPavel Emelyanov 252509ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 2526752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 252751456b29SIan Morris if (!all) 2528752d14dcSPavel Emelyanov goto err_alloc_all; 2529752d14dcSPavel Emelyanov 2530752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 253151456b29SIan Morris if (!dflt) 2532752d14dcSPavel Emelyanov goto err_alloc_dflt; 2533752d14dcSPavel Emelyanov 25342a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2535752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 253651456b29SIan Morris if (!tbl) 2537752d14dcSPavel Emelyanov goto err_alloc_ctl; 2538752d14dcSPavel Emelyanov 253902291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2540752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2541752d14dcSPavel Emelyanov tbl[0].extra2 = net; 25422a75de0cSEric Dumazet #endif 2543752d14dcSPavel Emelyanov } 2544752d14dcSPavel Emelyanov 2545752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 254629c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all); 2547752d14dcSPavel Emelyanov if (err < 0) 2548752d14dcSPavel Emelyanov goto err_reg_all; 2549752d14dcSPavel Emelyanov 255029c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "default", 255129c994e3SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, dflt); 2552752d14dcSPavel Emelyanov if (err < 0) 2553752d14dcSPavel Emelyanov goto err_reg_dflt; 2554752d14dcSPavel Emelyanov 2555752d14dcSPavel Emelyanov err = -ENOMEM; 25568607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 255751456b29SIan Morris if (!forw_hdr) 2558752d14dcSPavel Emelyanov goto err_reg_ctl; 25592a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2560752d14dcSPavel Emelyanov #endif 2561752d14dcSPavel Emelyanov 2562752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2563752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2564752d14dcSPavel Emelyanov return 0; 2565752d14dcSPavel Emelyanov 2566752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2567752d14dcSPavel Emelyanov err_reg_ctl: 2568b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT); 2569752d14dcSPavel Emelyanov err_reg_dflt: 2570b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); 2571752d14dcSPavel Emelyanov err_reg_all: 2572752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 2573752d14dcSPavel Emelyanov kfree(tbl); 2574752d14dcSPavel Emelyanov err_alloc_ctl: 25752a75de0cSEric Dumazet #endif 2576752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 2577752d14dcSPavel Emelyanov kfree(dflt); 2578752d14dcSPavel Emelyanov err_alloc_dflt: 2579752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 2580752d14dcSPavel Emelyanov kfree(all); 2581752d14dcSPavel Emelyanov err_alloc_all: 2582752d14dcSPavel Emelyanov return err; 2583752d14dcSPavel Emelyanov } 2584752d14dcSPavel Emelyanov 2585752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2586752d14dcSPavel Emelyanov { 25872a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2588752d14dcSPavel Emelyanov struct ctl_table *tbl; 2589752d14dcSPavel Emelyanov 2590752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2591752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2592b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_dflt, 2593b5c9641dSDavid Ahern NETCONFA_IFINDEX_DEFAULT); 2594b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_all, 2595b5c9641dSDavid Ahern NETCONFA_IFINDEX_ALL); 2596752d14dcSPavel Emelyanov kfree(tbl); 25972a75de0cSEric Dumazet #endif 2598752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2599752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2600752d14dcSPavel Emelyanov } 2601752d14dcSPavel Emelyanov 2602752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2603752d14dcSPavel Emelyanov .init = devinet_init_net, 2604752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2605752d14dcSPavel Emelyanov }; 2606752d14dcSPavel Emelyanov 2607207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = { 26089f0f7272SThomas Graf .family = AF_INET, 26099f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 26109f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2611cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2612cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 26139f0f7272SThomas Graf }; 26149f0f7272SThomas Graf 26151da177e4SLinus Torvalds void __init devinet_init(void) 26161da177e4SLinus Torvalds { 2617fd23c3b3SDavid S. Miller int i; 2618fd23c3b3SDavid S. Miller 2619fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2620fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2621fd23c3b3SDavid S. Miller 2622752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 2623752d14dcSPavel Emelyanov 26241da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 26251da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 262663f3444fSThomas Graf 2627906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 26285c766d64SJiri Pirko 26299f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 26309f0f7272SThomas Graf 2631b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0); 2632b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0); 2633b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0); 26349e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 2635b97bac64SFlorian Westphal inet_netconf_dump_devconf, 0); 26361da177e4SLinus Torvalds } 2637