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 291da177e4SLinus Torvalds #include <asm/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> 351da177e4SLinus Torvalds #include <linux/string.h> 361da177e4SLinus Torvalds #include <linux/mm.h> 371da177e4SLinus Torvalds #include <linux/socket.h> 381da177e4SLinus Torvalds #include <linux/sockios.h> 391da177e4SLinus Torvalds #include <linux/in.h> 401da177e4SLinus Torvalds #include <linux/errno.h> 411da177e4SLinus Torvalds #include <linux/interrupt.h> 421823730fSThomas Graf #include <linux/if_addr.h> 431da177e4SLinus Torvalds #include <linux/if_ether.h> 441da177e4SLinus Torvalds #include <linux/inet.h> 451da177e4SLinus Torvalds #include <linux/netdevice.h> 461da177e4SLinus Torvalds #include <linux/etherdevice.h> 471da177e4SLinus Torvalds #include <linux/skbuff.h> 481da177e4SLinus Torvalds #include <linux/init.h> 491da177e4SLinus Torvalds #include <linux/notifier.h> 501da177e4SLinus Torvalds #include <linux/inetdevice.h> 511da177e4SLinus Torvalds #include <linux/igmp.h> 525a0e3ad6STejun Heo #include <linux/slab.h> 53fd23c3b3SDavid S. Miller #include <linux/hash.h> 541da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 551da177e4SLinus Torvalds #include <linux/sysctl.h> 561da177e4SLinus Torvalds #endif 571da177e4SLinus Torvalds #include <linux/kmod.h> 58edc9e748SNicolas Dichtel #include <linux/netconf.h> 591da177e4SLinus Torvalds 6014c85021SArnaldo Carvalho de Melo #include <net/arp.h> 611da177e4SLinus Torvalds #include <net/ip.h> 621da177e4SLinus Torvalds #include <net/route.h> 631da177e4SLinus Torvalds #include <net/ip_fib.h> 6463f3444fSThomas Graf #include <net/rtnetlink.h> 65752d14dcSPavel Emelyanov #include <net/net_namespace.h> 665c766d64SJiri Pirko #include <net/addrconf.h> 671da177e4SLinus Torvalds 68406b6f97SDavid S. Miller #include "fib_lookup.h" 69406b6f97SDavid S. Miller 700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 7142f811b8SHerbert Xu .data = { 7202291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7302291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7402291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7502291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 762690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 772690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 7842f811b8SHerbert Xu }, 791da177e4SLinus Torvalds }; 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 8242f811b8SHerbert Xu .data = { 8302291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8402291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8502291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8602291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8702291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 882690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 892690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 9042f811b8SHerbert Xu }, 911da177e4SLinus Torvalds }; 921da177e4SLinus Torvalds 939355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 949355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9542f811b8SHerbert Xu 96ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 975c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 985c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 995c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 1005176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 1015c766d64SJiri Pirko [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 102ad6c8135SJiri Pirko [IFA_FLAGS] = { .type = NLA_U32 }, 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 static DEFINE_SPINLOCK(inet_addr_hash_lock); 110fd23c3b3SDavid S. Miller 11140384999SEric Dumazet static u32 inet_addr_hash(struct net *net, __be32 addr) 112fd23c3b3SDavid S. Miller { 11340384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 114fd23c3b3SDavid S. Miller 11540384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 116fd23c3b3SDavid S. Miller } 117fd23c3b3SDavid S. Miller 118fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 119fd23c3b3SDavid S. Miller { 12040384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 121fd23c3b3SDavid S. Miller 122fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 123fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 124fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 125fd23c3b3SDavid S. Miller } 126fd23c3b3SDavid S. Miller 127fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 128fd23c3b3SDavid S. Miller { 129fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 130fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 131fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 132fd23c3b3SDavid S. Miller } 133fd23c3b3SDavid S. Miller 1349435eb1cSDavid S. Miller /** 1359435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1369435eb1cSDavid S. Miller * @net: the net namespace 1379435eb1cSDavid S. Miller * @addr: the source address 1389435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1399435eb1cSDavid S. Miller * 1409435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1419435eb1cSDavid S. Miller */ 1429435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1439435eb1cSDavid S. Miller { 14440384999SEric Dumazet u32 hash = inet_addr_hash(net, addr); 1459435eb1cSDavid S. Miller struct net_device *result = NULL; 1469435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1479435eb1cSDavid S. Miller 1489435eb1cSDavid S. Miller rcu_read_lock(); 149b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) { 15040384999SEric Dumazet if (ifa->ifa_local == addr) { 1519435eb1cSDavid S. Miller struct net_device *dev = ifa->ifa_dev->dev; 1529435eb1cSDavid S. Miller 1539435eb1cSDavid S. Miller if (!net_eq(dev_net(dev), net)) 1549435eb1cSDavid S. Miller continue; 1559435eb1cSDavid S. Miller result = dev; 1569435eb1cSDavid S. Miller break; 1579435eb1cSDavid S. Miller } 1589435eb1cSDavid S. Miller } 159406b6f97SDavid S. Miller if (!result) { 160406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 161406b6f97SDavid S. Miller struct fib_result res = { 0 }; 162406b6f97SDavid S. Miller struct fib_table *local; 163406b6f97SDavid S. Miller 164406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 165406b6f97SDavid S. Miller * over loopback subnets work. 166406b6f97SDavid S. Miller */ 167406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 168406b6f97SDavid S. Miller if (local && 169406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 170406b6f97SDavid S. Miller res.type == RTN_LOCAL) 171406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 172406b6f97SDavid S. Miller } 1739435eb1cSDavid S. Miller if (result && devref) 1749435eb1cSDavid S. Miller dev_hold(result); 1759435eb1cSDavid S. Miller rcu_read_unlock(); 1769435eb1cSDavid S. Miller return result; 1779435eb1cSDavid S. Miller } 1789435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1799435eb1cSDavid S. Miller 180d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1811da177e4SLinus Torvalds 182e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1831da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1841da177e4SLinus Torvalds int destroy); 1851da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 18666f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev); 18751602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 18851602b2aSPavel Emelyanov #else 18940384999SEric Dumazet static void devinet_sysctl_register(struct in_device *idev) 19051602b2aSPavel Emelyanov { 19151602b2aSPavel Emelyanov } 19240384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 19351602b2aSPavel Emelyanov { 19451602b2aSPavel Emelyanov } 1951da177e4SLinus Torvalds #endif 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds /* Locks all the inet devices. */ 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 2001da177e4SLinus Torvalds { 20193adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2051da177e4SLinus Torvalds { 2061da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2071da177e4SLinus Torvalds if (ifa->ifa_dev) 2081da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2091da177e4SLinus Torvalds kfree(ifa); 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 21240384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2131da177e4SLinus Torvalds { 2141da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2151da177e4SLinus Torvalds } 2161da177e4SLinus Torvalds 2171da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2181da177e4SLinus Torvalds { 2191da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2201da177e4SLinus Torvalds 221547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 222547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 223e9897071SEric Dumazet kfree(rcu_dereference_protected(idev->mc_hash, 1)); 2241da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 22591df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2261da177e4SLinus Torvalds #endif 2271da177e4SLinus Torvalds dev_put(dev); 2281da177e4SLinus Torvalds if (!idev->dead) 2299f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2309f9354b9SEric Dumazet else 2311da177e4SLinus Torvalds kfree(idev); 2321da177e4SLinus Torvalds } 2339f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2341da177e4SLinus Torvalds 23571e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2361da177e4SLinus Torvalds { 2371da177e4SLinus Torvalds struct in_device *in_dev; 2381da177e4SLinus Torvalds 2391da177e4SLinus Torvalds ASSERT_RTNL(); 2401da177e4SLinus Torvalds 2410da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2421da177e4SLinus Torvalds if (!in_dev) 2431da177e4SLinus Torvalds goto out; 244c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2459355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2461da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2471da177e4SLinus Torvalds in_dev->dev = dev; 2489f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2499f9354b9SEric Dumazet if (!in_dev->arp_parms) 2501da177e4SLinus Torvalds goto out_kfree; 2510187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2520187bdfbSBen Hutchings dev_disable_lro(dev); 2531da177e4SLinus Torvalds /* Reference in_dev->dev */ 2541da177e4SLinus Torvalds dev_hold(dev); 25530c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2561da177e4SLinus Torvalds in_dev_hold(in_dev); 2571da177e4SLinus Torvalds 25866f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 2591da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2601da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2611da177e4SLinus Torvalds ip_mc_up(in_dev); 262483479ecSJarek Poplawski 26330c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 264cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 265483479ecSJarek Poplawski out: 2661da177e4SLinus Torvalds return in_dev; 2671da177e4SLinus Torvalds out_kfree: 2681da177e4SLinus Torvalds kfree(in_dev); 2691da177e4SLinus Torvalds in_dev = NULL; 2701da177e4SLinus Torvalds goto out; 2711da177e4SLinus Torvalds } 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2741da177e4SLinus Torvalds { 2751da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2761da177e4SLinus Torvalds in_dev_put(idev); 2771da177e4SLinus Torvalds } 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2801da177e4SLinus Torvalds { 2811da177e4SLinus Torvalds struct in_ifaddr *ifa; 2821da177e4SLinus Torvalds struct net_device *dev; 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds ASSERT_RTNL(); 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds dev = in_dev->dev; 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds in_dev->dead = 1; 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 2931da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 2941da177e4SLinus Torvalds inet_free_ifa(ifa); 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds 297a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 2981da177e4SLinus Torvalds 29951602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 3001da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 3011da177e4SLinus Torvalds arp_ifdown(dev); 3021da177e4SLinus Torvalds 3031da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 3041da177e4SLinus Torvalds } 3051da177e4SLinus Torvalds 306ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3071da177e4SLinus Torvalds { 3081da177e4SLinus Torvalds rcu_read_lock(); 3091da177e4SLinus Torvalds for_primary_ifa(in_dev) { 3101da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3111da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3121da177e4SLinus Torvalds rcu_read_unlock(); 3131da177e4SLinus Torvalds return 1; 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds } 3161da177e4SLinus Torvalds } endfor_ifa(in_dev); 3171da177e4SLinus Torvalds rcu_read_unlock(); 3181da177e4SLinus Torvalds return 0; 3191da177e4SLinus Torvalds } 3201da177e4SLinus Torvalds 321d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 32215e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3231da177e4SLinus Torvalds { 3248f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3250ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3260ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3270ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3280ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds ASSERT_RTNL(); 3311da177e4SLinus Torvalds 3328f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3338f937c60SHarald Welte * unless alias promotion is set 3348f937c60SHarald Welte **/ 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3371da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3381da177e4SLinus Torvalds 3391da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3400ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3410ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3420ff60a45SJamal Hadi Salim last_prim = ifa; 3430ff60a45SJamal Hadi Salim 3441da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3451da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3461da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3471da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3480ff60a45SJamal Hadi Salim prev_prom = ifa; 3491da177e4SLinus Torvalds continue; 3501da177e4SLinus Torvalds } 3511da177e4SLinus Torvalds 3520ff60a45SJamal Hadi Salim if (!do_promote) { 353fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3541da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3551da177e4SLinus Torvalds 35615e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 357e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 358e041c683SAlan Stern NETDEV_DOWN, ifa); 3591da177e4SLinus Torvalds inet_free_ifa(ifa); 3608f937c60SHarald Welte } else { 3618f937c60SHarald Welte promote = ifa; 3628f937c60SHarald Welte break; 3638f937c60SHarald Welte } 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds 3672d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 3682d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 3692d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 3702d230e2bSJulian Anastasov * while all addresses are on the device list. 3712d230e2bSJulian Anastasov */ 3722d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 3732d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 3742d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 3752d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 3762d230e2bSJulian Anastasov } 3772d230e2bSJulian Anastasov 3781da177e4SLinus Torvalds /* 2. Unlink it */ 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 381fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3821da177e4SLinus Torvalds 3831da177e4SLinus Torvalds /* 3. Announce address deletion */ 3841da177e4SLinus Torvalds 3851da177e4SLinus Torvalds /* Send message first, then call notifier. 3861da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 3871da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 3881da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 3891da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 3901da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 3911da177e4SLinus Torvalds So that, this order is correct. 3921da177e4SLinus Torvalds */ 39315e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 394e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 3950ff60a45SJamal Hadi Salim 3960ff60a45SJamal Hadi Salim if (promote) { 39704024b93SJulian Anastasov struct in_ifaddr *next_sec = promote->ifa_next; 3980ff60a45SJamal Hadi Salim 3990ff60a45SJamal Hadi Salim if (prev_prom) { 4000ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 4010ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 4020ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 4030ff60a45SJamal Hadi Salim } 4040ff60a45SJamal Hadi Salim 4050ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 40615e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 407e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 408e041c683SAlan Stern NETDEV_UP, promote); 40904024b93SJulian Anastasov for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { 4100ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4110ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4120ff60a45SJamal Hadi Salim continue; 4130ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4140ff60a45SJamal Hadi Salim } 4150ff60a45SJamal Hadi Salim 4160ff60a45SJamal Hadi Salim } 4176363097cSHerbert Xu if (destroy) 4181da177e4SLinus Torvalds inet_free_ifa(ifa1); 4191da177e4SLinus Torvalds } 4201da177e4SLinus Torvalds 421d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 422d6062cbbSThomas Graf int destroy) 423d6062cbbSThomas Graf { 424d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 425d6062cbbSThomas Graf } 426d6062cbbSThomas Graf 4275c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4285c766d64SJiri Pirko 4295c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4305c766d64SJiri Pirko 431d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 43215e47304SEric W. Biederman u32 portid) 4331da177e4SLinus Torvalds { 4341da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4351da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds ASSERT_RTNL(); 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds if (!ifa->ifa_local) { 4401da177e4SLinus Torvalds inet_free_ifa(ifa); 4411da177e4SLinus Torvalds return 0; 4421da177e4SLinus Torvalds } 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4451da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4461da177e4SLinus Torvalds 4471da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4481da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4491da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4501da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4511da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4521da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4531da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4541da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4551da177e4SLinus Torvalds inet_free_ifa(ifa); 4561da177e4SLinus Torvalds return -EEXIST; 4571da177e4SLinus Torvalds } 4581da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4591da177e4SLinus Torvalds inet_free_ifa(ifa); 4601da177e4SLinus Torvalds return -EINVAL; 4611da177e4SLinus Torvalds } 4621da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 46763862b5bSAruna-Hewapathirane prandom_seed((__force u32) ifa->ifa_local); 4681da177e4SLinus Torvalds ifap = last_primary; 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds ifa->ifa_next = *ifap; 4721da177e4SLinus Torvalds *ifap = ifa; 4731da177e4SLinus Torvalds 474fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 475fd23c3b3SDavid S. Miller 4765c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 477906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 4785c766d64SJiri Pirko 4791da177e4SLinus Torvalds /* Send message first, then call notifier. 4801da177e4SLinus Torvalds Notifier will trigger FIB update, so that 4811da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 48215e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 483e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds return 0; 4861da177e4SLinus Torvalds } 4871da177e4SLinus Torvalds 488d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 489d6062cbbSThomas Graf { 490d6062cbbSThomas Graf return __inet_insert_ifa(ifa, NULL, 0); 491d6062cbbSThomas Graf } 492d6062cbbSThomas Graf 4931da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 4941da177e4SLinus Torvalds { 495e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 4961da177e4SLinus Torvalds 4971da177e4SLinus Torvalds ASSERT_RTNL(); 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds if (!in_dev) { 5001da177e4SLinus Torvalds inet_free_ifa(ifa); 5011da177e4SLinus Torvalds return -ENOBUFS; 5021da177e4SLinus Torvalds } 50371e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 5041d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 5051da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 506547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 5071da177e4SLinus Torvalds in_dev_hold(in_dev); 5081da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5091da177e4SLinus Torvalds } 510f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5111da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5121da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5131da177e4SLinus Torvalds } 5141da177e4SLinus Torvalds 5158723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5168723e1b4SEric Dumazet * We dont take a reference on found in_device 5178723e1b4SEric Dumazet */ 5187fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5191da177e4SLinus Torvalds { 5201da177e4SLinus Torvalds struct net_device *dev; 5211da177e4SLinus Torvalds struct in_device *in_dev = NULL; 522c148fc2eSEric Dumazet 523c148fc2eSEric Dumazet rcu_read_lock(); 524c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5251da177e4SLinus Torvalds if (dev) 5268723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 527c148fc2eSEric Dumazet rcu_read_unlock(); 5281da177e4SLinus Torvalds return in_dev; 5291da177e4SLinus Torvalds } 5309f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5331da177e4SLinus Torvalds 53460cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 53560cad5daSAl Viro __be32 mask) 5361da177e4SLinus Torvalds { 5371da177e4SLinus Torvalds ASSERT_RTNL(); 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5401da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5411da177e4SLinus Torvalds return ifa; 5421da177e4SLinus Torvalds } endfor_ifa(in_dev); 5431da177e4SLinus Torvalds return NULL; 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 546661d2967SThomas Graf static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) 5471da177e4SLinus Torvalds { 5483b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 549dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5501da177e4SLinus Torvalds struct in_device *in_dev; 551dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 5521da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 553dfdd5fd4SThomas Graf int err = -EINVAL; 5541da177e4SLinus Torvalds 5551da177e4SLinus Torvalds ASSERT_RTNL(); 5561da177e4SLinus Torvalds 557dfdd5fd4SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 558dfdd5fd4SThomas Graf if (err < 0) 559dfdd5fd4SThomas Graf goto errout; 560dfdd5fd4SThomas Graf 561dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 5627fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 563dfdd5fd4SThomas Graf if (in_dev == NULL) { 564dfdd5fd4SThomas Graf err = -ENODEV; 565dfdd5fd4SThomas Graf goto errout; 566dfdd5fd4SThomas Graf } 567dfdd5fd4SThomas Graf 5681da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 5691da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 570dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 571a7a628c4SAl Viro ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) 5721da177e4SLinus Torvalds continue; 573dfdd5fd4SThomas Graf 574dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 575dfdd5fd4SThomas Graf continue; 576dfdd5fd4SThomas Graf 577dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 578dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 579a7a628c4SAl Viro !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 580dfdd5fd4SThomas Graf continue; 581dfdd5fd4SThomas Graf 58215e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 5831da177e4SLinus Torvalds return 0; 5841da177e4SLinus Torvalds } 585dfdd5fd4SThomas Graf 586dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 587dfdd5fd4SThomas Graf errout: 588dfdd5fd4SThomas Graf return err; 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds 5915c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 5925c766d64SJiri Pirko 5935c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 5945c766d64SJiri Pirko { 5955c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 5965c766d64SJiri Pirko struct in_ifaddr *ifa; 597c988d1e8SJiri Pirko struct hlist_node *n; 5985c766d64SJiri Pirko int i; 5995c766d64SJiri Pirko 6005c766d64SJiri Pirko now = jiffies; 6015c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 6025c766d64SJiri Pirko 6035c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 604c988d1e8SJiri Pirko bool change_needed = false; 605c988d1e8SJiri Pirko 606c988d1e8SJiri Pirko rcu_read_lock(); 607b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 6085c766d64SJiri Pirko unsigned long age; 6095c766d64SJiri Pirko 6105c766d64SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 6115c766d64SJiri Pirko continue; 6125c766d64SJiri Pirko 6135c766d64SJiri Pirko /* We try to batch several events at once. */ 6145c766d64SJiri Pirko age = (now - ifa->ifa_tstamp + 6155c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 6165c766d64SJiri Pirko 6175c766d64SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 6185c766d64SJiri Pirko age >= ifa->ifa_valid_lft) { 619c988d1e8SJiri Pirko change_needed = true; 620c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft == 621c988d1e8SJiri Pirko INFINITY_LIFE_TIME) { 622c988d1e8SJiri Pirko continue; 623c988d1e8SJiri Pirko } else if (age >= ifa->ifa_preferred_lft) { 624c988d1e8SJiri Pirko if (time_before(ifa->ifa_tstamp + 625c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ, next)) 626c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 627c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ; 628c988d1e8SJiri Pirko 629c988d1e8SJiri Pirko if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) 630c988d1e8SJiri Pirko change_needed = true; 631c988d1e8SJiri Pirko } else if (time_before(ifa->ifa_tstamp + 632c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ, 633c988d1e8SJiri Pirko next)) { 634c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 635c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ; 636c988d1e8SJiri Pirko } 637c988d1e8SJiri Pirko } 638c988d1e8SJiri Pirko rcu_read_unlock(); 639c988d1e8SJiri Pirko if (!change_needed) 640c988d1e8SJiri Pirko continue; 641c988d1e8SJiri Pirko rtnl_lock(); 642c988d1e8SJiri Pirko hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 643c988d1e8SJiri Pirko unsigned long age; 644c988d1e8SJiri Pirko 645c988d1e8SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 646c988d1e8SJiri Pirko continue; 647c988d1e8SJiri Pirko 648c988d1e8SJiri Pirko /* We try to batch several events at once. */ 649c988d1e8SJiri Pirko age = (now - ifa->ifa_tstamp + 650c988d1e8SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 651c988d1e8SJiri Pirko 652c988d1e8SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 653c988d1e8SJiri Pirko age >= ifa->ifa_valid_lft) { 6545c766d64SJiri Pirko struct in_ifaddr **ifap; 6555c766d64SJiri Pirko 6565c766d64SJiri Pirko for (ifap = &ifa->ifa_dev->ifa_list; 657c988d1e8SJiri Pirko *ifap != NULL; ifap = &(*ifap)->ifa_next) { 658c988d1e8SJiri Pirko if (*ifap == ifa) { 6595c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 6605c766d64SJiri Pirko ifap, 1); 661c988d1e8SJiri Pirko break; 6625c766d64SJiri Pirko } 663c988d1e8SJiri Pirko } 664c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft != 665c988d1e8SJiri Pirko INFINITY_LIFE_TIME && 666c988d1e8SJiri Pirko age >= ifa->ifa_preferred_lft && 667c988d1e8SJiri Pirko !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 6685c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 6695c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 6705c766d64SJiri Pirko } 6715c766d64SJiri Pirko } 672c988d1e8SJiri Pirko rtnl_unlock(); 6735c766d64SJiri Pirko } 6745c766d64SJiri Pirko 6755c766d64SJiri Pirko next_sec = round_jiffies_up(next); 6765c766d64SJiri Pirko next_sched = next; 6775c766d64SJiri Pirko 6785c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 6795c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 6805c766d64SJiri Pirko next_sched = next_sec; 6815c766d64SJiri Pirko 6825c766d64SJiri Pirko now = jiffies; 6835c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 6845c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 6855c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 6865c766d64SJiri Pirko 687906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 688906e073fSviresh kumar next_sched - now); 6895c766d64SJiri Pirko } 6905c766d64SJiri Pirko 6915c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 6925c766d64SJiri Pirko __u32 prefered_lft) 6935c766d64SJiri Pirko { 6945c766d64SJiri Pirko unsigned long timeout; 6955c766d64SJiri Pirko 6965c766d64SJiri Pirko ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 6975c766d64SJiri Pirko 6985c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 6995c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 7005c766d64SJiri Pirko ifa->ifa_valid_lft = timeout; 7015c766d64SJiri Pirko else 7025c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_PERMANENT; 7035c766d64SJiri Pirko 7045c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 7055c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 7065c766d64SJiri Pirko if (timeout == 0) 7075c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7085c766d64SJiri Pirko ifa->ifa_preferred_lft = timeout; 7095c766d64SJiri Pirko } 7105c766d64SJiri Pirko ifa->ifa_tstamp = jiffies; 7115c766d64SJiri Pirko if (!ifa->ifa_cstamp) 7125c766d64SJiri Pirko ifa->ifa_cstamp = ifa->ifa_tstamp; 7135c766d64SJiri Pirko } 7145c766d64SJiri Pirko 7155c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 7165c766d64SJiri Pirko __u32 *pvalid_lft, __u32 *pprefered_lft) 7171da177e4SLinus Torvalds { 7185c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 7195c753978SThomas Graf struct in_ifaddr *ifa; 7205c753978SThomas Graf struct ifaddrmsg *ifm; 7211da177e4SLinus Torvalds struct net_device *dev; 7221da177e4SLinus Torvalds struct in_device *in_dev; 7237b218574SDenis V. Lunev int err; 7241da177e4SLinus Torvalds 7255c753978SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 7265c753978SThomas Graf if (err < 0) 7275c753978SThomas Graf goto errout; 7281da177e4SLinus Torvalds 7295c753978SThomas Graf ifm = nlmsg_data(nlh); 730c4e38f41SEvgeniy Polyakov err = -EINVAL; 7317b218574SDenis V. Lunev if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) 7325c753978SThomas Graf goto errout; 7331da177e4SLinus Torvalds 7344b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 7355c753978SThomas Graf err = -ENODEV; 7367b218574SDenis V. Lunev if (dev == NULL) 7375c753978SThomas Graf goto errout; 7381da177e4SLinus Torvalds 7395c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 7405c753978SThomas Graf err = -ENOBUFS; 7417b218574SDenis V. Lunev if (in_dev == NULL) 7425c753978SThomas Graf goto errout; 74371e27da9SHerbert Xu 7445c753978SThomas Graf ifa = inet_alloc_ifa(); 7457b218574SDenis V. Lunev if (ifa == NULL) 7465c753978SThomas Graf /* 7475c753978SThomas Graf * A potential indev allocation can be left alive, it stays 7485c753978SThomas Graf * assigned to its device and is destroy with it. 7495c753978SThomas Graf */ 7505c753978SThomas Graf goto errout; 7515c753978SThomas Graf 752a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 7531d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 7545c753978SThomas Graf in_dev_hold(in_dev); 7555c753978SThomas Graf 7565c753978SThomas Graf if (tb[IFA_ADDRESS] == NULL) 7575c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 7585c753978SThomas Graf 759fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 7601da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 7611da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 762ad6c8135SJiri Pirko ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : 763ad6c8135SJiri Pirko ifm->ifa_flags; 7641da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 7651da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 7665c753978SThomas Graf 767a7a628c4SAl Viro ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); 768a7a628c4SAl Viro ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); 7695c753978SThomas Graf 7705c753978SThomas Graf if (tb[IFA_BROADCAST]) 771a7a628c4SAl Viro ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); 7725c753978SThomas Graf 7735c753978SThomas Graf if (tb[IFA_LABEL]) 7745c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 7751da177e4SLinus Torvalds else 7761da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 7771da177e4SLinus Torvalds 7785c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 7795c766d64SJiri Pirko struct ifa_cacheinfo *ci; 7805c766d64SJiri Pirko 7815c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 7825c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 7835c766d64SJiri Pirko err = -EINVAL; 784446266b0SDaniel Borkmann goto errout_free; 7855c766d64SJiri Pirko } 7865c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 7875c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 7885c766d64SJiri Pirko } 7895c766d64SJiri Pirko 7905c753978SThomas Graf return ifa; 7915c753978SThomas Graf 792446266b0SDaniel Borkmann errout_free: 793446266b0SDaniel Borkmann inet_free_ifa(ifa); 7945c753978SThomas Graf errout: 7955c753978SThomas Graf return ERR_PTR(err); 7965c753978SThomas Graf } 7975c753978SThomas Graf 7985c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 7995c766d64SJiri Pirko { 8005c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 8015c766d64SJiri Pirko struct in_ifaddr *ifa1, **ifap; 8025c766d64SJiri Pirko 8035c766d64SJiri Pirko if (!ifa->ifa_local) 8045c766d64SJiri Pirko return NULL; 8055c766d64SJiri Pirko 8065c766d64SJiri Pirko for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 8075c766d64SJiri Pirko ifap = &ifa1->ifa_next) { 8085c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 8095c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 8105c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 8115c766d64SJiri Pirko return ifa1; 8125c766d64SJiri Pirko } 8135c766d64SJiri Pirko return NULL; 8145c766d64SJiri Pirko } 8155c766d64SJiri Pirko 816661d2967SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) 8175c753978SThomas Graf { 8183b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 8195c753978SThomas Graf struct in_ifaddr *ifa; 8205c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 8215c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 8225c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 8235c753978SThomas Graf 8245c753978SThomas Graf ASSERT_RTNL(); 8255c753978SThomas Graf 8265c766d64SJiri Pirko ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft); 8275c753978SThomas Graf if (IS_ERR(ifa)) 8285c753978SThomas Graf return PTR_ERR(ifa); 8295c753978SThomas Graf 8305c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 8315c766d64SJiri Pirko if (!ifa_existing) { 8325c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 8335c766d64SJiri Pirko * userspace alreay relies on not having to provide this. 8345c766d64SJiri Pirko */ 8355c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 83615e47304SEric W. Biederman return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); 8375c766d64SJiri Pirko } else { 8385c766d64SJiri Pirko inet_free_ifa(ifa); 8395c766d64SJiri Pirko 8405c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 8415c766d64SJiri Pirko !(nlh->nlmsg_flags & NLM_F_REPLACE)) 8425c766d64SJiri Pirko return -EEXIST; 84334e2ed34SJiri Pirko ifa = ifa_existing; 84434e2ed34SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 84505a324b9SJiri Pirko cancel_delayed_work(&check_lifetime_work); 846906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, 847906e073fSviresh kumar &check_lifetime_work, 0); 84834e2ed34SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 84934e2ed34SJiri Pirko blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 8505c766d64SJiri Pirko } 8515c766d64SJiri Pirko return 0; 8521da177e4SLinus Torvalds } 8531da177e4SLinus Torvalds 8541da177e4SLinus Torvalds /* 8551da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 8561da177e4SLinus Torvalds */ 8571da177e4SLinus Torvalds 85840384999SEric Dumazet static int inet_abc_len(__be32 addr) 8591da177e4SLinus Torvalds { 8601da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 8611da177e4SLinus Torvalds 862f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 8631da177e4SLinus Torvalds rc = 0; 8641da177e4SLinus Torvalds else { 865714e85beSAl Viro __u32 haddr = ntohl(addr); 8661da177e4SLinus Torvalds 867714e85beSAl Viro if (IN_CLASSA(haddr)) 8681da177e4SLinus Torvalds rc = 8; 869714e85beSAl Viro else if (IN_CLASSB(haddr)) 8701da177e4SLinus Torvalds rc = 16; 871714e85beSAl Viro else if (IN_CLASSC(haddr)) 8721da177e4SLinus Torvalds rc = 24; 8731da177e4SLinus Torvalds } 8741da177e4SLinus Torvalds 8751da177e4SLinus Torvalds return rc; 8761da177e4SLinus Torvalds } 8771da177e4SLinus Torvalds 8781da177e4SLinus Torvalds 879e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) 8801da177e4SLinus Torvalds { 8811da177e4SLinus Torvalds struct ifreq ifr; 8821da177e4SLinus Torvalds struct sockaddr_in sin_orig; 8831da177e4SLinus Torvalds struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 8841da177e4SLinus Torvalds struct in_device *in_dev; 8851da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 8861da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 8871da177e4SLinus Torvalds struct net_device *dev; 8881da177e4SLinus Torvalds char *colon; 8891da177e4SLinus Torvalds int ret = -EFAULT; 8901da177e4SLinus Torvalds int tryaddrmatch = 0; 8911da177e4SLinus Torvalds 8921da177e4SLinus Torvalds /* 8931da177e4SLinus Torvalds * Fetch the caller's info block into kernel space 8941da177e4SLinus Torvalds */ 8951da177e4SLinus Torvalds 8961da177e4SLinus Torvalds if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 8971da177e4SLinus Torvalds goto out; 8981da177e4SLinus Torvalds ifr.ifr_name[IFNAMSIZ - 1] = 0; 8991da177e4SLinus Torvalds 9001da177e4SLinus Torvalds /* save original address for comparison */ 9011da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds colon = strchr(ifr.ifr_name, ':'); 9041da177e4SLinus Torvalds if (colon) 9051da177e4SLinus Torvalds *colon = 0; 9061da177e4SLinus Torvalds 907e5b13cb1SDenis V. Lunev dev_load(net, ifr.ifr_name); 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds switch (cmd) { 9101da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9111da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9121da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9131da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 9141da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 9151da177e4SLinus Torvalds so that we do not impose a lock. 9161da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 9171da177e4SLinus Torvalds */ 9181da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 9191da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 9201da177e4SLinus Torvalds sin->sin_family = AF_INET; 9211da177e4SLinus Torvalds break; 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds case SIOCSIFFLAGS: 924bf5b30b8SZhao Hongjiang ret = -EPERM; 92552e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 9261da177e4SLinus Torvalds goto out; 9271da177e4SLinus Torvalds break; 9281da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 9291da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 9301da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 9311da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 932bf5b30b8SZhao Hongjiang ret = -EPERM; 93352e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 9341da177e4SLinus Torvalds goto out; 9351da177e4SLinus Torvalds ret = -EINVAL; 9361da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 9371da177e4SLinus Torvalds goto out; 9381da177e4SLinus Torvalds break; 9391da177e4SLinus Torvalds default: 9401da177e4SLinus Torvalds ret = -EINVAL; 9411da177e4SLinus Torvalds goto out; 9421da177e4SLinus Torvalds } 9431da177e4SLinus Torvalds 9441da177e4SLinus Torvalds rtnl_lock(); 9451da177e4SLinus Torvalds 9461da177e4SLinus Torvalds ret = -ENODEV; 9479f9354b9SEric Dumazet dev = __dev_get_by_name(net, ifr.ifr_name); 9489f9354b9SEric Dumazet if (!dev) 9491da177e4SLinus Torvalds goto done; 9501da177e4SLinus Torvalds 9511da177e4SLinus Torvalds if (colon) 9521da177e4SLinus Torvalds *colon = ':'; 9531da177e4SLinus Torvalds 9549f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 9559f9354b9SEric Dumazet if (in_dev) { 9561da177e4SLinus Torvalds if (tryaddrmatch) { 9571da177e4SLinus Torvalds /* Matthias Andree */ 9581da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 9591da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 9601da177e4SLinus Torvalds and only if the original address family was AF_INET. 9611da177e4SLinus Torvalds This is checked above. */ 9621da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 9631da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 9641da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label) && 9651da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 9666c91afe1SDavid S. Miller ifa->ifa_local) { 9671da177e4SLinus Torvalds break; /* found */ 9681da177e4SLinus Torvalds } 9691da177e4SLinus Torvalds } 9701da177e4SLinus Torvalds } 9711da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 9721da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 9731da177e4SLinus Torvalds comparing just the label */ 9741da177e4SLinus Torvalds if (!ifa) { 9751da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 9761da177e4SLinus Torvalds ifap = &ifa->ifa_next) 9771da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label)) 9781da177e4SLinus Torvalds break; 9791da177e4SLinus Torvalds } 9801da177e4SLinus Torvalds } 9811da177e4SLinus Torvalds 9821da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 9831da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 9841da177e4SLinus Torvalds goto done; 9851da177e4SLinus Torvalds 9861da177e4SLinus Torvalds switch (cmd) { 9871da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9881da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 9891da177e4SLinus Torvalds goto rarok; 9901da177e4SLinus Torvalds 9911da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9921da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 9931da177e4SLinus Torvalds goto rarok; 9941da177e4SLinus Torvalds 9951da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9961da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 9971da177e4SLinus Torvalds goto rarok; 9981da177e4SLinus Torvalds 9991da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 10001da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 10011da177e4SLinus Torvalds goto rarok; 10021da177e4SLinus Torvalds 10031da177e4SLinus Torvalds case SIOCSIFFLAGS: 10041da177e4SLinus Torvalds if (colon) { 10051da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 10061da177e4SLinus Torvalds if (!ifa) 10071da177e4SLinus Torvalds break; 10081da177e4SLinus Torvalds ret = 0; 10091da177e4SLinus Torvalds if (!(ifr.ifr_flags & IFF_UP)) 10101da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 10111da177e4SLinus Torvalds break; 10121da177e4SLinus Torvalds } 10131da177e4SLinus Torvalds ret = dev_change_flags(dev, ifr.ifr_flags); 10141da177e4SLinus Torvalds break; 10151da177e4SLinus Torvalds 10161da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10171da177e4SLinus Torvalds ret = -EINVAL; 10181da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 10191da177e4SLinus Torvalds break; 10201da177e4SLinus Torvalds 10211da177e4SLinus Torvalds if (!ifa) { 10221da177e4SLinus Torvalds ret = -ENOBUFS; 10239f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 10249f9354b9SEric Dumazet if (!ifa) 10251da177e4SLinus Torvalds break; 1026c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 10271da177e4SLinus Torvalds if (colon) 10281da177e4SLinus Torvalds memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); 10291da177e4SLinus Torvalds else 10301da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 10311da177e4SLinus Torvalds } else { 10321da177e4SLinus Torvalds ret = 0; 10331da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 10341da177e4SLinus Torvalds break; 10351da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10361da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 1037148f9729SBjorn Mork ifa->ifa_scope = 0; 10381da177e4SLinus Torvalds } 10391da177e4SLinus Torvalds 10401da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 10411da177e4SLinus Torvalds 10421da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 10431da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 10441da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 10451da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 10461da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 10471da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 10481da177e4SLinus Torvalds ~ifa->ifa_mask; 10491da177e4SLinus Torvalds } else { 10501da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 10511da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 10521da177e4SLinus Torvalds } 10535c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 10541da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 10551da177e4SLinus Torvalds break; 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10581da177e4SLinus Torvalds ret = 0; 10591da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 10601da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10611da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 10621da177e4SLinus Torvalds inet_insert_ifa(ifa); 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds break; 10651da177e4SLinus Torvalds 10661da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 10671da177e4SLinus Torvalds ret = 0; 10681da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 10691da177e4SLinus Torvalds break; 10701da177e4SLinus Torvalds ret = -EINVAL; 10711da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 10721da177e4SLinus Torvalds break; 10731da177e4SLinus Torvalds ret = 0; 10741da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10751da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 10761da177e4SLinus Torvalds inet_insert_ifa(ifa); 10771da177e4SLinus Torvalds break; 10781da177e4SLinus Torvalds 10791da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 10801da177e4SLinus Torvalds 10811da177e4SLinus Torvalds /* 10821da177e4SLinus Torvalds * The mask we set must be legal. 10831da177e4SLinus Torvalds */ 10841da177e4SLinus Torvalds ret = -EINVAL; 10851da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 10861da177e4SLinus Torvalds break; 10871da177e4SLinus Torvalds ret = 0; 10881da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1089a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 10901da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10911da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 10921da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 10931da177e4SLinus Torvalds 10941da177e4SLinus Torvalds /* See if current broadcast address matches 10951da177e4SLinus Torvalds * with current netmask, then recalculate 10961da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 10971da177e4SLinus Torvalds * funny address, so don't touch it since 10981da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 10991da177e4SLinus Torvalds */ 11001da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 11011da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 11021da177e4SLinus Torvalds (ifa->ifa_broadcast == 1103dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 11041da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 11051da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 11061da177e4SLinus Torvalds } 11071da177e4SLinus Torvalds inet_insert_ifa(ifa); 11081da177e4SLinus Torvalds } 11091da177e4SLinus Torvalds break; 11101da177e4SLinus Torvalds } 11111da177e4SLinus Torvalds done: 11121da177e4SLinus Torvalds rtnl_unlock(); 11131da177e4SLinus Torvalds out: 11141da177e4SLinus Torvalds return ret; 11151da177e4SLinus Torvalds rarok: 11161da177e4SLinus Torvalds rtnl_unlock(); 11171da177e4SLinus Torvalds ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; 11181da177e4SLinus Torvalds goto out; 11191da177e4SLinus Torvalds } 11201da177e4SLinus Torvalds 11211da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 11221da177e4SLinus Torvalds { 1123e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 11241da177e4SLinus Torvalds struct in_ifaddr *ifa; 11251da177e4SLinus Torvalds struct ifreq ifr; 11261da177e4SLinus Torvalds int done = 0; 11271da177e4SLinus Torvalds 11289f9354b9SEric Dumazet if (!in_dev) 11291da177e4SLinus Torvalds goto out; 11301da177e4SLinus Torvalds 11319f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 11321da177e4SLinus Torvalds if (!buf) { 11331da177e4SLinus Torvalds done += sizeof(ifr); 11341da177e4SLinus Torvalds continue; 11351da177e4SLinus Torvalds } 11361da177e4SLinus Torvalds if (len < (int) sizeof(ifr)) 11371da177e4SLinus Torvalds break; 11381da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 11391da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 11401da177e4SLinus Torvalds 11411da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 11421da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 11431da177e4SLinus Torvalds ifa->ifa_local; 11441da177e4SLinus Torvalds 11451da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 11461da177e4SLinus Torvalds done = -EFAULT; 11471da177e4SLinus Torvalds break; 11481da177e4SLinus Torvalds } 11491da177e4SLinus Torvalds buf += sizeof(struct ifreq); 11501da177e4SLinus Torvalds len -= sizeof(struct ifreq); 11511da177e4SLinus Torvalds done += sizeof(struct ifreq); 11521da177e4SLinus Torvalds } 11531da177e4SLinus Torvalds out: 11541da177e4SLinus Torvalds return done; 11551da177e4SLinus Torvalds } 11561da177e4SLinus Torvalds 1157a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 11581da177e4SLinus Torvalds { 1159a61ced5dSAl Viro __be32 addr = 0; 11601da177e4SLinus Torvalds struct in_device *in_dev; 1161c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 11621da177e4SLinus Torvalds 11631da177e4SLinus Torvalds rcu_read_lock(); 1164e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 11651da177e4SLinus Torvalds if (!in_dev) 11661da177e4SLinus Torvalds goto no_in_dev; 11671da177e4SLinus Torvalds 11681da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11691da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 11701da177e4SLinus Torvalds continue; 11711da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 11721da177e4SLinus Torvalds addr = ifa->ifa_local; 11731da177e4SLinus Torvalds break; 11741da177e4SLinus Torvalds } 11751da177e4SLinus Torvalds if (!addr) 11761da177e4SLinus Torvalds addr = ifa->ifa_local; 11771da177e4SLinus Torvalds } endfor_ifa(in_dev); 11781da177e4SLinus Torvalds 11791da177e4SLinus Torvalds if (addr) 1180c6d14c84SEric Dumazet goto out_unlock; 11819f9354b9SEric Dumazet no_in_dev: 11821da177e4SLinus Torvalds 11831da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 11841da177e4SLinus Torvalds in this case. It is importnat that lo is the first interface 11851da177e4SLinus Torvalds in dev_base list. 11861da177e4SLinus Torvalds */ 1187c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 11889f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 11899f9354b9SEric Dumazet if (!in_dev) 11901da177e4SLinus Torvalds continue; 11911da177e4SLinus Torvalds 11921da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11931da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 11941da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 11951da177e4SLinus Torvalds addr = ifa->ifa_local; 1196c6d14c84SEric Dumazet goto out_unlock; 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds } endfor_ifa(in_dev); 11991da177e4SLinus Torvalds } 1200c6d14c84SEric Dumazet out_unlock: 12011da177e4SLinus Torvalds rcu_read_unlock(); 12021da177e4SLinus Torvalds return addr; 12031da177e4SLinus Torvalds } 12049f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 12051da177e4SLinus Torvalds 120660cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 120760cad5daSAl Viro __be32 local, int scope) 12081da177e4SLinus Torvalds { 12091da177e4SLinus Torvalds int same = 0; 1210a144ea4bSAl Viro __be32 addr = 0; 12111da177e4SLinus Torvalds 12121da177e4SLinus Torvalds for_ifa(in_dev) { 12131da177e4SLinus Torvalds if (!addr && 12141da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 12151da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 12161da177e4SLinus Torvalds addr = ifa->ifa_local; 12171da177e4SLinus Torvalds if (same) 12181da177e4SLinus Torvalds break; 12191da177e4SLinus Torvalds } 12201da177e4SLinus Torvalds if (!same) { 12211da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 12221da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 12231da177e4SLinus Torvalds if (same && addr) { 12241da177e4SLinus Torvalds if (local || !dst) 12251da177e4SLinus Torvalds break; 12261da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 12271da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 12281da177e4SLinus Torvalds break; 12291da177e4SLinus Torvalds /* No, then can we use new local src? */ 12301da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 12311da177e4SLinus Torvalds addr = ifa->ifa_local; 12321da177e4SLinus Torvalds break; 12331da177e4SLinus Torvalds } 12341da177e4SLinus Torvalds /* search for large dst subnet for addr */ 12351da177e4SLinus Torvalds same = 0; 12361da177e4SLinus Torvalds } 12371da177e4SLinus Torvalds } 12381da177e4SLinus Torvalds } endfor_ifa(in_dev); 12391da177e4SLinus Torvalds 12401da177e4SLinus Torvalds return same ? addr : 0; 12411da177e4SLinus Torvalds } 12421da177e4SLinus Torvalds 12431da177e4SLinus Torvalds /* 12441da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 1245b601fa19SNicolas Dichtel * - net: netns to check, cannot be NULL 1246b601fa19SNicolas Dichtel * - in_dev: only on this interface, NULL=any interface 12471da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 12481da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 12491da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 12501da177e4SLinus Torvalds */ 1251b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, 12529bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 12531da177e4SLinus Torvalds { 125460cad5daSAl Viro __be32 addr = 0; 12559bd85e32SDenis V. Lunev struct net_device *dev; 12561da177e4SLinus Torvalds 1257b601fa19SNicolas Dichtel if (in_dev != NULL) 12589bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 12591da177e4SLinus Torvalds 12601da177e4SLinus Torvalds rcu_read_lock(); 1261c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 12629f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12639f9354b9SEric Dumazet if (in_dev) { 12641da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 12651da177e4SLinus Torvalds if (addr) 12661da177e4SLinus Torvalds break; 12671da177e4SLinus Torvalds } 12681da177e4SLinus Torvalds } 12691da177e4SLinus Torvalds rcu_read_unlock(); 12701da177e4SLinus Torvalds 12711da177e4SLinus Torvalds return addr; 12721da177e4SLinus Torvalds } 1273eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 12741da177e4SLinus Torvalds 12751da177e4SLinus Torvalds /* 12761da177e4SLinus Torvalds * Device notifier 12771da177e4SLinus Torvalds */ 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 12801da177e4SLinus Torvalds { 1281e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 12821da177e4SLinus Torvalds } 12839f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 12841da177e4SLinus Torvalds 12851da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 12861da177e4SLinus Torvalds { 1287e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 12881da177e4SLinus Torvalds } 12899f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 12901da177e4SLinus Torvalds 12919f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 12929f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 12931da177e4SLinus Torvalds */ 12941da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 12951da177e4SLinus Torvalds { 12961da177e4SLinus Torvalds struct in_ifaddr *ifa; 12971da177e4SLinus Torvalds int named = 0; 12981da177e4SLinus Torvalds 12991da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 13001da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 13011da177e4SLinus Torvalds 13021da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 13031da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 13041da177e4SLinus Torvalds if (named++ == 0) 1305573bf470SThomas Graf goto skip; 130644344b2aSMark McLoughlin dot = strchr(old, ':'); 13071da177e4SLinus Torvalds if (dot == NULL) { 13081da177e4SLinus Torvalds sprintf(old, ":%d", named); 13091da177e4SLinus Torvalds dot = old; 13101da177e4SLinus Torvalds } 13119f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 13121da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 13139f9354b9SEric Dumazet else 13141da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1315573bf470SThomas Graf skip: 1316573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 13171da177e4SLinus Torvalds } 13181da177e4SLinus Torvalds } 13191da177e4SLinus Torvalds 132040384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu) 132106770843SBreno Leitao { 132206770843SBreno Leitao return mtu >= 68; 132306770843SBreno Leitao } 132406770843SBreno Leitao 1325d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1326d11327adSIan Campbell struct in_device *in_dev) 1327d11327adSIan Campbell 1328d11327adSIan Campbell { 1329b76d0789SZoltan Kiss struct in_ifaddr *ifa; 1330d11327adSIan Campbell 1331b76d0789SZoltan Kiss for (ifa = in_dev->ifa_list; ifa; 1332b76d0789SZoltan Kiss ifa = ifa->ifa_next) { 1333d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 13346c91afe1SDavid S. Miller ifa->ifa_local, dev, 13356c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1336d11327adSIan Campbell dev->dev_addr, NULL); 1337d11327adSIan Campbell } 1338b76d0789SZoltan Kiss } 1339d11327adSIan Campbell 13401da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 13411da177e4SLinus Torvalds 13421da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 13431da177e4SLinus Torvalds void *ptr) 13441da177e4SLinus Torvalds { 1345351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1346748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13471da177e4SLinus Torvalds 13481da177e4SLinus Torvalds ASSERT_RTNL(); 13491da177e4SLinus Torvalds 13501da177e4SLinus Torvalds if (!in_dev) { 13518030f544SHerbert Xu if (event == NETDEV_REGISTER) { 13521da177e4SLinus Torvalds in_dev = inetdev_init(dev); 13538d76527eSHerbert Xu if (!in_dev) 1354b217d616SHerbert Xu return notifier_from_errno(-ENOMEM); 13550cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 135642f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 135742f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 13581da177e4SLinus Torvalds } 135906770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 136006770843SBreno Leitao /* Re-enabling IP */ 136106770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 136206770843SBreno Leitao in_dev = inetdev_init(dev); 13638030f544SHerbert Xu } 13641da177e4SLinus Torvalds goto out; 13651da177e4SLinus Torvalds } 13661da177e4SLinus Torvalds 13671da177e4SLinus Torvalds switch (event) { 13681da177e4SLinus Torvalds case NETDEV_REGISTER: 136991df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1370a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 13711da177e4SLinus Torvalds break; 13721da177e4SLinus Torvalds case NETDEV_UP: 137306770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 13741da177e4SLinus Torvalds break; 13750cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 13769f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 13779f9354b9SEric Dumazet 13789f9354b9SEric Dumazet if (ifa) { 1379fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 13801da177e4SLinus Torvalds ifa->ifa_local = 13811da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 13821da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 13831da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 13841da177e4SLinus Torvalds in_dev_hold(in_dev); 13851da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 13861da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 13871da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 13885c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 13895c766d64SJiri Pirko INFINITY_LIFE_TIME); 1390dfd1582dSJiri Pirko ipv4_devconf_setall(in_dev); 1391dfd1582dSJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 13921da177e4SLinus Torvalds inet_insert_ifa(ifa); 13931da177e4SLinus Torvalds } 13941da177e4SLinus Torvalds } 13951da177e4SLinus Torvalds ip_mc_up(in_dev); 1396eefef1cfSStephen Hemminger /* fall through */ 1397eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1398d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1399d11327adSIan Campbell break; 1400d11327adSIan Campbell /* fall through */ 1401d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1402a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1403d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 14041da177e4SLinus Torvalds break; 14051da177e4SLinus Torvalds case NETDEV_DOWN: 14061da177e4SLinus Torvalds ip_mc_down(in_dev); 14071da177e4SLinus Torvalds break; 140893d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 140975c78500SMoni Shoua ip_mc_unmap(in_dev); 141075c78500SMoni Shoua break; 141193d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 141275c78500SMoni Shoua ip_mc_remap(in_dev); 141375c78500SMoni Shoua break; 14141da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 141506770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 14161da177e4SLinus Torvalds break; 141706770843SBreno Leitao /* disable IP when MTU is not enough */ 14181da177e4SLinus Torvalds case NETDEV_UNREGISTER: 14191da177e4SLinus Torvalds inetdev_destroy(in_dev); 14201da177e4SLinus Torvalds break; 14211da177e4SLinus Torvalds case NETDEV_CHANGENAME: 14221da177e4SLinus Torvalds /* Do not notify about label change, this event is 14231da177e4SLinus Torvalds * not interesting to applications using netlink. 14241da177e4SLinus Torvalds */ 14251da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 14261da177e4SLinus Torvalds 142751602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 142866f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 14291da177e4SLinus Torvalds break; 14301da177e4SLinus Torvalds } 14311da177e4SLinus Torvalds out: 14321da177e4SLinus Torvalds return NOTIFY_DONE; 14331da177e4SLinus Torvalds } 14341da177e4SLinus Torvalds 14351da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 14361da177e4SLinus Torvalds .notifier_call = inetdev_event, 14371da177e4SLinus Torvalds }; 14381da177e4SLinus Torvalds 143940384999SEric Dumazet static size_t inet_nlmsg_size(void) 1440339bf98fSThomas Graf { 1441339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1442339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1443339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1444339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1445ad6c8135SJiri Pirko + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ 144663b5f152SGeert Uytterhoeven + nla_total_size(4) /* IFA_FLAGS */ 144763b5f152SGeert Uytterhoeven + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ 1448339bf98fSThomas Graf } 1449339bf98fSThomas Graf 14505c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 14515c766d64SJiri Pirko { 14525c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 14535c766d64SJiri Pirko } 14545c766d64SJiri Pirko 14555c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 14565c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 14575c766d64SJiri Pirko { 14585c766d64SJiri Pirko struct ifa_cacheinfo ci; 14595c766d64SJiri Pirko 14605c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 14615c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 14625c766d64SJiri Pirko ci.ifa_prefered = preferred; 14635c766d64SJiri Pirko ci.ifa_valid = valid; 14645c766d64SJiri Pirko 14655c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 14665c766d64SJiri Pirko } 14675c766d64SJiri Pirko 14681da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 146915e47304SEric W. Biederman u32 portid, u32 seq, int event, unsigned int flags) 14701da177e4SLinus Torvalds { 14711da177e4SLinus Torvalds struct ifaddrmsg *ifm; 14721da177e4SLinus Torvalds struct nlmsghdr *nlh; 14735c766d64SJiri Pirko u32 preferred, valid; 14741da177e4SLinus Torvalds 147515e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); 147647f68512SThomas Graf if (nlh == NULL) 147726932566SPatrick McHardy return -EMSGSIZE; 147847f68512SThomas Graf 147947f68512SThomas Graf ifm = nlmsg_data(nlh); 14801da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 14811da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 14825c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 14831da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 14841da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 14851da177e4SLinus Torvalds 14865c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 14875c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 14885c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 14895c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 14905c766d64SJiri Pirko long tval = (jiffies - ifa->ifa_tstamp) / HZ; 14915c766d64SJiri Pirko 14925c766d64SJiri Pirko if (preferred > tval) 14935c766d64SJiri Pirko preferred -= tval; 14945c766d64SJiri Pirko else 14955c766d64SJiri Pirko preferred = 0; 14965c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 14975c766d64SJiri Pirko if (valid > tval) 14985c766d64SJiri Pirko valid -= tval; 14995c766d64SJiri Pirko else 15005c766d64SJiri Pirko valid = 0; 15015c766d64SJiri Pirko } 15025c766d64SJiri Pirko } 15035c766d64SJiri Pirko } else { 15045c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 15055c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 15065c766d64SJiri Pirko } 1507f3756b79SDavid S. Miller if ((ifa->ifa_address && 1508f3756b79SDavid S. Miller nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) || 1509f3756b79SDavid S. Miller (ifa->ifa_local && 1510f3756b79SDavid S. Miller nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) || 1511f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1512f3756b79SDavid S. Miller nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1513f3756b79SDavid S. Miller (ifa->ifa_label[0] && 15145c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 1515ad6c8135SJiri Pirko nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || 15165c766d64SJiri Pirko put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 15175c766d64SJiri Pirko preferred, valid)) 1518f3756b79SDavid S. Miller goto nla_put_failure; 151947f68512SThomas Graf 152047f68512SThomas Graf return nlmsg_end(skb, nlh); 152147f68512SThomas Graf 152247f68512SThomas Graf nla_put_failure: 152326932566SPatrick McHardy nlmsg_cancel(skb, nlh); 152426932566SPatrick McHardy return -EMSGSIZE; 15251da177e4SLinus Torvalds } 15261da177e4SLinus Torvalds 15271da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 15281da177e4SLinus Torvalds { 15293b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1530eec4df98SEric Dumazet int h, s_h; 1531eec4df98SEric Dumazet int idx, s_idx; 1532eec4df98SEric Dumazet int ip_idx, s_ip_idx; 15331da177e4SLinus Torvalds struct net_device *dev; 15341da177e4SLinus Torvalds struct in_device *in_dev; 15351da177e4SLinus Torvalds struct in_ifaddr *ifa; 1536eec4df98SEric Dumazet struct hlist_head *head; 15371da177e4SLinus Torvalds 1538eec4df98SEric Dumazet s_h = cb->args[0]; 1539eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1540eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1541eec4df98SEric Dumazet 1542eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 15437562f876SPavel Emelianov idx = 0; 1544eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1545eec4df98SEric Dumazet rcu_read_lock(); 15460465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 15470465277fSNicolas Dichtel net->dev_base_seq; 1548b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 15491da177e4SLinus Torvalds if (idx < s_idx) 15507562f876SPavel Emelianov goto cont; 15514b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 15521da177e4SLinus Torvalds s_ip_idx = 0; 1553eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 15549f9354b9SEric Dumazet if (!in_dev) 15557562f876SPavel Emelianov goto cont; 15561da177e4SLinus Torvalds 15571da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 15581da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 15591da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1560596e4150SStephen Hemminger continue; 1561eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 156215e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 15631da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1564eec4df98SEric Dumazet RTM_NEWADDR, NLM_F_MULTI) <= 0) { 1565eec4df98SEric Dumazet rcu_read_unlock(); 15661da177e4SLinus Torvalds goto done; 15671da177e4SLinus Torvalds } 15680465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1569eec4df98SEric Dumazet } 15707562f876SPavel Emelianov cont: 15717562f876SPavel Emelianov idx++; 15721da177e4SLinus Torvalds } 1573eec4df98SEric Dumazet rcu_read_unlock(); 1574eec4df98SEric Dumazet } 15751da177e4SLinus Torvalds 15761da177e4SLinus Torvalds done: 1577eec4df98SEric Dumazet cb->args[0] = h; 1578eec4df98SEric Dumazet cb->args[1] = idx; 1579eec4df98SEric Dumazet cb->args[2] = ip_idx; 15801da177e4SLinus Torvalds 15811da177e4SLinus Torvalds return skb->len; 15821da177e4SLinus Torvalds } 15831da177e4SLinus Torvalds 1584d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 158515e47304SEric W. Biederman u32 portid) 15861da177e4SLinus Torvalds { 158747f68512SThomas Graf struct sk_buff *skb; 1588d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1589d6062cbbSThomas Graf int err = -ENOBUFS; 15904b8aa9abSDenis V. Lunev struct net *net; 15911da177e4SLinus Torvalds 1592c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1593339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 159447f68512SThomas Graf if (skb == NULL) 1595d6062cbbSThomas Graf goto errout; 1596d6062cbbSThomas Graf 159715e47304SEric W. Biederman err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0); 159826932566SPatrick McHardy if (err < 0) { 159926932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 160026932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 160126932566SPatrick McHardy kfree_skb(skb); 160226932566SPatrick McHardy goto errout; 160326932566SPatrick McHardy } 160415e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 16051ce85fe4SPablo Neira Ayuso return; 1606d6062cbbSThomas Graf errout: 1607d6062cbbSThomas Graf if (err < 0) 16084b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 16091da177e4SLinus Torvalds } 16101da177e4SLinus Torvalds 16119f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 16129f0f7272SThomas Graf { 16131fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16149f0f7272SThomas Graf 16159f0f7272SThomas Graf if (!in_dev) 16169f0f7272SThomas Graf return 0; 16179f0f7272SThomas Graf 16189f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 16199f0f7272SThomas Graf } 16209f0f7272SThomas Graf 16219f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 16229f0f7272SThomas Graf { 16231fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16249f0f7272SThomas Graf struct nlattr *nla; 16259f0f7272SThomas Graf int i; 16269f0f7272SThomas Graf 16279f0f7272SThomas Graf if (!in_dev) 16289f0f7272SThomas Graf return -ENODATA; 16299f0f7272SThomas Graf 16309f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 16319f0f7272SThomas Graf if (nla == NULL) 16329f0f7272SThomas Graf return -EMSGSIZE; 16339f0f7272SThomas Graf 16349f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 16359f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 16369f0f7272SThomas Graf 16379f0f7272SThomas Graf return 0; 16389f0f7272SThomas Graf } 16399f0f7272SThomas Graf 16409f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 16419f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 16429f0f7272SThomas Graf }; 16439f0f7272SThomas Graf 1644cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1645cf7afbfeSThomas Graf const struct nlattr *nla) 16469f0f7272SThomas Graf { 16479f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 16489f0f7272SThomas Graf int err, rem; 16499f0f7272SThomas Graf 1650f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1651cf7afbfeSThomas Graf return -EAFNOSUPPORT; 16529f0f7272SThomas Graf 16539f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 16549f0f7272SThomas Graf if (err < 0) 16559f0f7272SThomas Graf return err; 16569f0f7272SThomas Graf 16579f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16589f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 16599f0f7272SThomas Graf int cfgid = nla_type(a); 16609f0f7272SThomas Graf 16619f0f7272SThomas Graf if (nla_len(a) < 4) 16629f0f7272SThomas Graf return -EINVAL; 16639f0f7272SThomas Graf 16649f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 16659f0f7272SThomas Graf return -EINVAL; 16669f0f7272SThomas Graf } 16679f0f7272SThomas Graf } 16689f0f7272SThomas Graf 1669cf7afbfeSThomas Graf return 0; 1670cf7afbfeSThomas Graf } 1671cf7afbfeSThomas Graf 1672cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1673cf7afbfeSThomas Graf { 1674f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1675cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1676cf7afbfeSThomas Graf int rem; 1677cf7afbfeSThomas Graf 1678cf7afbfeSThomas Graf if (!in_dev) 1679cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1680cf7afbfeSThomas Graf 1681cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1682cf7afbfeSThomas Graf BUG(); 1683cf7afbfeSThomas Graf 16849f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16859f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 16869f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 16879f0f7272SThomas Graf } 16889f0f7272SThomas Graf 16899f0f7272SThomas Graf return 0; 16909f0f7272SThomas Graf } 16919f0f7272SThomas Graf 1692edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 1693edc9e748SNicolas Dichtel { 1694edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 1695edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 1696edc9e748SNicolas Dichtel 16979e551110SNicolas Dichtel /* type -1 is used for ALL */ 16989e551110SNicolas Dichtel if (type == -1 || type == NETCONFA_FORWARDING) 1699edc9e748SNicolas Dichtel size += nla_total_size(4); 1700cc535dfbSNicolas Dichtel if (type == -1 || type == NETCONFA_RP_FILTER) 1701cc535dfbSNicolas Dichtel size += nla_total_size(4); 1702d67b8c61SNicolas Dichtel if (type == -1 || type == NETCONFA_MC_FORWARDING) 1703d67b8c61SNicolas Dichtel size += nla_total_size(4); 170409aea5dfSstephen hemminger if (type == -1 || type == NETCONFA_PROXY_NEIGH) 1705f085ff1cSstephen hemminger size += nla_total_size(4); 1706edc9e748SNicolas Dichtel 1707edc9e748SNicolas Dichtel return size; 1708edc9e748SNicolas Dichtel } 1709edc9e748SNicolas Dichtel 1710edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 1711edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 1712edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 1713edc9e748SNicolas Dichtel int type) 1714edc9e748SNicolas Dichtel { 1715edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 1716edc9e748SNicolas Dichtel struct netconfmsg *ncm; 1717edc9e748SNicolas Dichtel 1718edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 1719edc9e748SNicolas Dichtel flags); 1720edc9e748SNicolas Dichtel if (nlh == NULL) 1721edc9e748SNicolas Dichtel return -EMSGSIZE; 1722edc9e748SNicolas Dichtel 1723edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 1724edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 1725edc9e748SNicolas Dichtel 1726edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 1727edc9e748SNicolas Dichtel goto nla_put_failure; 1728edc9e748SNicolas Dichtel 17299e551110SNicolas Dichtel /* type -1 is used for ALL */ 17309e551110SNicolas Dichtel if ((type == -1 || type == NETCONFA_FORWARDING) && 1731edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 1732edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 1733edc9e748SNicolas Dichtel goto nla_put_failure; 1734cc535dfbSNicolas Dichtel if ((type == -1 || type == NETCONFA_RP_FILTER) && 1735cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 1736cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 1737cc535dfbSNicolas Dichtel goto nla_put_failure; 1738d67b8c61SNicolas Dichtel if ((type == -1 || type == NETCONFA_MC_FORWARDING) && 1739d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 1740d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 1741d67b8c61SNicolas Dichtel goto nla_put_failure; 174209aea5dfSstephen hemminger if ((type == -1 || type == NETCONFA_PROXY_NEIGH) && 174309aea5dfSstephen hemminger nla_put_s32(skb, NETCONFA_PROXY_NEIGH, 1744f085ff1cSstephen hemminger IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0) 1745f085ff1cSstephen hemminger goto nla_put_failure; 1746edc9e748SNicolas Dichtel 1747edc9e748SNicolas Dichtel return nlmsg_end(skb, nlh); 1748edc9e748SNicolas Dichtel 1749edc9e748SNicolas Dichtel nla_put_failure: 1750edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 1751edc9e748SNicolas Dichtel return -EMSGSIZE; 1752edc9e748SNicolas Dichtel } 1753edc9e748SNicolas Dichtel 1754d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, 1755edc9e748SNicolas Dichtel struct ipv4_devconf *devconf) 1756edc9e748SNicolas Dichtel { 1757edc9e748SNicolas Dichtel struct sk_buff *skb; 1758edc9e748SNicolas Dichtel int err = -ENOBUFS; 1759edc9e748SNicolas Dichtel 1760edc9e748SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC); 1761edc9e748SNicolas Dichtel if (skb == NULL) 1762edc9e748SNicolas Dichtel goto errout; 1763edc9e748SNicolas Dichtel 1764edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 1765edc9e748SNicolas Dichtel RTM_NEWNETCONF, 0, type); 1766edc9e748SNicolas Dichtel if (err < 0) { 1767edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 1768edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 1769edc9e748SNicolas Dichtel kfree_skb(skb); 1770edc9e748SNicolas Dichtel goto errout; 1771edc9e748SNicolas Dichtel } 1772edc9e748SNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC); 1773edc9e748SNicolas Dichtel return; 1774edc9e748SNicolas Dichtel errout: 1775edc9e748SNicolas Dichtel if (err < 0) 1776edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 1777edc9e748SNicolas Dichtel } 1778edc9e748SNicolas Dichtel 17799e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 17809e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 17819e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 1782cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 178309aea5dfSstephen hemminger [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 17849e551110SNicolas Dichtel }; 17859e551110SNicolas Dichtel 17869e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 1787661d2967SThomas Graf struct nlmsghdr *nlh) 17889e551110SNicolas Dichtel { 17899e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 17909e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 17919e551110SNicolas Dichtel struct netconfmsg *ncm; 17929e551110SNicolas Dichtel struct sk_buff *skb; 17939e551110SNicolas Dichtel struct ipv4_devconf *devconf; 17949e551110SNicolas Dichtel struct in_device *in_dev; 17959e551110SNicolas Dichtel struct net_device *dev; 17969e551110SNicolas Dichtel int ifindex; 17979e551110SNicolas Dichtel int err; 17989e551110SNicolas Dichtel 17999e551110SNicolas Dichtel err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 18009e551110SNicolas Dichtel devconf_ipv4_policy); 18019e551110SNicolas Dichtel if (err < 0) 18029e551110SNicolas Dichtel goto errout; 18039e551110SNicolas Dichtel 18049e551110SNicolas Dichtel err = EINVAL; 18059e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 18069e551110SNicolas Dichtel goto errout; 18079e551110SNicolas Dichtel 18089e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 18099e551110SNicolas Dichtel switch (ifindex) { 18109e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 18119e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 18129e551110SNicolas Dichtel break; 18139e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 18149e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 18159e551110SNicolas Dichtel break; 18169e551110SNicolas Dichtel default: 18179e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 18189e551110SNicolas Dichtel if (dev == NULL) 18199e551110SNicolas Dichtel goto errout; 18209e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 18219e551110SNicolas Dichtel if (in_dev == NULL) 18229e551110SNicolas Dichtel goto errout; 18239e551110SNicolas Dichtel devconf = &in_dev->cnf; 18249e551110SNicolas Dichtel break; 18259e551110SNicolas Dichtel } 18269e551110SNicolas Dichtel 18279e551110SNicolas Dichtel err = -ENOBUFS; 18289e551110SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC); 18299e551110SNicolas Dichtel if (skb == NULL) 18309e551110SNicolas Dichtel goto errout; 18319e551110SNicolas Dichtel 18329e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 18339e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 18349e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 18359e551110SNicolas Dichtel -1); 18369e551110SNicolas Dichtel if (err < 0) { 18379e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 18389e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 18399e551110SNicolas Dichtel kfree_skb(skb); 18409e551110SNicolas Dichtel goto errout; 18419e551110SNicolas Dichtel } 18429e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 18439e551110SNicolas Dichtel errout: 18449e551110SNicolas Dichtel return err; 18459e551110SNicolas Dichtel } 18469e551110SNicolas Dichtel 18477a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 18487a674200SNicolas Dichtel struct netlink_callback *cb) 18497a674200SNicolas Dichtel { 18507a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 18517a674200SNicolas Dichtel int h, s_h; 18527a674200SNicolas Dichtel int idx, s_idx; 18537a674200SNicolas Dichtel struct net_device *dev; 18547a674200SNicolas Dichtel struct in_device *in_dev; 18557a674200SNicolas Dichtel struct hlist_head *head; 18567a674200SNicolas Dichtel 18577a674200SNicolas Dichtel s_h = cb->args[0]; 18587a674200SNicolas Dichtel s_idx = idx = cb->args[1]; 18597a674200SNicolas Dichtel 18607a674200SNicolas Dichtel for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 18617a674200SNicolas Dichtel idx = 0; 18627a674200SNicolas Dichtel head = &net->dev_index_head[h]; 18637a674200SNicolas Dichtel rcu_read_lock(); 18640465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 18650465277fSNicolas Dichtel net->dev_base_seq; 18667a674200SNicolas Dichtel hlist_for_each_entry_rcu(dev, head, index_hlist) { 18677a674200SNicolas Dichtel if (idx < s_idx) 18687a674200SNicolas Dichtel goto cont; 18697a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 18707a674200SNicolas Dichtel if (!in_dev) 18717a674200SNicolas Dichtel goto cont; 18727a674200SNicolas Dichtel 18737a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, dev->ifindex, 18747a674200SNicolas Dichtel &in_dev->cnf, 18757a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18767a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18777a674200SNicolas Dichtel RTM_NEWNETCONF, 18787a674200SNicolas Dichtel NLM_F_MULTI, 18797a674200SNicolas Dichtel -1) <= 0) { 18807a674200SNicolas Dichtel rcu_read_unlock(); 18817a674200SNicolas Dichtel goto done; 18827a674200SNicolas Dichtel } 18830465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 18847a674200SNicolas Dichtel cont: 18857a674200SNicolas Dichtel idx++; 18867a674200SNicolas Dichtel } 18877a674200SNicolas Dichtel rcu_read_unlock(); 18887a674200SNicolas Dichtel } 18897a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES) { 18907a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 18917a674200SNicolas Dichtel net->ipv4.devconf_all, 18927a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18937a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18947a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 18957a674200SNicolas Dichtel -1) <= 0) 18967a674200SNicolas Dichtel goto done; 18977a674200SNicolas Dichtel else 18987a674200SNicolas Dichtel h++; 18997a674200SNicolas Dichtel } 19007a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES + 1) { 19017a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 19027a674200SNicolas Dichtel net->ipv4.devconf_dflt, 19037a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 19047a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 19057a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 19067a674200SNicolas Dichtel -1) <= 0) 19077a674200SNicolas Dichtel goto done; 19087a674200SNicolas Dichtel else 19097a674200SNicolas Dichtel h++; 19107a674200SNicolas Dichtel } 19117a674200SNicolas Dichtel done: 19127a674200SNicolas Dichtel cb->args[0] = h; 19137a674200SNicolas Dichtel cb->args[1] = idx; 19147a674200SNicolas Dichtel 19157a674200SNicolas Dichtel return skb->len; 19167a674200SNicolas Dichtel } 19177a674200SNicolas Dichtel 19181da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 19191da177e4SLinus Torvalds 1920c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 192131be3085SHerbert Xu { 192231be3085SHerbert Xu struct net_device *dev; 192331be3085SHerbert Xu 192431be3085SHerbert Xu rcu_read_lock(); 1925c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1926c6d14c84SEric Dumazet struct in_device *in_dev; 1927c6d14c84SEric Dumazet 192831be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 192931be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 19309355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1931c6d14c84SEric Dumazet } 193231be3085SHerbert Xu rcu_read_unlock(); 193331be3085SHerbert Xu } 193431be3085SHerbert Xu 1935c6d14c84SEric Dumazet /* called with RTNL locked */ 1936c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 193768dd299bSPavel Emelyanov { 193868dd299bSPavel Emelyanov struct net_device *dev; 1939586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 194068dd299bSPavel Emelyanov 1941586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 19429355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 1943edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1944edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1945edc9e748SNicolas Dichtel net->ipv4.devconf_all); 1946edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1947edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 1948edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 194968dd299bSPavel Emelyanov 1950c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 195168dd299bSPavel Emelyanov struct in_device *in_dev; 19520187bdfbSBen Hutchings if (on) 19530187bdfbSBen Hutchings dev_disable_lro(dev); 195468dd299bSPavel Emelyanov rcu_read_lock(); 195568dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 1956edc9e748SNicolas Dichtel if (in_dev) { 195768dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 1958edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1959edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 1960edc9e748SNicolas Dichtel } 196168dd299bSPavel Emelyanov rcu_read_unlock(); 196268dd299bSPavel Emelyanov } 196368dd299bSPavel Emelyanov } 196468dd299bSPavel Emelyanov 1965f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf) 1966f085ff1cSstephen hemminger { 1967f085ff1cSstephen hemminger if (cnf == net->ipv4.devconf_dflt) 1968f085ff1cSstephen hemminger return NETCONFA_IFINDEX_DEFAULT; 1969f085ff1cSstephen hemminger else if (cnf == net->ipv4.devconf_all) 1970f085ff1cSstephen hemminger return NETCONFA_IFINDEX_ALL; 1971f085ff1cSstephen hemminger else { 1972f085ff1cSstephen hemminger struct in_device *idev 1973f085ff1cSstephen hemminger = container_of(cnf, struct in_device, cnf); 1974f085ff1cSstephen hemminger return idev->dev->ifindex; 1975f085ff1cSstephen hemminger } 1976f085ff1cSstephen hemminger } 1977f085ff1cSstephen hemminger 1978fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write, 19798d65af78SAlexey Dobriyan void __user *buffer, 198031be3085SHerbert Xu size_t *lenp, loff_t *ppos) 198131be3085SHerbert Xu { 1982d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 19838d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 1984d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 198531be3085SHerbert Xu 198631be3085SHerbert Xu if (write) { 198731be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 1988c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 198931be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 1990f085ff1cSstephen hemminger int ifindex; 199131be3085SHerbert Xu 199231be3085SHerbert Xu set_bit(i, cnf->state); 199331be3085SHerbert Xu 19949355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 1995c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 1996d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 1997d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 1998d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 19994ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2000f085ff1cSstephen hemminger 2001cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 2002cc535dfbSNicolas Dichtel new_value != old_value) { 2003f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 2004cc535dfbSNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER, 2005cc535dfbSNicolas Dichtel ifindex, cnf); 2006cc535dfbSNicolas Dichtel } 2007f085ff1cSstephen hemminger if (i == IPV4_DEVCONF_PROXY_ARP - 1 && 2008f085ff1cSstephen hemminger new_value != old_value) { 2009f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 201009aea5dfSstephen hemminger inet_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH, 2011f085ff1cSstephen hemminger ifindex, cnf); 2012f085ff1cSstephen hemminger } 201331be3085SHerbert Xu } 201431be3085SHerbert Xu 201531be3085SHerbert Xu return ret; 201631be3085SHerbert Xu } 201731be3085SHerbert Xu 2018fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write, 20198d65af78SAlexey Dobriyan void __user *buffer, 20201da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 20211da177e4SLinus Torvalds { 20221da177e4SLinus Torvalds int *valp = ctl->data; 20231da177e4SLinus Torvalds int val = *valp; 202488af182eSEric W. Biederman loff_t pos = *ppos; 20258d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 20261da177e4SLinus Torvalds 20271da177e4SLinus Torvalds if (write && *valp != val) { 2028c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 2029c0ce9fb3SPavel Emelyanov 20300187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 203188af182eSEric W. Biederman if (!rtnl_trylock()) { 203288af182eSEric W. Biederman /* Restore the original values before restarting */ 203388af182eSEric W. Biederman *valp = val; 203488af182eSEric W. Biederman *ppos = pos; 20359b8adb5eSEric W. Biederman return restart_syscall(); 203688af182eSEric W. Biederman } 20370187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2038c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2039edc9e748SNicolas Dichtel } else { 20400187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 20410187bdfbSBen Hutchings struct in_device *idev = 20420187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2043edc9e748SNicolas Dichtel if (*valp) 20440187bdfbSBen Hutchings dev_disable_lro(idev->dev); 2045edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, 2046edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2047edc9e748SNicolas Dichtel idev->dev->ifindex, 2048edc9e748SNicolas Dichtel cnf); 20490187bdfbSBen Hutchings } 20500187bdfbSBen Hutchings rtnl_unlock(); 20514ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2052edc9e748SNicolas Dichtel } else 2053edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 2054edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2055edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 20560187bdfbSBen Hutchings } 20571da177e4SLinus Torvalds 20581da177e4SLinus Torvalds return ret; 20591da177e4SLinus Torvalds } 20601da177e4SLinus Torvalds 2061fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 20628d65af78SAlexey Dobriyan void __user *buffer, 20631da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 20641da177e4SLinus Torvalds { 20651da177e4SLinus Torvalds int *valp = ctl->data; 20661da177e4SLinus Torvalds int val = *valp; 20678d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 206876e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 20691da177e4SLinus Torvalds 20701da177e4SLinus Torvalds if (write && *valp != val) 20714ccfe6d4SNicolas Dichtel rt_cache_flush(net); 20721da177e4SLinus Torvalds 20731da177e4SLinus Torvalds return ret; 20741da177e4SLinus Torvalds } 20751da177e4SLinus Torvalds 2076f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 207742f811b8SHerbert Xu { \ 207842f811b8SHerbert Xu .procname = name, \ 207942f811b8SHerbert Xu .data = ipv4_devconf.data + \ 208002291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 208142f811b8SHerbert Xu .maxlen = sizeof(int), \ 208242f811b8SHerbert Xu .mode = mval, \ 208342f811b8SHerbert Xu .proc_handler = proc, \ 208431be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 208542f811b8SHerbert Xu } 208642f811b8SHerbert Xu 208742f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2088f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 208942f811b8SHerbert Xu 209042f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2091f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 209242f811b8SHerbert Xu 2093f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2094f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 209542f811b8SHerbert Xu 209642f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2097f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 209842f811b8SHerbert Xu 20991da177e4SLinus Torvalds static struct devinet_sysctl_table { 21001da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 210102291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 21021da177e4SLinus Torvalds } devinet_sysctl = { 21031da177e4SLinus Torvalds .devinet_vars = { 210442f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2105f8572d8fSEric W. Biederman devinet_sysctl_forward), 210642f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 210742f811b8SHerbert Xu 210842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 210942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 211042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 211142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 211242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 211342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 211442f811b8SHerbert Xu "accept_source_route"), 21158153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 211628f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 211742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 211842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 211942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 212042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 212142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 212242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 212342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 212442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 212542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2126eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 212765324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 21285c6fe01cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 21295c6fe01cSWilliam Manley "force_igmp_version"), 21302690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, 21312690048cSWilliam Manley "igmpv2_unsolicited_report_interval"), 21322690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, 21332690048cSWilliam Manley "igmpv3_unsolicited_report_interval"), 213442f811b8SHerbert Xu 213542f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 213642f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 213742f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 213842f811b8SHerbert Xu "promote_secondaries"), 2139d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2140d0daebc3SThomas Graf "route_localnet"), 21411da177e4SLinus Torvalds }, 21421da177e4SLinus Torvalds }; 21431da177e4SLinus Torvalds 2144ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 2145f8572d8fSEric W. Biederman struct ipv4_devconf *p) 21461da177e4SLinus Torvalds { 21471da177e4SLinus Torvalds int i; 21489fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 21498607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2150bfada697SPavel Emelyanov 21519fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 21521da177e4SLinus Torvalds if (!t) 21539fa89642SPavel Emelyanov goto out; 21549fa89642SPavel Emelyanov 21551da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 21561da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 215731be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2158c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 21591da177e4SLinus Torvalds } 21601da177e4SLinus Torvalds 21618607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 21621da177e4SLinus Torvalds 21638607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 21641da177e4SLinus Torvalds if (!t->sysctl_header) 21658607ddb8SEric W. Biederman goto free; 21661da177e4SLinus Torvalds 21671da177e4SLinus Torvalds p->sysctl = t; 2168ea40b324SPavel Emelyanov return 0; 21691da177e4SLinus Torvalds 21701da177e4SLinus Torvalds free: 21711da177e4SLinus Torvalds kfree(t); 21729fa89642SPavel Emelyanov out: 2173ea40b324SPavel Emelyanov return -ENOBUFS; 21741da177e4SLinus Torvalds } 21751da177e4SLinus Torvalds 217651602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 217766f27a52SPavel Emelyanov { 217851602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 217966f27a52SPavel Emelyanov 218051602b2aSPavel Emelyanov if (t == NULL) 218151602b2aSPavel Emelyanov return; 218251602b2aSPavel Emelyanov 218351602b2aSPavel Emelyanov cnf->sysctl = NULL; 2184ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 21851da177e4SLinus Torvalds kfree(t); 21861da177e4SLinus Torvalds } 218751602b2aSPavel Emelyanov 218851602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev) 218951602b2aSPavel Emelyanov { 219073af614aSJiri Pirko neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); 2191c346dca1SYOSHIFUJI Hideaki __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 2192f8572d8fSEric W. Biederman &idev->cnf); 219351602b2aSPavel Emelyanov } 219451602b2aSPavel Emelyanov 219551602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 219651602b2aSPavel Emelyanov { 219751602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 219851602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 21991da177e4SLinus Torvalds } 22001da177e4SLinus Torvalds 220168dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 220268dd299bSPavel Emelyanov { 220368dd299bSPavel Emelyanov .procname = "ip_forward", 220468dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 220502291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 220668dd299bSPavel Emelyanov .maxlen = sizeof(int), 220768dd299bSPavel Emelyanov .mode = 0644, 220868dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 220968dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2210c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 221168dd299bSPavel Emelyanov }, 221268dd299bSPavel Emelyanov { }, 221368dd299bSPavel Emelyanov }; 22142a75de0cSEric Dumazet #endif 221568dd299bSPavel Emelyanov 2216752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2217752d14dcSPavel Emelyanov { 2218752d14dcSPavel Emelyanov int err; 2219752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 22202a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 22212a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 2222752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 22232a75de0cSEric Dumazet #endif 2224752d14dcSPavel Emelyanov 2225752d14dcSPavel Emelyanov err = -ENOMEM; 2226752d14dcSPavel Emelyanov all = &ipv4_devconf; 2227752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 2228752d14dcSPavel Emelyanov 222909ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 2230752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 2231752d14dcSPavel Emelyanov if (all == NULL) 2232752d14dcSPavel Emelyanov goto err_alloc_all; 2233752d14dcSPavel Emelyanov 2234752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 2235752d14dcSPavel Emelyanov if (dflt == NULL) 2236752d14dcSPavel Emelyanov goto err_alloc_dflt; 2237752d14dcSPavel Emelyanov 22382a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2239752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 2240752d14dcSPavel Emelyanov if (tbl == NULL) 2241752d14dcSPavel Emelyanov goto err_alloc_ctl; 2242752d14dcSPavel Emelyanov 224302291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2244752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2245752d14dcSPavel Emelyanov tbl[0].extra2 = net; 22462a75de0cSEric Dumazet #endif 2247752d14dcSPavel Emelyanov } 2248752d14dcSPavel Emelyanov 2249752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2250f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 2251752d14dcSPavel Emelyanov if (err < 0) 2252752d14dcSPavel Emelyanov goto err_reg_all; 2253752d14dcSPavel Emelyanov 2254f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 2255752d14dcSPavel Emelyanov if (err < 0) 2256752d14dcSPavel Emelyanov goto err_reg_dflt; 2257752d14dcSPavel Emelyanov 2258752d14dcSPavel Emelyanov err = -ENOMEM; 22598607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 2260752d14dcSPavel Emelyanov if (forw_hdr == NULL) 2261752d14dcSPavel Emelyanov goto err_reg_ctl; 22622a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2263752d14dcSPavel Emelyanov #endif 2264752d14dcSPavel Emelyanov 2265752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2266752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2267752d14dcSPavel Emelyanov return 0; 2268752d14dcSPavel Emelyanov 2269752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2270752d14dcSPavel Emelyanov err_reg_ctl: 2271752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 2272752d14dcSPavel Emelyanov err_reg_dflt: 2273752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 2274752d14dcSPavel Emelyanov err_reg_all: 2275752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 2276752d14dcSPavel Emelyanov kfree(tbl); 2277752d14dcSPavel Emelyanov err_alloc_ctl: 22782a75de0cSEric Dumazet #endif 2279752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 2280752d14dcSPavel Emelyanov kfree(dflt); 2281752d14dcSPavel Emelyanov err_alloc_dflt: 2282752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 2283752d14dcSPavel Emelyanov kfree(all); 2284752d14dcSPavel Emelyanov err_alloc_all: 2285752d14dcSPavel Emelyanov return err; 2286752d14dcSPavel Emelyanov } 2287752d14dcSPavel Emelyanov 2288752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2289752d14dcSPavel Emelyanov { 22902a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2291752d14dcSPavel Emelyanov struct ctl_table *tbl; 2292752d14dcSPavel Emelyanov 2293752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2294752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2295752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 2296752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 2297752d14dcSPavel Emelyanov kfree(tbl); 22982a75de0cSEric Dumazet #endif 2299752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2300752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2301752d14dcSPavel Emelyanov } 2302752d14dcSPavel Emelyanov 2303752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2304752d14dcSPavel Emelyanov .init = devinet_init_net, 2305752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2306752d14dcSPavel Emelyanov }; 2307752d14dcSPavel Emelyanov 23089f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = { 23099f0f7272SThomas Graf .family = AF_INET, 23109f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 23119f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2312cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2313cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 23149f0f7272SThomas Graf }; 23159f0f7272SThomas Graf 23161da177e4SLinus Torvalds void __init devinet_init(void) 23171da177e4SLinus Torvalds { 2318fd23c3b3SDavid S. Miller int i; 2319fd23c3b3SDavid S. Miller 2320fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2321fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2322fd23c3b3SDavid S. Miller 2323752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 2324752d14dcSPavel Emelyanov 23251da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 23261da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 232763f3444fSThomas Graf 2328906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 23295c766d64SJiri Pirko 23309f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 23319f0f7272SThomas Graf 2332c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); 2333c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); 2334c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); 23359e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 23367a674200SNicolas Dichtel inet_netconf_dump_devconf, NULL); 23371da177e4SLinus Torvalds } 23381da177e4SLinus Torvalds 2339