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 }, 102*af4d768aSDavid Ahern [IFA_RT_PRIORITY] = { .type = NLA_U32 }, 1035c753978SThomas Graf }; 1045c753978SThomas Graf 10540384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT 8 10640384999SEric Dumazet #define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 10740384999SEric Dumazet 108fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 109fd23c3b3SDavid S. Miller 1106eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr) 111fd23c3b3SDavid S. Miller { 11240384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 113fd23c3b3SDavid S. Miller 11440384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 115fd23c3b3SDavid S. Miller } 116fd23c3b3SDavid S. Miller 117fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 118fd23c3b3SDavid S. Miller { 11940384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 120fd23c3b3SDavid S. Miller 12132a4be48SWANG Cong ASSERT_RTNL(); 122fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 123fd23c3b3SDavid S. Miller } 124fd23c3b3SDavid S. Miller 125fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 126fd23c3b3SDavid S. Miller { 12732a4be48SWANG Cong ASSERT_RTNL(); 128fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 129fd23c3b3SDavid S. Miller } 130fd23c3b3SDavid S. Miller 1319435eb1cSDavid S. Miller /** 1329435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1339435eb1cSDavid S. Miller * @net: the net namespace 1349435eb1cSDavid S. Miller * @addr: the source address 1359435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1369435eb1cSDavid S. Miller * 1379435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1389435eb1cSDavid S. Miller */ 1399435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1409435eb1cSDavid S. Miller { 1419435eb1cSDavid S. Miller struct net_device *result = NULL; 1429435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1439435eb1cSDavid S. Miller 1449435eb1cSDavid S. Miller rcu_read_lock(); 1456e617de8SPaolo Abeni ifa = inet_lookup_ifaddr_rcu(net, addr); 1466e617de8SPaolo Abeni if (!ifa) { 147406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 148406b6f97SDavid S. Miller struct fib_result res = { 0 }; 149406b6f97SDavid S. Miller struct fib_table *local; 150406b6f97SDavid S. Miller 151406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 152406b6f97SDavid S. Miller * over loopback subnets work. 153406b6f97SDavid S. Miller */ 154406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 155406b6f97SDavid S. Miller if (local && 156406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 157406b6f97SDavid S. Miller res.type == RTN_LOCAL) 158406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 1596e617de8SPaolo Abeni } else { 1606e617de8SPaolo Abeni result = ifa->ifa_dev->dev; 161406b6f97SDavid S. Miller } 1629435eb1cSDavid S. Miller if (result && devref) 1639435eb1cSDavid S. Miller dev_hold(result); 1649435eb1cSDavid S. Miller rcu_read_unlock(); 1659435eb1cSDavid S. Miller return result; 1669435eb1cSDavid S. Miller } 1679435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1689435eb1cSDavid S. Miller 1696e617de8SPaolo Abeni /* called under RCU lock */ 1706e617de8SPaolo Abeni struct in_ifaddr *inet_lookup_ifaddr_rcu(struct net *net, __be32 addr) 1716e617de8SPaolo Abeni { 1726e617de8SPaolo Abeni u32 hash = inet_addr_hash(net, addr); 1736e617de8SPaolo Abeni struct in_ifaddr *ifa; 1746e617de8SPaolo Abeni 1756e617de8SPaolo Abeni hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) 1766e617de8SPaolo Abeni if (ifa->ifa_local == addr && 1776e617de8SPaolo Abeni net_eq(dev_net(ifa->ifa_dev->dev), net)) 1786e617de8SPaolo Abeni return ifa; 1796e617de8SPaolo Abeni 1806e617de8SPaolo Abeni return NULL; 1816e617de8SPaolo Abeni } 1826e617de8SPaolo Abeni 183d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1841da177e4SLinus Torvalds 185e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1863ad7d246SKrister Johansen static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain); 1871da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1881da177e4SLinus Torvalds int destroy); 1891da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 19020e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev); 19151602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 19251602b2aSPavel Emelyanov #else 19320e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 19451602b2aSPavel Emelyanov { 19520e61da7SWANG Cong return 0; 19651602b2aSPavel Emelyanov } 19740384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 19851602b2aSPavel Emelyanov { 19951602b2aSPavel Emelyanov } 2001da177e4SLinus Torvalds #endif 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds /* Locks all the inet devices. */ 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 2051da177e4SLinus Torvalds { 20693adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2101da177e4SLinus Torvalds { 2111da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2121da177e4SLinus Torvalds if (ifa->ifa_dev) 2131da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2141da177e4SLinus Torvalds kfree(ifa); 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds 21740384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2181da177e4SLinus Torvalds { 2191da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2201da177e4SLinus Torvalds } 2211da177e4SLinus Torvalds 2221da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2231da177e4SLinus Torvalds { 2241da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2251da177e4SLinus Torvalds 226547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 227547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 228e9897071SEric Dumazet kfree(rcu_dereference_protected(idev->mc_hash, 1)); 2291da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 23091df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2311da177e4SLinus Torvalds #endif 2321da177e4SLinus Torvalds dev_put(dev); 2331da177e4SLinus Torvalds if (!idev->dead) 2349f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2359f9354b9SEric Dumazet else 2361da177e4SLinus Torvalds kfree(idev); 2371da177e4SLinus Torvalds } 2389f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2391da177e4SLinus Torvalds 24071e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2411da177e4SLinus Torvalds { 2421da177e4SLinus Torvalds struct in_device *in_dev; 24320e61da7SWANG Cong int err = -ENOMEM; 2441da177e4SLinus Torvalds 2451da177e4SLinus Torvalds ASSERT_RTNL(); 2461da177e4SLinus Torvalds 2470da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2481da177e4SLinus Torvalds if (!in_dev) 2491da177e4SLinus Torvalds goto out; 250c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2519355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2521da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2531da177e4SLinus Torvalds in_dev->dev = dev; 2549f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2559f9354b9SEric Dumazet if (!in_dev->arp_parms) 2561da177e4SLinus Torvalds goto out_kfree; 2570187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2580187bdfbSBen Hutchings dev_disable_lro(dev); 2591da177e4SLinus Torvalds /* Reference in_dev->dev */ 2601da177e4SLinus Torvalds dev_hold(dev); 26130c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2627658b36fSReshetova, Elena refcount_set(&in_dev->refcnt, 1); 2631da177e4SLinus Torvalds 26420e61da7SWANG Cong err = devinet_sysctl_register(in_dev); 26520e61da7SWANG Cong if (err) { 26620e61da7SWANG Cong in_dev->dead = 1; 26720e61da7SWANG Cong in_dev_put(in_dev); 26820e61da7SWANG Cong in_dev = NULL; 26920e61da7SWANG Cong goto out; 27020e61da7SWANG Cong } 2711da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2721da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2731da177e4SLinus Torvalds ip_mc_up(in_dev); 274483479ecSJarek Poplawski 27530c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 276cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 277483479ecSJarek Poplawski out: 27820e61da7SWANG Cong return in_dev ?: ERR_PTR(err); 2791da177e4SLinus Torvalds out_kfree: 2801da177e4SLinus Torvalds kfree(in_dev); 2811da177e4SLinus Torvalds in_dev = NULL; 2821da177e4SLinus Torvalds goto out; 2831da177e4SLinus Torvalds } 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2861da177e4SLinus Torvalds { 2871da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2881da177e4SLinus Torvalds in_dev_put(idev); 2891da177e4SLinus Torvalds } 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2921da177e4SLinus Torvalds { 2931da177e4SLinus Torvalds struct in_ifaddr *ifa; 2941da177e4SLinus Torvalds struct net_device *dev; 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds ASSERT_RTNL(); 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds dev = in_dev->dev; 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds in_dev->dead = 1; 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 3051da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 3061da177e4SLinus Torvalds inet_free_ifa(ifa); 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds 309a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 3101da177e4SLinus Torvalds 31151602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 3121da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 3131da177e4SLinus Torvalds arp_ifdown(dev); 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 318ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3191da177e4SLinus Torvalds { 3201da177e4SLinus Torvalds rcu_read_lock(); 3211da177e4SLinus Torvalds for_primary_ifa(in_dev) { 3221da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3231da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3241da177e4SLinus Torvalds rcu_read_unlock(); 3251da177e4SLinus Torvalds return 1; 3261da177e4SLinus Torvalds } 3271da177e4SLinus Torvalds } 3281da177e4SLinus Torvalds } endfor_ifa(in_dev); 3291da177e4SLinus Torvalds rcu_read_unlock(); 3301da177e4SLinus Torvalds return 0; 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds 333d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 33415e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3351da177e4SLinus Torvalds { 3368f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3370ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3380ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3390ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3400ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3411da177e4SLinus Torvalds 3421da177e4SLinus Torvalds ASSERT_RTNL(); 3431da177e4SLinus Torvalds 344fbd40ea0SDavid S. Miller if (in_dev->dead) 345fbd40ea0SDavid S. Miller goto no_promotions; 346fbd40ea0SDavid S. Miller 3478f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3488f937c60SHarald Welte * unless alias promotion is set 3498f937c60SHarald Welte **/ 3501da177e4SLinus Torvalds 3511da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3521da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3531da177e4SLinus Torvalds 3541da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3550ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3560ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3570ff60a45SJamal Hadi Salim last_prim = ifa; 3580ff60a45SJamal Hadi Salim 3591da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3601da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3611da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3621da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3630ff60a45SJamal Hadi Salim prev_prom = ifa; 3641da177e4SLinus Torvalds continue; 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds 3670ff60a45SJamal Hadi Salim if (!do_promote) { 368fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3691da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3701da177e4SLinus Torvalds 37115e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 372e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 373e041c683SAlan Stern NETDEV_DOWN, ifa); 3741da177e4SLinus Torvalds inet_free_ifa(ifa); 3758f937c60SHarald Welte } else { 3768f937c60SHarald Welte promote = ifa; 3778f937c60SHarald Welte break; 3788f937c60SHarald Welte } 3791da177e4SLinus Torvalds } 3801da177e4SLinus Torvalds } 3811da177e4SLinus Torvalds 3822d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 3832d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 3842d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 3852d230e2bSJulian Anastasov * while all addresses are on the device list. 3862d230e2bSJulian Anastasov */ 3872d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 3882d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 3892d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 3902d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 3912d230e2bSJulian Anastasov } 3922d230e2bSJulian Anastasov 393fbd40ea0SDavid S. Miller no_promotions: 3941da177e4SLinus Torvalds /* 2. Unlink it */ 3951da177e4SLinus Torvalds 3961da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 397fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3981da177e4SLinus Torvalds 3991da177e4SLinus Torvalds /* 3. Announce address deletion */ 4001da177e4SLinus Torvalds 4011da177e4SLinus Torvalds /* Send message first, then call notifier. 4021da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 4031da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 4041da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 4051da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 4061da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 4071da177e4SLinus Torvalds So that, this order is correct. 4081da177e4SLinus Torvalds */ 40915e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 410e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 4110ff60a45SJamal Hadi Salim 4120ff60a45SJamal Hadi Salim if (promote) { 41304024b93SJulian Anastasov struct in_ifaddr *next_sec = promote->ifa_next; 4140ff60a45SJamal Hadi Salim 4150ff60a45SJamal Hadi Salim if (prev_prom) { 4160ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 4170ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 4180ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 4190ff60a45SJamal Hadi Salim } 4200ff60a45SJamal Hadi Salim 4210ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 42215e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 423e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 424e041c683SAlan Stern NETDEV_UP, promote); 42504024b93SJulian Anastasov for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { 4260ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4270ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4280ff60a45SJamal Hadi Salim continue; 4290ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4300ff60a45SJamal Hadi Salim } 4310ff60a45SJamal Hadi Salim 4320ff60a45SJamal Hadi Salim } 4336363097cSHerbert Xu if (destroy) 4341da177e4SLinus Torvalds inet_free_ifa(ifa1); 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds 437d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 438d6062cbbSThomas Graf int destroy) 439d6062cbbSThomas Graf { 440d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 441d6062cbbSThomas Graf } 442d6062cbbSThomas Graf 4435c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4445c766d64SJiri Pirko 4455c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4465c766d64SJiri Pirko 447d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 448de95e047SDavid Ahern u32 portid, struct netlink_ext_ack *extack) 4491da177e4SLinus Torvalds { 4501da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4511da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4523ad7d246SKrister Johansen struct in_validator_info ivi; 4533ad7d246SKrister Johansen int ret; 4541da177e4SLinus Torvalds 4551da177e4SLinus Torvalds ASSERT_RTNL(); 4561da177e4SLinus Torvalds 4571da177e4SLinus Torvalds if (!ifa->ifa_local) { 4581da177e4SLinus Torvalds inet_free_ifa(ifa); 4591da177e4SLinus Torvalds return 0; 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds 4621da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4631da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4641da177e4SLinus Torvalds 4651da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4661da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4671da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4681da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4691da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4701da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4711da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4721da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4731da177e4SLinus Torvalds inet_free_ifa(ifa); 4741da177e4SLinus Torvalds return -EEXIST; 4751da177e4SLinus Torvalds } 4761da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4771da177e4SLinus Torvalds inet_free_ifa(ifa); 4781da177e4SLinus Torvalds return -EINVAL; 4791da177e4SLinus Torvalds } 4801da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4811da177e4SLinus Torvalds } 4821da177e4SLinus Torvalds } 4831da177e4SLinus Torvalds 4843ad7d246SKrister Johansen /* Allow any devices that wish to register ifaddr validtors to weigh 4853ad7d246SKrister Johansen * in now, before changes are committed. The rntl lock is serializing 4863ad7d246SKrister Johansen * access here, so the state should not change between a validator call 4873ad7d246SKrister Johansen * and a final notify on commit. This isn't invoked on promotion under 4883ad7d246SKrister Johansen * the assumption that validators are checking the address itself, and 4893ad7d246SKrister Johansen * not the flags. 4903ad7d246SKrister Johansen */ 4913ad7d246SKrister Johansen ivi.ivi_addr = ifa->ifa_address; 4923ad7d246SKrister Johansen ivi.ivi_dev = ifa->ifa_dev; 493de95e047SDavid Ahern ivi.extack = extack; 4943ad7d246SKrister Johansen ret = blocking_notifier_call_chain(&inetaddr_validator_chain, 4953ad7d246SKrister Johansen NETDEV_UP, &ivi); 4963ad7d246SKrister Johansen ret = notifier_to_errno(ret); 4973ad7d246SKrister Johansen if (ret) { 4983ad7d246SKrister Johansen inet_free_ifa(ifa); 4993ad7d246SKrister Johansen return ret; 5003ad7d246SKrister Johansen } 5013ad7d246SKrister Johansen 5021da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 50363862b5bSAruna-Hewapathirane prandom_seed((__force u32) ifa->ifa_local); 5041da177e4SLinus Torvalds ifap = last_primary; 5051da177e4SLinus Torvalds } 5061da177e4SLinus Torvalds 5071da177e4SLinus Torvalds ifa->ifa_next = *ifap; 5081da177e4SLinus Torvalds *ifap = ifa; 5091da177e4SLinus Torvalds 510fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 511fd23c3b3SDavid S. Miller 5125c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 513906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 5145c766d64SJiri Pirko 5151da177e4SLinus Torvalds /* Send message first, then call notifier. 5161da177e4SLinus Torvalds Notifier will trigger FIB update, so that 5171da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 51815e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 519e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 5201da177e4SLinus Torvalds 5211da177e4SLinus Torvalds return 0; 5221da177e4SLinus Torvalds } 5231da177e4SLinus Torvalds 524d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 525d6062cbbSThomas Graf { 526de95e047SDavid Ahern return __inet_insert_ifa(ifa, NULL, 0, NULL); 527d6062cbbSThomas Graf } 528d6062cbbSThomas Graf 5291da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 5301da177e4SLinus Torvalds { 531e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds ASSERT_RTNL(); 5341da177e4SLinus Torvalds 5351da177e4SLinus Torvalds if (!in_dev) { 5361da177e4SLinus Torvalds inet_free_ifa(ifa); 5371da177e4SLinus Torvalds return -ENOBUFS; 5381da177e4SLinus Torvalds } 53971e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 5401d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 5411da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 542547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 5431da177e4SLinus Torvalds in_dev_hold(in_dev); 5441da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5451da177e4SLinus Torvalds } 546f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5471da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5481da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5491da177e4SLinus Torvalds } 5501da177e4SLinus Torvalds 5518723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5528723e1b4SEric Dumazet * We dont take a reference on found in_device 5538723e1b4SEric Dumazet */ 5547fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5551da177e4SLinus Torvalds { 5561da177e4SLinus Torvalds struct net_device *dev; 5571da177e4SLinus Torvalds struct in_device *in_dev = NULL; 558c148fc2eSEric Dumazet 559c148fc2eSEric Dumazet rcu_read_lock(); 560c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5611da177e4SLinus Torvalds if (dev) 5628723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 563c148fc2eSEric Dumazet rcu_read_unlock(); 5641da177e4SLinus Torvalds return in_dev; 5651da177e4SLinus Torvalds } 5669f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5671da177e4SLinus Torvalds 5681da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5691da177e4SLinus Torvalds 57060cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 57160cad5daSAl Viro __be32 mask) 5721da177e4SLinus Torvalds { 5731da177e4SLinus Torvalds ASSERT_RTNL(); 5741da177e4SLinus Torvalds 5751da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5761da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5771da177e4SLinus Torvalds return ifa; 5781da177e4SLinus Torvalds } endfor_ifa(in_dev); 5791da177e4SLinus Torvalds return NULL; 5801da177e4SLinus Torvalds } 5811da177e4SLinus Torvalds 58293a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa) 58393a714d6SMadhu Challa { 58493a714d6SMadhu Challa struct ip_mreqn mreq = { 58593a714d6SMadhu Challa .imr_multiaddr.s_addr = ifa->ifa_address, 58693a714d6SMadhu Challa .imr_ifindex = ifa->ifa_dev->dev->ifindex, 58793a714d6SMadhu Challa }; 58893a714d6SMadhu Challa int ret; 58993a714d6SMadhu Challa 59093a714d6SMadhu Challa ASSERT_RTNL(); 59193a714d6SMadhu Challa 59293a714d6SMadhu Challa lock_sock(sk); 59393a714d6SMadhu Challa if (join) 59454ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_join_group(sk, &mreq); 59593a714d6SMadhu Challa else 59654ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_leave_group(sk, &mreq); 59793a714d6SMadhu Challa release_sock(sk); 59893a714d6SMadhu Challa 59993a714d6SMadhu Challa return ret; 60093a714d6SMadhu Challa } 60193a714d6SMadhu Challa 602c21ef3e3SDavid Ahern static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, 603c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 6041da177e4SLinus Torvalds { 6053b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 606dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 6071da177e4SLinus Torvalds struct in_device *in_dev; 608dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 6091da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 610dfdd5fd4SThomas Graf int err = -EINVAL; 6111da177e4SLinus Torvalds 6121da177e4SLinus Torvalds ASSERT_RTNL(); 6131da177e4SLinus Torvalds 614fceb6435SJohannes Berg err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy, 615c21ef3e3SDavid Ahern extack); 616dfdd5fd4SThomas Graf if (err < 0) 617dfdd5fd4SThomas Graf goto errout; 618dfdd5fd4SThomas Graf 619dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 6207fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 62151456b29SIan Morris if (!in_dev) { 622dfdd5fd4SThomas Graf err = -ENODEV; 623dfdd5fd4SThomas Graf goto errout; 624dfdd5fd4SThomas Graf } 625dfdd5fd4SThomas Graf 6261da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 6271da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 628dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 62967b61f6cSJiri Benc ifa->ifa_local != nla_get_in_addr(tb[IFA_LOCAL])) 6301da177e4SLinus Torvalds continue; 631dfdd5fd4SThomas Graf 632dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 633dfdd5fd4SThomas Graf continue; 634dfdd5fd4SThomas Graf 635dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 636dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 63767b61f6cSJiri Benc !inet_ifa_match(nla_get_in_addr(tb[IFA_ADDRESS]), ifa))) 638dfdd5fd4SThomas Graf continue; 639dfdd5fd4SThomas Graf 64093a714d6SMadhu Challa if (ipv4_is_multicast(ifa->ifa_address)) 64193a714d6SMadhu Challa ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa); 64215e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 6431da177e4SLinus Torvalds return 0; 6441da177e4SLinus Torvalds } 645dfdd5fd4SThomas Graf 646dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 647dfdd5fd4SThomas Graf errout: 648dfdd5fd4SThomas Graf return err; 6491da177e4SLinus Torvalds } 6501da177e4SLinus Torvalds 6515c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 6525c766d64SJiri Pirko 6535c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 6545c766d64SJiri Pirko { 6555c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 6565c766d64SJiri Pirko struct in_ifaddr *ifa; 657c988d1e8SJiri Pirko struct hlist_node *n; 6585c766d64SJiri Pirko int i; 6595c766d64SJiri Pirko 6605c766d64SJiri Pirko now = jiffies; 6615c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 6625c766d64SJiri Pirko 6635c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 664c988d1e8SJiri Pirko bool change_needed = false; 665c988d1e8SJiri Pirko 666c988d1e8SJiri Pirko rcu_read_lock(); 667b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 6685c766d64SJiri Pirko unsigned long age; 6695c766d64SJiri Pirko 6705c766d64SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 6715c766d64SJiri Pirko continue; 6725c766d64SJiri Pirko 6735c766d64SJiri Pirko /* We try to batch several events at once. */ 6745c766d64SJiri Pirko age = (now - ifa->ifa_tstamp + 6755c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 6765c766d64SJiri Pirko 6775c766d64SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 6785c766d64SJiri Pirko age >= ifa->ifa_valid_lft) { 679c988d1e8SJiri Pirko change_needed = true; 680c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft == 681c988d1e8SJiri Pirko INFINITY_LIFE_TIME) { 682c988d1e8SJiri Pirko continue; 683c988d1e8SJiri Pirko } else if (age >= ifa->ifa_preferred_lft) { 684c988d1e8SJiri Pirko if (time_before(ifa->ifa_tstamp + 685c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ, next)) 686c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 687c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ; 688c988d1e8SJiri Pirko 689c988d1e8SJiri Pirko if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) 690c988d1e8SJiri Pirko change_needed = true; 691c988d1e8SJiri Pirko } else if (time_before(ifa->ifa_tstamp + 692c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ, 693c988d1e8SJiri Pirko next)) { 694c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 695c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ; 696c988d1e8SJiri Pirko } 697c988d1e8SJiri Pirko } 698c988d1e8SJiri Pirko rcu_read_unlock(); 699c988d1e8SJiri Pirko if (!change_needed) 700c988d1e8SJiri Pirko continue; 701c988d1e8SJiri Pirko rtnl_lock(); 702c988d1e8SJiri Pirko hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 703c988d1e8SJiri Pirko unsigned long age; 704c988d1e8SJiri Pirko 705c988d1e8SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 706c988d1e8SJiri Pirko continue; 707c988d1e8SJiri Pirko 708c988d1e8SJiri Pirko /* We try to batch several events at once. */ 709c988d1e8SJiri Pirko age = (now - ifa->ifa_tstamp + 710c988d1e8SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 711c988d1e8SJiri Pirko 712c988d1e8SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 713c988d1e8SJiri Pirko age >= ifa->ifa_valid_lft) { 7145c766d64SJiri Pirko struct in_ifaddr **ifap; 7155c766d64SJiri Pirko 7165c766d64SJiri Pirko for (ifap = &ifa->ifa_dev->ifa_list; 717c988d1e8SJiri Pirko *ifap != NULL; ifap = &(*ifap)->ifa_next) { 718c988d1e8SJiri Pirko if (*ifap == ifa) { 7195c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 7205c766d64SJiri Pirko ifap, 1); 721c988d1e8SJiri Pirko break; 7225c766d64SJiri Pirko } 723c988d1e8SJiri Pirko } 724c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft != 725c988d1e8SJiri Pirko INFINITY_LIFE_TIME && 726c988d1e8SJiri Pirko age >= ifa->ifa_preferred_lft && 727c988d1e8SJiri Pirko !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 7285c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7295c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 7305c766d64SJiri Pirko } 7315c766d64SJiri Pirko } 732c988d1e8SJiri Pirko rtnl_unlock(); 7335c766d64SJiri Pirko } 7345c766d64SJiri Pirko 7355c766d64SJiri Pirko next_sec = round_jiffies_up(next); 7365c766d64SJiri Pirko next_sched = next; 7375c766d64SJiri Pirko 7385c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 7395c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 7405c766d64SJiri Pirko next_sched = next_sec; 7415c766d64SJiri Pirko 7425c766d64SJiri Pirko now = jiffies; 7435c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 7445c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 7455c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 7465c766d64SJiri Pirko 747906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 748906e073fSviresh kumar next_sched - now); 7495c766d64SJiri Pirko } 7505c766d64SJiri Pirko 7515c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 7525c766d64SJiri Pirko __u32 prefered_lft) 7535c766d64SJiri Pirko { 7545c766d64SJiri Pirko unsigned long timeout; 7555c766d64SJiri Pirko 7565c766d64SJiri Pirko ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 7575c766d64SJiri Pirko 7585c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 7595c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 7605c766d64SJiri Pirko ifa->ifa_valid_lft = timeout; 7615c766d64SJiri Pirko else 7625c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_PERMANENT; 7635c766d64SJiri Pirko 7645c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 7655c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 7665c766d64SJiri Pirko if (timeout == 0) 7675c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7685c766d64SJiri Pirko ifa->ifa_preferred_lft = timeout; 7695c766d64SJiri Pirko } 7705c766d64SJiri Pirko ifa->ifa_tstamp = jiffies; 7715c766d64SJiri Pirko if (!ifa->ifa_cstamp) 7725c766d64SJiri Pirko ifa->ifa_cstamp = ifa->ifa_tstamp; 7735c766d64SJiri Pirko } 7745c766d64SJiri Pirko 7755c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 7765c766d64SJiri Pirko __u32 *pvalid_lft, __u32 *pprefered_lft) 7771da177e4SLinus Torvalds { 7785c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 7795c753978SThomas Graf struct in_ifaddr *ifa; 7805c753978SThomas Graf struct ifaddrmsg *ifm; 7811da177e4SLinus Torvalds struct net_device *dev; 7821da177e4SLinus Torvalds struct in_device *in_dev; 7837b218574SDenis V. Lunev int err; 7841da177e4SLinus Torvalds 785fceb6435SJohannes Berg err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy, 786fceb6435SJohannes Berg NULL); 7875c753978SThomas Graf if (err < 0) 7885c753978SThomas Graf goto errout; 7891da177e4SLinus Torvalds 7905c753978SThomas Graf ifm = nlmsg_data(nlh); 791c4e38f41SEvgeniy Polyakov err = -EINVAL; 79251456b29SIan Morris if (ifm->ifa_prefixlen > 32 || !tb[IFA_LOCAL]) 7935c753978SThomas Graf goto errout; 7941da177e4SLinus Torvalds 7954b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 7965c753978SThomas Graf err = -ENODEV; 79751456b29SIan Morris if (!dev) 7985c753978SThomas Graf goto errout; 7991da177e4SLinus Torvalds 8005c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 8015c753978SThomas Graf err = -ENOBUFS; 80251456b29SIan Morris if (!in_dev) 8035c753978SThomas Graf goto errout; 80471e27da9SHerbert Xu 8055c753978SThomas Graf ifa = inet_alloc_ifa(); 80651456b29SIan Morris if (!ifa) 8075c753978SThomas Graf /* 8085c753978SThomas Graf * A potential indev allocation can be left alive, it stays 8095c753978SThomas Graf * assigned to its device and is destroy with it. 8105c753978SThomas Graf */ 8115c753978SThomas Graf goto errout; 8125c753978SThomas Graf 813a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 8141d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 8155c753978SThomas Graf in_dev_hold(in_dev); 8165c753978SThomas Graf 81751456b29SIan Morris if (!tb[IFA_ADDRESS]) 8185c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 8195c753978SThomas Graf 820fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 8211da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 8221da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 823ad6c8135SJiri Pirko ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : 824ad6c8135SJiri Pirko ifm->ifa_flags; 8251da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 8261da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 8275c753978SThomas Graf 82867b61f6cSJiri Benc ifa->ifa_local = nla_get_in_addr(tb[IFA_LOCAL]); 82967b61f6cSJiri Benc ifa->ifa_address = nla_get_in_addr(tb[IFA_ADDRESS]); 8305c753978SThomas Graf 8315c753978SThomas Graf if (tb[IFA_BROADCAST]) 83267b61f6cSJiri Benc ifa->ifa_broadcast = nla_get_in_addr(tb[IFA_BROADCAST]); 8335c753978SThomas Graf 8345c753978SThomas Graf if (tb[IFA_LABEL]) 8355c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 8361da177e4SLinus Torvalds else 8371da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 8381da177e4SLinus Torvalds 839*af4d768aSDavid Ahern if (tb[IFA_RT_PRIORITY]) 840*af4d768aSDavid Ahern ifa->ifa_rt_priority = nla_get_u32(tb[IFA_RT_PRIORITY]); 841*af4d768aSDavid Ahern 8425c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 8435c766d64SJiri Pirko struct ifa_cacheinfo *ci; 8445c766d64SJiri Pirko 8455c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 8465c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 8475c766d64SJiri Pirko err = -EINVAL; 848446266b0SDaniel Borkmann goto errout_free; 8495c766d64SJiri Pirko } 8505c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 8515c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 8525c766d64SJiri Pirko } 8535c766d64SJiri Pirko 8545c753978SThomas Graf return ifa; 8555c753978SThomas Graf 856446266b0SDaniel Borkmann errout_free: 857446266b0SDaniel Borkmann inet_free_ifa(ifa); 8585c753978SThomas Graf errout: 8595c753978SThomas Graf return ERR_PTR(err); 8605c753978SThomas Graf } 8615c753978SThomas Graf 8625c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 8635c766d64SJiri Pirko { 8645c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 8655c766d64SJiri Pirko struct in_ifaddr *ifa1, **ifap; 8665c766d64SJiri Pirko 8675c766d64SJiri Pirko if (!ifa->ifa_local) 8685c766d64SJiri Pirko return NULL; 8695c766d64SJiri Pirko 8705c766d64SJiri Pirko for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 8715c766d64SJiri Pirko ifap = &ifa1->ifa_next) { 8725c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 8735c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 8745c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 8755c766d64SJiri Pirko return ifa1; 8765c766d64SJiri Pirko } 8775c766d64SJiri Pirko return NULL; 8785c766d64SJiri Pirko } 8795c766d64SJiri Pirko 880c21ef3e3SDavid Ahern static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, 881c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 8825c753978SThomas Graf { 8833b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 8845c753978SThomas Graf struct in_ifaddr *ifa; 8855c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 8865c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 8875c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 8885c753978SThomas Graf 8895c753978SThomas Graf ASSERT_RTNL(); 8905c753978SThomas Graf 8915c766d64SJiri Pirko ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft); 8925c753978SThomas Graf if (IS_ERR(ifa)) 8935c753978SThomas Graf return PTR_ERR(ifa); 8945c753978SThomas Graf 8955c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 8965c766d64SJiri Pirko if (!ifa_existing) { 8975c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 898614d056cSstephen hemminger * userspace already relies on not having to provide this. 8995c766d64SJiri Pirko */ 9005c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 90193a714d6SMadhu Challa if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) { 90293a714d6SMadhu Challa int ret = ip_mc_config(net->ipv4.mc_autojoin_sk, 90393a714d6SMadhu Challa true, ifa); 90493a714d6SMadhu Challa 90593a714d6SMadhu Challa if (ret < 0) { 90693a714d6SMadhu Challa inet_free_ifa(ifa); 90793a714d6SMadhu Challa return ret; 90893a714d6SMadhu Challa } 90993a714d6SMadhu Challa } 910de95e047SDavid Ahern return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid, 911de95e047SDavid Ahern extack); 9125c766d64SJiri Pirko } else { 913*af4d768aSDavid Ahern u32 new_metric = ifa->ifa_rt_priority; 914*af4d768aSDavid Ahern 9155c766d64SJiri Pirko inet_free_ifa(ifa); 9165c766d64SJiri Pirko 9175c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 9185c766d64SJiri Pirko !(nlh->nlmsg_flags & NLM_F_REPLACE)) 9195c766d64SJiri Pirko return -EEXIST; 92034e2ed34SJiri Pirko ifa = ifa_existing; 921*af4d768aSDavid Ahern 922*af4d768aSDavid Ahern if (ifa->ifa_rt_priority != new_metric) { 923*af4d768aSDavid Ahern fib_modify_prefix_metric(ifa, new_metric); 924*af4d768aSDavid Ahern ifa->ifa_rt_priority = new_metric; 925*af4d768aSDavid Ahern } 926*af4d768aSDavid Ahern 92734e2ed34SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 92805a324b9SJiri Pirko cancel_delayed_work(&check_lifetime_work); 929906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, 930906e073fSviresh kumar &check_lifetime_work, 0); 93134e2ed34SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 9325c766d64SJiri Pirko } 9335c766d64SJiri Pirko return 0; 9341da177e4SLinus Torvalds } 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds /* 9371da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 9381da177e4SLinus Torvalds */ 9391da177e4SLinus Torvalds 94040384999SEric Dumazet static int inet_abc_len(__be32 addr) 9411da177e4SLinus Torvalds { 9421da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 9431da177e4SLinus Torvalds 944f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 9451da177e4SLinus Torvalds rc = 0; 9461da177e4SLinus Torvalds else { 947714e85beSAl Viro __u32 haddr = ntohl(addr); 9481da177e4SLinus Torvalds 949714e85beSAl Viro if (IN_CLASSA(haddr)) 9501da177e4SLinus Torvalds rc = 8; 951714e85beSAl Viro else if (IN_CLASSB(haddr)) 9521da177e4SLinus Torvalds rc = 16; 953714e85beSAl Viro else if (IN_CLASSC(haddr)) 9541da177e4SLinus Torvalds rc = 24; 9551da177e4SLinus Torvalds } 9561da177e4SLinus Torvalds 9571da177e4SLinus Torvalds return rc; 9581da177e4SLinus Torvalds } 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds 96103aef17bSAl Viro int devinet_ioctl(struct net *net, unsigned int cmd, struct ifreq *ifr) 9621da177e4SLinus Torvalds { 9631da177e4SLinus Torvalds struct sockaddr_in sin_orig; 96403aef17bSAl Viro struct sockaddr_in *sin = (struct sockaddr_in *)&ifr->ifr_addr; 9651da177e4SLinus Torvalds struct in_device *in_dev; 9661da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 9671da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 9681da177e4SLinus Torvalds struct net_device *dev; 9691da177e4SLinus Torvalds char *colon; 9701da177e4SLinus Torvalds int ret = -EFAULT; 9711da177e4SLinus Torvalds int tryaddrmatch = 0; 9721da177e4SLinus Torvalds 97303aef17bSAl Viro ifr->ifr_name[IFNAMSIZ - 1] = 0; 9741da177e4SLinus Torvalds 9751da177e4SLinus Torvalds /* save original address for comparison */ 9761da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 9771da177e4SLinus Torvalds 97803aef17bSAl Viro colon = strchr(ifr->ifr_name, ':'); 9791da177e4SLinus Torvalds if (colon) 9801da177e4SLinus Torvalds *colon = 0; 9811da177e4SLinus Torvalds 98203aef17bSAl Viro dev_load(net, ifr->ifr_name); 9831da177e4SLinus Torvalds 9841da177e4SLinus Torvalds switch (cmd) { 9851da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9861da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9871da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9881da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 9891da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 9901da177e4SLinus Torvalds so that we do not impose a lock. 9911da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 9921da177e4SLinus Torvalds */ 9931da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 9941da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 9951da177e4SLinus Torvalds sin->sin_family = AF_INET; 9961da177e4SLinus Torvalds break; 9971da177e4SLinus Torvalds 9981da177e4SLinus Torvalds case SIOCSIFFLAGS: 999bf5b30b8SZhao Hongjiang ret = -EPERM; 100052e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10011da177e4SLinus Torvalds goto out; 10021da177e4SLinus Torvalds break; 10031da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10041da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10051da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 10061da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 1007bf5b30b8SZhao Hongjiang ret = -EPERM; 100852e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 10091da177e4SLinus Torvalds goto out; 10101da177e4SLinus Torvalds ret = -EINVAL; 10111da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 10121da177e4SLinus Torvalds goto out; 10131da177e4SLinus Torvalds break; 10141da177e4SLinus Torvalds default: 10151da177e4SLinus Torvalds ret = -EINVAL; 10161da177e4SLinus Torvalds goto out; 10171da177e4SLinus Torvalds } 10181da177e4SLinus Torvalds 10191da177e4SLinus Torvalds rtnl_lock(); 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds ret = -ENODEV; 102203aef17bSAl Viro dev = __dev_get_by_name(net, ifr->ifr_name); 10239f9354b9SEric Dumazet if (!dev) 10241da177e4SLinus Torvalds goto done; 10251da177e4SLinus Torvalds 10261da177e4SLinus Torvalds if (colon) 10271da177e4SLinus Torvalds *colon = ':'; 10281da177e4SLinus Torvalds 10299f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 10309f9354b9SEric Dumazet if (in_dev) { 10311da177e4SLinus Torvalds if (tryaddrmatch) { 10321da177e4SLinus Torvalds /* Matthias Andree */ 10331da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 10341da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 10351da177e4SLinus Torvalds and only if the original address family was AF_INET. 10361da177e4SLinus Torvalds This is checked above. */ 10371da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 10381da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 103903aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label) && 10401da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 10416c91afe1SDavid S. Miller ifa->ifa_local) { 10421da177e4SLinus Torvalds break; /* found */ 10431da177e4SLinus Torvalds } 10441da177e4SLinus Torvalds } 10451da177e4SLinus Torvalds } 10461da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 10471da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 10481da177e4SLinus Torvalds comparing just the label */ 10491da177e4SLinus Torvalds if (!ifa) { 10501da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 10511da177e4SLinus Torvalds ifap = &ifa->ifa_next) 105203aef17bSAl Viro if (!strcmp(ifr->ifr_name, ifa->ifa_label)) 10531da177e4SLinus Torvalds break; 10541da177e4SLinus Torvalds } 10551da177e4SLinus Torvalds } 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 10581da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 10591da177e4SLinus Torvalds goto done; 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds switch (cmd) { 10621da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 106330e948a3STonghao Zhang ret = 0; 10641da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 106503aef17bSAl Viro break; 10661da177e4SLinus Torvalds 10671da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 106830e948a3STonghao Zhang ret = 0; 10691da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 107003aef17bSAl Viro break; 10711da177e4SLinus Torvalds 10721da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 107330e948a3STonghao Zhang ret = 0; 10741da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 107503aef17bSAl Viro break; 10761da177e4SLinus Torvalds 10771da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 107830e948a3STonghao Zhang ret = 0; 10791da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 108003aef17bSAl Viro break; 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds case SIOCSIFFLAGS: 10831da177e4SLinus Torvalds if (colon) { 10841da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 10851da177e4SLinus Torvalds if (!ifa) 10861da177e4SLinus Torvalds break; 10871da177e4SLinus Torvalds ret = 0; 108803aef17bSAl Viro if (!(ifr->ifr_flags & IFF_UP)) 10891da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 10901da177e4SLinus Torvalds break; 10911da177e4SLinus Torvalds } 109203aef17bSAl Viro ret = dev_change_flags(dev, ifr->ifr_flags); 10931da177e4SLinus Torvalds break; 10941da177e4SLinus Torvalds 10951da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10961da177e4SLinus Torvalds ret = -EINVAL; 10971da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 10981da177e4SLinus Torvalds break; 10991da177e4SLinus Torvalds 11001da177e4SLinus Torvalds if (!ifa) { 11011da177e4SLinus Torvalds ret = -ENOBUFS; 11029f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 11039f9354b9SEric Dumazet if (!ifa) 11041da177e4SLinus Torvalds break; 1105c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 11061da177e4SLinus Torvalds if (colon) 110703aef17bSAl Viro memcpy(ifa->ifa_label, ifr->ifr_name, IFNAMSIZ); 11081da177e4SLinus Torvalds else 11091da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11101da177e4SLinus Torvalds } else { 11111da177e4SLinus Torvalds ret = 0; 11121da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 11131da177e4SLinus Torvalds break; 11141da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11151da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 1116148f9729SBjorn Mork ifa->ifa_scope = 0; 11171da177e4SLinus Torvalds } 11181da177e4SLinus Torvalds 11191da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 11201da177e4SLinus Torvalds 11211da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 11221da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 11231da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 11241da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 11251da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 11261da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 11271da177e4SLinus Torvalds ~ifa->ifa_mask; 11281da177e4SLinus Torvalds } else { 11291da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 11301da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 11311da177e4SLinus Torvalds } 11325c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 11331da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 11341da177e4SLinus Torvalds break; 11351da177e4SLinus Torvalds 11361da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 11371da177e4SLinus Torvalds ret = 0; 11381da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 11391da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11401da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 11411da177e4SLinus Torvalds inet_insert_ifa(ifa); 11421da177e4SLinus Torvalds } 11431da177e4SLinus Torvalds break; 11441da177e4SLinus Torvalds 11451da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 11461da177e4SLinus Torvalds ret = 0; 11471da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 11481da177e4SLinus Torvalds break; 11491da177e4SLinus Torvalds ret = -EINVAL; 11501da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 11511da177e4SLinus Torvalds break; 11521da177e4SLinus Torvalds ret = 0; 11531da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11541da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 11551da177e4SLinus Torvalds inet_insert_ifa(ifa); 11561da177e4SLinus Torvalds break; 11571da177e4SLinus Torvalds 11581da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 11591da177e4SLinus Torvalds 11601da177e4SLinus Torvalds /* 11611da177e4SLinus Torvalds * The mask we set must be legal. 11621da177e4SLinus Torvalds */ 11631da177e4SLinus Torvalds ret = -EINVAL; 11641da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 11651da177e4SLinus Torvalds break; 11661da177e4SLinus Torvalds ret = 0; 11671da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1168a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 11691da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11701da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 11711da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 11721da177e4SLinus Torvalds 11731da177e4SLinus Torvalds /* See if current broadcast address matches 11741da177e4SLinus Torvalds * with current netmask, then recalculate 11751da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 11761da177e4SLinus Torvalds * funny address, so don't touch it since 11771da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 11781da177e4SLinus Torvalds */ 11791da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 11801da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 11811da177e4SLinus Torvalds (ifa->ifa_broadcast == 1182dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 11831da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 11841da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 11851da177e4SLinus Torvalds } 11861da177e4SLinus Torvalds inet_insert_ifa(ifa); 11871da177e4SLinus Torvalds } 11881da177e4SLinus Torvalds break; 11891da177e4SLinus Torvalds } 11901da177e4SLinus Torvalds done: 11911da177e4SLinus Torvalds rtnl_unlock(); 11921da177e4SLinus Torvalds out: 11931da177e4SLinus Torvalds return ret; 11941da177e4SLinus Torvalds } 11951da177e4SLinus Torvalds 119636fd633eSAl Viro static int inet_gifconf(struct net_device *dev, char __user *buf, int len, int size) 11971da177e4SLinus Torvalds { 1198e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 11991da177e4SLinus Torvalds struct in_ifaddr *ifa; 12001da177e4SLinus Torvalds struct ifreq ifr; 12011da177e4SLinus Torvalds int done = 0; 12021da177e4SLinus Torvalds 120336fd633eSAl Viro if (WARN_ON(size > sizeof(struct ifreq))) 120436fd633eSAl Viro goto out; 120536fd633eSAl Viro 12069f9354b9SEric Dumazet if (!in_dev) 12071da177e4SLinus Torvalds goto out; 12081da177e4SLinus Torvalds 12099f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 12101da177e4SLinus Torvalds if (!buf) { 121136fd633eSAl Viro done += size; 12121da177e4SLinus Torvalds continue; 12131da177e4SLinus Torvalds } 121436fd633eSAl Viro if (len < size) 12151da177e4SLinus Torvalds break; 12161da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 12171da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 12181da177e4SLinus Torvalds 12191da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 12201da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 12211da177e4SLinus Torvalds ifa->ifa_local; 12221da177e4SLinus Torvalds 122336fd633eSAl Viro if (copy_to_user(buf + done, &ifr, size)) { 12241da177e4SLinus Torvalds done = -EFAULT; 12251da177e4SLinus Torvalds break; 12261da177e4SLinus Torvalds } 122736fd633eSAl Viro len -= size; 122836fd633eSAl Viro done += size; 12291da177e4SLinus Torvalds } 12301da177e4SLinus Torvalds out: 12311da177e4SLinus Torvalds return done; 12321da177e4SLinus Torvalds } 12331da177e4SLinus Torvalds 12348b57fd1eSGao Feng static __be32 in_dev_select_addr(const struct in_device *in_dev, 12358b57fd1eSGao Feng int scope) 12368b57fd1eSGao Feng { 12378b57fd1eSGao Feng for_primary_ifa(in_dev) { 12388b57fd1eSGao Feng if (ifa->ifa_scope != RT_SCOPE_LINK && 12398b57fd1eSGao Feng ifa->ifa_scope <= scope) 12408b57fd1eSGao Feng return ifa->ifa_local; 12418b57fd1eSGao Feng } endfor_ifa(in_dev); 12428b57fd1eSGao Feng 12438b57fd1eSGao Feng return 0; 12448b57fd1eSGao Feng } 12458b57fd1eSGao Feng 1246a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 12471da177e4SLinus Torvalds { 1248a61ced5dSAl Viro __be32 addr = 0; 12491da177e4SLinus Torvalds struct in_device *in_dev; 1250c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 12513f2fb9a8SDavid Ahern int master_idx; 12521da177e4SLinus Torvalds 12531da177e4SLinus Torvalds rcu_read_lock(); 1254e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 12551da177e4SLinus Torvalds if (!in_dev) 12561da177e4SLinus Torvalds goto no_in_dev; 12571da177e4SLinus Torvalds 12581da177e4SLinus Torvalds for_primary_ifa(in_dev) { 12591da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 12601da177e4SLinus Torvalds continue; 12611da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 12621da177e4SLinus Torvalds addr = ifa->ifa_local; 12631da177e4SLinus Torvalds break; 12641da177e4SLinus Torvalds } 12651da177e4SLinus Torvalds if (!addr) 12661da177e4SLinus Torvalds addr = ifa->ifa_local; 12671da177e4SLinus Torvalds } endfor_ifa(in_dev); 12681da177e4SLinus Torvalds 12691da177e4SLinus Torvalds if (addr) 1270c6d14c84SEric Dumazet goto out_unlock; 12719f9354b9SEric Dumazet no_in_dev: 12723f2fb9a8SDavid Ahern master_idx = l3mdev_master_ifindex_rcu(dev); 12731da177e4SLinus Torvalds 127417b693cdSDavid Lamparter /* For VRFs, the VRF device takes the place of the loopback device, 127517b693cdSDavid Lamparter * with addresses on it being preferred. Note in such cases the 127617b693cdSDavid Lamparter * loopback device will be among the devices that fail the master_idx 127717b693cdSDavid Lamparter * equality check in the loop below. 127817b693cdSDavid Lamparter */ 127917b693cdSDavid Lamparter if (master_idx && 128017b693cdSDavid Lamparter (dev = dev_get_by_index_rcu(net, master_idx)) && 128117b693cdSDavid Lamparter (in_dev = __in_dev_get_rcu(dev))) { 12828b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 12838b57fd1eSGao Feng if (addr) 128417b693cdSDavid Lamparter goto out_unlock; 128517b693cdSDavid Lamparter } 128617b693cdSDavid Lamparter 12871da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 1288ca9f1fd2SStephen Hemminger in this case. It is important that lo is the first interface 12891da177e4SLinus Torvalds in dev_base list. 12901da177e4SLinus Torvalds */ 1291c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 12923f2fb9a8SDavid Ahern if (l3mdev_master_ifindex_rcu(dev) != master_idx) 12933f2fb9a8SDavid Ahern continue; 12943f2fb9a8SDavid Ahern 12959f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12969f9354b9SEric Dumazet if (!in_dev) 12971da177e4SLinus Torvalds continue; 12981da177e4SLinus Torvalds 12998b57fd1eSGao Feng addr = in_dev_select_addr(in_dev, scope); 13008b57fd1eSGao Feng if (addr) 1301c6d14c84SEric Dumazet goto out_unlock; 13021da177e4SLinus Torvalds } 1303c6d14c84SEric Dumazet out_unlock: 13041da177e4SLinus Torvalds rcu_read_unlock(); 13051da177e4SLinus Torvalds return addr; 13061da177e4SLinus Torvalds } 13079f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 13081da177e4SLinus Torvalds 130960cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 131060cad5daSAl Viro __be32 local, int scope) 13111da177e4SLinus Torvalds { 13121da177e4SLinus Torvalds int same = 0; 1313a144ea4bSAl Viro __be32 addr = 0; 13141da177e4SLinus Torvalds 13151da177e4SLinus Torvalds for_ifa(in_dev) { 13161da177e4SLinus Torvalds if (!addr && 13171da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 13181da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 13191da177e4SLinus Torvalds addr = ifa->ifa_local; 13201da177e4SLinus Torvalds if (same) 13211da177e4SLinus Torvalds break; 13221da177e4SLinus Torvalds } 13231da177e4SLinus Torvalds if (!same) { 13241da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 13251da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 13261da177e4SLinus Torvalds if (same && addr) { 13271da177e4SLinus Torvalds if (local || !dst) 13281da177e4SLinus Torvalds break; 13291da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 13301da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 13311da177e4SLinus Torvalds break; 13321da177e4SLinus Torvalds /* No, then can we use new local src? */ 13331da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 13341da177e4SLinus Torvalds addr = ifa->ifa_local; 13351da177e4SLinus Torvalds break; 13361da177e4SLinus Torvalds } 13371da177e4SLinus Torvalds /* search for large dst subnet for addr */ 13381da177e4SLinus Torvalds same = 0; 13391da177e4SLinus Torvalds } 13401da177e4SLinus Torvalds } 13411da177e4SLinus Torvalds } endfor_ifa(in_dev); 13421da177e4SLinus Torvalds 13431da177e4SLinus Torvalds return same ? addr : 0; 13441da177e4SLinus Torvalds } 13451da177e4SLinus Torvalds 13461da177e4SLinus Torvalds /* 13471da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 1348b601fa19SNicolas Dichtel * - net: netns to check, cannot be NULL 1349b601fa19SNicolas Dichtel * - in_dev: only on this interface, NULL=any interface 13501da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 13511da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 13521da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 13531da177e4SLinus Torvalds */ 1354b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, 13559bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 13561da177e4SLinus Torvalds { 135760cad5daSAl Viro __be32 addr = 0; 13589bd85e32SDenis V. Lunev struct net_device *dev; 13591da177e4SLinus Torvalds 136000db4124SIan Morris if (in_dev) 13619bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 13621da177e4SLinus Torvalds 13631da177e4SLinus Torvalds rcu_read_lock(); 1364c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 13659f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 13669f9354b9SEric Dumazet if (in_dev) { 13671da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 13681da177e4SLinus Torvalds if (addr) 13691da177e4SLinus Torvalds break; 13701da177e4SLinus Torvalds } 13711da177e4SLinus Torvalds } 13721da177e4SLinus Torvalds rcu_read_unlock(); 13731da177e4SLinus Torvalds 13741da177e4SLinus Torvalds return addr; 13751da177e4SLinus Torvalds } 1376eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 13771da177e4SLinus Torvalds 13781da177e4SLinus Torvalds /* 13791da177e4SLinus Torvalds * Device notifier 13801da177e4SLinus Torvalds */ 13811da177e4SLinus Torvalds 13821da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 13831da177e4SLinus Torvalds { 1384e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 13851da177e4SLinus Torvalds } 13869f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 13871da177e4SLinus Torvalds 13881da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 13891da177e4SLinus Torvalds { 1390e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 13911da177e4SLinus Torvalds } 13929f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 13931da177e4SLinus Torvalds 13943ad7d246SKrister Johansen int register_inetaddr_validator_notifier(struct notifier_block *nb) 13953ad7d246SKrister Johansen { 13963ad7d246SKrister Johansen return blocking_notifier_chain_register(&inetaddr_validator_chain, nb); 13973ad7d246SKrister Johansen } 13983ad7d246SKrister Johansen EXPORT_SYMBOL(register_inetaddr_validator_notifier); 13993ad7d246SKrister Johansen 14003ad7d246SKrister Johansen int unregister_inetaddr_validator_notifier(struct notifier_block *nb) 14013ad7d246SKrister Johansen { 14023ad7d246SKrister Johansen return blocking_notifier_chain_unregister(&inetaddr_validator_chain, 14033ad7d246SKrister Johansen nb); 14043ad7d246SKrister Johansen } 14053ad7d246SKrister Johansen EXPORT_SYMBOL(unregister_inetaddr_validator_notifier); 14063ad7d246SKrister Johansen 14079f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 14089f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 14091da177e4SLinus Torvalds */ 14101da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 14111da177e4SLinus Torvalds { 14121da177e4SLinus Torvalds struct in_ifaddr *ifa; 14131da177e4SLinus Torvalds int named = 0; 14141da177e4SLinus Torvalds 14151da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 14161da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 14171da177e4SLinus Torvalds 14181da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 14191da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 14201da177e4SLinus Torvalds if (named++ == 0) 1421573bf470SThomas Graf goto skip; 142244344b2aSMark McLoughlin dot = strchr(old, ':'); 142351456b29SIan Morris if (!dot) { 14241da177e4SLinus Torvalds sprintf(old, ":%d", named); 14251da177e4SLinus Torvalds dot = old; 14261da177e4SLinus Torvalds } 14279f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 14281da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 14299f9354b9SEric Dumazet else 14301da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1431573bf470SThomas Graf skip: 1432573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 14331da177e4SLinus Torvalds } 14341da177e4SLinus Torvalds } 14351da177e4SLinus Torvalds 143640384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu) 143706770843SBreno Leitao { 1438b5476022SEric Dumazet return mtu >= IPV4_MIN_MTU; 143906770843SBreno Leitao } 144006770843SBreno Leitao 1441d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1442d11327adSIan Campbell struct in_device *in_dev) 1443d11327adSIan Campbell 1444d11327adSIan Campbell { 1445b76d0789SZoltan Kiss struct in_ifaddr *ifa; 1446d11327adSIan Campbell 1447b76d0789SZoltan Kiss for (ifa = in_dev->ifa_list; ifa; 1448b76d0789SZoltan Kiss ifa = ifa->ifa_next) { 1449d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 14506c91afe1SDavid S. Miller ifa->ifa_local, dev, 14516c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1452d11327adSIan Campbell dev->dev_addr, NULL); 1453d11327adSIan Campbell } 1454b76d0789SZoltan Kiss } 1455d11327adSIan Campbell 14561da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 14571da177e4SLinus Torvalds 14581da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 14591da177e4SLinus Torvalds void *ptr) 14601da177e4SLinus Torvalds { 1461351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1462748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 14631da177e4SLinus Torvalds 14641da177e4SLinus Torvalds ASSERT_RTNL(); 14651da177e4SLinus Torvalds 14661da177e4SLinus Torvalds if (!in_dev) { 14678030f544SHerbert Xu if (event == NETDEV_REGISTER) { 14681da177e4SLinus Torvalds in_dev = inetdev_init(dev); 146920e61da7SWANG Cong if (IS_ERR(in_dev)) 147020e61da7SWANG Cong return notifier_from_errno(PTR_ERR(in_dev)); 14710cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 147242f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 147342f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 14741da177e4SLinus Torvalds } 147506770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 147606770843SBreno Leitao /* Re-enabling IP */ 147706770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 147806770843SBreno Leitao in_dev = inetdev_init(dev); 14798030f544SHerbert Xu } 14801da177e4SLinus Torvalds goto out; 14811da177e4SLinus Torvalds } 14821da177e4SLinus Torvalds 14831da177e4SLinus Torvalds switch (event) { 14841da177e4SLinus Torvalds case NETDEV_REGISTER: 148591df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1486a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 14871da177e4SLinus Torvalds break; 14881da177e4SLinus Torvalds case NETDEV_UP: 148906770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 14901da177e4SLinus Torvalds break; 14910cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 14929f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 14939f9354b9SEric Dumazet 14949f9354b9SEric Dumazet if (ifa) { 1495fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 14961da177e4SLinus Torvalds ifa->ifa_local = 14971da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 14981da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 14991da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 15001da177e4SLinus Torvalds in_dev_hold(in_dev); 15011da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 15021da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 15031da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 15045c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 15055c766d64SJiri Pirko INFINITY_LIFE_TIME); 1506dfd1582dSJiri Pirko ipv4_devconf_setall(in_dev); 1507dfd1582dSJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 15081da177e4SLinus Torvalds inet_insert_ifa(ifa); 15091da177e4SLinus Torvalds } 15101da177e4SLinus Torvalds } 15111da177e4SLinus Torvalds ip_mc_up(in_dev); 1512eefef1cfSStephen Hemminger /* fall through */ 1513eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1514d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1515d11327adSIan Campbell break; 1516d11327adSIan Campbell /* fall through */ 1517d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1518a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1519d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 15201da177e4SLinus Torvalds break; 15211da177e4SLinus Torvalds case NETDEV_DOWN: 15221da177e4SLinus Torvalds ip_mc_down(in_dev); 15231da177e4SLinus Torvalds break; 152493d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 152575c78500SMoni Shoua ip_mc_unmap(in_dev); 152675c78500SMoni Shoua break; 152793d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 152875c78500SMoni Shoua ip_mc_remap(in_dev); 152975c78500SMoni Shoua break; 15301da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 153106770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 15321da177e4SLinus Torvalds break; 153306770843SBreno Leitao /* disable IP when MTU is not enough */ 1534fcfd6dfaSGustavo A. R. Silva /* fall through */ 15351da177e4SLinus Torvalds case NETDEV_UNREGISTER: 15361da177e4SLinus Torvalds inetdev_destroy(in_dev); 15371da177e4SLinus Torvalds break; 15381da177e4SLinus Torvalds case NETDEV_CHANGENAME: 15391da177e4SLinus Torvalds /* Do not notify about label change, this event is 15401da177e4SLinus Torvalds * not interesting to applications using netlink. 15411da177e4SLinus Torvalds */ 15421da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 15431da177e4SLinus Torvalds 154451602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 154566f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 15461da177e4SLinus Torvalds break; 15471da177e4SLinus Torvalds } 15481da177e4SLinus Torvalds out: 15491da177e4SLinus Torvalds return NOTIFY_DONE; 15501da177e4SLinus Torvalds } 15511da177e4SLinus Torvalds 15521da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 15531da177e4SLinus Torvalds .notifier_call = inetdev_event, 15541da177e4SLinus Torvalds }; 15551da177e4SLinus Torvalds 155640384999SEric Dumazet static size_t inet_nlmsg_size(void) 1557339bf98fSThomas Graf { 1558339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1559339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1560339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1561339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1562ad6c8135SJiri Pirko + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ 156363b5f152SGeert Uytterhoeven + nla_total_size(4) /* IFA_FLAGS */ 1564*af4d768aSDavid Ahern + nla_total_size(4) /* IFA_RT_PRIORITY */ 156563b5f152SGeert Uytterhoeven + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ 1566339bf98fSThomas Graf } 1567339bf98fSThomas Graf 15685c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 15695c766d64SJiri Pirko { 15705c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 15715c766d64SJiri Pirko } 15725c766d64SJiri Pirko 15735c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 15745c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 15755c766d64SJiri Pirko { 15765c766d64SJiri Pirko struct ifa_cacheinfo ci; 15775c766d64SJiri Pirko 15785c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 15795c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 15805c766d64SJiri Pirko ci.ifa_prefered = preferred; 15815c766d64SJiri Pirko ci.ifa_valid = valid; 15825c766d64SJiri Pirko 15835c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 15845c766d64SJiri Pirko } 15855c766d64SJiri Pirko 15861da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 158715e47304SEric W. Biederman u32 portid, u32 seq, int event, unsigned int flags) 15881da177e4SLinus Torvalds { 15891da177e4SLinus Torvalds struct ifaddrmsg *ifm; 15901da177e4SLinus Torvalds struct nlmsghdr *nlh; 15915c766d64SJiri Pirko u32 preferred, valid; 15921da177e4SLinus Torvalds 159315e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); 159451456b29SIan Morris if (!nlh) 159526932566SPatrick McHardy return -EMSGSIZE; 159647f68512SThomas Graf 159747f68512SThomas Graf ifm = nlmsg_data(nlh); 15981da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 15991da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 16005c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 16011da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 16021da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 16031da177e4SLinus Torvalds 16045c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 16055c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 16065c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 16075c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 16085c766d64SJiri Pirko long tval = (jiffies - ifa->ifa_tstamp) / HZ; 16095c766d64SJiri Pirko 16105c766d64SJiri Pirko if (preferred > tval) 16115c766d64SJiri Pirko preferred -= tval; 16125c766d64SJiri Pirko else 16135c766d64SJiri Pirko preferred = 0; 16145c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 16155c766d64SJiri Pirko if (valid > tval) 16165c766d64SJiri Pirko valid -= tval; 16175c766d64SJiri Pirko else 16185c766d64SJiri Pirko valid = 0; 16195c766d64SJiri Pirko } 16205c766d64SJiri Pirko } 16215c766d64SJiri Pirko } else { 16225c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 16235c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 16245c766d64SJiri Pirko } 1625f3756b79SDavid S. Miller if ((ifa->ifa_address && 1626930345eaSJiri Benc nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) || 1627f3756b79SDavid S. Miller (ifa->ifa_local && 1628930345eaSJiri Benc nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) || 1629f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1630930345eaSJiri Benc nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1631f3756b79SDavid S. Miller (ifa->ifa_label[0] && 16325c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 1633ad6c8135SJiri Pirko nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || 1634*af4d768aSDavid Ahern (ifa->ifa_rt_priority && 1635*af4d768aSDavid Ahern nla_put_u32(skb, IFA_RT_PRIORITY, ifa->ifa_rt_priority)) || 16365c766d64SJiri Pirko put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 16375c766d64SJiri Pirko preferred, valid)) 1638f3756b79SDavid S. Miller goto nla_put_failure; 163947f68512SThomas Graf 1640053c095aSJohannes Berg nlmsg_end(skb, nlh); 1641053c095aSJohannes Berg return 0; 164247f68512SThomas Graf 164347f68512SThomas Graf nla_put_failure: 164426932566SPatrick McHardy nlmsg_cancel(skb, nlh); 164526932566SPatrick McHardy return -EMSGSIZE; 16461da177e4SLinus Torvalds } 16471da177e4SLinus Torvalds 16481da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 16491da177e4SLinus Torvalds { 16503b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1651eec4df98SEric Dumazet int h, s_h; 1652eec4df98SEric Dumazet int idx, s_idx; 1653eec4df98SEric Dumazet int ip_idx, s_ip_idx; 16541da177e4SLinus Torvalds struct net_device *dev; 16551da177e4SLinus Torvalds struct in_device *in_dev; 16561da177e4SLinus Torvalds struct in_ifaddr *ifa; 1657eec4df98SEric Dumazet struct hlist_head *head; 16581da177e4SLinus Torvalds 1659eec4df98SEric Dumazet s_h = cb->args[0]; 1660eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1661eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1662eec4df98SEric Dumazet 1663eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 16647562f876SPavel Emelianov idx = 0; 1665eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1666eec4df98SEric Dumazet rcu_read_lock(); 16670465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 16680465277fSNicolas Dichtel net->dev_base_seq; 1669b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 16701da177e4SLinus Torvalds if (idx < s_idx) 16717562f876SPavel Emelianov goto cont; 16724b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 16731da177e4SLinus Torvalds s_ip_idx = 0; 1674eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 16759f9354b9SEric Dumazet if (!in_dev) 16767562f876SPavel Emelianov goto cont; 16771da177e4SLinus Torvalds 16781da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 16791da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 16801da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1681596e4150SStephen Hemminger continue; 1682eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 168315e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 16841da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1685053c095aSJohannes Berg RTM_NEWADDR, NLM_F_MULTI) < 0) { 1686eec4df98SEric Dumazet rcu_read_unlock(); 16871da177e4SLinus Torvalds goto done; 16881da177e4SLinus Torvalds } 16890465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1690eec4df98SEric Dumazet } 16917562f876SPavel Emelianov cont: 16927562f876SPavel Emelianov idx++; 16931da177e4SLinus Torvalds } 1694eec4df98SEric Dumazet rcu_read_unlock(); 1695eec4df98SEric Dumazet } 16961da177e4SLinus Torvalds 16971da177e4SLinus Torvalds done: 1698eec4df98SEric Dumazet cb->args[0] = h; 1699eec4df98SEric Dumazet cb->args[1] = idx; 1700eec4df98SEric Dumazet cb->args[2] = ip_idx; 17011da177e4SLinus Torvalds 17021da177e4SLinus Torvalds return skb->len; 17031da177e4SLinus Torvalds } 17041da177e4SLinus Torvalds 1705d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 170615e47304SEric W. Biederman u32 portid) 17071da177e4SLinus Torvalds { 170847f68512SThomas Graf struct sk_buff *skb; 1709d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1710d6062cbbSThomas Graf int err = -ENOBUFS; 17114b8aa9abSDenis V. Lunev struct net *net; 17121da177e4SLinus Torvalds 1713c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1714339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 171551456b29SIan Morris if (!skb) 1716d6062cbbSThomas Graf goto errout; 1717d6062cbbSThomas Graf 171815e47304SEric W. Biederman err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0); 171926932566SPatrick McHardy if (err < 0) { 172026932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 172126932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 172226932566SPatrick McHardy kfree_skb(skb); 172326932566SPatrick McHardy goto errout; 172426932566SPatrick McHardy } 172515e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 17261ce85fe4SPablo Neira Ayuso return; 1727d6062cbbSThomas Graf errout: 1728d6062cbbSThomas Graf if (err < 0) 17294b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 17301da177e4SLinus Torvalds } 17311da177e4SLinus Torvalds 1732b1974ed0SArad, Ronen static size_t inet_get_link_af_size(const struct net_device *dev, 1733b1974ed0SArad, Ronen u32 ext_filter_mask) 17349f0f7272SThomas Graf { 17351fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 17369f0f7272SThomas Graf 17379f0f7272SThomas Graf if (!in_dev) 17389f0f7272SThomas Graf return 0; 17399f0f7272SThomas Graf 17409f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 17419f0f7272SThomas Graf } 17429f0f7272SThomas Graf 1743d5566fd7SSowmini Varadhan static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev, 1744d5566fd7SSowmini Varadhan u32 ext_filter_mask) 17459f0f7272SThomas Graf { 17461fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 17479f0f7272SThomas Graf struct nlattr *nla; 17489f0f7272SThomas Graf int i; 17499f0f7272SThomas Graf 17509f0f7272SThomas Graf if (!in_dev) 17519f0f7272SThomas Graf return -ENODATA; 17529f0f7272SThomas Graf 17539f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 175451456b29SIan Morris if (!nla) 17559f0f7272SThomas Graf return -EMSGSIZE; 17569f0f7272SThomas Graf 17579f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 17589f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 17599f0f7272SThomas Graf 17609f0f7272SThomas Graf return 0; 17619f0f7272SThomas Graf } 17629f0f7272SThomas Graf 17639f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 17649f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 17659f0f7272SThomas Graf }; 17669f0f7272SThomas Graf 1767cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1768cf7afbfeSThomas Graf const struct nlattr *nla) 17699f0f7272SThomas Graf { 17709f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 17719f0f7272SThomas Graf int err, rem; 17729f0f7272SThomas Graf 17735fa85a09SFlorian Westphal if (dev && !__in_dev_get_rcu(dev)) 1774cf7afbfeSThomas Graf return -EAFNOSUPPORT; 17759f0f7272SThomas Graf 1776fceb6435SJohannes Berg err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy, NULL); 17779f0f7272SThomas Graf if (err < 0) 17789f0f7272SThomas Graf return err; 17799f0f7272SThomas Graf 17809f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 17819f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 17829f0f7272SThomas Graf int cfgid = nla_type(a); 17839f0f7272SThomas Graf 17849f0f7272SThomas Graf if (nla_len(a) < 4) 17859f0f7272SThomas Graf return -EINVAL; 17869f0f7272SThomas Graf 17879f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 17889f0f7272SThomas Graf return -EINVAL; 17899f0f7272SThomas Graf } 17909f0f7272SThomas Graf } 17919f0f7272SThomas Graf 1792cf7afbfeSThomas Graf return 0; 1793cf7afbfeSThomas Graf } 1794cf7afbfeSThomas Graf 1795cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1796cf7afbfeSThomas Graf { 17975fa85a09SFlorian Westphal struct in_device *in_dev = __in_dev_get_rcu(dev); 1798cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1799cf7afbfeSThomas Graf int rem; 1800cf7afbfeSThomas Graf 1801cf7afbfeSThomas Graf if (!in_dev) 1802cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1803cf7afbfeSThomas Graf 1804fceb6435SJohannes Berg if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL, NULL) < 0) 1805cf7afbfeSThomas Graf BUG(); 1806cf7afbfeSThomas Graf 18079f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 18089f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 18099f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 18109f0f7272SThomas Graf } 18119f0f7272SThomas Graf 18129f0f7272SThomas Graf return 0; 18139f0f7272SThomas Graf } 18149f0f7272SThomas Graf 1815edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 1816edc9e748SNicolas Dichtel { 1817edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 1818edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 1819136ba622SZhang Shengju bool all = false; 1820edc9e748SNicolas Dichtel 1821136ba622SZhang Shengju if (type == NETCONFA_ALL) 1822136ba622SZhang Shengju all = true; 1823136ba622SZhang Shengju 1824136ba622SZhang Shengju if (all || type == NETCONFA_FORWARDING) 1825edc9e748SNicolas Dichtel size += nla_total_size(4); 1826136ba622SZhang Shengju if (all || type == NETCONFA_RP_FILTER) 1827cc535dfbSNicolas Dichtel size += nla_total_size(4); 1828136ba622SZhang Shengju if (all || type == NETCONFA_MC_FORWARDING) 1829d67b8c61SNicolas Dichtel size += nla_total_size(4); 1830136ba622SZhang Shengju if (all || type == NETCONFA_PROXY_NEIGH) 1831f085ff1cSstephen hemminger size += nla_total_size(4); 1832136ba622SZhang Shengju if (all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) 1833974d7af5SAndy Gospodarek size += nla_total_size(4); 1834edc9e748SNicolas Dichtel 1835edc9e748SNicolas Dichtel return size; 1836edc9e748SNicolas Dichtel } 1837edc9e748SNicolas Dichtel 1838edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 1839edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 1840edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 1841edc9e748SNicolas Dichtel int type) 1842edc9e748SNicolas Dichtel { 1843edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 1844edc9e748SNicolas Dichtel struct netconfmsg *ncm; 1845136ba622SZhang Shengju bool all = false; 1846edc9e748SNicolas Dichtel 1847edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 1848edc9e748SNicolas Dichtel flags); 184951456b29SIan Morris if (!nlh) 1850edc9e748SNicolas Dichtel return -EMSGSIZE; 1851edc9e748SNicolas Dichtel 1852136ba622SZhang Shengju if (type == NETCONFA_ALL) 1853136ba622SZhang Shengju all = true; 1854136ba622SZhang Shengju 1855edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 1856edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 1857edc9e748SNicolas Dichtel 1858edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 1859edc9e748SNicolas Dichtel goto nla_put_failure; 1860edc9e748SNicolas Dichtel 1861b5c9641dSDavid Ahern if (!devconf) 1862b5c9641dSDavid Ahern goto out; 1863b5c9641dSDavid Ahern 1864136ba622SZhang Shengju if ((all || type == NETCONFA_FORWARDING) && 1865edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 1866edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 1867edc9e748SNicolas Dichtel goto nla_put_failure; 1868136ba622SZhang Shengju if ((all || type == NETCONFA_RP_FILTER) && 1869cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 1870cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 1871cc535dfbSNicolas Dichtel goto nla_put_failure; 1872136ba622SZhang Shengju if ((all || type == NETCONFA_MC_FORWARDING) && 1873d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 1874d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 1875d67b8c61SNicolas Dichtel goto nla_put_failure; 1876136ba622SZhang Shengju if ((all || type == NETCONFA_PROXY_NEIGH) && 187709aea5dfSstephen hemminger nla_put_s32(skb, NETCONFA_PROXY_NEIGH, 1878f085ff1cSstephen hemminger IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0) 1879f085ff1cSstephen hemminger goto nla_put_failure; 1880136ba622SZhang Shengju if ((all || type == NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN) && 1881974d7af5SAndy Gospodarek nla_put_s32(skb, NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 1882974d7af5SAndy Gospodarek IPV4_DEVCONF(*devconf, IGNORE_ROUTES_WITH_LINKDOWN)) < 0) 1883974d7af5SAndy Gospodarek goto nla_put_failure; 1884edc9e748SNicolas Dichtel 1885b5c9641dSDavid Ahern out: 1886053c095aSJohannes Berg nlmsg_end(skb, nlh); 1887053c095aSJohannes Berg return 0; 1888edc9e748SNicolas Dichtel 1889edc9e748SNicolas Dichtel nla_put_failure: 1890edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 1891edc9e748SNicolas Dichtel return -EMSGSIZE; 1892edc9e748SNicolas Dichtel } 1893edc9e748SNicolas Dichtel 18943b022865SDavid Ahern void inet_netconf_notify_devconf(struct net *net, int event, int type, 18953b022865SDavid Ahern int ifindex, struct ipv4_devconf *devconf) 1896edc9e748SNicolas Dichtel { 1897edc9e748SNicolas Dichtel struct sk_buff *skb; 1898edc9e748SNicolas Dichtel int err = -ENOBUFS; 1899edc9e748SNicolas Dichtel 1900fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_KERNEL); 190151456b29SIan Morris if (!skb) 1902edc9e748SNicolas Dichtel goto errout; 1903edc9e748SNicolas Dichtel 1904edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 19053b022865SDavid Ahern event, 0, type); 1906edc9e748SNicolas Dichtel if (err < 0) { 1907edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 1908edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 1909edc9e748SNicolas Dichtel kfree_skb(skb); 1910edc9e748SNicolas Dichtel goto errout; 1911edc9e748SNicolas Dichtel } 1912fa17806cSEric Dumazet rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_KERNEL); 1913edc9e748SNicolas Dichtel return; 1914edc9e748SNicolas Dichtel errout: 1915edc9e748SNicolas Dichtel if (err < 0) 1916edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 1917edc9e748SNicolas Dichtel } 1918edc9e748SNicolas Dichtel 19199e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 19209e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 19219e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 1922cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 192309aea5dfSstephen hemminger [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 1924974d7af5SAndy Gospodarek [NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN] = { .len = sizeof(int) }, 19259e551110SNicolas Dichtel }; 19269e551110SNicolas Dichtel 19279e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 1928c21ef3e3SDavid Ahern struct nlmsghdr *nlh, 1929c21ef3e3SDavid Ahern struct netlink_ext_ack *extack) 19309e551110SNicolas Dichtel { 19319e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 19329e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 19339e551110SNicolas Dichtel struct netconfmsg *ncm; 19349e551110SNicolas Dichtel struct sk_buff *skb; 19359e551110SNicolas Dichtel struct ipv4_devconf *devconf; 19369e551110SNicolas Dichtel struct in_device *in_dev; 19379e551110SNicolas Dichtel struct net_device *dev; 19389e551110SNicolas Dichtel int ifindex; 19399e551110SNicolas Dichtel int err; 19409e551110SNicolas Dichtel 19419e551110SNicolas Dichtel err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 1942c21ef3e3SDavid Ahern devconf_ipv4_policy, extack); 19439e551110SNicolas Dichtel if (err < 0) 19449e551110SNicolas Dichtel goto errout; 19459e551110SNicolas Dichtel 1946a97eb33fSAnton Protopopov err = -EINVAL; 19479e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 19489e551110SNicolas Dichtel goto errout; 19499e551110SNicolas Dichtel 19509e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 19519e551110SNicolas Dichtel switch (ifindex) { 19529e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 19539e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 19549e551110SNicolas Dichtel break; 19559e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 19569e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 19579e551110SNicolas Dichtel break; 19589e551110SNicolas Dichtel default: 19599e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 196051456b29SIan Morris if (!dev) 19619e551110SNicolas Dichtel goto errout; 19629e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 196351456b29SIan Morris if (!in_dev) 19649e551110SNicolas Dichtel goto errout; 19659e551110SNicolas Dichtel devconf = &in_dev->cnf; 19669e551110SNicolas Dichtel break; 19679e551110SNicolas Dichtel } 19689e551110SNicolas Dichtel 19699e551110SNicolas Dichtel err = -ENOBUFS; 1970fa17806cSEric Dumazet skb = nlmsg_new(inet_netconf_msgsize_devconf(NETCONFA_ALL), GFP_KERNEL); 197151456b29SIan Morris if (!skb) 19729e551110SNicolas Dichtel goto errout; 19739e551110SNicolas Dichtel 19749e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 19759e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 19769e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 1977136ba622SZhang Shengju NETCONFA_ALL); 19789e551110SNicolas Dichtel if (err < 0) { 19799e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 19809e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 19819e551110SNicolas Dichtel kfree_skb(skb); 19829e551110SNicolas Dichtel goto errout; 19839e551110SNicolas Dichtel } 19849e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 19859e551110SNicolas Dichtel errout: 19869e551110SNicolas Dichtel return err; 19879e551110SNicolas Dichtel } 19889e551110SNicolas Dichtel 19897a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 19907a674200SNicolas Dichtel struct netlink_callback *cb) 19917a674200SNicolas Dichtel { 19927a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 19937a674200SNicolas Dichtel int h, s_h; 19947a674200SNicolas Dichtel int idx, s_idx; 19957a674200SNicolas Dichtel struct net_device *dev; 19967a674200SNicolas Dichtel struct in_device *in_dev; 19977a674200SNicolas Dichtel struct hlist_head *head; 19987a674200SNicolas Dichtel 19997a674200SNicolas Dichtel s_h = cb->args[0]; 20007a674200SNicolas Dichtel s_idx = idx = cb->args[1]; 20017a674200SNicolas Dichtel 20027a674200SNicolas Dichtel for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 20037a674200SNicolas Dichtel idx = 0; 20047a674200SNicolas Dichtel head = &net->dev_index_head[h]; 20057a674200SNicolas Dichtel rcu_read_lock(); 20060465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 20070465277fSNicolas Dichtel net->dev_base_seq; 20087a674200SNicolas Dichtel hlist_for_each_entry_rcu(dev, head, index_hlist) { 20097a674200SNicolas Dichtel if (idx < s_idx) 20107a674200SNicolas Dichtel goto cont; 20117a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 20127a674200SNicolas Dichtel if (!in_dev) 20137a674200SNicolas Dichtel goto cont; 20147a674200SNicolas Dichtel 20157a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, dev->ifindex, 20167a674200SNicolas Dichtel &in_dev->cnf, 20177a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 20187a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 20197a674200SNicolas Dichtel RTM_NEWNETCONF, 20207a674200SNicolas Dichtel NLM_F_MULTI, 2021136ba622SZhang Shengju NETCONFA_ALL) < 0) { 20227a674200SNicolas Dichtel rcu_read_unlock(); 20237a674200SNicolas Dichtel goto done; 20247a674200SNicolas Dichtel } 20250465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 20267a674200SNicolas Dichtel cont: 20277a674200SNicolas Dichtel idx++; 20287a674200SNicolas Dichtel } 20297a674200SNicolas Dichtel rcu_read_unlock(); 20307a674200SNicolas Dichtel } 20317a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES) { 20327a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 20337a674200SNicolas Dichtel net->ipv4.devconf_all, 20347a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 20357a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 20367a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 2037136ba622SZhang Shengju NETCONFA_ALL) < 0) 20387a674200SNicolas Dichtel goto done; 20397a674200SNicolas Dichtel else 20407a674200SNicolas Dichtel h++; 20417a674200SNicolas Dichtel } 20427a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES + 1) { 20437a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 20447a674200SNicolas Dichtel net->ipv4.devconf_dflt, 20457a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 20467a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 20477a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 2048136ba622SZhang Shengju NETCONFA_ALL) < 0) 20497a674200SNicolas Dichtel goto done; 20507a674200SNicolas Dichtel else 20517a674200SNicolas Dichtel h++; 20527a674200SNicolas Dichtel } 20537a674200SNicolas Dichtel done: 20547a674200SNicolas Dichtel cb->args[0] = h; 20557a674200SNicolas Dichtel cb->args[1] = idx; 20567a674200SNicolas Dichtel 20577a674200SNicolas Dichtel return skb->len; 20587a674200SNicolas Dichtel } 20597a674200SNicolas Dichtel 20601da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 20611da177e4SLinus Torvalds 2062c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 206331be3085SHerbert Xu { 206431be3085SHerbert Xu struct net_device *dev; 206531be3085SHerbert Xu 206631be3085SHerbert Xu rcu_read_lock(); 2067c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 2068c6d14c84SEric Dumazet struct in_device *in_dev; 2069c6d14c84SEric Dumazet 207031be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 207131be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 20729355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 2073c6d14c84SEric Dumazet } 207431be3085SHerbert Xu rcu_read_unlock(); 207531be3085SHerbert Xu } 207631be3085SHerbert Xu 2077c6d14c84SEric Dumazet /* called with RTNL locked */ 2078c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 207968dd299bSPavel Emelyanov { 208068dd299bSPavel Emelyanov struct net_device *dev; 2081586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 208268dd299bSPavel Emelyanov 2083586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 20849355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 20853b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 20863b022865SDavid Ahern NETCONFA_FORWARDING, 2087edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 2088edc9e748SNicolas Dichtel net->ipv4.devconf_all); 20893b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 20903b022865SDavid Ahern NETCONFA_FORWARDING, 2091edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2092edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 209368dd299bSPavel Emelyanov 2094c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 209568dd299bSPavel Emelyanov struct in_device *in_dev; 2096fa17806cSEric Dumazet 20970187bdfbSBen Hutchings if (on) 20980187bdfbSBen Hutchings dev_disable_lro(dev); 2099fa17806cSEric Dumazet 2100fa17806cSEric Dumazet in_dev = __in_dev_get_rtnl(dev); 2101edc9e748SNicolas Dichtel if (in_dev) { 210268dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 21033b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 21043b022865SDavid Ahern NETCONFA_FORWARDING, 2105edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 2106edc9e748SNicolas Dichtel } 210768dd299bSPavel Emelyanov } 210868dd299bSPavel Emelyanov } 210968dd299bSPavel Emelyanov 2110f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf) 2111f085ff1cSstephen hemminger { 2112f085ff1cSstephen hemminger if (cnf == net->ipv4.devconf_dflt) 2113f085ff1cSstephen hemminger return NETCONFA_IFINDEX_DEFAULT; 2114f085ff1cSstephen hemminger else if (cnf == net->ipv4.devconf_all) 2115f085ff1cSstephen hemminger return NETCONFA_IFINDEX_ALL; 2116f085ff1cSstephen hemminger else { 2117f085ff1cSstephen hemminger struct in_device *idev 2118f085ff1cSstephen hemminger = container_of(cnf, struct in_device, cnf); 2119f085ff1cSstephen hemminger return idev->dev->ifindex; 2120f085ff1cSstephen hemminger } 2121f085ff1cSstephen hemminger } 2122f085ff1cSstephen hemminger 2123fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write, 21248d65af78SAlexey Dobriyan void __user *buffer, 212531be3085SHerbert Xu size_t *lenp, loff_t *ppos) 212631be3085SHerbert Xu { 2127d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 21288d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 2129d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 213031be3085SHerbert Xu 213131be3085SHerbert Xu if (write) { 213231be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 2133c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 213431be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 2135f085ff1cSstephen hemminger int ifindex; 213631be3085SHerbert Xu 213731be3085SHerbert Xu set_bit(i, cnf->state); 213831be3085SHerbert Xu 21399355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 2140c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 2141d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 2142d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 2143d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 21444ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2145f085ff1cSstephen hemminger 2146cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 2147cc535dfbSNicolas Dichtel new_value != old_value) { 2148f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 21493b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 21503b022865SDavid Ahern NETCONFA_RP_FILTER, 2151cc535dfbSNicolas Dichtel ifindex, cnf); 2152cc535dfbSNicolas Dichtel } 2153f085ff1cSstephen hemminger if (i == IPV4_DEVCONF_PROXY_ARP - 1 && 2154f085ff1cSstephen hemminger new_value != old_value) { 2155f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 21563b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 21573b022865SDavid Ahern NETCONFA_PROXY_NEIGH, 2158f085ff1cSstephen hemminger ifindex, cnf); 2159f085ff1cSstephen hemminger } 2160974d7af5SAndy Gospodarek if (i == IPV4_DEVCONF_IGNORE_ROUTES_WITH_LINKDOWN - 1 && 2161974d7af5SAndy Gospodarek new_value != old_value) { 2162974d7af5SAndy Gospodarek ifindex = devinet_conf_ifindex(net, cnf); 21633b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 21643b022865SDavid Ahern NETCONFA_IGNORE_ROUTES_WITH_LINKDOWN, 2165974d7af5SAndy Gospodarek ifindex, cnf); 2166974d7af5SAndy Gospodarek } 216731be3085SHerbert Xu } 216831be3085SHerbert Xu 216931be3085SHerbert Xu return ret; 217031be3085SHerbert Xu } 217131be3085SHerbert Xu 2172fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write, 21738d65af78SAlexey Dobriyan void __user *buffer, 21741da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 21751da177e4SLinus Torvalds { 21761da177e4SLinus Torvalds int *valp = ctl->data; 21771da177e4SLinus Torvalds int val = *valp; 217888af182eSEric W. Biederman loff_t pos = *ppos; 21798d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 21801da177e4SLinus Torvalds 21811da177e4SLinus Torvalds if (write && *valp != val) { 2182c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 2183c0ce9fb3SPavel Emelyanov 21840187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 218588af182eSEric W. Biederman if (!rtnl_trylock()) { 218688af182eSEric W. Biederman /* Restore the original values before restarting */ 218788af182eSEric W. Biederman *valp = val; 218888af182eSEric W. Biederman *ppos = pos; 21899b8adb5eSEric W. Biederman return restart_syscall(); 219088af182eSEric W. Biederman } 21910187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2192c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2193edc9e748SNicolas Dichtel } else { 21940187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 21950187bdfbSBen Hutchings struct in_device *idev = 21960187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2197edc9e748SNicolas Dichtel if (*valp) 21980187bdfbSBen Hutchings dev_disable_lro(idev->dev); 21993b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 2200edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2201edc9e748SNicolas Dichtel idev->dev->ifindex, 2202edc9e748SNicolas Dichtel cnf); 22030187bdfbSBen Hutchings } 22040187bdfbSBen Hutchings rtnl_unlock(); 22054ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2206edc9e748SNicolas Dichtel } else 22073b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, 22083b022865SDavid Ahern NETCONFA_FORWARDING, 2209edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2210edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 22110187bdfbSBen Hutchings } 22121da177e4SLinus Torvalds 22131da177e4SLinus Torvalds return ret; 22141da177e4SLinus Torvalds } 22151da177e4SLinus Torvalds 2216fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 22178d65af78SAlexey Dobriyan void __user *buffer, 22181da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 22191da177e4SLinus Torvalds { 22201da177e4SLinus Torvalds int *valp = ctl->data; 22211da177e4SLinus Torvalds int val = *valp; 22228d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 222376e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 22241da177e4SLinus Torvalds 22251da177e4SLinus Torvalds if (write && *valp != val) 22264ccfe6d4SNicolas Dichtel rt_cache_flush(net); 22271da177e4SLinus Torvalds 22281da177e4SLinus Torvalds return ret; 22291da177e4SLinus Torvalds } 22301da177e4SLinus Torvalds 2231f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 223242f811b8SHerbert Xu { \ 223342f811b8SHerbert Xu .procname = name, \ 223442f811b8SHerbert Xu .data = ipv4_devconf.data + \ 223502291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 223642f811b8SHerbert Xu .maxlen = sizeof(int), \ 223742f811b8SHerbert Xu .mode = mval, \ 223842f811b8SHerbert Xu .proc_handler = proc, \ 223931be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 224042f811b8SHerbert Xu } 224142f811b8SHerbert Xu 224242f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2243f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 224442f811b8SHerbert Xu 224542f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2246f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 224742f811b8SHerbert Xu 2248f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2249f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 225042f811b8SHerbert Xu 225142f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2252f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 225342f811b8SHerbert Xu 22541da177e4SLinus Torvalds static struct devinet_sysctl_table { 22551da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 225602291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 22571da177e4SLinus Torvalds } devinet_sysctl = { 22581da177e4SLinus Torvalds .devinet_vars = { 225942f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2260f8572d8fSEric W. Biederman devinet_sysctl_forward), 226142f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 226242f811b8SHerbert Xu 226342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 226442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 226542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 226642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 226742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 226842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 226942f811b8SHerbert Xu "accept_source_route"), 22708153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 227128f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 227242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 227342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 227442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 227542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 227642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 227742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 227842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 227942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 228042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2281eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 228265324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 22835c6fe01cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 22845c6fe01cSWilliam Manley "force_igmp_version"), 22852690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, 22862690048cSWilliam Manley "igmpv2_unsolicited_report_interval"), 22872690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, 22882690048cSWilliam Manley "igmpv3_unsolicited_report_interval"), 22890eeb075fSAndy Gospodarek DEVINET_SYSCTL_RW_ENTRY(IGNORE_ROUTES_WITH_LINKDOWN, 22900eeb075fSAndy Gospodarek "ignore_routes_with_linkdown"), 229197daf331SJohannes Berg DEVINET_SYSCTL_RW_ENTRY(DROP_GRATUITOUS_ARP, 229297daf331SJohannes Berg "drop_gratuitous_arp"), 229342f811b8SHerbert Xu 229442f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 229542f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 229642f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 229742f811b8SHerbert Xu "promote_secondaries"), 2298d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2299d0daebc3SThomas Graf "route_localnet"), 230012b74dfaSJohannes Berg DEVINET_SYSCTL_FLUSHING_ENTRY(DROP_UNICAST_IN_L2_MULTICAST, 230112b74dfaSJohannes Berg "drop_unicast_in_l2_multicast"), 23021da177e4SLinus Torvalds }, 23031da177e4SLinus Torvalds }; 23041da177e4SLinus Torvalds 2305ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 230629c994e3SNicolas Dichtel int ifindex, struct ipv4_devconf *p) 23071da177e4SLinus Torvalds { 23081da177e4SLinus Torvalds int i; 23099fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 23108607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2311bfada697SPavel Emelyanov 23129fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 23131da177e4SLinus Torvalds if (!t) 23149fa89642SPavel Emelyanov goto out; 23159fa89642SPavel Emelyanov 23161da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 23171da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 231831be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2319c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 23201da177e4SLinus Torvalds } 23211da177e4SLinus Torvalds 23228607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 23231da177e4SLinus Torvalds 23248607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 23251da177e4SLinus Torvalds if (!t->sysctl_header) 23268607ddb8SEric W. Biederman goto free; 23271da177e4SLinus Torvalds 23281da177e4SLinus Torvalds p->sysctl = t; 232929c994e3SNicolas Dichtel 23303b022865SDavid Ahern inet_netconf_notify_devconf(net, RTM_NEWNETCONF, NETCONFA_ALL, 23313b022865SDavid Ahern ifindex, p); 2332ea40b324SPavel Emelyanov return 0; 23331da177e4SLinus Torvalds 23341da177e4SLinus Torvalds free: 23351da177e4SLinus Torvalds kfree(t); 23369fa89642SPavel Emelyanov out: 2337ea40b324SPavel Emelyanov return -ENOBUFS; 23381da177e4SLinus Torvalds } 23391da177e4SLinus Torvalds 2340b5c9641dSDavid Ahern static void __devinet_sysctl_unregister(struct net *net, 2341b5c9641dSDavid Ahern struct ipv4_devconf *cnf, int ifindex) 234266f27a52SPavel Emelyanov { 234351602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 234466f27a52SPavel Emelyanov 2345b5c9641dSDavid Ahern if (t) { 234651602b2aSPavel Emelyanov cnf->sysctl = NULL; 2347ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 23481da177e4SLinus Torvalds kfree(t); 23491da177e4SLinus Torvalds } 235051602b2aSPavel Emelyanov 2351b5c9641dSDavid Ahern inet_netconf_notify_devconf(net, RTM_DELNETCONF, 0, ifindex, NULL); 2352b5c9641dSDavid Ahern } 2353b5c9641dSDavid Ahern 235420e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 235551602b2aSPavel Emelyanov { 235620e61da7SWANG Cong int err; 235720e61da7SWANG Cong 235820e61da7SWANG Cong if (!sysctl_dev_name_is_allowed(idev->dev->name)) 235920e61da7SWANG Cong return -EINVAL; 236020e61da7SWANG Cong 236120e61da7SWANG Cong err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); 236220e61da7SWANG Cong if (err) 236320e61da7SWANG Cong return err; 236420e61da7SWANG Cong err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 236529c994e3SNicolas Dichtel idev->dev->ifindex, &idev->cnf); 236620e61da7SWANG Cong if (err) 236720e61da7SWANG Cong neigh_sysctl_unregister(idev->arp_parms); 236820e61da7SWANG Cong return err; 236951602b2aSPavel Emelyanov } 237051602b2aSPavel Emelyanov 237151602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 237251602b2aSPavel Emelyanov { 2373b5c9641dSDavid Ahern struct net *net = dev_net(idev->dev); 2374b5c9641dSDavid Ahern 2375b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, &idev->cnf, idev->dev->ifindex); 237651602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 23771da177e4SLinus Torvalds } 23781da177e4SLinus Torvalds 237968dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 238068dd299bSPavel Emelyanov { 238168dd299bSPavel Emelyanov .procname = "ip_forward", 238268dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 238302291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 238468dd299bSPavel Emelyanov .maxlen = sizeof(int), 238568dd299bSPavel Emelyanov .mode = 0644, 238668dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 238768dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2388c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 238968dd299bSPavel Emelyanov }, 239068dd299bSPavel Emelyanov { }, 239168dd299bSPavel Emelyanov }; 23922a75de0cSEric Dumazet #endif 239368dd299bSPavel Emelyanov 2394752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2395752d14dcSPavel Emelyanov { 2396752d14dcSPavel Emelyanov int err; 2397752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 23982a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 23992a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 2400752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 24012a75de0cSEric Dumazet #endif 2402752d14dcSPavel Emelyanov 2403752d14dcSPavel Emelyanov err = -ENOMEM; 2404752d14dcSPavel Emelyanov all = &ipv4_devconf; 2405752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 2406752d14dcSPavel Emelyanov 240709ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 2408752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 240951456b29SIan Morris if (!all) 2410752d14dcSPavel Emelyanov goto err_alloc_all; 2411752d14dcSPavel Emelyanov 2412752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 241351456b29SIan Morris if (!dflt) 2414752d14dcSPavel Emelyanov goto err_alloc_dflt; 2415752d14dcSPavel Emelyanov 24162a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2417752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 241851456b29SIan Morris if (!tbl) 2419752d14dcSPavel Emelyanov goto err_alloc_ctl; 2420752d14dcSPavel Emelyanov 242102291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2422752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2423752d14dcSPavel Emelyanov tbl[0].extra2 = net; 24242a75de0cSEric Dumazet #endif 2425752d14dcSPavel Emelyanov } 2426752d14dcSPavel Emelyanov 2427752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 242829c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "all", NETCONFA_IFINDEX_ALL, all); 2429752d14dcSPavel Emelyanov if (err < 0) 2430752d14dcSPavel Emelyanov goto err_reg_all; 2431752d14dcSPavel Emelyanov 243229c994e3SNicolas Dichtel err = __devinet_sysctl_register(net, "default", 243329c994e3SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, dflt); 2434752d14dcSPavel Emelyanov if (err < 0) 2435752d14dcSPavel Emelyanov goto err_reg_dflt; 2436752d14dcSPavel Emelyanov 2437752d14dcSPavel Emelyanov err = -ENOMEM; 24388607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 243951456b29SIan Morris if (!forw_hdr) 2440752d14dcSPavel Emelyanov goto err_reg_ctl; 24412a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2442752d14dcSPavel Emelyanov #endif 2443752d14dcSPavel Emelyanov 2444752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2445752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2446752d14dcSPavel Emelyanov return 0; 2447752d14dcSPavel Emelyanov 2448752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2449752d14dcSPavel Emelyanov err_reg_ctl: 2450b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, dflt, NETCONFA_IFINDEX_DEFAULT); 2451752d14dcSPavel Emelyanov err_reg_dflt: 2452b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, all, NETCONFA_IFINDEX_ALL); 2453752d14dcSPavel Emelyanov err_reg_all: 2454752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 2455752d14dcSPavel Emelyanov kfree(tbl); 2456752d14dcSPavel Emelyanov err_alloc_ctl: 24572a75de0cSEric Dumazet #endif 2458752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 2459752d14dcSPavel Emelyanov kfree(dflt); 2460752d14dcSPavel Emelyanov err_alloc_dflt: 2461752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 2462752d14dcSPavel Emelyanov kfree(all); 2463752d14dcSPavel Emelyanov err_alloc_all: 2464752d14dcSPavel Emelyanov return err; 2465752d14dcSPavel Emelyanov } 2466752d14dcSPavel Emelyanov 2467752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2468752d14dcSPavel Emelyanov { 24692a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2470752d14dcSPavel Emelyanov struct ctl_table *tbl; 2471752d14dcSPavel Emelyanov 2472752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2473752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2474b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_dflt, 2475b5c9641dSDavid Ahern NETCONFA_IFINDEX_DEFAULT); 2476b5c9641dSDavid Ahern __devinet_sysctl_unregister(net, net->ipv4.devconf_all, 2477b5c9641dSDavid Ahern NETCONFA_IFINDEX_ALL); 2478752d14dcSPavel Emelyanov kfree(tbl); 24792a75de0cSEric Dumazet #endif 2480752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2481752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2482752d14dcSPavel Emelyanov } 2483752d14dcSPavel Emelyanov 2484752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2485752d14dcSPavel Emelyanov .init = devinet_init_net, 2486752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2487752d14dcSPavel Emelyanov }; 2488752d14dcSPavel Emelyanov 2489207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = { 24909f0f7272SThomas Graf .family = AF_INET, 24919f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 24929f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2493cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2494cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 24959f0f7272SThomas Graf }; 24969f0f7272SThomas Graf 24971da177e4SLinus Torvalds void __init devinet_init(void) 24981da177e4SLinus Torvalds { 2499fd23c3b3SDavid S. Miller int i; 2500fd23c3b3SDavid S. Miller 2501fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2502fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2503fd23c3b3SDavid S. Miller 2504752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 2505752d14dcSPavel Emelyanov 25061da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 25071da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 250863f3444fSThomas Graf 2509906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 25105c766d64SJiri Pirko 25119f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 25129f0f7272SThomas Graf 2513b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0); 2514b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0); 2515b97bac64SFlorian Westphal rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0); 25169e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 2517b97bac64SFlorian Westphal inet_netconf_dump_devconf, 0); 25181da177e4SLinus Torvalds } 2519