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 { 114fd23c3b3SDavid S. Miller unsigned int hash = inet_addr_hash(net, ifa->ifa_address); 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 128d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1291da177e4SLinus Torvalds 130e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1311da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1321da177e4SLinus Torvalds int destroy); 1331da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 13466f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev); 13551602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 13651602b2aSPavel Emelyanov #else 13751602b2aSPavel Emelyanov static inline void devinet_sysctl_register(struct in_device *idev) 13851602b2aSPavel Emelyanov { 13951602b2aSPavel Emelyanov } 14051602b2aSPavel Emelyanov static inline void devinet_sysctl_unregister(struct in_device *idev) 14151602b2aSPavel Emelyanov { 14251602b2aSPavel Emelyanov } 1431da177e4SLinus Torvalds #endif 1441da177e4SLinus Torvalds 1451da177e4SLinus Torvalds /* Locks all the inet devices. */ 1461da177e4SLinus Torvalds 1471da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 1481da177e4SLinus Torvalds { 14993adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 1501da177e4SLinus Torvalds } 1511da177e4SLinus Torvalds 1521da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 1531da177e4SLinus Torvalds { 1541da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 1551da177e4SLinus Torvalds if (ifa->ifa_dev) 1561da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 1571da177e4SLinus Torvalds kfree(ifa); 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds 1601da177e4SLinus Torvalds static inline void inet_free_ifa(struct in_ifaddr *ifa) 1611da177e4SLinus Torvalds { 1621da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 1631da177e4SLinus Torvalds } 1641da177e4SLinus Torvalds 1651da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 1661da177e4SLinus Torvalds { 1671da177e4SLinus Torvalds struct net_device *dev = idev->dev; 1681da177e4SLinus Torvalds 169547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 170547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 1711da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 1721da177e4SLinus Torvalds printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n", 1731da177e4SLinus Torvalds idev, dev ? dev->name : "NIL"); 1741da177e4SLinus Torvalds #endif 1751da177e4SLinus Torvalds dev_put(dev); 1761da177e4SLinus Torvalds if (!idev->dead) 1779f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 1789f9354b9SEric Dumazet else 1791da177e4SLinus Torvalds kfree(idev); 1801da177e4SLinus Torvalds } 1819f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 1821da177e4SLinus Torvalds 18371e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 1841da177e4SLinus Torvalds { 1851da177e4SLinus Torvalds struct in_device *in_dev; 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds ASSERT_RTNL(); 1881da177e4SLinus Torvalds 1890da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 1901da177e4SLinus Torvalds if (!in_dev) 1911da177e4SLinus Torvalds goto out; 192c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 1939355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 1941da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 1951da177e4SLinus Torvalds in_dev->dev = dev; 1969f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 1979f9354b9SEric Dumazet if (!in_dev->arp_parms) 1981da177e4SLinus Torvalds goto out_kfree; 1990187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2000187bdfbSBen Hutchings dev_disable_lro(dev); 2011da177e4SLinus Torvalds /* Reference in_dev->dev */ 2021da177e4SLinus Torvalds dev_hold(dev); 20330c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2041da177e4SLinus Torvalds in_dev_hold(in_dev); 2051da177e4SLinus Torvalds 20666f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 2071da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2081da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2091da177e4SLinus Torvalds ip_mc_up(in_dev); 210483479ecSJarek Poplawski 21130c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 21230c4cf57SDavid L Stevens rcu_assign_pointer(dev->ip_ptr, in_dev); 213483479ecSJarek Poplawski out: 2141da177e4SLinus Torvalds return in_dev; 2151da177e4SLinus Torvalds out_kfree: 2161da177e4SLinus Torvalds kfree(in_dev); 2171da177e4SLinus Torvalds in_dev = NULL; 2181da177e4SLinus Torvalds goto out; 2191da177e4SLinus Torvalds } 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2221da177e4SLinus Torvalds { 2231da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2241da177e4SLinus Torvalds in_dev_put(idev); 2251da177e4SLinus Torvalds } 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2281da177e4SLinus Torvalds { 2291da177e4SLinus Torvalds struct in_ifaddr *ifa; 2301da177e4SLinus Torvalds struct net_device *dev; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds ASSERT_RTNL(); 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds dev = in_dev->dev; 2351da177e4SLinus Torvalds 2361da177e4SLinus Torvalds in_dev->dead = 1; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 2391da177e4SLinus Torvalds 2401da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 2411da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 2421da177e4SLinus Torvalds inet_free_ifa(ifa); 2431da177e4SLinus Torvalds } 2441da177e4SLinus Torvalds 24595ae6b22SEric Dumazet rcu_assign_pointer(dev->ip_ptr, NULL); 2461da177e4SLinus Torvalds 24751602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 2481da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 2491da177e4SLinus Torvalds arp_ifdown(dev); 2501da177e4SLinus Torvalds 2511da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds 254ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 2551da177e4SLinus Torvalds { 2561da177e4SLinus Torvalds rcu_read_lock(); 2571da177e4SLinus Torvalds for_primary_ifa(in_dev) { 2581da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 2591da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 2601da177e4SLinus Torvalds rcu_read_unlock(); 2611da177e4SLinus Torvalds return 1; 2621da177e4SLinus Torvalds } 2631da177e4SLinus Torvalds } 2641da177e4SLinus Torvalds } endfor_ifa(in_dev); 2651da177e4SLinus Torvalds rcu_read_unlock(); 2661da177e4SLinus Torvalds return 0; 2671da177e4SLinus Torvalds } 2681da177e4SLinus Torvalds 269d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 270d6062cbbSThomas Graf int destroy, struct nlmsghdr *nlh, u32 pid) 2711da177e4SLinus Torvalds { 2728f937c60SHarald Welte struct in_ifaddr *promote = NULL; 2730ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 2740ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 2750ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 2760ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds ASSERT_RTNL(); 2791da177e4SLinus Torvalds 2808f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 2818f937c60SHarald Welte * unless alias promotion is set 2828f937c60SHarald Welte **/ 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 2851da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 2880ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 2890ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 2900ff60a45SJamal Hadi Salim last_prim = ifa; 2910ff60a45SJamal Hadi Salim 2921da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 2931da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 2941da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 2951da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 2960ff60a45SJamal Hadi Salim prev_prom = ifa; 2971da177e4SLinus Torvalds continue; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3000ff60a45SJamal Hadi Salim if (!do_promote) { 301fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3021da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3031da177e4SLinus Torvalds 304d6062cbbSThomas Graf rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid); 305e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 306e041c683SAlan Stern NETDEV_DOWN, ifa); 3071da177e4SLinus Torvalds inet_free_ifa(ifa); 3088f937c60SHarald Welte } else { 3098f937c60SHarald Welte promote = ifa; 3108f937c60SHarald Welte break; 3118f937c60SHarald Welte } 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 3151da177e4SLinus Torvalds /* 2. Unlink it */ 3161da177e4SLinus Torvalds 3171da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 318fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds /* 3. Announce address deletion */ 3211da177e4SLinus Torvalds 3221da177e4SLinus Torvalds /* Send message first, then call notifier. 3231da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 3241da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 3251da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 3261da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 3271da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 3281da177e4SLinus Torvalds So that, this order is correct. 3291da177e4SLinus Torvalds */ 330d6062cbbSThomas Graf rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid); 331e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 3320ff60a45SJamal Hadi Salim 3330ff60a45SJamal Hadi Salim if (promote) { 3340ff60a45SJamal Hadi Salim 3350ff60a45SJamal Hadi Salim if (prev_prom) { 3360ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 3370ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 3380ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 3390ff60a45SJamal Hadi Salim } 3400ff60a45SJamal Hadi Salim 3410ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 342d6062cbbSThomas Graf rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid); 343e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 344e041c683SAlan Stern NETDEV_UP, promote); 3450ff60a45SJamal Hadi Salim for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { 3460ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 3470ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 3480ff60a45SJamal Hadi Salim continue; 3490ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 3500ff60a45SJamal Hadi Salim } 3510ff60a45SJamal Hadi Salim 3520ff60a45SJamal Hadi Salim } 3536363097cSHerbert Xu if (destroy) 3541da177e4SLinus Torvalds inet_free_ifa(ifa1); 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds 357d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 358d6062cbbSThomas Graf int destroy) 359d6062cbbSThomas Graf { 360d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 361d6062cbbSThomas Graf } 362d6062cbbSThomas Graf 363d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 364d6062cbbSThomas Graf u32 pid) 3651da177e4SLinus Torvalds { 3661da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 3671da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds ASSERT_RTNL(); 3701da177e4SLinus Torvalds 3711da177e4SLinus Torvalds if (!ifa->ifa_local) { 3721da177e4SLinus Torvalds inet_free_ifa(ifa); 3731da177e4SLinus Torvalds return 0; 3741da177e4SLinus Torvalds } 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 3771da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 3801da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 3811da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 3821da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 3831da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 3841da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 3851da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 3861da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 3871da177e4SLinus Torvalds inet_free_ifa(ifa); 3881da177e4SLinus Torvalds return -EEXIST; 3891da177e4SLinus Torvalds } 3901da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 3911da177e4SLinus Torvalds inet_free_ifa(ifa); 3921da177e4SLinus Torvalds return -EINVAL; 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 3951da177e4SLinus Torvalds } 3961da177e4SLinus Torvalds } 3971da177e4SLinus Torvalds 3981da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 3991da177e4SLinus Torvalds net_srandom(ifa->ifa_local); 4001da177e4SLinus Torvalds ifap = last_primary; 4011da177e4SLinus Torvalds } 4021da177e4SLinus Torvalds 4031da177e4SLinus Torvalds ifa->ifa_next = *ifap; 4041da177e4SLinus Torvalds *ifap = ifa; 4051da177e4SLinus Torvalds 406fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 407fd23c3b3SDavid S. Miller 4081da177e4SLinus Torvalds /* Send message first, then call notifier. 4091da177e4SLinus Torvalds Notifier will trigger FIB update, so that 4101da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 411d6062cbbSThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid); 412e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 4131da177e4SLinus Torvalds 4141da177e4SLinus Torvalds return 0; 4151da177e4SLinus Torvalds } 4161da177e4SLinus Torvalds 417d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 418d6062cbbSThomas Graf { 419d6062cbbSThomas Graf return __inet_insert_ifa(ifa, NULL, 0); 420d6062cbbSThomas Graf } 421d6062cbbSThomas Graf 4221da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 4231da177e4SLinus Torvalds { 424e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 4251da177e4SLinus Torvalds 4261da177e4SLinus Torvalds ASSERT_RTNL(); 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds if (!in_dev) { 4291da177e4SLinus Torvalds inet_free_ifa(ifa); 4301da177e4SLinus Torvalds return -ENOBUFS; 4311da177e4SLinus Torvalds } 43271e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 4331da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 434547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 4351da177e4SLinus Torvalds in_dev_hold(in_dev); 4361da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 4371da177e4SLinus Torvalds } 438f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 4391da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 4401da177e4SLinus Torvalds return inet_insert_ifa(ifa); 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds 4438723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 4448723e1b4SEric Dumazet * We dont take a reference on found in_device 4458723e1b4SEric Dumazet */ 4467fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 4471da177e4SLinus Torvalds { 4481da177e4SLinus Torvalds struct net_device *dev; 4491da177e4SLinus Torvalds struct in_device *in_dev = NULL; 450c148fc2eSEric Dumazet 451c148fc2eSEric Dumazet rcu_read_lock(); 452c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 4531da177e4SLinus Torvalds if (dev) 4548723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 455c148fc2eSEric Dumazet rcu_read_unlock(); 4561da177e4SLinus Torvalds return in_dev; 4571da177e4SLinus Torvalds } 4589f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 4611da177e4SLinus Torvalds 46260cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 46360cad5daSAl Viro __be32 mask) 4641da177e4SLinus Torvalds { 4651da177e4SLinus Torvalds ASSERT_RTNL(); 4661da177e4SLinus Torvalds 4671da177e4SLinus Torvalds for_primary_ifa(in_dev) { 4681da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 4691da177e4SLinus Torvalds return ifa; 4701da177e4SLinus Torvalds } endfor_ifa(in_dev); 4711da177e4SLinus Torvalds return NULL; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds 4741da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 4751da177e4SLinus Torvalds { 4763b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 477dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 4781da177e4SLinus Torvalds struct in_device *in_dev; 479dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 4801da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 481dfdd5fd4SThomas Graf int err = -EINVAL; 4821da177e4SLinus Torvalds 4831da177e4SLinus Torvalds ASSERT_RTNL(); 4841da177e4SLinus Torvalds 485dfdd5fd4SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 486dfdd5fd4SThomas Graf if (err < 0) 487dfdd5fd4SThomas Graf goto errout; 488dfdd5fd4SThomas Graf 489dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 4907fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 491dfdd5fd4SThomas Graf if (in_dev == NULL) { 492dfdd5fd4SThomas Graf err = -ENODEV; 493dfdd5fd4SThomas Graf goto errout; 494dfdd5fd4SThomas Graf } 495dfdd5fd4SThomas Graf 4961da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 4971da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 498dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 499a7a628c4SAl Viro ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) 5001da177e4SLinus Torvalds continue; 501dfdd5fd4SThomas Graf 502dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 503dfdd5fd4SThomas Graf continue; 504dfdd5fd4SThomas Graf 505dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 506dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 507a7a628c4SAl Viro !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 508dfdd5fd4SThomas Graf continue; 509dfdd5fd4SThomas Graf 510d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid); 5111da177e4SLinus Torvalds return 0; 5121da177e4SLinus Torvalds } 513dfdd5fd4SThomas Graf 514dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 515dfdd5fd4SThomas Graf errout: 516dfdd5fd4SThomas Graf return err; 5171da177e4SLinus Torvalds } 5181da177e4SLinus Torvalds 5194b8aa9abSDenis V. Lunev static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh) 5201da177e4SLinus Torvalds { 5215c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5225c753978SThomas Graf struct in_ifaddr *ifa; 5235c753978SThomas Graf struct ifaddrmsg *ifm; 5241da177e4SLinus Torvalds struct net_device *dev; 5251da177e4SLinus Torvalds struct in_device *in_dev; 5267b218574SDenis V. Lunev int err; 5271da177e4SLinus Torvalds 5285c753978SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 5295c753978SThomas Graf if (err < 0) 5305c753978SThomas Graf goto errout; 5311da177e4SLinus Torvalds 5325c753978SThomas Graf ifm = nlmsg_data(nlh); 533c4e38f41SEvgeniy Polyakov err = -EINVAL; 5347b218574SDenis V. Lunev if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) 5355c753978SThomas Graf goto errout; 5361da177e4SLinus Torvalds 5374b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 5385c753978SThomas Graf err = -ENODEV; 5397b218574SDenis V. Lunev if (dev == NULL) 5405c753978SThomas Graf goto errout; 5411da177e4SLinus Torvalds 5425c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 5435c753978SThomas Graf err = -ENOBUFS; 5447b218574SDenis V. Lunev if (in_dev == NULL) 5455c753978SThomas Graf goto errout; 54671e27da9SHerbert Xu 5475c753978SThomas Graf ifa = inet_alloc_ifa(); 5487b218574SDenis V. Lunev if (ifa == NULL) 5495c753978SThomas Graf /* 5505c753978SThomas Graf * A potential indev allocation can be left alive, it stays 5515c753978SThomas Graf * assigned to its device and is destroy with it. 5525c753978SThomas Graf */ 5535c753978SThomas Graf goto errout; 5545c753978SThomas Graf 555a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 5565c753978SThomas Graf in_dev_hold(in_dev); 5575c753978SThomas Graf 5585c753978SThomas Graf if (tb[IFA_ADDRESS] == NULL) 5595c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 5605c753978SThomas Graf 561fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 5621da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 5631da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 5641da177e4SLinus Torvalds ifa->ifa_flags = ifm->ifa_flags; 5651da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 5661da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5675c753978SThomas Graf 568a7a628c4SAl Viro ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); 569a7a628c4SAl Viro ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); 5705c753978SThomas Graf 5715c753978SThomas Graf if (tb[IFA_BROADCAST]) 572a7a628c4SAl Viro ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); 5735c753978SThomas Graf 5745c753978SThomas Graf if (tb[IFA_LABEL]) 5755c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 5761da177e4SLinus Torvalds else 5771da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 5781da177e4SLinus Torvalds 5795c753978SThomas Graf return ifa; 5805c753978SThomas Graf 5815c753978SThomas Graf errout: 5825c753978SThomas Graf return ERR_PTR(err); 5835c753978SThomas Graf } 5845c753978SThomas Graf 5855c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 5865c753978SThomas Graf { 5873b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 5885c753978SThomas Graf struct in_ifaddr *ifa; 5895c753978SThomas Graf 5905c753978SThomas Graf ASSERT_RTNL(); 5915c753978SThomas Graf 5924b8aa9abSDenis V. Lunev ifa = rtm_to_ifaddr(net, nlh); 5935c753978SThomas Graf if (IS_ERR(ifa)) 5945c753978SThomas Graf return PTR_ERR(ifa); 5955c753978SThomas Graf 596d6062cbbSThomas Graf return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid); 5971da177e4SLinus Torvalds } 5981da177e4SLinus Torvalds 5991da177e4SLinus Torvalds /* 6001da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 6011da177e4SLinus Torvalds */ 6021da177e4SLinus Torvalds 6039f9354b9SEric Dumazet static inline int inet_abc_len(__be32 addr) 6041da177e4SLinus Torvalds { 6051da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 6061da177e4SLinus Torvalds 607f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 6081da177e4SLinus Torvalds rc = 0; 6091da177e4SLinus Torvalds else { 610714e85beSAl Viro __u32 haddr = ntohl(addr); 6111da177e4SLinus Torvalds 612714e85beSAl Viro if (IN_CLASSA(haddr)) 6131da177e4SLinus Torvalds rc = 8; 614714e85beSAl Viro else if (IN_CLASSB(haddr)) 6151da177e4SLinus Torvalds rc = 16; 616714e85beSAl Viro else if (IN_CLASSC(haddr)) 6171da177e4SLinus Torvalds rc = 24; 6181da177e4SLinus Torvalds } 6191da177e4SLinus Torvalds 6201da177e4SLinus Torvalds return rc; 6211da177e4SLinus Torvalds } 6221da177e4SLinus Torvalds 6231da177e4SLinus Torvalds 624e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) 6251da177e4SLinus Torvalds { 6261da177e4SLinus Torvalds struct ifreq ifr; 6271da177e4SLinus Torvalds struct sockaddr_in sin_orig; 6281da177e4SLinus Torvalds struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 6291da177e4SLinus Torvalds struct in_device *in_dev; 6301da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 6311da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 6321da177e4SLinus Torvalds struct net_device *dev; 6331da177e4SLinus Torvalds char *colon; 6341da177e4SLinus Torvalds int ret = -EFAULT; 6351da177e4SLinus Torvalds int tryaddrmatch = 0; 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds /* 6381da177e4SLinus Torvalds * Fetch the caller's info block into kernel space 6391da177e4SLinus Torvalds */ 6401da177e4SLinus Torvalds 6411da177e4SLinus Torvalds if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 6421da177e4SLinus Torvalds goto out; 6431da177e4SLinus Torvalds ifr.ifr_name[IFNAMSIZ - 1] = 0; 6441da177e4SLinus Torvalds 6451da177e4SLinus Torvalds /* save original address for comparison */ 6461da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds colon = strchr(ifr.ifr_name, ':'); 6491da177e4SLinus Torvalds if (colon) 6501da177e4SLinus Torvalds *colon = 0; 6511da177e4SLinus Torvalds 652e5b13cb1SDenis V. Lunev dev_load(net, ifr.ifr_name); 6531da177e4SLinus Torvalds 6541da177e4SLinus Torvalds switch (cmd) { 6551da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 6561da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 6571da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 6581da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 6591da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 6601da177e4SLinus Torvalds so that we do not impose a lock. 6611da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 6621da177e4SLinus Torvalds */ 6631da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 6641da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 6651da177e4SLinus Torvalds sin->sin_family = AF_INET; 6661da177e4SLinus Torvalds break; 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds case SIOCSIFFLAGS: 6691da177e4SLinus Torvalds ret = -EACCES; 6701da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 6711da177e4SLinus Torvalds goto out; 6721da177e4SLinus Torvalds break; 6731da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 6741da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 6751da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 6761da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 6771da177e4SLinus Torvalds ret = -EACCES; 6781da177e4SLinus Torvalds if (!capable(CAP_NET_ADMIN)) 6791da177e4SLinus Torvalds goto out; 6801da177e4SLinus Torvalds ret = -EINVAL; 6811da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 6821da177e4SLinus Torvalds goto out; 6831da177e4SLinus Torvalds break; 6841da177e4SLinus Torvalds default: 6851da177e4SLinus Torvalds ret = -EINVAL; 6861da177e4SLinus Torvalds goto out; 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds 6891da177e4SLinus Torvalds rtnl_lock(); 6901da177e4SLinus Torvalds 6911da177e4SLinus Torvalds ret = -ENODEV; 6929f9354b9SEric Dumazet dev = __dev_get_by_name(net, ifr.ifr_name); 6939f9354b9SEric Dumazet if (!dev) 6941da177e4SLinus Torvalds goto done; 6951da177e4SLinus Torvalds 6961da177e4SLinus Torvalds if (colon) 6971da177e4SLinus Torvalds *colon = ':'; 6981da177e4SLinus Torvalds 6999f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 7009f9354b9SEric Dumazet if (in_dev) { 7011da177e4SLinus Torvalds if (tryaddrmatch) { 7021da177e4SLinus Torvalds /* Matthias Andree */ 7031da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 7041da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 7051da177e4SLinus Torvalds and only if the original address family was AF_INET. 7061da177e4SLinus Torvalds This is checked above. */ 7071da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 7081da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 7091da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label) && 7101da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 7111da177e4SLinus Torvalds ifa->ifa_address) { 7121da177e4SLinus Torvalds break; /* found */ 7131da177e4SLinus Torvalds } 7141da177e4SLinus Torvalds } 7151da177e4SLinus Torvalds } 7161da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 7171da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 7181da177e4SLinus Torvalds comparing just the label */ 7191da177e4SLinus Torvalds if (!ifa) { 7201da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 7211da177e4SLinus Torvalds ifap = &ifa->ifa_next) 7221da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label)) 7231da177e4SLinus Torvalds break; 7241da177e4SLinus Torvalds } 7251da177e4SLinus Torvalds } 7261da177e4SLinus Torvalds 7271da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 7281da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 7291da177e4SLinus Torvalds goto done; 7301da177e4SLinus Torvalds 7311da177e4SLinus Torvalds switch (cmd) { 7321da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 7331da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 7341da177e4SLinus Torvalds goto rarok; 7351da177e4SLinus Torvalds 7361da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 7371da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 7381da177e4SLinus Torvalds goto rarok; 7391da177e4SLinus Torvalds 7401da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 7411da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 7421da177e4SLinus Torvalds goto rarok; 7431da177e4SLinus Torvalds 7441da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 7451da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 7461da177e4SLinus Torvalds goto rarok; 7471da177e4SLinus Torvalds 7481da177e4SLinus Torvalds case SIOCSIFFLAGS: 7491da177e4SLinus Torvalds if (colon) { 7501da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 7511da177e4SLinus Torvalds if (!ifa) 7521da177e4SLinus Torvalds break; 7531da177e4SLinus Torvalds ret = 0; 7541da177e4SLinus Torvalds if (!(ifr.ifr_flags & IFF_UP)) 7551da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 7561da177e4SLinus Torvalds break; 7571da177e4SLinus Torvalds } 7581da177e4SLinus Torvalds ret = dev_change_flags(dev, ifr.ifr_flags); 7591da177e4SLinus Torvalds break; 7601da177e4SLinus Torvalds 7611da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 7621da177e4SLinus Torvalds ret = -EINVAL; 7631da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 7641da177e4SLinus Torvalds break; 7651da177e4SLinus Torvalds 7661da177e4SLinus Torvalds if (!ifa) { 7671da177e4SLinus Torvalds ret = -ENOBUFS; 7689f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 769fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 7709f9354b9SEric Dumazet if (!ifa) 7711da177e4SLinus Torvalds break; 7721da177e4SLinus Torvalds if (colon) 7731da177e4SLinus Torvalds memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); 7741da177e4SLinus Torvalds else 7751da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 7761da177e4SLinus Torvalds } else { 7771da177e4SLinus Torvalds ret = 0; 7781da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 7791da177e4SLinus Torvalds break; 7801da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 7811da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 782148f9729SBjorn Mork ifa->ifa_scope = 0; 7831da177e4SLinus Torvalds } 7841da177e4SLinus Torvalds 7851da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 7861da177e4SLinus Torvalds 7871da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 7881da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 7891da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 7901da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 7911da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 7921da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 7931da177e4SLinus Torvalds ~ifa->ifa_mask; 7941da177e4SLinus Torvalds } else { 7951da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 7961da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 7971da177e4SLinus Torvalds } 7981da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 7991da177e4SLinus Torvalds break; 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 8021da177e4SLinus Torvalds ret = 0; 8031da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 8041da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8051da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 8061da177e4SLinus Torvalds inet_insert_ifa(ifa); 8071da177e4SLinus Torvalds } 8081da177e4SLinus Torvalds break; 8091da177e4SLinus Torvalds 8101da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 8111da177e4SLinus Torvalds ret = 0; 8121da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 8131da177e4SLinus Torvalds break; 8141da177e4SLinus Torvalds ret = -EINVAL; 8151da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 8161da177e4SLinus Torvalds break; 8171da177e4SLinus Torvalds ret = 0; 8181da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8191da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 8201da177e4SLinus Torvalds inet_insert_ifa(ifa); 8211da177e4SLinus Torvalds break; 8221da177e4SLinus Torvalds 8231da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 8241da177e4SLinus Torvalds 8251da177e4SLinus Torvalds /* 8261da177e4SLinus Torvalds * The mask we set must be legal. 8271da177e4SLinus Torvalds */ 8281da177e4SLinus Torvalds ret = -EINVAL; 8291da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 8301da177e4SLinus Torvalds break; 8311da177e4SLinus Torvalds ret = 0; 8321da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 833a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 8341da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8351da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 8361da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 8371da177e4SLinus Torvalds 8381da177e4SLinus Torvalds /* See if current broadcast address matches 8391da177e4SLinus Torvalds * with current netmask, then recalculate 8401da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 8411da177e4SLinus Torvalds * funny address, so don't touch it since 8421da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 8431da177e4SLinus Torvalds */ 8441da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 8451da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 8461da177e4SLinus Torvalds (ifa->ifa_broadcast == 847dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 8481da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 8491da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 8501da177e4SLinus Torvalds } 8511da177e4SLinus Torvalds inet_insert_ifa(ifa); 8521da177e4SLinus Torvalds } 8531da177e4SLinus Torvalds break; 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds done: 8561da177e4SLinus Torvalds rtnl_unlock(); 8571da177e4SLinus Torvalds out: 8581da177e4SLinus Torvalds return ret; 8591da177e4SLinus Torvalds rarok: 8601da177e4SLinus Torvalds rtnl_unlock(); 8611da177e4SLinus Torvalds ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; 8621da177e4SLinus Torvalds goto out; 8631da177e4SLinus Torvalds } 8641da177e4SLinus Torvalds 8651da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 8661da177e4SLinus Torvalds { 867e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 8681da177e4SLinus Torvalds struct in_ifaddr *ifa; 8691da177e4SLinus Torvalds struct ifreq ifr; 8701da177e4SLinus Torvalds int done = 0; 8711da177e4SLinus Torvalds 8729f9354b9SEric Dumazet if (!in_dev) 8731da177e4SLinus Torvalds goto out; 8741da177e4SLinus Torvalds 8759f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 8761da177e4SLinus Torvalds if (!buf) { 8771da177e4SLinus Torvalds done += sizeof(ifr); 8781da177e4SLinus Torvalds continue; 8791da177e4SLinus Torvalds } 8801da177e4SLinus Torvalds if (len < (int) sizeof(ifr)) 8811da177e4SLinus Torvalds break; 8821da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 8831da177e4SLinus Torvalds if (ifa->ifa_label) 8841da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 8851da177e4SLinus Torvalds else 8861da177e4SLinus Torvalds strcpy(ifr.ifr_name, dev->name); 8871da177e4SLinus Torvalds 8881da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 8891da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 8901da177e4SLinus Torvalds ifa->ifa_local; 8911da177e4SLinus Torvalds 8921da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 8931da177e4SLinus Torvalds done = -EFAULT; 8941da177e4SLinus Torvalds break; 8951da177e4SLinus Torvalds } 8961da177e4SLinus Torvalds buf += sizeof(struct ifreq); 8971da177e4SLinus Torvalds len -= sizeof(struct ifreq); 8981da177e4SLinus Torvalds done += sizeof(struct ifreq); 8991da177e4SLinus Torvalds } 9001da177e4SLinus Torvalds out: 9011da177e4SLinus Torvalds return done; 9021da177e4SLinus Torvalds } 9031da177e4SLinus Torvalds 904a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 9051da177e4SLinus Torvalds { 906a61ced5dSAl Viro __be32 addr = 0; 9071da177e4SLinus Torvalds struct in_device *in_dev; 908c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 9091da177e4SLinus Torvalds 9101da177e4SLinus Torvalds rcu_read_lock(); 911e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 9121da177e4SLinus Torvalds if (!in_dev) 9131da177e4SLinus Torvalds goto no_in_dev; 9141da177e4SLinus Torvalds 9151da177e4SLinus Torvalds for_primary_ifa(in_dev) { 9161da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 9171da177e4SLinus Torvalds continue; 9181da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 9191da177e4SLinus Torvalds addr = ifa->ifa_local; 9201da177e4SLinus Torvalds break; 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds if (!addr) 9231da177e4SLinus Torvalds addr = ifa->ifa_local; 9241da177e4SLinus Torvalds } endfor_ifa(in_dev); 9251da177e4SLinus Torvalds 9261da177e4SLinus Torvalds if (addr) 927c6d14c84SEric Dumazet goto out_unlock; 9289f9354b9SEric Dumazet no_in_dev: 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 9311da177e4SLinus Torvalds in this case. It is importnat that lo is the first interface 9321da177e4SLinus Torvalds in dev_base list. 9331da177e4SLinus Torvalds */ 934c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 9359f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 9369f9354b9SEric Dumazet if (!in_dev) 9371da177e4SLinus Torvalds continue; 9381da177e4SLinus Torvalds 9391da177e4SLinus Torvalds for_primary_ifa(in_dev) { 9401da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 9411da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 9421da177e4SLinus Torvalds addr = ifa->ifa_local; 943c6d14c84SEric Dumazet goto out_unlock; 9441da177e4SLinus Torvalds } 9451da177e4SLinus Torvalds } endfor_ifa(in_dev); 9461da177e4SLinus Torvalds } 947c6d14c84SEric Dumazet out_unlock: 9481da177e4SLinus Torvalds rcu_read_unlock(); 9491da177e4SLinus Torvalds return addr; 9501da177e4SLinus Torvalds } 9519f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 9521da177e4SLinus Torvalds 95360cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 95460cad5daSAl Viro __be32 local, int scope) 9551da177e4SLinus Torvalds { 9561da177e4SLinus Torvalds int same = 0; 957a144ea4bSAl Viro __be32 addr = 0; 9581da177e4SLinus Torvalds 9591da177e4SLinus Torvalds for_ifa(in_dev) { 9601da177e4SLinus Torvalds if (!addr && 9611da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 9621da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 9631da177e4SLinus Torvalds addr = ifa->ifa_local; 9641da177e4SLinus Torvalds if (same) 9651da177e4SLinus Torvalds break; 9661da177e4SLinus Torvalds } 9671da177e4SLinus Torvalds if (!same) { 9681da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 9691da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 9701da177e4SLinus Torvalds if (same && addr) { 9711da177e4SLinus Torvalds if (local || !dst) 9721da177e4SLinus Torvalds break; 9731da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 9741da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 9751da177e4SLinus Torvalds break; 9761da177e4SLinus Torvalds /* No, then can we use new local src? */ 9771da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 9781da177e4SLinus Torvalds addr = ifa->ifa_local; 9791da177e4SLinus Torvalds break; 9801da177e4SLinus Torvalds } 9811da177e4SLinus Torvalds /* search for large dst subnet for addr */ 9821da177e4SLinus Torvalds same = 0; 9831da177e4SLinus Torvalds } 9841da177e4SLinus Torvalds } 9851da177e4SLinus Torvalds } endfor_ifa(in_dev); 9861da177e4SLinus Torvalds 9871da177e4SLinus Torvalds return same ? addr : 0; 9881da177e4SLinus Torvalds } 9891da177e4SLinus Torvalds 9901da177e4SLinus Torvalds /* 9911da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 9929bd85e32SDenis V. Lunev * - in_dev: only on this interface, 0=any interface 9931da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 9941da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 9951da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 9961da177e4SLinus Torvalds */ 9979bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev, 9989bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 9991da177e4SLinus Torvalds { 100060cad5daSAl Viro __be32 addr = 0; 10019bd85e32SDenis V. Lunev struct net_device *dev; 100239a6d063SDenis V. Lunev struct net *net; 10031da177e4SLinus Torvalds 100439a6d063SDenis V. Lunev if (scope != RT_SCOPE_LINK) 10059bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 10061da177e4SLinus Torvalds 1007c346dca1SYOSHIFUJI Hideaki net = dev_net(in_dev->dev); 10081da177e4SLinus Torvalds rcu_read_lock(); 1009c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 10109f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 10119f9354b9SEric Dumazet if (in_dev) { 10121da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 10131da177e4SLinus Torvalds if (addr) 10141da177e4SLinus Torvalds break; 10151da177e4SLinus Torvalds } 10161da177e4SLinus Torvalds } 10171da177e4SLinus Torvalds rcu_read_unlock(); 10181da177e4SLinus Torvalds 10191da177e4SLinus Torvalds return addr; 10201da177e4SLinus Torvalds } 10211da177e4SLinus Torvalds 10221da177e4SLinus Torvalds /* 10231da177e4SLinus Torvalds * Device notifier 10241da177e4SLinus Torvalds */ 10251da177e4SLinus Torvalds 10261da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 10271da177e4SLinus Torvalds { 1028e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 10291da177e4SLinus Torvalds } 10309f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 10311da177e4SLinus Torvalds 10321da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 10331da177e4SLinus Torvalds { 1034e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 10351da177e4SLinus Torvalds } 10369f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 10371da177e4SLinus Torvalds 10389f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 10399f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 10401da177e4SLinus Torvalds */ 10411da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 10421da177e4SLinus Torvalds { 10431da177e4SLinus Torvalds struct in_ifaddr *ifa; 10441da177e4SLinus Torvalds int named = 0; 10451da177e4SLinus Torvalds 10461da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 10471da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 10481da177e4SLinus Torvalds 10491da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 10501da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 10511da177e4SLinus Torvalds if (named++ == 0) 1052573bf470SThomas Graf goto skip; 105344344b2aSMark McLoughlin dot = strchr(old, ':'); 10541da177e4SLinus Torvalds if (dot == NULL) { 10551da177e4SLinus Torvalds sprintf(old, ":%d", named); 10561da177e4SLinus Torvalds dot = old; 10571da177e4SLinus Torvalds } 10589f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 10591da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 10609f9354b9SEric Dumazet else 10611da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1062573bf470SThomas Graf skip: 1063573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 10641da177e4SLinus Torvalds } 10651da177e4SLinus Torvalds } 10661da177e4SLinus Torvalds 106706770843SBreno Leitao static inline bool inetdev_valid_mtu(unsigned mtu) 106806770843SBreno Leitao { 106906770843SBreno Leitao return mtu >= 68; 107006770843SBreno Leitao } 107106770843SBreno Leitao 10721da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 10731da177e4SLinus Torvalds 10741da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 10751da177e4SLinus Torvalds void *ptr) 10761da177e4SLinus Torvalds { 10771da177e4SLinus Torvalds struct net_device *dev = ptr; 1078e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds ASSERT_RTNL(); 10811da177e4SLinus Torvalds 10821da177e4SLinus Torvalds if (!in_dev) { 10838030f544SHerbert Xu if (event == NETDEV_REGISTER) { 10841da177e4SLinus Torvalds in_dev = inetdev_init(dev); 10858d76527eSHerbert Xu if (!in_dev) 1086b217d616SHerbert Xu return notifier_from_errno(-ENOMEM); 10870cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 108842f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 108942f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 10901da177e4SLinus Torvalds } 109106770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 109206770843SBreno Leitao /* Re-enabling IP */ 109306770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 109406770843SBreno Leitao in_dev = inetdev_init(dev); 10958030f544SHerbert Xu } 10961da177e4SLinus Torvalds goto out; 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds 10991da177e4SLinus Torvalds switch (event) { 11001da177e4SLinus Torvalds case NETDEV_REGISTER: 11011da177e4SLinus Torvalds printk(KERN_DEBUG "inetdev_event: bug\n"); 110295ae6b22SEric Dumazet rcu_assign_pointer(dev->ip_ptr, NULL); 11031da177e4SLinus Torvalds break; 11041da177e4SLinus Torvalds case NETDEV_UP: 110506770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 11061da177e4SLinus Torvalds break; 11070cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 11089f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 11099f9354b9SEric Dumazet 11109f9354b9SEric Dumazet if (ifa) { 1111fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 11121da177e4SLinus Torvalds ifa->ifa_local = 11131da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 11141da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 11151da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 11161da177e4SLinus Torvalds in_dev_hold(in_dev); 11171da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 11181da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 11191da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11201da177e4SLinus Torvalds inet_insert_ifa(ifa); 11211da177e4SLinus Torvalds } 11221da177e4SLinus Torvalds } 11231da177e4SLinus Torvalds ip_mc_up(in_dev); 1124eefef1cfSStephen Hemminger /* fall through */ 112506c4648dSIan Campbell case NETDEV_NOTIFY_PEERS: 1126eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1127a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1128a21090cfSStephen Hemminger if (IN_DEV_ARP_NOTIFY(in_dev)) { 1129a21090cfSStephen Hemminger struct in_ifaddr *ifa = in_dev->ifa_list; 1130a21090cfSStephen Hemminger 1131a21090cfSStephen Hemminger if (ifa) 1132eefef1cfSStephen Hemminger arp_send(ARPOP_REQUEST, ETH_P_ARP, 1133a21090cfSStephen Hemminger ifa->ifa_address, dev, 1134a21090cfSStephen Hemminger ifa->ifa_address, NULL, 1135a21090cfSStephen Hemminger dev->dev_addr, NULL); 1136a21090cfSStephen Hemminger } 11371da177e4SLinus Torvalds break; 11381da177e4SLinus Torvalds case NETDEV_DOWN: 11391da177e4SLinus Torvalds ip_mc_down(in_dev); 11401da177e4SLinus Torvalds break; 114193d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 114275c78500SMoni Shoua ip_mc_unmap(in_dev); 114375c78500SMoni Shoua break; 114493d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 114575c78500SMoni Shoua ip_mc_remap(in_dev); 114675c78500SMoni Shoua break; 11471da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 114806770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 11491da177e4SLinus Torvalds break; 115006770843SBreno Leitao /* disable IP when MTU is not enough */ 11511da177e4SLinus Torvalds case NETDEV_UNREGISTER: 11521da177e4SLinus Torvalds inetdev_destroy(in_dev); 11531da177e4SLinus Torvalds break; 11541da177e4SLinus Torvalds case NETDEV_CHANGENAME: 11551da177e4SLinus Torvalds /* Do not notify about label change, this event is 11561da177e4SLinus Torvalds * not interesting to applications using netlink. 11571da177e4SLinus Torvalds */ 11581da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 11591da177e4SLinus Torvalds 116051602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 116166f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 11621da177e4SLinus Torvalds break; 11631da177e4SLinus Torvalds } 11641da177e4SLinus Torvalds out: 11651da177e4SLinus Torvalds return NOTIFY_DONE; 11661da177e4SLinus Torvalds } 11671da177e4SLinus Torvalds 11681da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 11691da177e4SLinus Torvalds .notifier_call = inetdev_event, 11701da177e4SLinus Torvalds }; 11711da177e4SLinus Torvalds 1172339bf98fSThomas Graf static inline size_t inet_nlmsg_size(void) 1173339bf98fSThomas Graf { 1174339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1175339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1176339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1177339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1178339bf98fSThomas Graf + nla_total_size(IFNAMSIZ); /* IFA_LABEL */ 1179339bf98fSThomas Graf } 1180339bf98fSThomas Graf 11811da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 1182b6544c0bSJamal Hadi Salim u32 pid, u32 seq, int event, unsigned int flags) 11831da177e4SLinus Torvalds { 11841da177e4SLinus Torvalds struct ifaddrmsg *ifm; 11851da177e4SLinus Torvalds struct nlmsghdr *nlh; 11861da177e4SLinus Torvalds 118747f68512SThomas Graf nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags); 118847f68512SThomas Graf if (nlh == NULL) 118926932566SPatrick McHardy return -EMSGSIZE; 119047f68512SThomas Graf 119147f68512SThomas Graf ifm = nlmsg_data(nlh); 11921da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 11931da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 11941da177e4SLinus Torvalds ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT; 11951da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 11961da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 11971da177e4SLinus Torvalds 119847f68512SThomas Graf if (ifa->ifa_address) 1199a7a628c4SAl Viro NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address); 120047f68512SThomas Graf 120147f68512SThomas Graf if (ifa->ifa_local) 1202a7a628c4SAl Viro NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local); 120347f68512SThomas Graf 120447f68512SThomas Graf if (ifa->ifa_broadcast) 1205a7a628c4SAl Viro NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast); 120647f68512SThomas Graf 120747f68512SThomas Graf if (ifa->ifa_label[0]) 120847f68512SThomas Graf NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label); 120947f68512SThomas Graf 121047f68512SThomas Graf return nlmsg_end(skb, nlh); 121147f68512SThomas Graf 121247f68512SThomas Graf nla_put_failure: 121326932566SPatrick McHardy nlmsg_cancel(skb, nlh); 121426932566SPatrick McHardy return -EMSGSIZE; 12151da177e4SLinus Torvalds } 12161da177e4SLinus Torvalds 12171da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 12181da177e4SLinus Torvalds { 12193b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1220eec4df98SEric Dumazet int h, s_h; 1221eec4df98SEric Dumazet int idx, s_idx; 1222eec4df98SEric Dumazet int ip_idx, s_ip_idx; 12231da177e4SLinus Torvalds struct net_device *dev; 12241da177e4SLinus Torvalds struct in_device *in_dev; 12251da177e4SLinus Torvalds struct in_ifaddr *ifa; 1226eec4df98SEric Dumazet struct hlist_head *head; 1227eec4df98SEric Dumazet struct hlist_node *node; 12281da177e4SLinus Torvalds 1229eec4df98SEric Dumazet s_h = cb->args[0]; 1230eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1231eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1232eec4df98SEric Dumazet 1233eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 12347562f876SPavel Emelianov idx = 0; 1235eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1236eec4df98SEric Dumazet rcu_read_lock(); 1237eec4df98SEric Dumazet hlist_for_each_entry_rcu(dev, node, head, index_hlist) { 12381da177e4SLinus Torvalds if (idx < s_idx) 12397562f876SPavel Emelianov goto cont; 12404b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 12411da177e4SLinus Torvalds s_ip_idx = 0; 1242eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12439f9354b9SEric Dumazet if (!in_dev) 12447562f876SPavel Emelianov goto cont; 12451da177e4SLinus Torvalds 12461da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 12471da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 12481da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1249596e4150SStephen Hemminger continue; 1250eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 1251eec4df98SEric Dumazet NETLINK_CB(cb->skb).pid, 12521da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1253eec4df98SEric Dumazet RTM_NEWADDR, NLM_F_MULTI) <= 0) { 1254eec4df98SEric Dumazet rcu_read_unlock(); 12551da177e4SLinus Torvalds goto done; 12561da177e4SLinus Torvalds } 1257eec4df98SEric Dumazet } 12587562f876SPavel Emelianov cont: 12597562f876SPavel Emelianov idx++; 12601da177e4SLinus Torvalds } 1261eec4df98SEric Dumazet rcu_read_unlock(); 1262eec4df98SEric Dumazet } 12631da177e4SLinus Torvalds 12641da177e4SLinus Torvalds done: 1265eec4df98SEric Dumazet cb->args[0] = h; 1266eec4df98SEric Dumazet cb->args[1] = idx; 1267eec4df98SEric Dumazet cb->args[2] = ip_idx; 12681da177e4SLinus Torvalds 12691da177e4SLinus Torvalds return skb->len; 12701da177e4SLinus Torvalds } 12711da177e4SLinus Torvalds 1272d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 1273d6062cbbSThomas Graf u32 pid) 12741da177e4SLinus Torvalds { 127547f68512SThomas Graf struct sk_buff *skb; 1276d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1277d6062cbbSThomas Graf int err = -ENOBUFS; 12784b8aa9abSDenis V. Lunev struct net *net; 12791da177e4SLinus Torvalds 1280c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1281339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 128247f68512SThomas Graf if (skb == NULL) 1283d6062cbbSThomas Graf goto errout; 1284d6062cbbSThomas Graf 1285d6062cbbSThomas Graf err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0); 128626932566SPatrick McHardy if (err < 0) { 128726932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 128826932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 128926932566SPatrick McHardy kfree_skb(skb); 129026932566SPatrick McHardy goto errout; 129126932566SPatrick McHardy } 12921ce85fe4SPablo Neira Ayuso rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 12931ce85fe4SPablo Neira Ayuso return; 1294d6062cbbSThomas Graf errout: 1295d6062cbbSThomas Graf if (err < 0) 12964b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 12971da177e4SLinus Torvalds } 12981da177e4SLinus Torvalds 12999f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 13009f0f7272SThomas Graf { 1301f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13029f0f7272SThomas Graf 13039f0f7272SThomas Graf if (!in_dev) 13049f0f7272SThomas Graf return 0; 13059f0f7272SThomas Graf 13069f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 13079f0f7272SThomas Graf } 13089f0f7272SThomas Graf 13099f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 13109f0f7272SThomas Graf { 1311f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13129f0f7272SThomas Graf struct nlattr *nla; 13139f0f7272SThomas Graf int i; 13149f0f7272SThomas Graf 13159f0f7272SThomas Graf if (!in_dev) 13169f0f7272SThomas Graf return -ENODATA; 13179f0f7272SThomas Graf 13189f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 13199f0f7272SThomas Graf if (nla == NULL) 13209f0f7272SThomas Graf return -EMSGSIZE; 13219f0f7272SThomas Graf 13229f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 13239f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 13249f0f7272SThomas Graf 13259f0f7272SThomas Graf return 0; 13269f0f7272SThomas Graf } 13279f0f7272SThomas Graf 13289f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 13299f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 13309f0f7272SThomas Graf }; 13319f0f7272SThomas Graf 1332cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1333cf7afbfeSThomas Graf const struct nlattr *nla) 13349f0f7272SThomas Graf { 13359f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 13369f0f7272SThomas Graf int err, rem; 13379f0f7272SThomas Graf 1338f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1339cf7afbfeSThomas Graf return -EAFNOSUPPORT; 13409f0f7272SThomas Graf 13419f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 13429f0f7272SThomas Graf if (err < 0) 13439f0f7272SThomas Graf return err; 13449f0f7272SThomas Graf 13459f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 13469f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 13479f0f7272SThomas Graf int cfgid = nla_type(a); 13489f0f7272SThomas Graf 13499f0f7272SThomas Graf if (nla_len(a) < 4) 13509f0f7272SThomas Graf return -EINVAL; 13519f0f7272SThomas Graf 13529f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 13539f0f7272SThomas Graf return -EINVAL; 13549f0f7272SThomas Graf } 13559f0f7272SThomas Graf } 13569f0f7272SThomas Graf 1357cf7afbfeSThomas Graf return 0; 1358cf7afbfeSThomas Graf } 1359cf7afbfeSThomas Graf 1360cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1361cf7afbfeSThomas Graf { 1362f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1363cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1364cf7afbfeSThomas Graf int rem; 1365cf7afbfeSThomas Graf 1366cf7afbfeSThomas Graf if (!in_dev) 1367cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1368cf7afbfeSThomas Graf 1369cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1370cf7afbfeSThomas Graf BUG(); 1371cf7afbfeSThomas Graf 13729f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 13739f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 13749f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 13759f0f7272SThomas Graf } 13769f0f7272SThomas Graf 13779f0f7272SThomas Graf return 0; 13789f0f7272SThomas Graf } 13799f0f7272SThomas Graf 13801da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 13811da177e4SLinus Torvalds 1382c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 138331be3085SHerbert Xu { 138431be3085SHerbert Xu struct net_device *dev; 138531be3085SHerbert Xu 138631be3085SHerbert Xu rcu_read_lock(); 1387c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1388c6d14c84SEric Dumazet struct in_device *in_dev; 1389c6d14c84SEric Dumazet 139031be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 139131be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 13929355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1393c6d14c84SEric Dumazet } 139431be3085SHerbert Xu rcu_read_unlock(); 139531be3085SHerbert Xu } 139631be3085SHerbert Xu 1397c6d14c84SEric Dumazet /* called with RTNL locked */ 1398c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 139968dd299bSPavel Emelyanov { 140068dd299bSPavel Emelyanov struct net_device *dev; 1401586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 140268dd299bSPavel Emelyanov 1403586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 14049355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 140568dd299bSPavel Emelyanov 1406c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 140768dd299bSPavel Emelyanov struct in_device *in_dev; 14080187bdfbSBen Hutchings if (on) 14090187bdfbSBen Hutchings dev_disable_lro(dev); 141068dd299bSPavel Emelyanov rcu_read_lock(); 141168dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 141268dd299bSPavel Emelyanov if (in_dev) 141368dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 141468dd299bSPavel Emelyanov rcu_read_unlock(); 141568dd299bSPavel Emelyanov } 141668dd299bSPavel Emelyanov } 141768dd299bSPavel Emelyanov 141831be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write, 14198d65af78SAlexey Dobriyan void __user *buffer, 142031be3085SHerbert Xu size_t *lenp, loff_t *ppos) 142131be3085SHerbert Xu { 14228d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 142331be3085SHerbert Xu 142431be3085SHerbert Xu if (write) { 142531be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 1426c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 142731be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 142831be3085SHerbert Xu 142931be3085SHerbert Xu set_bit(i, cnf->state); 143031be3085SHerbert Xu 14319355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 1432c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 143331be3085SHerbert Xu } 143431be3085SHerbert Xu 143531be3085SHerbert Xu return ret; 143631be3085SHerbert Xu } 143731be3085SHerbert Xu 14381da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write, 14398d65af78SAlexey Dobriyan void __user *buffer, 14401da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 14411da177e4SLinus Torvalds { 14421da177e4SLinus Torvalds int *valp = ctl->data; 14431da177e4SLinus Torvalds int val = *valp; 144488af182eSEric W. Biederman loff_t pos = *ppos; 14458d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 14461da177e4SLinus Torvalds 14471da177e4SLinus Torvalds if (write && *valp != val) { 1448c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 1449c0ce9fb3SPavel Emelyanov 14500187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 145188af182eSEric W. Biederman if (!rtnl_trylock()) { 145288af182eSEric W. Biederman /* Restore the original values before restarting */ 145388af182eSEric W. Biederman *valp = val; 145488af182eSEric W. Biederman *ppos = pos; 14559b8adb5eSEric W. Biederman return restart_syscall(); 145688af182eSEric W. Biederman } 14570187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 1458c0ce9fb3SPavel Emelyanov inet_forward_change(net); 14590187bdfbSBen Hutchings } else if (*valp) { 14600187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 14610187bdfbSBen Hutchings struct in_device *idev = 14620187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 14630187bdfbSBen Hutchings dev_disable_lro(idev->dev); 14640187bdfbSBen Hutchings } 14650187bdfbSBen Hutchings rtnl_unlock(); 146676e6ebfbSDenis V. Lunev rt_cache_flush(net, 0); 14671da177e4SLinus Torvalds } 14680187bdfbSBen Hutchings } 14691da177e4SLinus Torvalds 14701da177e4SLinus Torvalds return ret; 14711da177e4SLinus Torvalds } 14721da177e4SLinus Torvalds 1473323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write, 14748d65af78SAlexey Dobriyan void __user *buffer, 14751da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 14761da177e4SLinus Torvalds { 14771da177e4SLinus Torvalds int *valp = ctl->data; 14781da177e4SLinus Torvalds int val = *valp; 14798d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 148076e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 14811da177e4SLinus Torvalds 14821da177e4SLinus Torvalds if (write && *valp != val) 148376e6ebfbSDenis V. Lunev rt_cache_flush(net, 0); 14841da177e4SLinus Torvalds 14851da177e4SLinus Torvalds return ret; 14861da177e4SLinus Torvalds } 14871da177e4SLinus Torvalds 1488f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 148942f811b8SHerbert Xu { \ 149042f811b8SHerbert Xu .procname = name, \ 149142f811b8SHerbert Xu .data = ipv4_devconf.data + \ 149202291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 149342f811b8SHerbert Xu .maxlen = sizeof(int), \ 149442f811b8SHerbert Xu .mode = mval, \ 149542f811b8SHerbert Xu .proc_handler = proc, \ 149631be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 149742f811b8SHerbert Xu } 149842f811b8SHerbert Xu 149942f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 1500f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 150142f811b8SHerbert Xu 150242f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 1503f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 150442f811b8SHerbert Xu 1505f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 1506f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 150742f811b8SHerbert Xu 150842f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 1509f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 151042f811b8SHerbert Xu 15111da177e4SLinus Torvalds static struct devinet_sysctl_table { 15121da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 151302291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 1514bfada697SPavel Emelyanov char *dev_name; 15151da177e4SLinus Torvalds } devinet_sysctl = { 15161da177e4SLinus Torvalds .devinet_vars = { 151742f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 1518f8572d8fSEric W. Biederman devinet_sysctl_forward), 151942f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 152042f811b8SHerbert Xu 152142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 152242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 152342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 152442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 152542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 152642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 152742f811b8SHerbert Xu "accept_source_route"), 15288153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 152928f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 153042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 153142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 153242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 153342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 153442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 153542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 153642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 153742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 153842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 1539eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 154065324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 154142f811b8SHerbert Xu 154242f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 154342f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 154442f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION, 154542f811b8SHerbert Xu "force_igmp_version"), 154642f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 154742f811b8SHerbert Xu "promote_secondaries"), 15481da177e4SLinus Torvalds }, 15491da177e4SLinus Torvalds }; 15501da177e4SLinus Torvalds 1551ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 1552f8572d8fSEric W. Biederman struct ipv4_devconf *p) 15531da177e4SLinus Torvalds { 15541da177e4SLinus Torvalds int i; 15559fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 15561da177e4SLinus Torvalds 1557bfada697SPavel Emelyanov #define DEVINET_CTL_PATH_DEV 3 1558bfada697SPavel Emelyanov 1559bfada697SPavel Emelyanov struct ctl_path devinet_ctl_path[] = { 1560f8572d8fSEric W. Biederman { .procname = "net", }, 1561f8572d8fSEric W. Biederman { .procname = "ipv4", }, 1562f8572d8fSEric W. Biederman { .procname = "conf", }, 1563bfada697SPavel Emelyanov { /* to be set */ }, 1564bfada697SPavel Emelyanov { }, 1565bfada697SPavel Emelyanov }; 1566bfada697SPavel Emelyanov 15679fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 15681da177e4SLinus Torvalds if (!t) 15699fa89642SPavel Emelyanov goto out; 15709fa89642SPavel Emelyanov 15711da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 15721da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 157331be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 1574c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 15751da177e4SLinus Torvalds } 15761da177e4SLinus Torvalds 15771da177e4SLinus Torvalds /* 15781da177e4SLinus Torvalds * Make a copy of dev_name, because '.procname' is regarded as const 15791da177e4SLinus Torvalds * by sysctl and we wouldn't want anyone to change it under our feet 15801da177e4SLinus Torvalds * (see SIOCSIFNAME). 15811da177e4SLinus Torvalds */ 1582bfada697SPavel Emelyanov t->dev_name = kstrdup(dev_name, GFP_KERNEL); 1583bfada697SPavel Emelyanov if (!t->dev_name) 15841da177e4SLinus Torvalds goto free; 15851da177e4SLinus Torvalds 1586bfada697SPavel Emelyanov devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name; 15871da177e4SLinus Torvalds 1588752d14dcSPavel Emelyanov t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path, 1589bfada697SPavel Emelyanov t->devinet_vars); 15901da177e4SLinus Torvalds if (!t->sysctl_header) 15911da177e4SLinus Torvalds goto free_procname; 15921da177e4SLinus Torvalds 15931da177e4SLinus Torvalds p->sysctl = t; 1594ea40b324SPavel Emelyanov return 0; 15951da177e4SLinus Torvalds 15961da177e4SLinus Torvalds free_procname: 1597bfada697SPavel Emelyanov kfree(t->dev_name); 15981da177e4SLinus Torvalds free: 15991da177e4SLinus Torvalds kfree(t); 16009fa89642SPavel Emelyanov out: 1601ea40b324SPavel Emelyanov return -ENOBUFS; 16021da177e4SLinus Torvalds } 16031da177e4SLinus Torvalds 160451602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 160566f27a52SPavel Emelyanov { 160651602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 160766f27a52SPavel Emelyanov 160851602b2aSPavel Emelyanov if (t == NULL) 160951602b2aSPavel Emelyanov return; 161051602b2aSPavel Emelyanov 161151602b2aSPavel Emelyanov cnf->sysctl = NULL; 16121da177e4SLinus Torvalds unregister_sysctl_table(t->sysctl_header); 1613bfada697SPavel Emelyanov kfree(t->dev_name); 16141da177e4SLinus Torvalds kfree(t); 16151da177e4SLinus Torvalds } 161651602b2aSPavel Emelyanov 161751602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev) 161851602b2aSPavel Emelyanov { 161954716e3bSEric W. Biederman neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL); 1620c346dca1SYOSHIFUJI Hideaki __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 1621f8572d8fSEric W. Biederman &idev->cnf); 162251602b2aSPavel Emelyanov } 162351602b2aSPavel Emelyanov 162451602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 162551602b2aSPavel Emelyanov { 162651602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 162751602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 16281da177e4SLinus Torvalds } 16291da177e4SLinus Torvalds 163068dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 163168dd299bSPavel Emelyanov { 163268dd299bSPavel Emelyanov .procname = "ip_forward", 163368dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 163402291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 163568dd299bSPavel Emelyanov .maxlen = sizeof(int), 163668dd299bSPavel Emelyanov .mode = 0644, 163768dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 163868dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 1639c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 164068dd299bSPavel Emelyanov }, 164168dd299bSPavel Emelyanov { }, 164268dd299bSPavel Emelyanov }; 164368dd299bSPavel Emelyanov 1644752d14dcSPavel Emelyanov static __net_initdata struct ctl_path net_ipv4_path[] = { 1645f8572d8fSEric W. Biederman { .procname = "net", }, 1646f8572d8fSEric W. Biederman { .procname = "ipv4", }, 164768dd299bSPavel Emelyanov { }, 164868dd299bSPavel Emelyanov }; 16492a75de0cSEric Dumazet #endif 165068dd299bSPavel Emelyanov 1651752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 1652752d14dcSPavel Emelyanov { 1653752d14dcSPavel Emelyanov int err; 1654752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 16552a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 16562a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 1657752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 16582a75de0cSEric Dumazet #endif 1659752d14dcSPavel Emelyanov 1660752d14dcSPavel Emelyanov err = -ENOMEM; 1661752d14dcSPavel Emelyanov all = &ipv4_devconf; 1662752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 1663752d14dcSPavel Emelyanov 166409ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 1665752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 1666752d14dcSPavel Emelyanov if (all == NULL) 1667752d14dcSPavel Emelyanov goto err_alloc_all; 1668752d14dcSPavel Emelyanov 1669752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 1670752d14dcSPavel Emelyanov if (dflt == NULL) 1671752d14dcSPavel Emelyanov goto err_alloc_dflt; 1672752d14dcSPavel Emelyanov 16732a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 1674752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 1675752d14dcSPavel Emelyanov if (tbl == NULL) 1676752d14dcSPavel Emelyanov goto err_alloc_ctl; 1677752d14dcSPavel Emelyanov 167802291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 1679752d14dcSPavel Emelyanov tbl[0].extra1 = all; 1680752d14dcSPavel Emelyanov tbl[0].extra2 = net; 16812a75de0cSEric Dumazet #endif 1682752d14dcSPavel Emelyanov } 1683752d14dcSPavel Emelyanov 1684752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 1685f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 1686752d14dcSPavel Emelyanov if (err < 0) 1687752d14dcSPavel Emelyanov goto err_reg_all; 1688752d14dcSPavel Emelyanov 1689f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 1690752d14dcSPavel Emelyanov if (err < 0) 1691752d14dcSPavel Emelyanov goto err_reg_dflt; 1692752d14dcSPavel Emelyanov 1693752d14dcSPavel Emelyanov err = -ENOMEM; 1694752d14dcSPavel Emelyanov forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl); 1695752d14dcSPavel Emelyanov if (forw_hdr == NULL) 1696752d14dcSPavel Emelyanov goto err_reg_ctl; 16972a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 1698752d14dcSPavel Emelyanov #endif 1699752d14dcSPavel Emelyanov 1700752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 1701752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 1702752d14dcSPavel Emelyanov return 0; 1703752d14dcSPavel Emelyanov 1704752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 1705752d14dcSPavel Emelyanov err_reg_ctl: 1706752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 1707752d14dcSPavel Emelyanov err_reg_dflt: 1708752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 1709752d14dcSPavel Emelyanov err_reg_all: 1710752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 1711752d14dcSPavel Emelyanov kfree(tbl); 1712752d14dcSPavel Emelyanov err_alloc_ctl: 17132a75de0cSEric Dumazet #endif 1714752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 1715752d14dcSPavel Emelyanov kfree(dflt); 1716752d14dcSPavel Emelyanov err_alloc_dflt: 1717752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 1718752d14dcSPavel Emelyanov kfree(all); 1719752d14dcSPavel Emelyanov err_alloc_all: 1720752d14dcSPavel Emelyanov return err; 1721752d14dcSPavel Emelyanov } 1722752d14dcSPavel Emelyanov 1723752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 1724752d14dcSPavel Emelyanov { 17252a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 1726752d14dcSPavel Emelyanov struct ctl_table *tbl; 1727752d14dcSPavel Emelyanov 1728752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 1729752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 1730752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 1731752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 1732752d14dcSPavel Emelyanov kfree(tbl); 17332a75de0cSEric Dumazet #endif 1734752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 1735752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 1736752d14dcSPavel Emelyanov } 1737752d14dcSPavel Emelyanov 1738752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 1739752d14dcSPavel Emelyanov .init = devinet_init_net, 1740752d14dcSPavel Emelyanov .exit = devinet_exit_net, 1741752d14dcSPavel Emelyanov }; 1742752d14dcSPavel Emelyanov 17439f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = { 17449f0f7272SThomas Graf .family = AF_INET, 17459f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 17469f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 1747cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 1748cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 17499f0f7272SThomas Graf }; 17509f0f7272SThomas Graf 17511da177e4SLinus Torvalds void __init devinet_init(void) 17521da177e4SLinus Torvalds { 1753fd23c3b3SDavid S. Miller int i; 1754fd23c3b3SDavid S. Miller 1755fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 1756fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 1757fd23c3b3SDavid S. Miller 1758752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 1759752d14dcSPavel Emelyanov 17601da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 17611da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 176263f3444fSThomas Graf 17639f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 17649f0f7272SThomas Graf 176563f3444fSThomas Graf rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL); 176663f3444fSThomas Graf rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL); 176763f3444fSThomas Graf rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); 17681da177e4SLinus Torvalds } 17691da177e4SLinus Torvalds 1770