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 <asm/system.h> 311da177e4SLinus Torvalds #include <linux/bitops.h> 324fc268d2SRandy Dunlap #include <linux/capability.h> 331da177e4SLinus Torvalds #include <linux/module.h> 341da177e4SLinus Torvalds #include <linux/types.h> 351da177e4SLinus Torvalds #include <linux/kernel.h> 361da177e4SLinus Torvalds #include <linux/string.h> 371da177e4SLinus Torvalds #include <linux/mm.h> 381da177e4SLinus Torvalds #include <linux/socket.h> 391da177e4SLinus Torvalds #include <linux/sockios.h> 401da177e4SLinus Torvalds #include <linux/in.h> 411da177e4SLinus Torvalds #include <linux/errno.h> 421da177e4SLinus Torvalds #include <linux/interrupt.h> 431823730fSThomas Graf #include <linux/if_addr.h> 441da177e4SLinus Torvalds #include <linux/if_ether.h> 451da177e4SLinus Torvalds #include <linux/inet.h> 461da177e4SLinus Torvalds #include <linux/netdevice.h> 471da177e4SLinus Torvalds #include <linux/etherdevice.h> 481da177e4SLinus Torvalds #include <linux/skbuff.h> 491da177e4SLinus Torvalds #include <linux/init.h> 501da177e4SLinus Torvalds #include <linux/notifier.h> 511da177e4SLinus Torvalds #include <linux/inetdevice.h> 521da177e4SLinus Torvalds #include <linux/igmp.h> 535a0e3ad6STejun Heo #include <linux/slab.h> 54fd23c3b3SDavid S. Miller #include <linux/hash.h> 551da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 561da177e4SLinus Torvalds #include <linux/sysctl.h> 571da177e4SLinus Torvalds #endif 581da177e4SLinus Torvalds #include <linux/kmod.h> 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> 661da177e4SLinus Torvalds 670027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 6842f811b8SHerbert Xu .data = { 6902291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7002291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7102291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7202291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 7342f811b8SHerbert Xu }, 741da177e4SLinus Torvalds }; 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 7742f811b8SHerbert Xu .data = { 7802291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7902291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8002291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8102291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8202291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 8342f811b8SHerbert Xu }, 841da177e4SLinus Torvalds }; 851da177e4SLinus Torvalds 869355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 879355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 8842f811b8SHerbert Xu 89ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 905c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 915c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 925c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 935176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 945c753978SThomas Graf }; 955c753978SThomas Graf 96fd23c3b3SDavid S. Miller /* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE 97fd23c3b3SDavid S. Miller * value. So if you change this define, make appropriate changes to 98fd23c3b3SDavid S. Miller * inet_addr_hash as well. 99fd23c3b3SDavid S. Miller */ 100fd23c3b3SDavid S. Miller #define IN4_ADDR_HSIZE 256 101fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 102fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock); 103fd23c3b3SDavid S. Miller 104fd23c3b3SDavid S. Miller static inline unsigned int inet_addr_hash(struct net *net, __be32 addr) 105fd23c3b3SDavid S. Miller { 106fd23c3b3SDavid S. Miller u32 val = (__force u32) addr ^ hash_ptr(net, 8); 107fd23c3b3SDavid S. Miller 108fd23c3b3SDavid S. Miller return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) & 109fd23c3b3SDavid S. Miller (IN4_ADDR_HSIZE - 1)); 110fd23c3b3SDavid S. Miller } 111fd23c3b3SDavid S. Miller 112fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 113fd23c3b3SDavid S. Miller { 114e066008bSDavid S. Miller unsigned int hash = inet_addr_hash(net, ifa->ifa_local); 115fd23c3b3SDavid S. Miller 116fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 117fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 118fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 119fd23c3b3SDavid S. Miller } 120fd23c3b3SDavid S. Miller 121fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 122fd23c3b3SDavid S. Miller { 123fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 124fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 125fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 126fd23c3b3SDavid S. Miller } 127fd23c3b3SDavid S. Miller 1289435eb1cSDavid S. Miller /** 1299435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1309435eb1cSDavid S. Miller * @net: the net namespace 1319435eb1cSDavid S. Miller * @addr: the source address 1329435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1339435eb1cSDavid S. Miller * 1349435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1359435eb1cSDavid S. Miller */ 1369435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1379435eb1cSDavid S. Miller { 1389435eb1cSDavid S. Miller unsigned int hash = inet_addr_hash(net, addr); 1399435eb1cSDavid S. Miller struct net_device *result = NULL; 1409435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1419435eb1cSDavid S. Miller struct hlist_node *node; 1429435eb1cSDavid S. Miller 1439435eb1cSDavid S. Miller rcu_read_lock(); 1449435eb1cSDavid S. Miller hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) { 1459435eb1cSDavid S. Miller struct net_device *dev = ifa->ifa_dev->dev; 1469435eb1cSDavid S. Miller 1479435eb1cSDavid S. Miller if (!net_eq(dev_net(dev), net)) 1489435eb1cSDavid S. Miller continue; 149e066008bSDavid S. Miller if (ifa->ifa_local == addr) { 1509435eb1cSDavid S. Miller result = dev; 1519435eb1cSDavid S. Miller break; 1529435eb1cSDavid S. Miller } 1539435eb1cSDavid S. Miller } 1549435eb1cSDavid S. Miller if (result && devref) 1559435eb1cSDavid S. Miller dev_hold(result); 1569435eb1cSDavid S. Miller rcu_read_unlock(); 1579435eb1cSDavid S. Miller return result; 1589435eb1cSDavid S. Miller } 1599435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1609435eb1cSDavid S. Miller 161d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1621da177e4SLinus Torvalds 163e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1641da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1651da177e4SLinus Torvalds int destroy); 1661da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 16766f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev); 16851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 16951602b2aSPavel Emelyanov #else 17051602b2aSPavel Emelyanov static inline void devinet_sysctl_register(struct in_device *idev) 17151602b2aSPavel Emelyanov { 17251602b2aSPavel Emelyanov } 17351602b2aSPavel Emelyanov static inline void devinet_sysctl_unregister(struct in_device *idev) 17451602b2aSPavel Emelyanov { 17551602b2aSPavel Emelyanov } 1761da177e4SLinus Torvalds #endif 1771da177e4SLinus Torvalds 1781da177e4SLinus Torvalds /* Locks all the inet devices. */ 1791da177e4SLinus Torvalds 1801da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 1811da177e4SLinus Torvalds { 18293adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 1831da177e4SLinus Torvalds } 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 1861da177e4SLinus Torvalds { 1871da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 1881da177e4SLinus Torvalds if (ifa->ifa_dev) 1891da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 1901da177e4SLinus Torvalds kfree(ifa); 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds static inline void inet_free_ifa(struct in_ifaddr *ifa) 1941da177e4SLinus Torvalds { 1951da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 1991da177e4SLinus Torvalds { 2001da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2011da177e4SLinus Torvalds 202547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 203547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 2041da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 2051da177e4SLinus Torvalds printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n", 2061da177e4SLinus Torvalds idev, dev ? dev->name : "NIL"); 2071da177e4SLinus Torvalds #endif 2081da177e4SLinus Torvalds dev_put(dev); 2091da177e4SLinus Torvalds if (!idev->dead) 2109f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2119f9354b9SEric Dumazet else 2121da177e4SLinus Torvalds kfree(idev); 2131da177e4SLinus Torvalds } 2149f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2151da177e4SLinus Torvalds 21671e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2171da177e4SLinus Torvalds { 2181da177e4SLinus Torvalds struct in_device *in_dev; 2191da177e4SLinus Torvalds 2201da177e4SLinus Torvalds ASSERT_RTNL(); 2211da177e4SLinus Torvalds 2220da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2231da177e4SLinus Torvalds if (!in_dev) 2241da177e4SLinus Torvalds goto out; 225c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2269355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2271da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2281da177e4SLinus Torvalds in_dev->dev = dev; 2299f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2309f9354b9SEric Dumazet if (!in_dev->arp_parms) 2311da177e4SLinus Torvalds goto out_kfree; 2320187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2330187bdfbSBen Hutchings dev_disable_lro(dev); 2341da177e4SLinus Torvalds /* Reference in_dev->dev */ 2351da177e4SLinus Torvalds dev_hold(dev); 23630c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2371da177e4SLinus Torvalds in_dev_hold(in_dev); 2381da177e4SLinus Torvalds 23966f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 2401da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2411da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2421da177e4SLinus Torvalds ip_mc_up(in_dev); 243483479ecSJarek Poplawski 24430c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 24530c4cf57SDavid L Stevens rcu_assign_pointer(dev->ip_ptr, in_dev); 246483479ecSJarek Poplawski out: 2471da177e4SLinus Torvalds return in_dev; 2481da177e4SLinus Torvalds out_kfree: 2491da177e4SLinus Torvalds kfree(in_dev); 2501da177e4SLinus Torvalds in_dev = NULL; 2511da177e4SLinus Torvalds goto out; 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds 2541da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2551da177e4SLinus Torvalds { 2561da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2571da177e4SLinus Torvalds in_dev_put(idev); 2581da177e4SLinus Torvalds } 2591da177e4SLinus Torvalds 2601da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2611da177e4SLinus Torvalds { 2621da177e4SLinus Torvalds struct in_ifaddr *ifa; 2631da177e4SLinus Torvalds struct net_device *dev; 2641da177e4SLinus Torvalds 2651da177e4SLinus Torvalds ASSERT_RTNL(); 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds dev = in_dev->dev; 2681da177e4SLinus Torvalds 2691da177e4SLinus Torvalds in_dev->dead = 1; 2701da177e4SLinus Torvalds 2711da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 2741da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 2751da177e4SLinus Torvalds inet_free_ifa(ifa); 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds 27895ae6b22SEric Dumazet rcu_assign_pointer(dev->ip_ptr, NULL); 2791da177e4SLinus Torvalds 28051602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 2811da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 2821da177e4SLinus Torvalds arp_ifdown(dev); 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 2851da177e4SLinus Torvalds } 2861da177e4SLinus Torvalds 287ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 2881da177e4SLinus Torvalds { 2891da177e4SLinus Torvalds rcu_read_lock(); 2901da177e4SLinus Torvalds for_primary_ifa(in_dev) { 2911da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 2921da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 2931da177e4SLinus Torvalds rcu_read_unlock(); 2941da177e4SLinus Torvalds return 1; 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds } 2971da177e4SLinus Torvalds } endfor_ifa(in_dev); 2981da177e4SLinus Torvalds rcu_read_unlock(); 2991da177e4SLinus Torvalds return 0; 3001da177e4SLinus Torvalds } 3011da177e4SLinus Torvalds 302d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 303d6062cbbSThomas Graf int destroy, struct nlmsghdr *nlh, u32 pid) 3041da177e4SLinus Torvalds { 3058f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3060ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3070ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3080ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3090ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds ASSERT_RTNL(); 3121da177e4SLinus Torvalds 3138f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3148f937c60SHarald Welte * unless alias promotion is set 3158f937c60SHarald Welte **/ 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3181da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3210ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3220ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3230ff60a45SJamal Hadi Salim last_prim = ifa; 3240ff60a45SJamal Hadi Salim 3251da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3261da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3271da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3281da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3290ff60a45SJamal Hadi Salim prev_prom = ifa; 3301da177e4SLinus Torvalds continue; 3311da177e4SLinus Torvalds } 3321da177e4SLinus Torvalds 3330ff60a45SJamal Hadi Salim if (!do_promote) { 334fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3351da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3361da177e4SLinus Torvalds 337d6062cbbSThomas Graf rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid); 338e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 339e041c683SAlan Stern NETDEV_DOWN, ifa); 3401da177e4SLinus Torvalds inet_free_ifa(ifa); 3418f937c60SHarald Welte } else { 3428f937c60SHarald Welte promote = ifa; 3438f937c60SHarald Welte break; 3448f937c60SHarald Welte } 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds } 3471da177e4SLinus Torvalds 348*2d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 349*2d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 350*2d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 351*2d230e2bSJulian Anastasov * while all addresses are on the device list. 352*2d230e2bSJulian Anastasov */ 353*2d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 354*2d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 355*2d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 356*2d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 357*2d230e2bSJulian Anastasov } 358*2d230e2bSJulian Anastasov 3591da177e4SLinus Torvalds /* 2. Unlink it */ 3601da177e4SLinus Torvalds 3611da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 362fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3631da177e4SLinus Torvalds 3641da177e4SLinus Torvalds /* 3. Announce address deletion */ 3651da177e4SLinus Torvalds 3661da177e4SLinus Torvalds /* Send message first, then call notifier. 3671da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 3681da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 3691da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 3701da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 3711da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 3721da177e4SLinus Torvalds So that, this order is correct. 3731da177e4SLinus Torvalds */ 374d6062cbbSThomas Graf rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid); 375e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 3760ff60a45SJamal Hadi Salim 3770ff60a45SJamal Hadi Salim if (promote) { 3780ff60a45SJamal Hadi Salim 3790ff60a45SJamal Hadi Salim if (prev_prom) { 3800ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 3810ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 3820ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 3830ff60a45SJamal Hadi Salim } 3840ff60a45SJamal Hadi Salim 3850ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 386d6062cbbSThomas Graf rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid); 387e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 388e041c683SAlan Stern NETDEV_UP, promote); 3890ff60a45SJamal Hadi Salim for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { 3900ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 3910ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 3920ff60a45SJamal Hadi Salim continue; 3930ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 3940ff60a45SJamal Hadi Salim } 3950ff60a45SJamal Hadi Salim 3960ff60a45SJamal Hadi Salim } 3976363097cSHerbert Xu if (destroy) 3981da177e4SLinus Torvalds inet_free_ifa(ifa1); 3991da177e4SLinus Torvalds } 4001da177e4SLinus Torvalds 401d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 402d6062cbbSThomas Graf int destroy) 403d6062cbbSThomas Graf { 404d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 405d6062cbbSThomas Graf } 406d6062cbbSThomas Graf 407d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 408d6062cbbSThomas Graf u32 pid) 4091da177e4SLinus Torvalds { 4101da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4111da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4121da177e4SLinus Torvalds 4131da177e4SLinus Torvalds ASSERT_RTNL(); 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds if (!ifa->ifa_local) { 4161da177e4SLinus Torvalds inet_free_ifa(ifa); 4171da177e4SLinus Torvalds return 0; 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds 4201da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4211da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4221da177e4SLinus Torvalds 4231da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4241da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4251da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4261da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4271da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4281da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4291da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4301da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4311da177e4SLinus Torvalds inet_free_ifa(ifa); 4321da177e4SLinus Torvalds return -EEXIST; 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4351da177e4SLinus Torvalds inet_free_ifa(ifa); 4361da177e4SLinus Torvalds return -EINVAL; 4371da177e4SLinus Torvalds } 4381da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4391da177e4SLinus Torvalds } 4401da177e4SLinus Torvalds } 4411da177e4SLinus Torvalds 4421da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 4431da177e4SLinus Torvalds net_srandom(ifa->ifa_local); 4441da177e4SLinus Torvalds ifap = last_primary; 4451da177e4SLinus Torvalds } 4461da177e4SLinus Torvalds 4471da177e4SLinus Torvalds ifa->ifa_next = *ifap; 4481da177e4SLinus Torvalds *ifap = ifa; 4491da177e4SLinus Torvalds 450fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 451fd23c3b3SDavid S. Miller 4521da177e4SLinus Torvalds /* Send message first, then call notifier. 4531da177e4SLinus Torvalds Notifier will trigger FIB update, so that 4541da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 455d6062cbbSThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid); 456e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 4571da177e4SLinus Torvalds 4581da177e4SLinus Torvalds return 0; 4591da177e4SLinus Torvalds } 4601da177e4SLinus Torvalds 461d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 462d6062cbbSThomas Graf { 463d6062cbbSThomas Graf return __inet_insert_ifa(ifa, NULL, 0); 464d6062cbbSThomas Graf } 465d6062cbbSThomas Graf 4661da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 4671da177e4SLinus Torvalds { 468e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 4691da177e4SLinus Torvalds 4701da177e4SLinus Torvalds ASSERT_RTNL(); 4711da177e4SLinus Torvalds 4721da177e4SLinus Torvalds if (!in_dev) { 4731da177e4SLinus Torvalds inet_free_ifa(ifa); 4741da177e4SLinus Torvalds return -ENOBUFS; 4751da177e4SLinus Torvalds } 47671e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 4771da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 478547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 4791da177e4SLinus Torvalds in_dev_hold(in_dev); 4801da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 4811da177e4SLinus Torvalds } 482f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 4831da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 4841da177e4SLinus Torvalds return inet_insert_ifa(ifa); 4851da177e4SLinus Torvalds } 4861da177e4SLinus Torvalds 4878723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 4888723e1b4SEric Dumazet * We dont take a reference on found in_device 4898723e1b4SEric Dumazet */ 4907fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 4911da177e4SLinus Torvalds { 4921da177e4SLinus Torvalds struct net_device *dev; 4931da177e4SLinus Torvalds struct in_device *in_dev = NULL; 494c148fc2eSEric Dumazet 495c148fc2eSEric Dumazet rcu_read_lock(); 496c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 4971da177e4SLinus Torvalds if (dev) 4988723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 499c148fc2eSEric Dumazet rcu_read_unlock(); 5001da177e4SLinus Torvalds return in_dev; 5011da177e4SLinus Torvalds } 5029f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5031da177e4SLinus Torvalds 5041da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5051da177e4SLinus Torvalds 50660cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 50760cad5daSAl Viro __be32 mask) 5081da177e4SLinus Torvalds { 5091da177e4SLinus Torvalds ASSERT_RTNL(); 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5121da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5131da177e4SLinus Torvalds return ifa; 5141da177e4SLinus Torvalds } endfor_ifa(in_dev); 5151da177e4SLinus Torvalds return NULL; 5161da177e4SLinus Torvalds } 5171da177e4SLinus Torvalds 5181da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 5191da177e4SLinus Torvalds { 5203b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 521dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5221da177e4SLinus Torvalds struct in_device *in_dev; 523dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 5241da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 525dfdd5fd4SThomas Graf int err = -EINVAL; 5261da177e4SLinus Torvalds 5271da177e4SLinus Torvalds ASSERT_RTNL(); 5281da177e4SLinus Torvalds 529dfdd5fd4SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 530dfdd5fd4SThomas Graf if (err < 0) 531dfdd5fd4SThomas Graf goto errout; 532dfdd5fd4SThomas Graf 533dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 5347fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 535dfdd5fd4SThomas Graf if (in_dev == NULL) { 536dfdd5fd4SThomas Graf err = -ENODEV; 537dfdd5fd4SThomas Graf goto errout; 538dfdd5fd4SThomas Graf } 539dfdd5fd4SThomas Graf 5401da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 5411da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 542dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 543a7a628c4SAl Viro ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) 5441da177e4SLinus Torvalds continue; 545dfdd5fd4SThomas Graf 546dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 547dfdd5fd4SThomas Graf continue; 548dfdd5fd4SThomas Graf 549dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 550dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 551a7a628c4SAl Viro !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 552dfdd5fd4SThomas Graf continue; 553dfdd5fd4SThomas Graf 554d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid); 5551da177e4SLinus Torvalds return 0; 5561da177e4SLinus Torvalds } 557dfdd5fd4SThomas Graf 558dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 559dfdd5fd4SThomas Graf errout: 560dfdd5fd4SThomas Graf return err; 5611da177e4SLinus Torvalds } 5621da177e4SLinus Torvalds 5634b8aa9abSDenis V. Lunev static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh) 5641da177e4SLinus Torvalds { 5655c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5665c753978SThomas Graf struct in_ifaddr *ifa; 5675c753978SThomas Graf struct ifaddrmsg *ifm; 5681da177e4SLinus Torvalds struct net_device *dev; 5691da177e4SLinus Torvalds struct in_device *in_dev; 5707b218574SDenis V. Lunev int err; 5711da177e4SLinus Torvalds 5725c753978SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 5735c753978SThomas Graf if (err < 0) 5745c753978SThomas Graf goto errout; 5751da177e4SLinus Torvalds 5765c753978SThomas Graf ifm = nlmsg_data(nlh); 577c4e38f41SEvgeniy Polyakov err = -EINVAL; 5787b218574SDenis V. Lunev if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) 5795c753978SThomas Graf goto errout; 5801da177e4SLinus Torvalds 5814b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 5825c753978SThomas Graf err = -ENODEV; 5837b218574SDenis V. Lunev if (dev == NULL) 5845c753978SThomas Graf goto errout; 5851da177e4SLinus Torvalds 5865c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 5875c753978SThomas Graf err = -ENOBUFS; 5887b218574SDenis V. Lunev if (in_dev == NULL) 5895c753978SThomas Graf goto errout; 59071e27da9SHerbert Xu 5915c753978SThomas Graf ifa = inet_alloc_ifa(); 5927b218574SDenis V. Lunev if (ifa == NULL) 5935c753978SThomas Graf /* 5945c753978SThomas Graf * A potential indev allocation can be left alive, it stays 5955c753978SThomas Graf * assigned to its device and is destroy with it. 5965c753978SThomas Graf */ 5975c753978SThomas Graf goto errout; 5985c753978SThomas Graf 599a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 6005c753978SThomas Graf in_dev_hold(in_dev); 6015c753978SThomas Graf 6025c753978SThomas Graf if (tb[IFA_ADDRESS] == NULL) 6035c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 6045c753978SThomas Graf 605fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 6061da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 6071da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 6081da177e4SLinus Torvalds ifa->ifa_flags = ifm->ifa_flags; 6091da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 6101da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 6115c753978SThomas Graf 612a7a628c4SAl Viro ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); 613a7a628c4SAl Viro ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); 6145c753978SThomas Graf 6155c753978SThomas Graf if (tb[IFA_BROADCAST]) 616a7a628c4SAl Viro ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); 6175c753978SThomas Graf 6185c753978SThomas Graf if (tb[IFA_LABEL]) 6195c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 6201da177e4SLinus Torvalds else 6211da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 6221da177e4SLinus Torvalds 6235c753978SThomas Graf return ifa; 6245c753978SThomas Graf 6255c753978SThomas Graf errout: 6265c753978SThomas Graf return ERR_PTR(err); 6275c753978SThomas Graf } 6285c753978SThomas Graf 6295c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 6305c753978SThomas Graf { 6313b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 6325c753978SThomas Graf struct in_ifaddr *ifa; 6335c753978SThomas Graf 6345c753978SThomas Graf ASSERT_RTNL(); 6355c753978SThomas Graf 6364b8aa9abSDenis V. Lunev ifa = rtm_to_ifaddr(net, nlh); 6375c753978SThomas Graf if (IS_ERR(ifa)) 6385c753978SThomas Graf return PTR_ERR(ifa); 6395c753978SThomas Graf 640d6062cbbSThomas Graf return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid); 6411da177e4SLinus Torvalds } 6421da177e4SLinus Torvalds 6431da177e4SLinus Torvalds /* 6441da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 6451da177e4SLinus Torvalds */ 6461da177e4SLinus Torvalds 6479f9354b9SEric Dumazet static inline int inet_abc_len(__be32 addr) 6481da177e4SLinus Torvalds { 6491da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 6501da177e4SLinus Torvalds 651f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 6521da177e4SLinus Torvalds rc = 0; 6531da177e4SLinus Torvalds else { 654714e85beSAl Viro __u32 haddr = ntohl(addr); 6551da177e4SLinus Torvalds 656714e85beSAl Viro if (IN_CLASSA(haddr)) 6571da177e4SLinus Torvalds rc = 8; 658714e85beSAl Viro else if (IN_CLASSB(haddr)) 6591da177e4SLinus Torvalds rc = 16; 660714e85beSAl Viro else if (IN_CLASSC(haddr)) 6611da177e4SLinus Torvalds rc = 24; 6621da177e4SLinus Torvalds } 6631da177e4SLinus Torvalds 6641da177e4SLinus Torvalds return rc; 6651da177e4SLinus Torvalds } 6661da177e4SLinus Torvalds 6671da177e4SLinus Torvalds 668e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) 6691da177e4SLinus Torvalds { 6701da177e4SLinus Torvalds struct ifreq ifr; 6711da177e4SLinus Torvalds struct sockaddr_in sin_orig; 6721da177e4SLinus Torvalds struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 6731da177e4SLinus Torvalds struct in_device *in_dev; 6741da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 6751da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 6761da177e4SLinus Torvalds struct net_device *dev; 6771da177e4SLinus Torvalds char *colon; 6781da177e4SLinus Torvalds int ret = -EFAULT; 6791da177e4SLinus Torvalds int tryaddrmatch = 0; 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds /* 6821da177e4SLinus Torvalds * Fetch the caller's info block into kernel space 6831da177e4SLinus Torvalds */ 6841da177e4SLinus Torvalds 6851da177e4SLinus Torvalds if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 6861da177e4SLinus Torvalds goto out; 6871da177e4SLinus Torvalds ifr.ifr_name[IFNAMSIZ - 1] = 0; 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds /* save original address for comparison */ 6901da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 6911da177e4SLinus Torvalds 6921da177e4SLinus Torvalds colon = strchr(ifr.ifr_name, ':'); 6931da177e4SLinus Torvalds if (colon) 6941da177e4SLinus Torvalds *colon = 0; 6951da177e4SLinus Torvalds 696e5b13cb1SDenis V. Lunev dev_load(net, ifr.ifr_name); 6971da177e4SLinus Torvalds 6981da177e4SLinus Torvalds switch (cmd) { 6991da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 7001da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 7011da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 7021da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 7031da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 7041da177e4SLinus Torvalds so that we do not impose a lock. 7051da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 7061da177e4SLinus Torvalds */ 7071da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 7081da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 7091da177e4SLinus Torvalds sin->sin_family = AF_INET; 7101da177e4SLinus Torvalds break; 7111da177e4SLinus Torvalds 7121da177e4SLinus Torvalds case SIOCSIFFLAGS: 7131da177e4SLinus Torvalds ret = -EACCES; 7141da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 7151da177e4SLinus Torvalds goto out; 7161da177e4SLinus Torvalds break; 7171da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 7181da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 7191da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 7201da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 7211da177e4SLinus Torvalds ret = -EACCES; 7221da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 7231da177e4SLinus Torvalds goto out; 7241da177e4SLinus Torvalds ret = -EINVAL; 7251da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 7261da177e4SLinus Torvalds goto out; 7271da177e4SLinus Torvalds break; 7281da177e4SLinus Torvalds default: 7291da177e4SLinus Torvalds ret = -EINVAL; 7301da177e4SLinus Torvalds goto out; 7311da177e4SLinus Torvalds } 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds rtnl_lock(); 7341da177e4SLinus Torvalds 7351da177e4SLinus Torvalds ret = -ENODEV; 7369f9354b9SEric Dumazet dev = __dev_get_by_name(net, ifr.ifr_name); 7379f9354b9SEric Dumazet if (!dev) 7381da177e4SLinus Torvalds goto done; 7391da177e4SLinus Torvalds 7401da177e4SLinus Torvalds if (colon) 7411da177e4SLinus Torvalds *colon = ':'; 7421da177e4SLinus Torvalds 7439f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 7449f9354b9SEric Dumazet if (in_dev) { 7451da177e4SLinus Torvalds if (tryaddrmatch) { 7461da177e4SLinus Torvalds /* Matthias Andree */ 7471da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 7481da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 7491da177e4SLinus Torvalds and only if the original address family was AF_INET. 7501da177e4SLinus Torvalds This is checked above. */ 7511da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 7521da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 7531da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label) && 7541da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 7556c91afe1SDavid S. Miller ifa->ifa_local) { 7561da177e4SLinus Torvalds break; /* found */ 7571da177e4SLinus Torvalds } 7581da177e4SLinus Torvalds } 7591da177e4SLinus Torvalds } 7601da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 7611da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 7621da177e4SLinus Torvalds comparing just the label */ 7631da177e4SLinus Torvalds if (!ifa) { 7641da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 7651da177e4SLinus Torvalds ifap = &ifa->ifa_next) 7661da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label)) 7671da177e4SLinus Torvalds break; 7681da177e4SLinus Torvalds } 7691da177e4SLinus Torvalds } 7701da177e4SLinus Torvalds 7711da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 7721da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 7731da177e4SLinus Torvalds goto done; 7741da177e4SLinus Torvalds 7751da177e4SLinus Torvalds switch (cmd) { 7761da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 7771da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 7781da177e4SLinus Torvalds goto rarok; 7791da177e4SLinus Torvalds 7801da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 7811da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 7821da177e4SLinus Torvalds goto rarok; 7831da177e4SLinus Torvalds 7841da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 7851da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 7861da177e4SLinus Torvalds goto rarok; 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 7891da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 7901da177e4SLinus Torvalds goto rarok; 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds case SIOCSIFFLAGS: 7931da177e4SLinus Torvalds if (colon) { 7941da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 7951da177e4SLinus Torvalds if (!ifa) 7961da177e4SLinus Torvalds break; 7971da177e4SLinus Torvalds ret = 0; 7981da177e4SLinus Torvalds if (!(ifr.ifr_flags & IFF_UP)) 7991da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 8001da177e4SLinus Torvalds break; 8011da177e4SLinus Torvalds } 8021da177e4SLinus Torvalds ret = dev_change_flags(dev, ifr.ifr_flags); 8031da177e4SLinus Torvalds break; 8041da177e4SLinus Torvalds 8051da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 8061da177e4SLinus Torvalds ret = -EINVAL; 8071da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 8081da177e4SLinus Torvalds break; 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds if (!ifa) { 8111da177e4SLinus Torvalds ret = -ENOBUFS; 8129f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 813fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 8149f9354b9SEric Dumazet if (!ifa) 8151da177e4SLinus Torvalds break; 8161da177e4SLinus Torvalds if (colon) 8171da177e4SLinus Torvalds memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); 8181da177e4SLinus Torvalds else 8191da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 8201da177e4SLinus Torvalds } else { 8211da177e4SLinus Torvalds ret = 0; 8221da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 8231da177e4SLinus Torvalds break; 8241da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8251da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 826148f9729SBjorn Mork ifa->ifa_scope = 0; 8271da177e4SLinus Torvalds } 8281da177e4SLinus Torvalds 8291da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 8301da177e4SLinus Torvalds 8311da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 8321da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 8331da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 8341da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 8351da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 8361da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 8371da177e4SLinus Torvalds ~ifa->ifa_mask; 8381da177e4SLinus Torvalds } else { 8391da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 8401da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 8411da177e4SLinus Torvalds } 8421da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 8431da177e4SLinus Torvalds break; 8441da177e4SLinus Torvalds 8451da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 8461da177e4SLinus Torvalds ret = 0; 8471da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 8481da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8491da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 8501da177e4SLinus Torvalds inet_insert_ifa(ifa); 8511da177e4SLinus Torvalds } 8521da177e4SLinus Torvalds break; 8531da177e4SLinus Torvalds 8541da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 8551da177e4SLinus Torvalds ret = 0; 8561da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 8571da177e4SLinus Torvalds break; 8581da177e4SLinus Torvalds ret = -EINVAL; 8591da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 8601da177e4SLinus Torvalds break; 8611da177e4SLinus Torvalds ret = 0; 8621da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8631da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 8641da177e4SLinus Torvalds inet_insert_ifa(ifa); 8651da177e4SLinus Torvalds break; 8661da177e4SLinus Torvalds 8671da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 8681da177e4SLinus Torvalds 8691da177e4SLinus Torvalds /* 8701da177e4SLinus Torvalds * The mask we set must be legal. 8711da177e4SLinus Torvalds */ 8721da177e4SLinus Torvalds ret = -EINVAL; 8731da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 8741da177e4SLinus Torvalds break; 8751da177e4SLinus Torvalds ret = 0; 8761da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 877a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 8781da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8791da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 8801da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds /* See if current broadcast address matches 8831da177e4SLinus Torvalds * with current netmask, then recalculate 8841da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 8851da177e4SLinus Torvalds * funny address, so don't touch it since 8861da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 8871da177e4SLinus Torvalds */ 8881da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 8891da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 8901da177e4SLinus Torvalds (ifa->ifa_broadcast == 891dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 8921da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 8931da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 8941da177e4SLinus Torvalds } 8951da177e4SLinus Torvalds inet_insert_ifa(ifa); 8961da177e4SLinus Torvalds } 8971da177e4SLinus Torvalds break; 8981da177e4SLinus Torvalds } 8991da177e4SLinus Torvalds done: 9001da177e4SLinus Torvalds rtnl_unlock(); 9011da177e4SLinus Torvalds out: 9021da177e4SLinus Torvalds return ret; 9031da177e4SLinus Torvalds rarok: 9041da177e4SLinus Torvalds rtnl_unlock(); 9051da177e4SLinus Torvalds ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; 9061da177e4SLinus Torvalds goto out; 9071da177e4SLinus Torvalds } 9081da177e4SLinus Torvalds 9091da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 9101da177e4SLinus Torvalds { 911e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 9121da177e4SLinus Torvalds struct in_ifaddr *ifa; 9131da177e4SLinus Torvalds struct ifreq ifr; 9141da177e4SLinus Torvalds int done = 0; 9151da177e4SLinus Torvalds 9169f9354b9SEric Dumazet if (!in_dev) 9171da177e4SLinus Torvalds goto out; 9181da177e4SLinus Torvalds 9199f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 9201da177e4SLinus Torvalds if (!buf) { 9211da177e4SLinus Torvalds done += sizeof(ifr); 9221da177e4SLinus Torvalds continue; 9231da177e4SLinus Torvalds } 9241da177e4SLinus Torvalds if (len < (int) sizeof(ifr)) 9251da177e4SLinus Torvalds break; 9261da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 9271da177e4SLinus Torvalds if (ifa->ifa_label) 9281da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 9291da177e4SLinus Torvalds else 9301da177e4SLinus Torvalds strcpy(ifr.ifr_name, dev->name); 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 9331da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 9341da177e4SLinus Torvalds ifa->ifa_local; 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 9371da177e4SLinus Torvalds done = -EFAULT; 9381da177e4SLinus Torvalds break; 9391da177e4SLinus Torvalds } 9401da177e4SLinus Torvalds buf += sizeof(struct ifreq); 9411da177e4SLinus Torvalds len -= sizeof(struct ifreq); 9421da177e4SLinus Torvalds done += sizeof(struct ifreq); 9431da177e4SLinus Torvalds } 9441da177e4SLinus Torvalds out: 9451da177e4SLinus Torvalds return done; 9461da177e4SLinus Torvalds } 9471da177e4SLinus Torvalds 948a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 9491da177e4SLinus Torvalds { 950a61ced5dSAl Viro __be32 addr = 0; 9511da177e4SLinus Torvalds struct in_device *in_dev; 952c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 9531da177e4SLinus Torvalds 9541da177e4SLinus Torvalds rcu_read_lock(); 955e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 9561da177e4SLinus Torvalds if (!in_dev) 9571da177e4SLinus Torvalds goto no_in_dev; 9581da177e4SLinus Torvalds 9591da177e4SLinus Torvalds for_primary_ifa(in_dev) { 9601da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 9611da177e4SLinus Torvalds continue; 9621da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 9631da177e4SLinus Torvalds addr = ifa->ifa_local; 9641da177e4SLinus Torvalds break; 9651da177e4SLinus Torvalds } 9661da177e4SLinus Torvalds if (!addr) 9671da177e4SLinus Torvalds addr = ifa->ifa_local; 9681da177e4SLinus Torvalds } endfor_ifa(in_dev); 9691da177e4SLinus Torvalds 9701da177e4SLinus Torvalds if (addr) 971c6d14c84SEric Dumazet goto out_unlock; 9729f9354b9SEric Dumazet no_in_dev: 9731da177e4SLinus Torvalds 9741da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 9751da177e4SLinus Torvalds in this case. It is importnat that lo is the first interface 9761da177e4SLinus Torvalds in dev_base list. 9771da177e4SLinus Torvalds */ 978c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 9799f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 9809f9354b9SEric Dumazet if (!in_dev) 9811da177e4SLinus Torvalds continue; 9821da177e4SLinus Torvalds 9831da177e4SLinus Torvalds for_primary_ifa(in_dev) { 9841da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 9851da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 9861da177e4SLinus Torvalds addr = ifa->ifa_local; 987c6d14c84SEric Dumazet goto out_unlock; 9881da177e4SLinus Torvalds } 9891da177e4SLinus Torvalds } endfor_ifa(in_dev); 9901da177e4SLinus Torvalds } 991c6d14c84SEric Dumazet out_unlock: 9921da177e4SLinus Torvalds rcu_read_unlock(); 9931da177e4SLinus Torvalds return addr; 9941da177e4SLinus Torvalds } 9959f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 9961da177e4SLinus Torvalds 99760cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 99860cad5daSAl Viro __be32 local, int scope) 9991da177e4SLinus Torvalds { 10001da177e4SLinus Torvalds int same = 0; 1001a144ea4bSAl Viro __be32 addr = 0; 10021da177e4SLinus Torvalds 10031da177e4SLinus Torvalds for_ifa(in_dev) { 10041da177e4SLinus Torvalds if (!addr && 10051da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 10061da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 10071da177e4SLinus Torvalds addr = ifa->ifa_local; 10081da177e4SLinus Torvalds if (same) 10091da177e4SLinus Torvalds break; 10101da177e4SLinus Torvalds } 10111da177e4SLinus Torvalds if (!same) { 10121da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 10131da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 10141da177e4SLinus Torvalds if (same && addr) { 10151da177e4SLinus Torvalds if (local || !dst) 10161da177e4SLinus Torvalds break; 10171da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 10181da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 10191da177e4SLinus Torvalds break; 10201da177e4SLinus Torvalds /* No, then can we use new local src? */ 10211da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 10221da177e4SLinus Torvalds addr = ifa->ifa_local; 10231da177e4SLinus Torvalds break; 10241da177e4SLinus Torvalds } 10251da177e4SLinus Torvalds /* search for large dst subnet for addr */ 10261da177e4SLinus Torvalds same = 0; 10271da177e4SLinus Torvalds } 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds } endfor_ifa(in_dev); 10301da177e4SLinus Torvalds 10311da177e4SLinus Torvalds return same ? addr : 0; 10321da177e4SLinus Torvalds } 10331da177e4SLinus Torvalds 10341da177e4SLinus Torvalds /* 10351da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 10369bd85e32SDenis V. Lunev * - in_dev: only on this interface, 0=any interface 10371da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 10381da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 10391da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 10401da177e4SLinus Torvalds */ 10419bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev, 10429bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 10431da177e4SLinus Torvalds { 104460cad5daSAl Viro __be32 addr = 0; 10459bd85e32SDenis V. Lunev struct net_device *dev; 104639a6d063SDenis V. Lunev struct net *net; 10471da177e4SLinus Torvalds 104839a6d063SDenis V. Lunev if (scope != RT_SCOPE_LINK) 10499bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 10501da177e4SLinus Torvalds 1051c346dca1SYOSHIFUJI Hideaki net = dev_net(in_dev->dev); 10521da177e4SLinus Torvalds rcu_read_lock(); 1053c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 10549f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 10559f9354b9SEric Dumazet if (in_dev) { 10561da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 10571da177e4SLinus Torvalds if (addr) 10581da177e4SLinus Torvalds break; 10591da177e4SLinus Torvalds } 10601da177e4SLinus Torvalds } 10611da177e4SLinus Torvalds rcu_read_unlock(); 10621da177e4SLinus Torvalds 10631da177e4SLinus Torvalds return addr; 10641da177e4SLinus Torvalds } 10651da177e4SLinus Torvalds 10661da177e4SLinus Torvalds /* 10671da177e4SLinus Torvalds * Device notifier 10681da177e4SLinus Torvalds */ 10691da177e4SLinus Torvalds 10701da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 10711da177e4SLinus Torvalds { 1072e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 10731da177e4SLinus Torvalds } 10749f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 10771da177e4SLinus Torvalds { 1078e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 10791da177e4SLinus Torvalds } 10809f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 10811da177e4SLinus Torvalds 10829f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 10839f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 10841da177e4SLinus Torvalds */ 10851da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 10861da177e4SLinus Torvalds { 10871da177e4SLinus Torvalds struct in_ifaddr *ifa; 10881da177e4SLinus Torvalds int named = 0; 10891da177e4SLinus Torvalds 10901da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 10911da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 10921da177e4SLinus Torvalds 10931da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 10941da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 10951da177e4SLinus Torvalds if (named++ == 0) 1096573bf470SThomas Graf goto skip; 109744344b2aSMark McLoughlin dot = strchr(old, ':'); 10981da177e4SLinus Torvalds if (dot == NULL) { 10991da177e4SLinus Torvalds sprintf(old, ":%d", named); 11001da177e4SLinus Torvalds dot = old; 11011da177e4SLinus Torvalds } 11029f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 11031da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 11049f9354b9SEric Dumazet else 11051da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1106573bf470SThomas Graf skip: 1107573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 11081da177e4SLinus Torvalds } 11091da177e4SLinus Torvalds } 11101da177e4SLinus Torvalds 111106770843SBreno Leitao static inline bool inetdev_valid_mtu(unsigned mtu) 111206770843SBreno Leitao { 111306770843SBreno Leitao return mtu >= 68; 111406770843SBreno Leitao } 111506770843SBreno Leitao 1116d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1117d11327adSIan Campbell struct in_device *in_dev) 1118d11327adSIan Campbell 1119d11327adSIan Campbell { 1120d11327adSIan Campbell struct in_ifaddr *ifa = in_dev->ifa_list; 1121d11327adSIan Campbell 1122d11327adSIan Campbell if (!ifa) 1123d11327adSIan Campbell return; 1124d11327adSIan Campbell 1125d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 11266c91afe1SDavid S. Miller ifa->ifa_local, dev, 11276c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1128d11327adSIan Campbell dev->dev_addr, NULL); 1129d11327adSIan Campbell } 1130d11327adSIan Campbell 11311da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 11321da177e4SLinus Torvalds 11331da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 11341da177e4SLinus Torvalds void *ptr) 11351da177e4SLinus Torvalds { 11361da177e4SLinus Torvalds struct net_device *dev = ptr; 1137e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds ASSERT_RTNL(); 11401da177e4SLinus Torvalds 11411da177e4SLinus Torvalds if (!in_dev) { 11428030f544SHerbert Xu if (event == NETDEV_REGISTER) { 11431da177e4SLinus Torvalds in_dev = inetdev_init(dev); 11448d76527eSHerbert Xu if (!in_dev) 1145b217d616SHerbert Xu return notifier_from_errno(-ENOMEM); 11460cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 114742f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 114842f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 11491da177e4SLinus Torvalds } 115006770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 115106770843SBreno Leitao /* Re-enabling IP */ 115206770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 115306770843SBreno Leitao in_dev = inetdev_init(dev); 11548030f544SHerbert Xu } 11551da177e4SLinus Torvalds goto out; 11561da177e4SLinus Torvalds } 11571da177e4SLinus Torvalds 11581da177e4SLinus Torvalds switch (event) { 11591da177e4SLinus Torvalds case NETDEV_REGISTER: 11601da177e4SLinus Torvalds printk(KERN_DEBUG "inetdev_event: bug\n"); 116195ae6b22SEric Dumazet rcu_assign_pointer(dev->ip_ptr, NULL); 11621da177e4SLinus Torvalds break; 11631da177e4SLinus Torvalds case NETDEV_UP: 116406770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 11651da177e4SLinus Torvalds break; 11660cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 11679f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 11689f9354b9SEric Dumazet 11699f9354b9SEric Dumazet if (ifa) { 1170fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 11711da177e4SLinus Torvalds ifa->ifa_local = 11721da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 11731da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 11741da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 11751da177e4SLinus Torvalds in_dev_hold(in_dev); 11761da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 11771da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 11781da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11791da177e4SLinus Torvalds inet_insert_ifa(ifa); 11801da177e4SLinus Torvalds } 11811da177e4SLinus Torvalds } 11821da177e4SLinus Torvalds ip_mc_up(in_dev); 1183eefef1cfSStephen Hemminger /* fall through */ 1184eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1185d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1186d11327adSIan Campbell break; 1187d11327adSIan Campbell /* fall through */ 1188d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1189a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1190d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 11911da177e4SLinus Torvalds break; 11921da177e4SLinus Torvalds case NETDEV_DOWN: 11931da177e4SLinus Torvalds ip_mc_down(in_dev); 11941da177e4SLinus Torvalds break; 119593d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 119675c78500SMoni Shoua ip_mc_unmap(in_dev); 119775c78500SMoni Shoua break; 119893d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 119975c78500SMoni Shoua ip_mc_remap(in_dev); 120075c78500SMoni Shoua break; 12011da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 120206770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 12031da177e4SLinus Torvalds break; 120406770843SBreno Leitao /* disable IP when MTU is not enough */ 12051da177e4SLinus Torvalds case NETDEV_UNREGISTER: 12061da177e4SLinus Torvalds inetdev_destroy(in_dev); 12071da177e4SLinus Torvalds break; 12081da177e4SLinus Torvalds case NETDEV_CHANGENAME: 12091da177e4SLinus Torvalds /* Do not notify about label change, this event is 12101da177e4SLinus Torvalds * not interesting to applications using netlink. 12111da177e4SLinus Torvalds */ 12121da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 12131da177e4SLinus Torvalds 121451602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 121566f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 12161da177e4SLinus Torvalds break; 12171da177e4SLinus Torvalds } 12181da177e4SLinus Torvalds out: 12191da177e4SLinus Torvalds return NOTIFY_DONE; 12201da177e4SLinus Torvalds } 12211da177e4SLinus Torvalds 12221da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 12231da177e4SLinus Torvalds .notifier_call = inetdev_event, 12241da177e4SLinus Torvalds }; 12251da177e4SLinus Torvalds 1226339bf98fSThomas Graf static inline size_t inet_nlmsg_size(void) 1227339bf98fSThomas Graf { 1228339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1229339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1230339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1231339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1232339bf98fSThomas Graf + nla_total_size(IFNAMSIZ); /* IFA_LABEL */ 1233339bf98fSThomas Graf } 1234339bf98fSThomas Graf 12351da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 1236b6544c0bSJamal Hadi Salim u32 pid, u32 seq, int event, unsigned int flags) 12371da177e4SLinus Torvalds { 12381da177e4SLinus Torvalds struct ifaddrmsg *ifm; 12391da177e4SLinus Torvalds struct nlmsghdr *nlh; 12401da177e4SLinus Torvalds 124147f68512SThomas Graf nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags); 124247f68512SThomas Graf if (nlh == NULL) 124326932566SPatrick McHardy return -EMSGSIZE; 124447f68512SThomas Graf 124547f68512SThomas Graf ifm = nlmsg_data(nlh); 12461da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 12471da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 12481da177e4SLinus Torvalds ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT; 12491da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 12501da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 12511da177e4SLinus Torvalds 125247f68512SThomas Graf if (ifa->ifa_address) 1253a7a628c4SAl Viro NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address); 125447f68512SThomas Graf 125547f68512SThomas Graf if (ifa->ifa_local) 1256a7a628c4SAl Viro NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local); 125747f68512SThomas Graf 125847f68512SThomas Graf if (ifa->ifa_broadcast) 1259a7a628c4SAl Viro NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast); 126047f68512SThomas Graf 126147f68512SThomas Graf if (ifa->ifa_label[0]) 126247f68512SThomas Graf NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label); 126347f68512SThomas Graf 126447f68512SThomas Graf return nlmsg_end(skb, nlh); 126547f68512SThomas Graf 126647f68512SThomas Graf nla_put_failure: 126726932566SPatrick McHardy nlmsg_cancel(skb, nlh); 126826932566SPatrick McHardy return -EMSGSIZE; 12691da177e4SLinus Torvalds } 12701da177e4SLinus Torvalds 12711da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 12721da177e4SLinus Torvalds { 12733b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1274eec4df98SEric Dumazet int h, s_h; 1275eec4df98SEric Dumazet int idx, s_idx; 1276eec4df98SEric Dumazet int ip_idx, s_ip_idx; 12771da177e4SLinus Torvalds struct net_device *dev; 12781da177e4SLinus Torvalds struct in_device *in_dev; 12791da177e4SLinus Torvalds struct in_ifaddr *ifa; 1280eec4df98SEric Dumazet struct hlist_head *head; 1281eec4df98SEric Dumazet struct hlist_node *node; 12821da177e4SLinus Torvalds 1283eec4df98SEric Dumazet s_h = cb->args[0]; 1284eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1285eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1286eec4df98SEric Dumazet 1287eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 12887562f876SPavel Emelianov idx = 0; 1289eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1290eec4df98SEric Dumazet rcu_read_lock(); 1291eec4df98SEric Dumazet hlist_for_each_entry_rcu(dev, node, head, index_hlist) { 12921da177e4SLinus Torvalds if (idx < s_idx) 12937562f876SPavel Emelianov goto cont; 12944b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 12951da177e4SLinus Torvalds s_ip_idx = 0; 1296eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12979f9354b9SEric Dumazet if (!in_dev) 12987562f876SPavel Emelianov goto cont; 12991da177e4SLinus Torvalds 13001da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 13011da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 13021da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1303596e4150SStephen Hemminger continue; 1304eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 1305eec4df98SEric Dumazet NETLINK_CB(cb->skb).pid, 13061da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1307eec4df98SEric Dumazet RTM_NEWADDR, NLM_F_MULTI) <= 0) { 1308eec4df98SEric Dumazet rcu_read_unlock(); 13091da177e4SLinus Torvalds goto done; 13101da177e4SLinus Torvalds } 1311eec4df98SEric Dumazet } 13127562f876SPavel Emelianov cont: 13137562f876SPavel Emelianov idx++; 13141da177e4SLinus Torvalds } 1315eec4df98SEric Dumazet rcu_read_unlock(); 1316eec4df98SEric Dumazet } 13171da177e4SLinus Torvalds 13181da177e4SLinus Torvalds done: 1319eec4df98SEric Dumazet cb->args[0] = h; 1320eec4df98SEric Dumazet cb->args[1] = idx; 1321eec4df98SEric Dumazet cb->args[2] = ip_idx; 13221da177e4SLinus Torvalds 13231da177e4SLinus Torvalds return skb->len; 13241da177e4SLinus Torvalds } 13251da177e4SLinus Torvalds 1326d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 1327d6062cbbSThomas Graf u32 pid) 13281da177e4SLinus Torvalds { 132947f68512SThomas Graf struct sk_buff *skb; 1330d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1331d6062cbbSThomas Graf int err = -ENOBUFS; 13324b8aa9abSDenis V. Lunev struct net *net; 13331da177e4SLinus Torvalds 1334c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1335339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 133647f68512SThomas Graf if (skb == NULL) 1337d6062cbbSThomas Graf goto errout; 1338d6062cbbSThomas Graf 1339d6062cbbSThomas Graf err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0); 134026932566SPatrick McHardy if (err < 0) { 134126932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 134226932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 134326932566SPatrick McHardy kfree_skb(skb); 134426932566SPatrick McHardy goto errout; 134526932566SPatrick McHardy } 13461ce85fe4SPablo Neira Ayuso rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 13471ce85fe4SPablo Neira Ayuso return; 1348d6062cbbSThomas Graf errout: 1349d6062cbbSThomas Graf if (err < 0) 13504b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 13511da177e4SLinus Torvalds } 13521da177e4SLinus Torvalds 13539f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 13549f0f7272SThomas Graf { 1355f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13569f0f7272SThomas Graf 13579f0f7272SThomas Graf if (!in_dev) 13589f0f7272SThomas Graf return 0; 13599f0f7272SThomas Graf 13609f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 13619f0f7272SThomas Graf } 13629f0f7272SThomas Graf 13639f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 13649f0f7272SThomas Graf { 1365f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13669f0f7272SThomas Graf struct nlattr *nla; 13679f0f7272SThomas Graf int i; 13689f0f7272SThomas Graf 13699f0f7272SThomas Graf if (!in_dev) 13709f0f7272SThomas Graf return -ENODATA; 13719f0f7272SThomas Graf 13729f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 13739f0f7272SThomas Graf if (nla == NULL) 13749f0f7272SThomas Graf return -EMSGSIZE; 13759f0f7272SThomas Graf 13769f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 13779f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 13789f0f7272SThomas Graf 13799f0f7272SThomas Graf return 0; 13809f0f7272SThomas Graf } 13819f0f7272SThomas Graf 13829f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 13839f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 13849f0f7272SThomas Graf }; 13859f0f7272SThomas Graf 1386cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1387cf7afbfeSThomas Graf const struct nlattr *nla) 13889f0f7272SThomas Graf { 13899f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 13909f0f7272SThomas Graf int err, rem; 13919f0f7272SThomas Graf 1392f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1393cf7afbfeSThomas Graf return -EAFNOSUPPORT; 13949f0f7272SThomas Graf 13959f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 13969f0f7272SThomas Graf if (err < 0) 13979f0f7272SThomas Graf return err; 13989f0f7272SThomas Graf 13999f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 14009f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 14019f0f7272SThomas Graf int cfgid = nla_type(a); 14029f0f7272SThomas Graf 14039f0f7272SThomas Graf if (nla_len(a) < 4) 14049f0f7272SThomas Graf return -EINVAL; 14059f0f7272SThomas Graf 14069f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 14079f0f7272SThomas Graf return -EINVAL; 14089f0f7272SThomas Graf } 14099f0f7272SThomas Graf } 14109f0f7272SThomas Graf 1411cf7afbfeSThomas Graf return 0; 1412cf7afbfeSThomas Graf } 1413cf7afbfeSThomas Graf 1414cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1415cf7afbfeSThomas Graf { 1416f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1417cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1418cf7afbfeSThomas Graf int rem; 1419cf7afbfeSThomas Graf 1420cf7afbfeSThomas Graf if (!in_dev) 1421cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1422cf7afbfeSThomas Graf 1423cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1424cf7afbfeSThomas Graf BUG(); 1425cf7afbfeSThomas Graf 14269f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 14279f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 14289f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 14299f0f7272SThomas Graf } 14309f0f7272SThomas Graf 14319f0f7272SThomas Graf return 0; 14329f0f7272SThomas Graf } 14339f0f7272SThomas Graf 14341da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 14351da177e4SLinus Torvalds 1436c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 143731be3085SHerbert Xu { 143831be3085SHerbert Xu struct net_device *dev; 143931be3085SHerbert Xu 144031be3085SHerbert Xu rcu_read_lock(); 1441c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1442c6d14c84SEric Dumazet struct in_device *in_dev; 1443c6d14c84SEric Dumazet 144431be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 144531be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 14469355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1447c6d14c84SEric Dumazet } 144831be3085SHerbert Xu rcu_read_unlock(); 144931be3085SHerbert Xu } 145031be3085SHerbert Xu 1451c6d14c84SEric Dumazet /* called with RTNL locked */ 1452c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 145368dd299bSPavel Emelyanov { 145468dd299bSPavel Emelyanov struct net_device *dev; 1455586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 145668dd299bSPavel Emelyanov 1457586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 14589355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 145968dd299bSPavel Emelyanov 1460c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 146168dd299bSPavel Emelyanov struct in_device *in_dev; 14620187bdfbSBen Hutchings if (on) 14630187bdfbSBen Hutchings dev_disable_lro(dev); 146468dd299bSPavel Emelyanov rcu_read_lock(); 146568dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 146668dd299bSPavel Emelyanov if (in_dev) 146768dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 146868dd299bSPavel Emelyanov rcu_read_unlock(); 146968dd299bSPavel Emelyanov } 147068dd299bSPavel Emelyanov } 147168dd299bSPavel Emelyanov 147231be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write, 14738d65af78SAlexey Dobriyan void __user *buffer, 147431be3085SHerbert Xu size_t *lenp, loff_t *ppos) 147531be3085SHerbert Xu { 14768d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 147731be3085SHerbert Xu 147831be3085SHerbert Xu if (write) { 147931be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 1480c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 148131be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 148231be3085SHerbert Xu 148331be3085SHerbert Xu set_bit(i, cnf->state); 148431be3085SHerbert Xu 14859355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 1486c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 148731be3085SHerbert Xu } 148831be3085SHerbert Xu 148931be3085SHerbert Xu return ret; 149031be3085SHerbert Xu } 149131be3085SHerbert Xu 14921da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write, 14938d65af78SAlexey Dobriyan void __user *buffer, 14941da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 14951da177e4SLinus Torvalds { 14961da177e4SLinus Torvalds int *valp = ctl->data; 14971da177e4SLinus Torvalds int val = *valp; 149888af182eSEric W. Biederman loff_t pos = *ppos; 14998d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 15001da177e4SLinus Torvalds 15011da177e4SLinus Torvalds if (write && *valp != val) { 1502c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 1503c0ce9fb3SPavel Emelyanov 15040187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 150588af182eSEric W. Biederman if (!rtnl_trylock()) { 150688af182eSEric W. Biederman /* Restore the original values before restarting */ 150788af182eSEric W. Biederman *valp = val; 150888af182eSEric W. Biederman *ppos = pos; 15099b8adb5eSEric W. Biederman return restart_syscall(); 151088af182eSEric W. Biederman } 15110187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 1512c0ce9fb3SPavel Emelyanov inet_forward_change(net); 15130187bdfbSBen Hutchings } else if (*valp) { 15140187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 15150187bdfbSBen Hutchings struct in_device *idev = 15160187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 15170187bdfbSBen Hutchings dev_disable_lro(idev->dev); 15180187bdfbSBen Hutchings } 15190187bdfbSBen Hutchings rtnl_unlock(); 152076e6ebfbSDenis V. Lunev rt_cache_flush(net, 0); 15211da177e4SLinus Torvalds } 15220187bdfbSBen Hutchings } 15231da177e4SLinus Torvalds 15241da177e4SLinus Torvalds return ret; 15251da177e4SLinus Torvalds } 15261da177e4SLinus Torvalds 1527323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write, 15288d65af78SAlexey Dobriyan void __user *buffer, 15291da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 15301da177e4SLinus Torvalds { 15311da177e4SLinus Torvalds int *valp = ctl->data; 15321da177e4SLinus Torvalds int val = *valp; 15338d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 153476e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 15351da177e4SLinus Torvalds 15361da177e4SLinus Torvalds if (write && *valp != val) 153776e6ebfbSDenis V. Lunev rt_cache_flush(net, 0); 15381da177e4SLinus Torvalds 15391da177e4SLinus Torvalds return ret; 15401da177e4SLinus Torvalds } 15411da177e4SLinus Torvalds 1542f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 154342f811b8SHerbert Xu { \ 154442f811b8SHerbert Xu .procname = name, \ 154542f811b8SHerbert Xu .data = ipv4_devconf.data + \ 154602291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 154742f811b8SHerbert Xu .maxlen = sizeof(int), \ 154842f811b8SHerbert Xu .mode = mval, \ 154942f811b8SHerbert Xu .proc_handler = proc, \ 155031be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 155142f811b8SHerbert Xu } 155242f811b8SHerbert Xu 155342f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 1554f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 155542f811b8SHerbert Xu 155642f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 1557f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 155842f811b8SHerbert Xu 1559f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 1560f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 156142f811b8SHerbert Xu 156242f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 1563f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 156442f811b8SHerbert Xu 15651da177e4SLinus Torvalds static struct devinet_sysctl_table { 15661da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 156702291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 1568bfada697SPavel Emelyanov char *dev_name; 15691da177e4SLinus Torvalds } devinet_sysctl = { 15701da177e4SLinus Torvalds .devinet_vars = { 157142f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 1572f8572d8fSEric W. Biederman devinet_sysctl_forward), 157342f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 157442f811b8SHerbert Xu 157542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 157642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 157742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 157842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 157942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 158042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 158142f811b8SHerbert Xu "accept_source_route"), 15828153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 158328f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 158442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 158542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 158642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 158742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 158842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 158942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 159042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 159142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 159242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 1593eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 159465324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 159542f811b8SHerbert Xu 159642f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 159742f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 159842f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION, 159942f811b8SHerbert Xu "force_igmp_version"), 160042f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 160142f811b8SHerbert Xu "promote_secondaries"), 16021da177e4SLinus Torvalds }, 16031da177e4SLinus Torvalds }; 16041da177e4SLinus Torvalds 1605ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 1606f8572d8fSEric W. Biederman struct ipv4_devconf *p) 16071da177e4SLinus Torvalds { 16081da177e4SLinus Torvalds int i; 16099fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 16101da177e4SLinus Torvalds 1611bfada697SPavel Emelyanov #define DEVINET_CTL_PATH_DEV 3 1612bfada697SPavel Emelyanov 1613bfada697SPavel Emelyanov struct ctl_path devinet_ctl_path[] = { 1614f8572d8fSEric W. Biederman { .procname = "net", }, 1615f8572d8fSEric W. Biederman { .procname = "ipv4", }, 1616f8572d8fSEric W. Biederman { .procname = "conf", }, 1617bfada697SPavel Emelyanov { /* to be set */ }, 1618bfada697SPavel Emelyanov { }, 1619bfada697SPavel Emelyanov }; 1620bfada697SPavel Emelyanov 16219fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 16221da177e4SLinus Torvalds if (!t) 16239fa89642SPavel Emelyanov goto out; 16249fa89642SPavel Emelyanov 16251da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 16261da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 162731be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 1628c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 16291da177e4SLinus Torvalds } 16301da177e4SLinus Torvalds 16311da177e4SLinus Torvalds /* 16321da177e4SLinus Torvalds * Make a copy of dev_name, because '.procname' is regarded as const 16331da177e4SLinus Torvalds * by sysctl and we wouldn't want anyone to change it under our feet 16341da177e4SLinus Torvalds * (see SIOCSIFNAME). 16351da177e4SLinus Torvalds */ 1636bfada697SPavel Emelyanov t->dev_name = kstrdup(dev_name, GFP_KERNEL); 1637bfada697SPavel Emelyanov if (!t->dev_name) 16381da177e4SLinus Torvalds goto free; 16391da177e4SLinus Torvalds 1640bfada697SPavel Emelyanov devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name; 16411da177e4SLinus Torvalds 1642752d14dcSPavel Emelyanov t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path, 1643bfada697SPavel Emelyanov t->devinet_vars); 16441da177e4SLinus Torvalds if (!t->sysctl_header) 16451da177e4SLinus Torvalds goto free_procname; 16461da177e4SLinus Torvalds 16471da177e4SLinus Torvalds p->sysctl = t; 1648ea40b324SPavel Emelyanov return 0; 16491da177e4SLinus Torvalds 16501da177e4SLinus Torvalds free_procname: 1651bfada697SPavel Emelyanov kfree(t->dev_name); 16521da177e4SLinus Torvalds free: 16531da177e4SLinus Torvalds kfree(t); 16549fa89642SPavel Emelyanov out: 1655ea40b324SPavel Emelyanov return -ENOBUFS; 16561da177e4SLinus Torvalds } 16571da177e4SLinus Torvalds 165851602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 165966f27a52SPavel Emelyanov { 166051602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 166166f27a52SPavel Emelyanov 166251602b2aSPavel Emelyanov if (t == NULL) 166351602b2aSPavel Emelyanov return; 166451602b2aSPavel Emelyanov 166551602b2aSPavel Emelyanov cnf->sysctl = NULL; 16661da177e4SLinus Torvalds unregister_sysctl_table(t->sysctl_header); 1667bfada697SPavel Emelyanov kfree(t->dev_name); 16681da177e4SLinus Torvalds kfree(t); 16691da177e4SLinus Torvalds } 167051602b2aSPavel Emelyanov 167151602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev) 167251602b2aSPavel Emelyanov { 167354716e3bSEric W. Biederman neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL); 1674c346dca1SYOSHIFUJI Hideaki __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 1675f8572d8fSEric W. Biederman &idev->cnf); 167651602b2aSPavel Emelyanov } 167751602b2aSPavel Emelyanov 167851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 167951602b2aSPavel Emelyanov { 168051602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 168151602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 16821da177e4SLinus Torvalds } 16831da177e4SLinus Torvalds 168468dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 168568dd299bSPavel Emelyanov { 168668dd299bSPavel Emelyanov .procname = "ip_forward", 168768dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 168802291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 168968dd299bSPavel Emelyanov .maxlen = sizeof(int), 169068dd299bSPavel Emelyanov .mode = 0644, 169168dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 169268dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 1693c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 169468dd299bSPavel Emelyanov }, 169568dd299bSPavel Emelyanov { }, 169668dd299bSPavel Emelyanov }; 169768dd299bSPavel Emelyanov 1698752d14dcSPavel Emelyanov static __net_initdata struct ctl_path net_ipv4_path[] = { 1699f8572d8fSEric W. Biederman { .procname = "net", }, 1700f8572d8fSEric W. Biederman { .procname = "ipv4", }, 170168dd299bSPavel Emelyanov { }, 170268dd299bSPavel Emelyanov }; 17032a75de0cSEric Dumazet #endif 170468dd299bSPavel Emelyanov 1705752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 1706752d14dcSPavel Emelyanov { 1707752d14dcSPavel Emelyanov int err; 1708752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 17092a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 17102a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 1711752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 17122a75de0cSEric Dumazet #endif 1713752d14dcSPavel Emelyanov 1714752d14dcSPavel Emelyanov err = -ENOMEM; 1715752d14dcSPavel Emelyanov all = &ipv4_devconf; 1716752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 1717752d14dcSPavel Emelyanov 171809ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 1719752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 1720752d14dcSPavel Emelyanov if (all == NULL) 1721752d14dcSPavel Emelyanov goto err_alloc_all; 1722752d14dcSPavel Emelyanov 1723752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 1724752d14dcSPavel Emelyanov if (dflt == NULL) 1725752d14dcSPavel Emelyanov goto err_alloc_dflt; 1726752d14dcSPavel Emelyanov 17272a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 1728752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 1729752d14dcSPavel Emelyanov if (tbl == NULL) 1730752d14dcSPavel Emelyanov goto err_alloc_ctl; 1731752d14dcSPavel Emelyanov 173202291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 1733752d14dcSPavel Emelyanov tbl[0].extra1 = all; 1734752d14dcSPavel Emelyanov tbl[0].extra2 = net; 17352a75de0cSEric Dumazet #endif 1736752d14dcSPavel Emelyanov } 1737752d14dcSPavel Emelyanov 1738752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 1739f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 1740752d14dcSPavel Emelyanov if (err < 0) 1741752d14dcSPavel Emelyanov goto err_reg_all; 1742752d14dcSPavel Emelyanov 1743f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 1744752d14dcSPavel Emelyanov if (err < 0) 1745752d14dcSPavel Emelyanov goto err_reg_dflt; 1746752d14dcSPavel Emelyanov 1747752d14dcSPavel Emelyanov err = -ENOMEM; 1748752d14dcSPavel Emelyanov forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl); 1749752d14dcSPavel Emelyanov if (forw_hdr == NULL) 1750752d14dcSPavel Emelyanov goto err_reg_ctl; 17512a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 1752752d14dcSPavel Emelyanov #endif 1753752d14dcSPavel Emelyanov 1754752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 1755752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 1756752d14dcSPavel Emelyanov return 0; 1757752d14dcSPavel Emelyanov 1758752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 1759752d14dcSPavel Emelyanov err_reg_ctl: 1760752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 1761752d14dcSPavel Emelyanov err_reg_dflt: 1762752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 1763752d14dcSPavel Emelyanov err_reg_all: 1764752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 1765752d14dcSPavel Emelyanov kfree(tbl); 1766752d14dcSPavel Emelyanov err_alloc_ctl: 17672a75de0cSEric Dumazet #endif 1768752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 1769752d14dcSPavel Emelyanov kfree(dflt); 1770752d14dcSPavel Emelyanov err_alloc_dflt: 1771752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 1772752d14dcSPavel Emelyanov kfree(all); 1773752d14dcSPavel Emelyanov err_alloc_all: 1774752d14dcSPavel Emelyanov return err; 1775752d14dcSPavel Emelyanov } 1776752d14dcSPavel Emelyanov 1777752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 1778752d14dcSPavel Emelyanov { 17792a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 1780752d14dcSPavel Emelyanov struct ctl_table *tbl; 1781752d14dcSPavel Emelyanov 1782752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 1783752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 1784752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 1785752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 1786752d14dcSPavel Emelyanov kfree(tbl); 17872a75de0cSEric Dumazet #endif 1788752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 1789752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 1790752d14dcSPavel Emelyanov } 1791752d14dcSPavel Emelyanov 1792752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 1793752d14dcSPavel Emelyanov .init = devinet_init_net, 1794752d14dcSPavel Emelyanov .exit = devinet_exit_net, 1795752d14dcSPavel Emelyanov }; 1796752d14dcSPavel Emelyanov 17979f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = { 17989f0f7272SThomas Graf .family = AF_INET, 17999f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 18009f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 1801cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 1802cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 18039f0f7272SThomas Graf }; 18049f0f7272SThomas Graf 18051da177e4SLinus Torvalds void __init devinet_init(void) 18061da177e4SLinus Torvalds { 1807fd23c3b3SDavid S. Miller int i; 1808fd23c3b3SDavid S. Miller 1809fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 1810fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 1811fd23c3b3SDavid S. Miller 1812752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 1813752d14dcSPavel Emelyanov 18141da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 18151da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 181663f3444fSThomas Graf 18179f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 18189f0f7272SThomas Graf 181963f3444fSThomas Graf rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL); 182063f3444fSThomas Graf rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL); 182163f3444fSThomas Graf rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); 18221da177e4SLinus Torvalds } 18231da177e4SLinus Torvalds 1824