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 67406b6f97SDavid S. Miller #include "fib_lookup.h" 68406b6f97SDavid S. Miller 690027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 7042f811b8SHerbert Xu .data = { 7102291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7202291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7302291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7402291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 7542f811b8SHerbert Xu }, 761da177e4SLinus Torvalds }; 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 7942f811b8SHerbert Xu .data = { 8002291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8102291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8202291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8302291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8402291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 8542f811b8SHerbert Xu }, 861da177e4SLinus Torvalds }; 871da177e4SLinus Torvalds 889355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 899355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9042f811b8SHerbert Xu 91ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 925c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 935c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 945c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 955176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 965c753978SThomas Graf }; 975c753978SThomas Graf 98fd23c3b3SDavid S. Miller /* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE 99fd23c3b3SDavid S. Miller * value. So if you change this define, make appropriate changes to 100fd23c3b3SDavid S. Miller * inet_addr_hash as well. 101fd23c3b3SDavid S. Miller */ 102fd23c3b3SDavid S. Miller #define IN4_ADDR_HSIZE 256 103fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 104fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock); 105fd23c3b3SDavid S. Miller 106fd23c3b3SDavid S. Miller static inline unsigned int inet_addr_hash(struct net *net, __be32 addr) 107fd23c3b3SDavid S. Miller { 108fd23c3b3SDavid S. Miller u32 val = (__force u32) addr ^ hash_ptr(net, 8); 109fd23c3b3SDavid S. Miller 110fd23c3b3SDavid S. Miller return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) & 111fd23c3b3SDavid S. Miller (IN4_ADDR_HSIZE - 1)); 112fd23c3b3SDavid S. Miller } 113fd23c3b3SDavid S. Miller 114fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 115fd23c3b3SDavid S. Miller { 116e066008bSDavid S. Miller unsigned int hash = inet_addr_hash(net, ifa->ifa_local); 117fd23c3b3SDavid S. Miller 118fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 119fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 120fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 121fd23c3b3SDavid S. Miller } 122fd23c3b3SDavid S. Miller 123fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 124fd23c3b3SDavid S. Miller { 125fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 126fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 127fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 128fd23c3b3SDavid S. Miller } 129fd23c3b3SDavid S. Miller 1309435eb1cSDavid S. Miller /** 1319435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1329435eb1cSDavid S. Miller * @net: the net namespace 1339435eb1cSDavid S. Miller * @addr: the source address 1349435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1359435eb1cSDavid S. Miller * 1369435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1379435eb1cSDavid S. Miller */ 1389435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1399435eb1cSDavid S. Miller { 1409435eb1cSDavid S. Miller unsigned int hash = inet_addr_hash(net, addr); 1419435eb1cSDavid S. Miller struct net_device *result = NULL; 1429435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1439435eb1cSDavid S. Miller struct hlist_node *node; 1449435eb1cSDavid S. Miller 1459435eb1cSDavid S. Miller rcu_read_lock(); 1469435eb1cSDavid S. Miller hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) { 1479435eb1cSDavid S. Miller struct net_device *dev = ifa->ifa_dev->dev; 1489435eb1cSDavid S. Miller 1499435eb1cSDavid S. Miller if (!net_eq(dev_net(dev), net)) 1509435eb1cSDavid S. Miller continue; 151e066008bSDavid S. Miller if (ifa->ifa_local == addr) { 1529435eb1cSDavid S. Miller result = dev; 1539435eb1cSDavid S. Miller break; 1549435eb1cSDavid S. Miller } 1559435eb1cSDavid S. Miller } 156406b6f97SDavid S. Miller if (!result) { 157406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 158406b6f97SDavid S. Miller struct fib_result res = { 0 }; 159406b6f97SDavid S. Miller struct fib_table *local; 160406b6f97SDavid S. Miller 161406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 162406b6f97SDavid S. Miller * over loopback subnets work. 163406b6f97SDavid S. Miller */ 164406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 165406b6f97SDavid S. Miller if (local && 166406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 167406b6f97SDavid S. Miller res.type == RTN_LOCAL) 168406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 169406b6f97SDavid S. Miller } 1709435eb1cSDavid S. Miller if (result && devref) 1719435eb1cSDavid S. Miller dev_hold(result); 1729435eb1cSDavid S. Miller rcu_read_unlock(); 1739435eb1cSDavid S. Miller return result; 1749435eb1cSDavid S. Miller } 1759435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1769435eb1cSDavid S. Miller 177d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1781da177e4SLinus Torvalds 179e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1801da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1811da177e4SLinus Torvalds int destroy); 1821da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 18366f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev); 18451602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 18551602b2aSPavel Emelyanov #else 18651602b2aSPavel Emelyanov static inline void devinet_sysctl_register(struct in_device *idev) 18751602b2aSPavel Emelyanov { 18851602b2aSPavel Emelyanov } 18951602b2aSPavel Emelyanov static inline void devinet_sysctl_unregister(struct in_device *idev) 19051602b2aSPavel Emelyanov { 19151602b2aSPavel Emelyanov } 1921da177e4SLinus Torvalds #endif 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds /* Locks all the inet devices. */ 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 1971da177e4SLinus Torvalds { 19893adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 1991da177e4SLinus Torvalds } 2001da177e4SLinus Torvalds 2011da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2021da177e4SLinus Torvalds { 2031da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2041da177e4SLinus Torvalds if (ifa->ifa_dev) 2051da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2061da177e4SLinus Torvalds kfree(ifa); 2071da177e4SLinus Torvalds } 2081da177e4SLinus Torvalds 2091da177e4SLinus Torvalds static inline void inet_free_ifa(struct in_ifaddr *ifa) 2101da177e4SLinus Torvalds { 2111da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2151da177e4SLinus Torvalds { 2161da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2171da177e4SLinus Torvalds 218547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 219547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 2201da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 2211da177e4SLinus Torvalds printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n", 2221da177e4SLinus Torvalds idev, dev ? dev->name : "NIL"); 2231da177e4SLinus Torvalds #endif 2241da177e4SLinus Torvalds dev_put(dev); 2251da177e4SLinus Torvalds if (!idev->dead) 2269f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2279f9354b9SEric Dumazet else 2281da177e4SLinus Torvalds kfree(idev); 2291da177e4SLinus Torvalds } 2309f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2311da177e4SLinus Torvalds 23271e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2331da177e4SLinus Torvalds { 2341da177e4SLinus Torvalds struct in_device *in_dev; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds ASSERT_RTNL(); 2371da177e4SLinus Torvalds 2380da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2391da177e4SLinus Torvalds if (!in_dev) 2401da177e4SLinus Torvalds goto out; 241c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2429355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2431da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2441da177e4SLinus Torvalds in_dev->dev = dev; 2459f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2469f9354b9SEric Dumazet if (!in_dev->arp_parms) 2471da177e4SLinus Torvalds goto out_kfree; 2480187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2490187bdfbSBen Hutchings dev_disable_lro(dev); 2501da177e4SLinus Torvalds /* Reference in_dev->dev */ 2511da177e4SLinus Torvalds dev_hold(dev); 25230c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2531da177e4SLinus Torvalds in_dev_hold(in_dev); 2541da177e4SLinus Torvalds 25566f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 2561da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2571da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2581da177e4SLinus Torvalds ip_mc_up(in_dev); 259483479ecSJarek Poplawski 26030c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 26130c4cf57SDavid L Stevens rcu_assign_pointer(dev->ip_ptr, in_dev); 262483479ecSJarek Poplawski out: 2631da177e4SLinus Torvalds return in_dev; 2641da177e4SLinus Torvalds out_kfree: 2651da177e4SLinus Torvalds kfree(in_dev); 2661da177e4SLinus Torvalds in_dev = NULL; 2671da177e4SLinus Torvalds goto out; 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2711da177e4SLinus Torvalds { 2721da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2731da177e4SLinus Torvalds in_dev_put(idev); 2741da177e4SLinus Torvalds } 2751da177e4SLinus Torvalds 2761da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2771da177e4SLinus Torvalds { 2781da177e4SLinus Torvalds struct in_ifaddr *ifa; 2791da177e4SLinus Torvalds struct net_device *dev; 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds ASSERT_RTNL(); 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds dev = in_dev->dev; 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds in_dev->dead = 1; 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 2901da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 2911da177e4SLinus Torvalds inet_free_ifa(ifa); 2921da177e4SLinus Torvalds } 2931da177e4SLinus Torvalds 29495ae6b22SEric Dumazet rcu_assign_pointer(dev->ip_ptr, NULL); 2951da177e4SLinus Torvalds 29651602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 2971da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 2981da177e4SLinus Torvalds arp_ifdown(dev); 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 3011da177e4SLinus Torvalds } 3021da177e4SLinus Torvalds 303ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3041da177e4SLinus Torvalds { 3051da177e4SLinus Torvalds rcu_read_lock(); 3061da177e4SLinus Torvalds for_primary_ifa(in_dev) { 3071da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3081da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3091da177e4SLinus Torvalds rcu_read_unlock(); 3101da177e4SLinus Torvalds return 1; 3111da177e4SLinus Torvalds } 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds } endfor_ifa(in_dev); 3141da177e4SLinus Torvalds rcu_read_unlock(); 3151da177e4SLinus Torvalds return 0; 3161da177e4SLinus Torvalds } 3171da177e4SLinus Torvalds 318d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 319d6062cbbSThomas Graf int destroy, struct nlmsghdr *nlh, u32 pid) 3201da177e4SLinus Torvalds { 3218f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3220ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3230ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3240ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3250ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3261da177e4SLinus Torvalds 3271da177e4SLinus Torvalds ASSERT_RTNL(); 3281da177e4SLinus Torvalds 3298f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3308f937c60SHarald Welte * unless alias promotion is set 3318f937c60SHarald Welte **/ 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3341da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3351da177e4SLinus Torvalds 3361da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3370ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3380ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3390ff60a45SJamal Hadi Salim last_prim = ifa; 3400ff60a45SJamal Hadi Salim 3411da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3421da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3431da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3441da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3450ff60a45SJamal Hadi Salim prev_prom = ifa; 3461da177e4SLinus Torvalds continue; 3471da177e4SLinus Torvalds } 3481da177e4SLinus Torvalds 3490ff60a45SJamal Hadi Salim if (!do_promote) { 350fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3511da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3521da177e4SLinus Torvalds 353d6062cbbSThomas Graf rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid); 354e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 355e041c683SAlan Stern NETDEV_DOWN, ifa); 3561da177e4SLinus Torvalds inet_free_ifa(ifa); 3578f937c60SHarald Welte } else { 3588f937c60SHarald Welte promote = ifa; 3598f937c60SHarald Welte break; 3608f937c60SHarald Welte } 3611da177e4SLinus Torvalds } 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds 3642d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 3652d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 3662d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 3672d230e2bSJulian Anastasov * while all addresses are on the device list. 3682d230e2bSJulian Anastasov */ 3692d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 3702d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 3712d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 3722d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 3732d230e2bSJulian Anastasov } 3742d230e2bSJulian Anastasov 3751da177e4SLinus Torvalds /* 2. Unlink it */ 3761da177e4SLinus Torvalds 3771da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 378fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds /* 3. Announce address deletion */ 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds /* Send message first, then call notifier. 3831da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 3841da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 3851da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 3861da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 3871da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 3881da177e4SLinus Torvalds So that, this order is correct. 3891da177e4SLinus Torvalds */ 390d6062cbbSThomas Graf rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid); 391e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 3920ff60a45SJamal Hadi Salim 3930ff60a45SJamal Hadi Salim if (promote) { 39404024b93SJulian Anastasov struct in_ifaddr *next_sec = promote->ifa_next; 3950ff60a45SJamal Hadi Salim 3960ff60a45SJamal Hadi Salim if (prev_prom) { 3970ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 3980ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 3990ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 4000ff60a45SJamal Hadi Salim } 4010ff60a45SJamal Hadi Salim 4020ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 403d6062cbbSThomas Graf rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid); 404e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 405e041c683SAlan Stern NETDEV_UP, promote); 40604024b93SJulian Anastasov for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { 4070ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4080ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4090ff60a45SJamal Hadi Salim continue; 4100ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4110ff60a45SJamal Hadi Salim } 4120ff60a45SJamal Hadi Salim 4130ff60a45SJamal Hadi Salim } 4146363097cSHerbert Xu if (destroy) 4151da177e4SLinus Torvalds inet_free_ifa(ifa1); 4161da177e4SLinus Torvalds } 4171da177e4SLinus Torvalds 418d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 419d6062cbbSThomas Graf int destroy) 420d6062cbbSThomas Graf { 421d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 422d6062cbbSThomas Graf } 423d6062cbbSThomas Graf 424d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 425d6062cbbSThomas Graf u32 pid) 4261da177e4SLinus Torvalds { 4271da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4281da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4291da177e4SLinus Torvalds 4301da177e4SLinus Torvalds ASSERT_RTNL(); 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds if (!ifa->ifa_local) { 4331da177e4SLinus Torvalds inet_free_ifa(ifa); 4341da177e4SLinus Torvalds return 0; 4351da177e4SLinus Torvalds } 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4381da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4391da177e4SLinus Torvalds 4401da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4411da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4421da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4431da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4441da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4451da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4461da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4471da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4481da177e4SLinus Torvalds inet_free_ifa(ifa); 4491da177e4SLinus Torvalds return -EEXIST; 4501da177e4SLinus Torvalds } 4511da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4521da177e4SLinus Torvalds inet_free_ifa(ifa); 4531da177e4SLinus Torvalds return -EINVAL; 4541da177e4SLinus Torvalds } 4551da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds } 4581da177e4SLinus Torvalds 4591da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 4601da177e4SLinus Torvalds net_srandom(ifa->ifa_local); 4611da177e4SLinus Torvalds ifap = last_primary; 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 4641da177e4SLinus Torvalds ifa->ifa_next = *ifap; 4651da177e4SLinus Torvalds *ifap = ifa; 4661da177e4SLinus Torvalds 467fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 468fd23c3b3SDavid S. Miller 4691da177e4SLinus Torvalds /* Send message first, then call notifier. 4701da177e4SLinus Torvalds Notifier will trigger FIB update, so that 4711da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 472d6062cbbSThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid); 473e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 4741da177e4SLinus Torvalds 4751da177e4SLinus Torvalds return 0; 4761da177e4SLinus Torvalds } 4771da177e4SLinus Torvalds 478d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 479d6062cbbSThomas Graf { 480d6062cbbSThomas Graf return __inet_insert_ifa(ifa, NULL, 0); 481d6062cbbSThomas Graf } 482d6062cbbSThomas Graf 4831da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 4841da177e4SLinus Torvalds { 485e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 4861da177e4SLinus Torvalds 4871da177e4SLinus Torvalds ASSERT_RTNL(); 4881da177e4SLinus Torvalds 4891da177e4SLinus Torvalds if (!in_dev) { 4901da177e4SLinus Torvalds inet_free_ifa(ifa); 4911da177e4SLinus Torvalds return -ENOBUFS; 4921da177e4SLinus Torvalds } 49371e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 4941da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 495547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 4961da177e4SLinus Torvalds in_dev_hold(in_dev); 4971da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 4981da177e4SLinus Torvalds } 499f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5001da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5011da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5021da177e4SLinus Torvalds } 5031da177e4SLinus Torvalds 5048723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5058723e1b4SEric Dumazet * We dont take a reference on found in_device 5068723e1b4SEric Dumazet */ 5077fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5081da177e4SLinus Torvalds { 5091da177e4SLinus Torvalds struct net_device *dev; 5101da177e4SLinus Torvalds struct in_device *in_dev = NULL; 511c148fc2eSEric Dumazet 512c148fc2eSEric Dumazet rcu_read_lock(); 513c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5141da177e4SLinus Torvalds if (dev) 5158723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 516c148fc2eSEric Dumazet rcu_read_unlock(); 5171da177e4SLinus Torvalds return in_dev; 5181da177e4SLinus Torvalds } 5199f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5201da177e4SLinus Torvalds 5211da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5221da177e4SLinus Torvalds 52360cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 52460cad5daSAl Viro __be32 mask) 5251da177e4SLinus Torvalds { 5261da177e4SLinus Torvalds ASSERT_RTNL(); 5271da177e4SLinus Torvalds 5281da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5291da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5301da177e4SLinus Torvalds return ifa; 5311da177e4SLinus Torvalds } endfor_ifa(in_dev); 5321da177e4SLinus Torvalds return NULL; 5331da177e4SLinus Torvalds } 5341da177e4SLinus Torvalds 5351da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 5361da177e4SLinus Torvalds { 5373b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 538dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5391da177e4SLinus Torvalds struct in_device *in_dev; 540dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 5411da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 542dfdd5fd4SThomas Graf int err = -EINVAL; 5431da177e4SLinus Torvalds 5441da177e4SLinus Torvalds ASSERT_RTNL(); 5451da177e4SLinus Torvalds 546dfdd5fd4SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 547dfdd5fd4SThomas Graf if (err < 0) 548dfdd5fd4SThomas Graf goto errout; 549dfdd5fd4SThomas Graf 550dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 5517fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 552dfdd5fd4SThomas Graf if (in_dev == NULL) { 553dfdd5fd4SThomas Graf err = -ENODEV; 554dfdd5fd4SThomas Graf goto errout; 555dfdd5fd4SThomas Graf } 556dfdd5fd4SThomas Graf 5571da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 5581da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 559dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 560a7a628c4SAl Viro ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) 5611da177e4SLinus Torvalds continue; 562dfdd5fd4SThomas Graf 563dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 564dfdd5fd4SThomas Graf continue; 565dfdd5fd4SThomas Graf 566dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 567dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 568a7a628c4SAl Viro !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 569dfdd5fd4SThomas Graf continue; 570dfdd5fd4SThomas Graf 571d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid); 5721da177e4SLinus Torvalds return 0; 5731da177e4SLinus Torvalds } 574dfdd5fd4SThomas Graf 575dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 576dfdd5fd4SThomas Graf errout: 577dfdd5fd4SThomas Graf return err; 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds 5804b8aa9abSDenis V. Lunev static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh) 5811da177e4SLinus Torvalds { 5825c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5835c753978SThomas Graf struct in_ifaddr *ifa; 5845c753978SThomas Graf struct ifaddrmsg *ifm; 5851da177e4SLinus Torvalds struct net_device *dev; 5861da177e4SLinus Torvalds struct in_device *in_dev; 5877b218574SDenis V. Lunev int err; 5881da177e4SLinus Torvalds 5895c753978SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 5905c753978SThomas Graf if (err < 0) 5915c753978SThomas Graf goto errout; 5921da177e4SLinus Torvalds 5935c753978SThomas Graf ifm = nlmsg_data(nlh); 594c4e38f41SEvgeniy Polyakov err = -EINVAL; 5957b218574SDenis V. Lunev if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) 5965c753978SThomas Graf goto errout; 5971da177e4SLinus Torvalds 5984b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 5995c753978SThomas Graf err = -ENODEV; 6007b218574SDenis V. Lunev if (dev == NULL) 6015c753978SThomas Graf goto errout; 6021da177e4SLinus Torvalds 6035c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 6045c753978SThomas Graf err = -ENOBUFS; 6057b218574SDenis V. Lunev if (in_dev == NULL) 6065c753978SThomas Graf goto errout; 60771e27da9SHerbert Xu 6085c753978SThomas Graf ifa = inet_alloc_ifa(); 6097b218574SDenis V. Lunev if (ifa == NULL) 6105c753978SThomas Graf /* 6115c753978SThomas Graf * A potential indev allocation can be left alive, it stays 6125c753978SThomas Graf * assigned to its device and is destroy with it. 6135c753978SThomas Graf */ 6145c753978SThomas Graf goto errout; 6155c753978SThomas Graf 616a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 6175c753978SThomas Graf in_dev_hold(in_dev); 6185c753978SThomas Graf 6195c753978SThomas Graf if (tb[IFA_ADDRESS] == NULL) 6205c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 6215c753978SThomas Graf 622fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 6231da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 6241da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 6251da177e4SLinus Torvalds ifa->ifa_flags = ifm->ifa_flags; 6261da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 6271da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 6285c753978SThomas Graf 629a7a628c4SAl Viro ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); 630a7a628c4SAl Viro ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); 6315c753978SThomas Graf 6325c753978SThomas Graf if (tb[IFA_BROADCAST]) 633a7a628c4SAl Viro ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); 6345c753978SThomas Graf 6355c753978SThomas Graf if (tb[IFA_LABEL]) 6365c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 6371da177e4SLinus Torvalds else 6381da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 6391da177e4SLinus Torvalds 6405c753978SThomas Graf return ifa; 6415c753978SThomas Graf 6425c753978SThomas Graf errout: 6435c753978SThomas Graf return ERR_PTR(err); 6445c753978SThomas Graf } 6455c753978SThomas Graf 6465c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 6475c753978SThomas Graf { 6483b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 6495c753978SThomas Graf struct in_ifaddr *ifa; 6505c753978SThomas Graf 6515c753978SThomas Graf ASSERT_RTNL(); 6525c753978SThomas Graf 6534b8aa9abSDenis V. Lunev ifa = rtm_to_ifaddr(net, nlh); 6545c753978SThomas Graf if (IS_ERR(ifa)) 6555c753978SThomas Graf return PTR_ERR(ifa); 6565c753978SThomas Graf 657d6062cbbSThomas Graf return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid); 6581da177e4SLinus Torvalds } 6591da177e4SLinus Torvalds 6601da177e4SLinus Torvalds /* 6611da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 6621da177e4SLinus Torvalds */ 6631da177e4SLinus Torvalds 6649f9354b9SEric Dumazet static inline int inet_abc_len(__be32 addr) 6651da177e4SLinus Torvalds { 6661da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 6671da177e4SLinus Torvalds 668f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 6691da177e4SLinus Torvalds rc = 0; 6701da177e4SLinus Torvalds else { 671714e85beSAl Viro __u32 haddr = ntohl(addr); 6721da177e4SLinus Torvalds 673714e85beSAl Viro if (IN_CLASSA(haddr)) 6741da177e4SLinus Torvalds rc = 8; 675714e85beSAl Viro else if (IN_CLASSB(haddr)) 6761da177e4SLinus Torvalds rc = 16; 677714e85beSAl Viro else if (IN_CLASSC(haddr)) 6781da177e4SLinus Torvalds rc = 24; 6791da177e4SLinus Torvalds } 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds return rc; 6821da177e4SLinus Torvalds } 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds 685e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) 6861da177e4SLinus Torvalds { 6871da177e4SLinus Torvalds struct ifreq ifr; 6881da177e4SLinus Torvalds struct sockaddr_in sin_orig; 6891da177e4SLinus Torvalds struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 6901da177e4SLinus Torvalds struct in_device *in_dev; 6911da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 6921da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 6931da177e4SLinus Torvalds struct net_device *dev; 6941da177e4SLinus Torvalds char *colon; 6951da177e4SLinus Torvalds int ret = -EFAULT; 6961da177e4SLinus Torvalds int tryaddrmatch = 0; 6971da177e4SLinus Torvalds 6981da177e4SLinus Torvalds /* 6991da177e4SLinus Torvalds * Fetch the caller's info block into kernel space 7001da177e4SLinus Torvalds */ 7011da177e4SLinus Torvalds 7021da177e4SLinus Torvalds if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 7031da177e4SLinus Torvalds goto out; 7041da177e4SLinus Torvalds ifr.ifr_name[IFNAMSIZ - 1] = 0; 7051da177e4SLinus Torvalds 7061da177e4SLinus Torvalds /* save original address for comparison */ 7071da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 7081da177e4SLinus Torvalds 7091da177e4SLinus Torvalds colon = strchr(ifr.ifr_name, ':'); 7101da177e4SLinus Torvalds if (colon) 7111da177e4SLinus Torvalds *colon = 0; 7121da177e4SLinus Torvalds 713e5b13cb1SDenis V. Lunev dev_load(net, ifr.ifr_name); 7141da177e4SLinus Torvalds 7151da177e4SLinus Torvalds switch (cmd) { 7161da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 7171da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 7181da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 7191da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 7201da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 7211da177e4SLinus Torvalds so that we do not impose a lock. 7221da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 7231da177e4SLinus Torvalds */ 7241da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 7251da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 7261da177e4SLinus Torvalds sin->sin_family = AF_INET; 7271da177e4SLinus Torvalds break; 7281da177e4SLinus Torvalds 7291da177e4SLinus Torvalds case SIOCSIFFLAGS: 7301da177e4SLinus Torvalds ret = -EACCES; 7311da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 7321da177e4SLinus Torvalds goto out; 7331da177e4SLinus Torvalds break; 7341da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 7351da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 7361da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 7371da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 7381da177e4SLinus Torvalds ret = -EACCES; 7391da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 7401da177e4SLinus Torvalds goto out; 7411da177e4SLinus Torvalds ret = -EINVAL; 7421da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 7431da177e4SLinus Torvalds goto out; 7441da177e4SLinus Torvalds break; 7451da177e4SLinus Torvalds default: 7461da177e4SLinus Torvalds ret = -EINVAL; 7471da177e4SLinus Torvalds goto out; 7481da177e4SLinus Torvalds } 7491da177e4SLinus Torvalds 7501da177e4SLinus Torvalds rtnl_lock(); 7511da177e4SLinus Torvalds 7521da177e4SLinus Torvalds ret = -ENODEV; 7539f9354b9SEric Dumazet dev = __dev_get_by_name(net, ifr.ifr_name); 7549f9354b9SEric Dumazet if (!dev) 7551da177e4SLinus Torvalds goto done; 7561da177e4SLinus Torvalds 7571da177e4SLinus Torvalds if (colon) 7581da177e4SLinus Torvalds *colon = ':'; 7591da177e4SLinus Torvalds 7609f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 7619f9354b9SEric Dumazet if (in_dev) { 7621da177e4SLinus Torvalds if (tryaddrmatch) { 7631da177e4SLinus Torvalds /* Matthias Andree */ 7641da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 7651da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 7661da177e4SLinus Torvalds and only if the original address family was AF_INET. 7671da177e4SLinus Torvalds This is checked above. */ 7681da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 7691da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 7701da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label) && 7711da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 7726c91afe1SDavid S. Miller ifa->ifa_local) { 7731da177e4SLinus Torvalds break; /* found */ 7741da177e4SLinus Torvalds } 7751da177e4SLinus Torvalds } 7761da177e4SLinus Torvalds } 7771da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 7781da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 7791da177e4SLinus Torvalds comparing just the label */ 7801da177e4SLinus Torvalds if (!ifa) { 7811da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 7821da177e4SLinus Torvalds ifap = &ifa->ifa_next) 7831da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label)) 7841da177e4SLinus Torvalds break; 7851da177e4SLinus Torvalds } 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 7891da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 7901da177e4SLinus Torvalds goto done; 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds switch (cmd) { 7931da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 7941da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 7951da177e4SLinus Torvalds goto rarok; 7961da177e4SLinus Torvalds 7971da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 7981da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 7991da177e4SLinus Torvalds goto rarok; 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 8021da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 8031da177e4SLinus Torvalds goto rarok; 8041da177e4SLinus Torvalds 8051da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 8061da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 8071da177e4SLinus Torvalds goto rarok; 8081da177e4SLinus Torvalds 8091da177e4SLinus Torvalds case SIOCSIFFLAGS: 8101da177e4SLinus Torvalds if (colon) { 8111da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 8121da177e4SLinus Torvalds if (!ifa) 8131da177e4SLinus Torvalds break; 8141da177e4SLinus Torvalds ret = 0; 8151da177e4SLinus Torvalds if (!(ifr.ifr_flags & IFF_UP)) 8161da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 8171da177e4SLinus Torvalds break; 8181da177e4SLinus Torvalds } 8191da177e4SLinus Torvalds ret = dev_change_flags(dev, ifr.ifr_flags); 8201da177e4SLinus Torvalds break; 8211da177e4SLinus Torvalds 8221da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 8231da177e4SLinus Torvalds ret = -EINVAL; 8241da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 8251da177e4SLinus Torvalds break; 8261da177e4SLinus Torvalds 8271da177e4SLinus Torvalds if (!ifa) { 8281da177e4SLinus Torvalds ret = -ENOBUFS; 8299f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 830fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 8319f9354b9SEric Dumazet if (!ifa) 8321da177e4SLinus Torvalds break; 8331da177e4SLinus Torvalds if (colon) 8341da177e4SLinus Torvalds memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); 8351da177e4SLinus Torvalds else 8361da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 8371da177e4SLinus Torvalds } else { 8381da177e4SLinus Torvalds ret = 0; 8391da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 8401da177e4SLinus Torvalds break; 8411da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8421da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 843148f9729SBjorn Mork ifa->ifa_scope = 0; 8441da177e4SLinus Torvalds } 8451da177e4SLinus Torvalds 8461da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 8491da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 8501da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 8511da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 8521da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 8531da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 8541da177e4SLinus Torvalds ~ifa->ifa_mask; 8551da177e4SLinus Torvalds } else { 8561da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 8571da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 8581da177e4SLinus Torvalds } 8591da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 8601da177e4SLinus Torvalds break; 8611da177e4SLinus Torvalds 8621da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 8631da177e4SLinus Torvalds ret = 0; 8641da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 8651da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8661da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 8671da177e4SLinus Torvalds inet_insert_ifa(ifa); 8681da177e4SLinus Torvalds } 8691da177e4SLinus Torvalds break; 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 8721da177e4SLinus Torvalds ret = 0; 8731da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 8741da177e4SLinus Torvalds break; 8751da177e4SLinus Torvalds ret = -EINVAL; 8761da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 8771da177e4SLinus Torvalds break; 8781da177e4SLinus Torvalds ret = 0; 8791da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8801da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 8811da177e4SLinus Torvalds inet_insert_ifa(ifa); 8821da177e4SLinus Torvalds break; 8831da177e4SLinus Torvalds 8841da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds /* 8871da177e4SLinus Torvalds * The mask we set must be legal. 8881da177e4SLinus Torvalds */ 8891da177e4SLinus Torvalds ret = -EINVAL; 8901da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 8911da177e4SLinus Torvalds break; 8921da177e4SLinus Torvalds ret = 0; 8931da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 894a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 8951da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8961da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 8971da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 8981da177e4SLinus Torvalds 8991da177e4SLinus Torvalds /* See if current broadcast address matches 9001da177e4SLinus Torvalds * with current netmask, then recalculate 9011da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 9021da177e4SLinus Torvalds * funny address, so don't touch it since 9031da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 9041da177e4SLinus Torvalds */ 9051da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 9061da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 9071da177e4SLinus Torvalds (ifa->ifa_broadcast == 908dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 9091da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 9101da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds inet_insert_ifa(ifa); 9131da177e4SLinus Torvalds } 9141da177e4SLinus Torvalds break; 9151da177e4SLinus Torvalds } 9161da177e4SLinus Torvalds done: 9171da177e4SLinus Torvalds rtnl_unlock(); 9181da177e4SLinus Torvalds out: 9191da177e4SLinus Torvalds return ret; 9201da177e4SLinus Torvalds rarok: 9211da177e4SLinus Torvalds rtnl_unlock(); 9221da177e4SLinus Torvalds ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; 9231da177e4SLinus Torvalds goto out; 9241da177e4SLinus Torvalds } 9251da177e4SLinus Torvalds 9261da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 9271da177e4SLinus Torvalds { 928e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 9291da177e4SLinus Torvalds struct in_ifaddr *ifa; 9301da177e4SLinus Torvalds struct ifreq ifr; 9311da177e4SLinus Torvalds int done = 0; 9321da177e4SLinus Torvalds 9339f9354b9SEric Dumazet if (!in_dev) 9341da177e4SLinus Torvalds goto out; 9351da177e4SLinus Torvalds 9369f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 9371da177e4SLinus Torvalds if (!buf) { 9381da177e4SLinus Torvalds done += sizeof(ifr); 9391da177e4SLinus Torvalds continue; 9401da177e4SLinus Torvalds } 9411da177e4SLinus Torvalds if (len < (int) sizeof(ifr)) 9421da177e4SLinus Torvalds break; 9431da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 9441da177e4SLinus Torvalds if (ifa->ifa_label) 9451da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 9461da177e4SLinus Torvalds else 9471da177e4SLinus Torvalds strcpy(ifr.ifr_name, dev->name); 9481da177e4SLinus Torvalds 9491da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 9501da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 9511da177e4SLinus Torvalds ifa->ifa_local; 9521da177e4SLinus Torvalds 9531da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 9541da177e4SLinus Torvalds done = -EFAULT; 9551da177e4SLinus Torvalds break; 9561da177e4SLinus Torvalds } 9571da177e4SLinus Torvalds buf += sizeof(struct ifreq); 9581da177e4SLinus Torvalds len -= sizeof(struct ifreq); 9591da177e4SLinus Torvalds done += sizeof(struct ifreq); 9601da177e4SLinus Torvalds } 9611da177e4SLinus Torvalds out: 9621da177e4SLinus Torvalds return done; 9631da177e4SLinus Torvalds } 9641da177e4SLinus Torvalds 965a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 9661da177e4SLinus Torvalds { 967a61ced5dSAl Viro __be32 addr = 0; 9681da177e4SLinus Torvalds struct in_device *in_dev; 969c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 9701da177e4SLinus Torvalds 9711da177e4SLinus Torvalds rcu_read_lock(); 972e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 9731da177e4SLinus Torvalds if (!in_dev) 9741da177e4SLinus Torvalds goto no_in_dev; 9751da177e4SLinus Torvalds 9761da177e4SLinus Torvalds for_primary_ifa(in_dev) { 9771da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 9781da177e4SLinus Torvalds continue; 9791da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 9801da177e4SLinus Torvalds addr = ifa->ifa_local; 9811da177e4SLinus Torvalds break; 9821da177e4SLinus Torvalds } 9831da177e4SLinus Torvalds if (!addr) 9841da177e4SLinus Torvalds addr = ifa->ifa_local; 9851da177e4SLinus Torvalds } endfor_ifa(in_dev); 9861da177e4SLinus Torvalds 9871da177e4SLinus Torvalds if (addr) 988c6d14c84SEric Dumazet goto out_unlock; 9899f9354b9SEric Dumazet no_in_dev: 9901da177e4SLinus Torvalds 9911da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 9921da177e4SLinus Torvalds in this case. It is importnat that lo is the first interface 9931da177e4SLinus Torvalds in dev_base list. 9941da177e4SLinus Torvalds */ 995c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 9969f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 9979f9354b9SEric Dumazet if (!in_dev) 9981da177e4SLinus Torvalds continue; 9991da177e4SLinus Torvalds 10001da177e4SLinus Torvalds for_primary_ifa(in_dev) { 10011da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 10021da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 10031da177e4SLinus Torvalds addr = ifa->ifa_local; 1004c6d14c84SEric Dumazet goto out_unlock; 10051da177e4SLinus Torvalds } 10061da177e4SLinus Torvalds } endfor_ifa(in_dev); 10071da177e4SLinus Torvalds } 1008c6d14c84SEric Dumazet out_unlock: 10091da177e4SLinus Torvalds rcu_read_unlock(); 10101da177e4SLinus Torvalds return addr; 10111da177e4SLinus Torvalds } 10129f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 10131da177e4SLinus Torvalds 101460cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 101560cad5daSAl Viro __be32 local, int scope) 10161da177e4SLinus Torvalds { 10171da177e4SLinus Torvalds int same = 0; 1018a144ea4bSAl Viro __be32 addr = 0; 10191da177e4SLinus Torvalds 10201da177e4SLinus Torvalds for_ifa(in_dev) { 10211da177e4SLinus Torvalds if (!addr && 10221da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 10231da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 10241da177e4SLinus Torvalds addr = ifa->ifa_local; 10251da177e4SLinus Torvalds if (same) 10261da177e4SLinus Torvalds break; 10271da177e4SLinus Torvalds } 10281da177e4SLinus Torvalds if (!same) { 10291da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 10301da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 10311da177e4SLinus Torvalds if (same && addr) { 10321da177e4SLinus Torvalds if (local || !dst) 10331da177e4SLinus Torvalds break; 10341da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 10351da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 10361da177e4SLinus Torvalds break; 10371da177e4SLinus Torvalds /* No, then can we use new local src? */ 10381da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 10391da177e4SLinus Torvalds addr = ifa->ifa_local; 10401da177e4SLinus Torvalds break; 10411da177e4SLinus Torvalds } 10421da177e4SLinus Torvalds /* search for large dst subnet for addr */ 10431da177e4SLinus Torvalds same = 0; 10441da177e4SLinus Torvalds } 10451da177e4SLinus Torvalds } 10461da177e4SLinus Torvalds } endfor_ifa(in_dev); 10471da177e4SLinus Torvalds 10481da177e4SLinus Torvalds return same ? addr : 0; 10491da177e4SLinus Torvalds } 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds /* 10521da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 10539bd85e32SDenis V. Lunev * - in_dev: only on this interface, 0=any interface 10541da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 10551da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 10561da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 10571da177e4SLinus Torvalds */ 10589bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev, 10599bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 10601da177e4SLinus Torvalds { 106160cad5daSAl Viro __be32 addr = 0; 10629bd85e32SDenis V. Lunev struct net_device *dev; 106339a6d063SDenis V. Lunev struct net *net; 10641da177e4SLinus Torvalds 106539a6d063SDenis V. Lunev if (scope != RT_SCOPE_LINK) 10669bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 10671da177e4SLinus Torvalds 1068c346dca1SYOSHIFUJI Hideaki net = dev_net(in_dev->dev); 10691da177e4SLinus Torvalds rcu_read_lock(); 1070c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 10719f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 10729f9354b9SEric Dumazet if (in_dev) { 10731da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 10741da177e4SLinus Torvalds if (addr) 10751da177e4SLinus Torvalds break; 10761da177e4SLinus Torvalds } 10771da177e4SLinus Torvalds } 10781da177e4SLinus Torvalds rcu_read_unlock(); 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds return addr; 10811da177e4SLinus Torvalds } 10821da177e4SLinus Torvalds 10831da177e4SLinus Torvalds /* 10841da177e4SLinus Torvalds * Device notifier 10851da177e4SLinus Torvalds */ 10861da177e4SLinus Torvalds 10871da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 10881da177e4SLinus Torvalds { 1089e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 10901da177e4SLinus Torvalds } 10919f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 10921da177e4SLinus Torvalds 10931da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 10941da177e4SLinus Torvalds { 1095e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 10961da177e4SLinus Torvalds } 10979f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 10981da177e4SLinus Torvalds 10999f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 11009f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 11011da177e4SLinus Torvalds */ 11021da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 11031da177e4SLinus Torvalds { 11041da177e4SLinus Torvalds struct in_ifaddr *ifa; 11051da177e4SLinus Torvalds int named = 0; 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 11081da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 11091da177e4SLinus Torvalds 11101da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 11111da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11121da177e4SLinus Torvalds if (named++ == 0) 1113573bf470SThomas Graf goto skip; 111444344b2aSMark McLoughlin dot = strchr(old, ':'); 11151da177e4SLinus Torvalds if (dot == NULL) { 11161da177e4SLinus Torvalds sprintf(old, ":%d", named); 11171da177e4SLinus Torvalds dot = old; 11181da177e4SLinus Torvalds } 11199f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 11201da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 11219f9354b9SEric Dumazet else 11221da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1123573bf470SThomas Graf skip: 1124573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds } 11271da177e4SLinus Torvalds 112806770843SBreno Leitao static inline bool inetdev_valid_mtu(unsigned mtu) 112906770843SBreno Leitao { 113006770843SBreno Leitao return mtu >= 68; 113106770843SBreno Leitao } 113206770843SBreno Leitao 1133d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1134d11327adSIan Campbell struct in_device *in_dev) 1135d11327adSIan Campbell 1136d11327adSIan Campbell { 1137d11327adSIan Campbell struct in_ifaddr *ifa = in_dev->ifa_list; 1138d11327adSIan Campbell 1139d11327adSIan Campbell if (!ifa) 1140d11327adSIan Campbell return; 1141d11327adSIan Campbell 1142d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 11436c91afe1SDavid S. Miller ifa->ifa_local, dev, 11446c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1145d11327adSIan Campbell dev->dev_addr, NULL); 1146d11327adSIan Campbell } 1147d11327adSIan Campbell 11481da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 11491da177e4SLinus Torvalds 11501da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 11511da177e4SLinus Torvalds void *ptr) 11521da177e4SLinus Torvalds { 11531da177e4SLinus Torvalds struct net_device *dev = ptr; 1154e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 11551da177e4SLinus Torvalds 11561da177e4SLinus Torvalds ASSERT_RTNL(); 11571da177e4SLinus Torvalds 11581da177e4SLinus Torvalds if (!in_dev) { 11598030f544SHerbert Xu if (event == NETDEV_REGISTER) { 11601da177e4SLinus Torvalds in_dev = inetdev_init(dev); 11618d76527eSHerbert Xu if (!in_dev) 1162b217d616SHerbert Xu return notifier_from_errno(-ENOMEM); 11630cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 116442f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 116542f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 11661da177e4SLinus Torvalds } 116706770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 116806770843SBreno Leitao /* Re-enabling IP */ 116906770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 117006770843SBreno Leitao in_dev = inetdev_init(dev); 11718030f544SHerbert Xu } 11721da177e4SLinus Torvalds goto out; 11731da177e4SLinus Torvalds } 11741da177e4SLinus Torvalds 11751da177e4SLinus Torvalds switch (event) { 11761da177e4SLinus Torvalds case NETDEV_REGISTER: 11771da177e4SLinus Torvalds printk(KERN_DEBUG "inetdev_event: bug\n"); 117895ae6b22SEric Dumazet rcu_assign_pointer(dev->ip_ptr, NULL); 11791da177e4SLinus Torvalds break; 11801da177e4SLinus Torvalds case NETDEV_UP: 118106770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 11821da177e4SLinus Torvalds break; 11830cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 11849f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 11859f9354b9SEric Dumazet 11869f9354b9SEric Dumazet if (ifa) { 1187fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 11881da177e4SLinus Torvalds ifa->ifa_local = 11891da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 11901da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 11911da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 11921da177e4SLinus Torvalds in_dev_hold(in_dev); 11931da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 11941da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 11951da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11961da177e4SLinus Torvalds inet_insert_ifa(ifa); 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds } 11991da177e4SLinus Torvalds ip_mc_up(in_dev); 1200eefef1cfSStephen Hemminger /* fall through */ 1201eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1202d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1203d11327adSIan Campbell break; 1204d11327adSIan Campbell /* fall through */ 1205d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1206a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1207d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 12081da177e4SLinus Torvalds break; 12091da177e4SLinus Torvalds case NETDEV_DOWN: 12101da177e4SLinus Torvalds ip_mc_down(in_dev); 12111da177e4SLinus Torvalds break; 121293d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 121375c78500SMoni Shoua ip_mc_unmap(in_dev); 121475c78500SMoni Shoua break; 121593d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 121675c78500SMoni Shoua ip_mc_remap(in_dev); 121775c78500SMoni Shoua break; 12181da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 121906770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 12201da177e4SLinus Torvalds break; 122106770843SBreno Leitao /* disable IP when MTU is not enough */ 12221da177e4SLinus Torvalds case NETDEV_UNREGISTER: 12231da177e4SLinus Torvalds inetdev_destroy(in_dev); 12241da177e4SLinus Torvalds break; 12251da177e4SLinus Torvalds case NETDEV_CHANGENAME: 12261da177e4SLinus Torvalds /* Do not notify about label change, this event is 12271da177e4SLinus Torvalds * not interesting to applications using netlink. 12281da177e4SLinus Torvalds */ 12291da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 12301da177e4SLinus Torvalds 123151602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 123266f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 12331da177e4SLinus Torvalds break; 12341da177e4SLinus Torvalds } 12351da177e4SLinus Torvalds out: 12361da177e4SLinus Torvalds return NOTIFY_DONE; 12371da177e4SLinus Torvalds } 12381da177e4SLinus Torvalds 12391da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 12401da177e4SLinus Torvalds .notifier_call = inetdev_event, 12411da177e4SLinus Torvalds }; 12421da177e4SLinus Torvalds 1243339bf98fSThomas Graf static inline size_t inet_nlmsg_size(void) 1244339bf98fSThomas Graf { 1245339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1246339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1247339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1248339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1249339bf98fSThomas Graf + nla_total_size(IFNAMSIZ); /* IFA_LABEL */ 1250339bf98fSThomas Graf } 1251339bf98fSThomas Graf 12521da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 1253b6544c0bSJamal Hadi Salim u32 pid, u32 seq, int event, unsigned int flags) 12541da177e4SLinus Torvalds { 12551da177e4SLinus Torvalds struct ifaddrmsg *ifm; 12561da177e4SLinus Torvalds struct nlmsghdr *nlh; 12571da177e4SLinus Torvalds 125847f68512SThomas Graf nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags); 125947f68512SThomas Graf if (nlh == NULL) 126026932566SPatrick McHardy return -EMSGSIZE; 126147f68512SThomas Graf 126247f68512SThomas Graf ifm = nlmsg_data(nlh); 12631da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 12641da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 12651da177e4SLinus Torvalds ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT; 12661da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 12671da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 12681da177e4SLinus Torvalds 126947f68512SThomas Graf if (ifa->ifa_address) 1270a7a628c4SAl Viro NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address); 127147f68512SThomas Graf 127247f68512SThomas Graf if (ifa->ifa_local) 1273a7a628c4SAl Viro NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local); 127447f68512SThomas Graf 127547f68512SThomas Graf if (ifa->ifa_broadcast) 1276a7a628c4SAl Viro NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast); 127747f68512SThomas Graf 127847f68512SThomas Graf if (ifa->ifa_label[0]) 127947f68512SThomas Graf NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label); 128047f68512SThomas Graf 128147f68512SThomas Graf return nlmsg_end(skb, nlh); 128247f68512SThomas Graf 128347f68512SThomas Graf nla_put_failure: 128426932566SPatrick McHardy nlmsg_cancel(skb, nlh); 128526932566SPatrick McHardy return -EMSGSIZE; 12861da177e4SLinus Torvalds } 12871da177e4SLinus Torvalds 12881da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 12891da177e4SLinus Torvalds { 12903b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1291eec4df98SEric Dumazet int h, s_h; 1292eec4df98SEric Dumazet int idx, s_idx; 1293eec4df98SEric Dumazet int ip_idx, s_ip_idx; 12941da177e4SLinus Torvalds struct net_device *dev; 12951da177e4SLinus Torvalds struct in_device *in_dev; 12961da177e4SLinus Torvalds struct in_ifaddr *ifa; 1297eec4df98SEric Dumazet struct hlist_head *head; 1298eec4df98SEric Dumazet struct hlist_node *node; 12991da177e4SLinus Torvalds 1300eec4df98SEric Dumazet s_h = cb->args[0]; 1301eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1302eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1303eec4df98SEric Dumazet 1304eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 13057562f876SPavel Emelianov idx = 0; 1306eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1307eec4df98SEric Dumazet rcu_read_lock(); 1308eec4df98SEric Dumazet hlist_for_each_entry_rcu(dev, node, head, index_hlist) { 13091da177e4SLinus Torvalds if (idx < s_idx) 13107562f876SPavel Emelianov goto cont; 13114b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 13121da177e4SLinus Torvalds s_ip_idx = 0; 1313eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 13149f9354b9SEric Dumazet if (!in_dev) 13157562f876SPavel Emelianov goto cont; 13161da177e4SLinus Torvalds 13171da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 13181da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 13191da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1320596e4150SStephen Hemminger continue; 1321eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 1322eec4df98SEric Dumazet NETLINK_CB(cb->skb).pid, 13231da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1324eec4df98SEric Dumazet RTM_NEWADDR, NLM_F_MULTI) <= 0) { 1325eec4df98SEric Dumazet rcu_read_unlock(); 13261da177e4SLinus Torvalds goto done; 13271da177e4SLinus Torvalds } 1328eec4df98SEric Dumazet } 13297562f876SPavel Emelianov cont: 13307562f876SPavel Emelianov idx++; 13311da177e4SLinus Torvalds } 1332eec4df98SEric Dumazet rcu_read_unlock(); 1333eec4df98SEric Dumazet } 13341da177e4SLinus Torvalds 13351da177e4SLinus Torvalds done: 1336eec4df98SEric Dumazet cb->args[0] = h; 1337eec4df98SEric Dumazet cb->args[1] = idx; 1338eec4df98SEric Dumazet cb->args[2] = ip_idx; 13391da177e4SLinus Torvalds 13401da177e4SLinus Torvalds return skb->len; 13411da177e4SLinus Torvalds } 13421da177e4SLinus Torvalds 1343d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 1344d6062cbbSThomas Graf u32 pid) 13451da177e4SLinus Torvalds { 134647f68512SThomas Graf struct sk_buff *skb; 1347d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1348d6062cbbSThomas Graf int err = -ENOBUFS; 13494b8aa9abSDenis V. Lunev struct net *net; 13501da177e4SLinus Torvalds 1351c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1352339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 135347f68512SThomas Graf if (skb == NULL) 1354d6062cbbSThomas Graf goto errout; 1355d6062cbbSThomas Graf 1356d6062cbbSThomas Graf err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0); 135726932566SPatrick McHardy if (err < 0) { 135826932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 135926932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 136026932566SPatrick McHardy kfree_skb(skb); 136126932566SPatrick McHardy goto errout; 136226932566SPatrick McHardy } 13631ce85fe4SPablo Neira Ayuso rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 13641ce85fe4SPablo Neira Ayuso return; 1365d6062cbbSThomas Graf errout: 1366d6062cbbSThomas Graf if (err < 0) 13674b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 13681da177e4SLinus Torvalds } 13691da177e4SLinus Torvalds 13709f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 13719f0f7272SThomas Graf { 1372f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13739f0f7272SThomas Graf 13749f0f7272SThomas Graf if (!in_dev) 13759f0f7272SThomas Graf return 0; 13769f0f7272SThomas Graf 13779f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 13789f0f7272SThomas Graf } 13799f0f7272SThomas Graf 13809f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 13819f0f7272SThomas Graf { 1382f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13839f0f7272SThomas Graf struct nlattr *nla; 13849f0f7272SThomas Graf int i; 13859f0f7272SThomas Graf 13869f0f7272SThomas Graf if (!in_dev) 13879f0f7272SThomas Graf return -ENODATA; 13889f0f7272SThomas Graf 13899f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 13909f0f7272SThomas Graf if (nla == NULL) 13919f0f7272SThomas Graf return -EMSGSIZE; 13929f0f7272SThomas Graf 13939f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 13949f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 13959f0f7272SThomas Graf 13969f0f7272SThomas Graf return 0; 13979f0f7272SThomas Graf } 13989f0f7272SThomas Graf 13999f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 14009f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 14019f0f7272SThomas Graf }; 14029f0f7272SThomas Graf 1403cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1404cf7afbfeSThomas Graf const struct nlattr *nla) 14059f0f7272SThomas Graf { 14069f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 14079f0f7272SThomas Graf int err, rem; 14089f0f7272SThomas Graf 1409f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1410cf7afbfeSThomas Graf return -EAFNOSUPPORT; 14119f0f7272SThomas Graf 14129f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 14139f0f7272SThomas Graf if (err < 0) 14149f0f7272SThomas Graf return err; 14159f0f7272SThomas Graf 14169f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 14179f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 14189f0f7272SThomas Graf int cfgid = nla_type(a); 14199f0f7272SThomas Graf 14209f0f7272SThomas Graf if (nla_len(a) < 4) 14219f0f7272SThomas Graf return -EINVAL; 14229f0f7272SThomas Graf 14239f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 14249f0f7272SThomas Graf return -EINVAL; 14259f0f7272SThomas Graf } 14269f0f7272SThomas Graf } 14279f0f7272SThomas Graf 1428cf7afbfeSThomas Graf return 0; 1429cf7afbfeSThomas Graf } 1430cf7afbfeSThomas Graf 1431cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1432cf7afbfeSThomas Graf { 1433f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1434cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1435cf7afbfeSThomas Graf int rem; 1436cf7afbfeSThomas Graf 1437cf7afbfeSThomas Graf if (!in_dev) 1438cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1439cf7afbfeSThomas Graf 1440cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1441cf7afbfeSThomas Graf BUG(); 1442cf7afbfeSThomas Graf 14439f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 14449f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 14459f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 14469f0f7272SThomas Graf } 14479f0f7272SThomas Graf 14489f0f7272SThomas Graf return 0; 14499f0f7272SThomas Graf } 14509f0f7272SThomas Graf 14511da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 14521da177e4SLinus Torvalds 1453c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 145431be3085SHerbert Xu { 145531be3085SHerbert Xu struct net_device *dev; 145631be3085SHerbert Xu 145731be3085SHerbert Xu rcu_read_lock(); 1458c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1459c6d14c84SEric Dumazet struct in_device *in_dev; 1460c6d14c84SEric Dumazet 146131be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 146231be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 14639355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1464c6d14c84SEric Dumazet } 146531be3085SHerbert Xu rcu_read_unlock(); 146631be3085SHerbert Xu } 146731be3085SHerbert Xu 1468c6d14c84SEric Dumazet /* called with RTNL locked */ 1469c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 147068dd299bSPavel Emelyanov { 147168dd299bSPavel Emelyanov struct net_device *dev; 1472586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 147368dd299bSPavel Emelyanov 1474586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 14759355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 147668dd299bSPavel Emelyanov 1477c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 147868dd299bSPavel Emelyanov struct in_device *in_dev; 14790187bdfbSBen Hutchings if (on) 14800187bdfbSBen Hutchings dev_disable_lro(dev); 148168dd299bSPavel Emelyanov rcu_read_lock(); 148268dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 148368dd299bSPavel Emelyanov if (in_dev) 148468dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 148568dd299bSPavel Emelyanov rcu_read_unlock(); 148668dd299bSPavel Emelyanov } 148768dd299bSPavel Emelyanov } 148868dd299bSPavel Emelyanov 148931be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write, 14908d65af78SAlexey Dobriyan void __user *buffer, 149131be3085SHerbert Xu size_t *lenp, loff_t *ppos) 149231be3085SHerbert Xu { 14938d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 149431be3085SHerbert Xu 149531be3085SHerbert Xu if (write) { 149631be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 1497c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 149831be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 149931be3085SHerbert Xu 150031be3085SHerbert Xu set_bit(i, cnf->state); 150131be3085SHerbert Xu 15029355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 1503c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 150431be3085SHerbert Xu } 150531be3085SHerbert Xu 150631be3085SHerbert Xu return ret; 150731be3085SHerbert Xu } 150831be3085SHerbert Xu 15091da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write, 15108d65af78SAlexey Dobriyan void __user *buffer, 15111da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 15121da177e4SLinus Torvalds { 15131da177e4SLinus Torvalds int *valp = ctl->data; 15141da177e4SLinus Torvalds int val = *valp; 151588af182eSEric W. Biederman loff_t pos = *ppos; 15168d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 15171da177e4SLinus Torvalds 15181da177e4SLinus Torvalds if (write && *valp != val) { 1519c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 1520c0ce9fb3SPavel Emelyanov 15210187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 152288af182eSEric W. Biederman if (!rtnl_trylock()) { 152388af182eSEric W. Biederman /* Restore the original values before restarting */ 152488af182eSEric W. Biederman *valp = val; 152588af182eSEric W. Biederman *ppos = pos; 15269b8adb5eSEric W. Biederman return restart_syscall(); 152788af182eSEric W. Biederman } 15280187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 1529c0ce9fb3SPavel Emelyanov inet_forward_change(net); 15300187bdfbSBen Hutchings } else if (*valp) { 15310187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 15320187bdfbSBen Hutchings struct in_device *idev = 15330187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 15340187bdfbSBen Hutchings dev_disable_lro(idev->dev); 15350187bdfbSBen Hutchings } 15360187bdfbSBen Hutchings rtnl_unlock(); 153776e6ebfbSDenis V. Lunev rt_cache_flush(net, 0); 15381da177e4SLinus Torvalds } 15390187bdfbSBen Hutchings } 15401da177e4SLinus Torvalds 15411da177e4SLinus Torvalds return ret; 15421da177e4SLinus Torvalds } 15431da177e4SLinus Torvalds 1544323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write, 15458d65af78SAlexey Dobriyan void __user *buffer, 15461da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 15471da177e4SLinus Torvalds { 15481da177e4SLinus Torvalds int *valp = ctl->data; 15491da177e4SLinus Torvalds int val = *valp; 15508d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 155176e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 15521da177e4SLinus Torvalds 15531da177e4SLinus Torvalds if (write && *valp != val) 155476e6ebfbSDenis V. Lunev rt_cache_flush(net, 0); 15551da177e4SLinus Torvalds 15561da177e4SLinus Torvalds return ret; 15571da177e4SLinus Torvalds } 15581da177e4SLinus Torvalds 1559f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 156042f811b8SHerbert Xu { \ 156142f811b8SHerbert Xu .procname = name, \ 156242f811b8SHerbert Xu .data = ipv4_devconf.data + \ 156302291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 156442f811b8SHerbert Xu .maxlen = sizeof(int), \ 156542f811b8SHerbert Xu .mode = mval, \ 156642f811b8SHerbert Xu .proc_handler = proc, \ 156731be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 156842f811b8SHerbert Xu } 156942f811b8SHerbert Xu 157042f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 1571f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 157242f811b8SHerbert Xu 157342f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 1574f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 157542f811b8SHerbert Xu 1576f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 1577f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 157842f811b8SHerbert Xu 157942f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 1580f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 158142f811b8SHerbert Xu 15821da177e4SLinus Torvalds static struct devinet_sysctl_table { 15831da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 158402291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 1585bfada697SPavel Emelyanov char *dev_name; 15861da177e4SLinus Torvalds } devinet_sysctl = { 15871da177e4SLinus Torvalds .devinet_vars = { 158842f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 1589f8572d8fSEric W. Biederman devinet_sysctl_forward), 159042f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 159142f811b8SHerbert Xu 159242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 159342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 159442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 159542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 159642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 159742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 159842f811b8SHerbert Xu "accept_source_route"), 15998153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 160028f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 160142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 160242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 160342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 160442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 160542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 160642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 160742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 160842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 160942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 1610eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 161165324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 161242f811b8SHerbert Xu 161342f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 161442f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 161542f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION, 161642f811b8SHerbert Xu "force_igmp_version"), 161742f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 161842f811b8SHerbert Xu "promote_secondaries"), 16191da177e4SLinus Torvalds }, 16201da177e4SLinus Torvalds }; 16211da177e4SLinus Torvalds 1622ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 1623f8572d8fSEric W. Biederman struct ipv4_devconf *p) 16241da177e4SLinus Torvalds { 16251da177e4SLinus Torvalds int i; 16269fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 16271da177e4SLinus Torvalds 1628bfada697SPavel Emelyanov #define DEVINET_CTL_PATH_DEV 3 1629bfada697SPavel Emelyanov 1630bfada697SPavel Emelyanov struct ctl_path devinet_ctl_path[] = { 1631f8572d8fSEric W. Biederman { .procname = "net", }, 1632f8572d8fSEric W. Biederman { .procname = "ipv4", }, 1633f8572d8fSEric W. Biederman { .procname = "conf", }, 1634bfada697SPavel Emelyanov { /* to be set */ }, 1635bfada697SPavel Emelyanov { }, 1636bfada697SPavel Emelyanov }; 1637bfada697SPavel Emelyanov 16389fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 16391da177e4SLinus Torvalds if (!t) 16409fa89642SPavel Emelyanov goto out; 16419fa89642SPavel Emelyanov 16421da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 16431da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 164431be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 1645c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 16461da177e4SLinus Torvalds } 16471da177e4SLinus Torvalds 16481da177e4SLinus Torvalds /* 16491da177e4SLinus Torvalds * Make a copy of dev_name, because '.procname' is regarded as const 16501da177e4SLinus Torvalds * by sysctl and we wouldn't want anyone to change it under our feet 16511da177e4SLinus Torvalds * (see SIOCSIFNAME). 16521da177e4SLinus Torvalds */ 1653bfada697SPavel Emelyanov t->dev_name = kstrdup(dev_name, GFP_KERNEL); 1654bfada697SPavel Emelyanov if (!t->dev_name) 16551da177e4SLinus Torvalds goto free; 16561da177e4SLinus Torvalds 1657bfada697SPavel Emelyanov devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name; 16581da177e4SLinus Torvalds 1659752d14dcSPavel Emelyanov t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path, 1660bfada697SPavel Emelyanov t->devinet_vars); 16611da177e4SLinus Torvalds if (!t->sysctl_header) 16621da177e4SLinus Torvalds goto free_procname; 16631da177e4SLinus Torvalds 16641da177e4SLinus Torvalds p->sysctl = t; 1665ea40b324SPavel Emelyanov return 0; 16661da177e4SLinus Torvalds 16671da177e4SLinus Torvalds free_procname: 1668bfada697SPavel Emelyanov kfree(t->dev_name); 16691da177e4SLinus Torvalds free: 16701da177e4SLinus Torvalds kfree(t); 16719fa89642SPavel Emelyanov out: 1672ea40b324SPavel Emelyanov return -ENOBUFS; 16731da177e4SLinus Torvalds } 16741da177e4SLinus Torvalds 167551602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 167666f27a52SPavel Emelyanov { 167751602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 167866f27a52SPavel Emelyanov 167951602b2aSPavel Emelyanov if (t == NULL) 168051602b2aSPavel Emelyanov return; 168151602b2aSPavel Emelyanov 168251602b2aSPavel Emelyanov cnf->sysctl = NULL; 1683ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 1684bfada697SPavel Emelyanov kfree(t->dev_name); 16851da177e4SLinus Torvalds kfree(t); 16861da177e4SLinus Torvalds } 168751602b2aSPavel Emelyanov 168851602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev) 168951602b2aSPavel Emelyanov { 169054716e3bSEric W. Biederman neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL); 1691c346dca1SYOSHIFUJI Hideaki __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 1692f8572d8fSEric W. Biederman &idev->cnf); 169351602b2aSPavel Emelyanov } 169451602b2aSPavel Emelyanov 169551602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 169651602b2aSPavel Emelyanov { 169751602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 169851602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 16991da177e4SLinus Torvalds } 17001da177e4SLinus Torvalds 170168dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 170268dd299bSPavel Emelyanov { 170368dd299bSPavel Emelyanov .procname = "ip_forward", 170468dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 170502291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 170668dd299bSPavel Emelyanov .maxlen = sizeof(int), 170768dd299bSPavel Emelyanov .mode = 0644, 170868dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 170968dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 1710c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 171168dd299bSPavel Emelyanov }, 171268dd299bSPavel Emelyanov { }, 171368dd299bSPavel Emelyanov }; 171468dd299bSPavel Emelyanov 1715752d14dcSPavel Emelyanov static __net_initdata struct ctl_path net_ipv4_path[] = { 1716f8572d8fSEric W. Biederman { .procname = "net", }, 1717f8572d8fSEric W. Biederman { .procname = "ipv4", }, 171868dd299bSPavel Emelyanov { }, 171968dd299bSPavel Emelyanov }; 17202a75de0cSEric Dumazet #endif 172168dd299bSPavel Emelyanov 1722752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 1723752d14dcSPavel Emelyanov { 1724752d14dcSPavel Emelyanov int err; 1725752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 17262a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 17272a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 1728752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 17292a75de0cSEric Dumazet #endif 1730752d14dcSPavel Emelyanov 1731752d14dcSPavel Emelyanov err = -ENOMEM; 1732752d14dcSPavel Emelyanov all = &ipv4_devconf; 1733752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 1734752d14dcSPavel Emelyanov 173509ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 1736752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 1737752d14dcSPavel Emelyanov if (all == NULL) 1738752d14dcSPavel Emelyanov goto err_alloc_all; 1739752d14dcSPavel Emelyanov 1740752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 1741752d14dcSPavel Emelyanov if (dflt == NULL) 1742752d14dcSPavel Emelyanov goto err_alloc_dflt; 1743752d14dcSPavel Emelyanov 17442a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 1745752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 1746752d14dcSPavel Emelyanov if (tbl == NULL) 1747752d14dcSPavel Emelyanov goto err_alloc_ctl; 1748752d14dcSPavel Emelyanov 174902291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 1750752d14dcSPavel Emelyanov tbl[0].extra1 = all; 1751752d14dcSPavel Emelyanov tbl[0].extra2 = net; 17522a75de0cSEric Dumazet #endif 1753752d14dcSPavel Emelyanov } 1754752d14dcSPavel Emelyanov 1755752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 1756f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 1757752d14dcSPavel Emelyanov if (err < 0) 1758752d14dcSPavel Emelyanov goto err_reg_all; 1759752d14dcSPavel Emelyanov 1760f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 1761752d14dcSPavel Emelyanov if (err < 0) 1762752d14dcSPavel Emelyanov goto err_reg_dflt; 1763752d14dcSPavel Emelyanov 1764752d14dcSPavel Emelyanov err = -ENOMEM; 1765752d14dcSPavel Emelyanov forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl); 1766752d14dcSPavel Emelyanov if (forw_hdr == NULL) 1767752d14dcSPavel Emelyanov goto err_reg_ctl; 17682a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 1769752d14dcSPavel Emelyanov #endif 1770752d14dcSPavel Emelyanov 1771752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 1772752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 1773752d14dcSPavel Emelyanov return 0; 1774752d14dcSPavel Emelyanov 1775752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 1776752d14dcSPavel Emelyanov err_reg_ctl: 1777752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 1778752d14dcSPavel Emelyanov err_reg_dflt: 1779752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 1780752d14dcSPavel Emelyanov err_reg_all: 1781752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 1782752d14dcSPavel Emelyanov kfree(tbl); 1783752d14dcSPavel Emelyanov err_alloc_ctl: 17842a75de0cSEric Dumazet #endif 1785752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 1786752d14dcSPavel Emelyanov kfree(dflt); 1787752d14dcSPavel Emelyanov err_alloc_dflt: 1788752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 1789752d14dcSPavel Emelyanov kfree(all); 1790752d14dcSPavel Emelyanov err_alloc_all: 1791752d14dcSPavel Emelyanov return err; 1792752d14dcSPavel Emelyanov } 1793752d14dcSPavel Emelyanov 1794752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 1795752d14dcSPavel Emelyanov { 17962a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 1797752d14dcSPavel Emelyanov struct ctl_table *tbl; 1798752d14dcSPavel Emelyanov 1799752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 1800752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 1801752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 1802752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 1803752d14dcSPavel Emelyanov kfree(tbl); 18042a75de0cSEric Dumazet #endif 1805752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 1806752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 1807752d14dcSPavel Emelyanov } 1808752d14dcSPavel Emelyanov 1809752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 1810752d14dcSPavel Emelyanov .init = devinet_init_net, 1811752d14dcSPavel Emelyanov .exit = devinet_exit_net, 1812752d14dcSPavel Emelyanov }; 1813752d14dcSPavel Emelyanov 18149f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = { 18159f0f7272SThomas Graf .family = AF_INET, 18169f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 18179f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 1818cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 1819cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 18209f0f7272SThomas Graf }; 18219f0f7272SThomas Graf 18221da177e4SLinus Torvalds void __init devinet_init(void) 18231da177e4SLinus Torvalds { 1824fd23c3b3SDavid S. Miller int i; 1825fd23c3b3SDavid S. Miller 1826fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 1827fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 1828fd23c3b3SDavid S. Miller 1829752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 1830752d14dcSPavel Emelyanov 18311da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 18321da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 183363f3444fSThomas Graf 18349f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 18359f0f7272SThomas Graf 183663f3444fSThomas Graf rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL); 183763f3444fSThomas Graf rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL); 183863f3444fSThomas Graf rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); 18391da177e4SLinus Torvalds } 18401da177e4SLinus Torvalds 1841