11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * NET3 IP device support routines. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 51da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 61da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 71da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Derived from the IP parts of dev.c 1.0.19 1002c30a84SJesper Juhl * Authors: Ross Biro 111da177e4SLinus Torvalds * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> 121da177e4SLinus Torvalds * Mark Evans, <evansmp@uhura.aston.ac.uk> 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * Additional Authors: 151da177e4SLinus Torvalds * Alan Cox, <gw4pts@gw4pts.ampr.org> 161da177e4SLinus Torvalds * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * Changes: 191da177e4SLinus Torvalds * Alexey Kuznetsov: pa_* fields are replaced with ifaddr 201da177e4SLinus Torvalds * lists. 211da177e4SLinus Torvalds * Cyrus Durgin: updated for kmod 221da177e4SLinus Torvalds * Matthias Andree: in devinet_ioctl, compare label and 231da177e4SLinus Torvalds * address (4.4BSD alias style support), 241da177e4SLinus Torvalds * fall back to comparing just the label 251da177e4SLinus Torvalds * if no match found. 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #include <asm/uaccess.h> 301da177e4SLinus Torvalds #include <linux/bitops.h> 314fc268d2SRandy Dunlap #include <linux/capability.h> 321da177e4SLinus Torvalds #include <linux/module.h> 331da177e4SLinus Torvalds #include <linux/types.h> 341da177e4SLinus Torvalds #include <linux/kernel.h> 351da177e4SLinus Torvalds #include <linux/string.h> 361da177e4SLinus Torvalds #include <linux/mm.h> 371da177e4SLinus Torvalds #include <linux/socket.h> 381da177e4SLinus Torvalds #include <linux/sockios.h> 391da177e4SLinus Torvalds #include <linux/in.h> 401da177e4SLinus Torvalds #include <linux/errno.h> 411da177e4SLinus Torvalds #include <linux/interrupt.h> 421823730fSThomas Graf #include <linux/if_addr.h> 431da177e4SLinus Torvalds #include <linux/if_ether.h> 441da177e4SLinus Torvalds #include <linux/inet.h> 451da177e4SLinus Torvalds #include <linux/netdevice.h> 461da177e4SLinus Torvalds #include <linux/etherdevice.h> 471da177e4SLinus Torvalds #include <linux/skbuff.h> 481da177e4SLinus Torvalds #include <linux/init.h> 491da177e4SLinus Torvalds #include <linux/notifier.h> 501da177e4SLinus Torvalds #include <linux/inetdevice.h> 511da177e4SLinus Torvalds #include <linux/igmp.h> 525a0e3ad6STejun Heo #include <linux/slab.h> 53fd23c3b3SDavid S. Miller #include <linux/hash.h> 541da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 551da177e4SLinus Torvalds #include <linux/sysctl.h> 561da177e4SLinus Torvalds #endif 571da177e4SLinus Torvalds #include <linux/kmod.h> 58edc9e748SNicolas Dichtel #include <linux/netconf.h> 591da177e4SLinus Torvalds 6014c85021SArnaldo Carvalho de Melo #include <net/arp.h> 611da177e4SLinus Torvalds #include <net/ip.h> 621da177e4SLinus Torvalds #include <net/route.h> 631da177e4SLinus Torvalds #include <net/ip_fib.h> 6463f3444fSThomas Graf #include <net/rtnetlink.h> 65752d14dcSPavel Emelyanov #include <net/net_namespace.h> 665c766d64SJiri Pirko #include <net/addrconf.h> 671da177e4SLinus Torvalds 68406b6f97SDavid S. Miller #include "fib_lookup.h" 69406b6f97SDavid S. Miller 700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 7142f811b8SHerbert Xu .data = { 7202291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7302291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7402291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7502291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 7642f811b8SHerbert Xu }, 771da177e4SLinus Torvalds }; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 8042f811b8SHerbert Xu .data = { 8102291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8202291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8302291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8402291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8502291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 8642f811b8SHerbert Xu }, 871da177e4SLinus Torvalds }; 881da177e4SLinus Torvalds 899355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 909355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9142f811b8SHerbert Xu 92ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 935c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 945c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 955c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 965176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 975c766d64SJiri Pirko [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 985c753978SThomas Graf }; 995c753978SThomas Graf 10040384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT 8 10140384999SEric Dumazet #define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 10240384999SEric Dumazet 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 10640384999SEric Dumazet static u32 inet_addr_hash(struct net *net, __be32 addr) 107fd23c3b3SDavid S. Miller { 10840384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 109fd23c3b3SDavid S. Miller 11040384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 111fd23c3b3SDavid S. Miller } 112fd23c3b3SDavid S. Miller 113fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 114fd23c3b3SDavid S. Miller { 11540384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 116fd23c3b3SDavid S. Miller 117fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 118fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 119fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 120fd23c3b3SDavid S. Miller } 121fd23c3b3SDavid S. Miller 122fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 123fd23c3b3SDavid S. Miller { 124fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 125fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 126fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 127fd23c3b3SDavid S. Miller } 128fd23c3b3SDavid S. Miller 1299435eb1cSDavid S. Miller /** 1309435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1319435eb1cSDavid S. Miller * @net: the net namespace 1329435eb1cSDavid S. Miller * @addr: the source address 1339435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1349435eb1cSDavid S. Miller * 1359435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1369435eb1cSDavid S. Miller */ 1379435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1389435eb1cSDavid S. Miller { 13940384999SEric Dumazet u32 hash = inet_addr_hash(net, addr); 1409435eb1cSDavid S. Miller struct net_device *result = NULL; 1419435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1429435eb1cSDavid S. Miller 1439435eb1cSDavid S. Miller rcu_read_lock(); 144b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) { 14540384999SEric Dumazet if (ifa->ifa_local == addr) { 1469435eb1cSDavid S. Miller struct net_device *dev = ifa->ifa_dev->dev; 1479435eb1cSDavid S. Miller 1489435eb1cSDavid S. Miller if (!net_eq(dev_net(dev), net)) 1499435eb1cSDavid S. Miller continue; 1509435eb1cSDavid S. Miller result = dev; 1519435eb1cSDavid S. Miller break; 1529435eb1cSDavid S. Miller } 1539435eb1cSDavid S. Miller } 154406b6f97SDavid S. Miller if (!result) { 155406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 156406b6f97SDavid S. Miller struct fib_result res = { 0 }; 157406b6f97SDavid S. Miller struct fib_table *local; 158406b6f97SDavid S. Miller 159406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 160406b6f97SDavid S. Miller * over loopback subnets work. 161406b6f97SDavid S. Miller */ 162406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 163406b6f97SDavid S. Miller if (local && 164406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 165406b6f97SDavid S. Miller res.type == RTN_LOCAL) 166406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 167406b6f97SDavid S. Miller } 1689435eb1cSDavid S. Miller if (result && devref) 1699435eb1cSDavid S. Miller dev_hold(result); 1709435eb1cSDavid S. Miller rcu_read_unlock(); 1719435eb1cSDavid S. Miller return result; 1729435eb1cSDavid S. Miller } 1739435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1749435eb1cSDavid S. Miller 175d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1761da177e4SLinus Torvalds 177e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1781da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1791da177e4SLinus Torvalds int destroy); 1801da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 18166f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev); 18251602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 18351602b2aSPavel Emelyanov #else 18440384999SEric Dumazet static void devinet_sysctl_register(struct in_device *idev) 18551602b2aSPavel Emelyanov { 18651602b2aSPavel Emelyanov } 18740384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 18851602b2aSPavel Emelyanov { 18951602b2aSPavel Emelyanov } 1901da177e4SLinus Torvalds #endif 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds /* Locks all the inet devices. */ 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 1951da177e4SLinus Torvalds { 19693adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2001da177e4SLinus Torvalds { 2011da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2021da177e4SLinus Torvalds if (ifa->ifa_dev) 2031da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2041da177e4SLinus Torvalds kfree(ifa); 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 20740384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2081da177e4SLinus Torvalds { 2091da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2131da177e4SLinus Torvalds { 2141da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2151da177e4SLinus Torvalds 216547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 217547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 2181da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 21991df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2201da177e4SLinus Torvalds #endif 2211da177e4SLinus Torvalds dev_put(dev); 2221da177e4SLinus Torvalds if (!idev->dead) 2239f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2249f9354b9SEric Dumazet else 2251da177e4SLinus Torvalds kfree(idev); 2261da177e4SLinus Torvalds } 2279f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2281da177e4SLinus Torvalds 22971e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2301da177e4SLinus Torvalds { 2311da177e4SLinus Torvalds struct in_device *in_dev; 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds ASSERT_RTNL(); 2341da177e4SLinus Torvalds 2350da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2361da177e4SLinus Torvalds if (!in_dev) 2371da177e4SLinus Torvalds goto out; 238c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2399355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2401da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2411da177e4SLinus Torvalds in_dev->dev = dev; 2429f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2439f9354b9SEric Dumazet if (!in_dev->arp_parms) 2441da177e4SLinus Torvalds goto out_kfree; 2450187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2460187bdfbSBen Hutchings dev_disable_lro(dev); 2471da177e4SLinus Torvalds /* Reference in_dev->dev */ 2481da177e4SLinus Torvalds dev_hold(dev); 24930c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2501da177e4SLinus Torvalds in_dev_hold(in_dev); 2511da177e4SLinus Torvalds 25266f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 2531da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2541da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2551da177e4SLinus Torvalds ip_mc_up(in_dev); 256483479ecSJarek Poplawski 25730c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 258cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 259483479ecSJarek Poplawski out: 2601da177e4SLinus Torvalds return in_dev; 2611da177e4SLinus Torvalds out_kfree: 2621da177e4SLinus Torvalds kfree(in_dev); 2631da177e4SLinus Torvalds in_dev = NULL; 2641da177e4SLinus Torvalds goto out; 2651da177e4SLinus Torvalds } 2661da177e4SLinus Torvalds 2671da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2681da177e4SLinus Torvalds { 2691da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2701da177e4SLinus Torvalds in_dev_put(idev); 2711da177e4SLinus Torvalds } 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2741da177e4SLinus Torvalds { 2751da177e4SLinus Torvalds struct in_ifaddr *ifa; 2761da177e4SLinus Torvalds struct net_device *dev; 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds ASSERT_RTNL(); 2791da177e4SLinus Torvalds 2801da177e4SLinus Torvalds dev = in_dev->dev; 2811da177e4SLinus Torvalds 2821da177e4SLinus Torvalds in_dev->dead = 1; 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 2871da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 2881da177e4SLinus Torvalds inet_free_ifa(ifa); 2891da177e4SLinus Torvalds } 2901da177e4SLinus Torvalds 291a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 2921da177e4SLinus Torvalds 29351602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 2941da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 2951da177e4SLinus Torvalds arp_ifdown(dev); 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 300ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds rcu_read_lock(); 3031da177e4SLinus Torvalds for_primary_ifa(in_dev) { 3041da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3051da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3061da177e4SLinus Torvalds rcu_read_unlock(); 3071da177e4SLinus Torvalds return 1; 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds } 3101da177e4SLinus Torvalds } endfor_ifa(in_dev); 3111da177e4SLinus Torvalds rcu_read_unlock(); 3121da177e4SLinus Torvalds return 0; 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds 315d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 31615e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3171da177e4SLinus Torvalds { 3188f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3190ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3200ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3210ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3220ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3231da177e4SLinus Torvalds 3241da177e4SLinus Torvalds ASSERT_RTNL(); 3251da177e4SLinus Torvalds 3268f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3278f937c60SHarald Welte * unless alias promotion is set 3288f937c60SHarald Welte **/ 3291da177e4SLinus Torvalds 3301da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3311da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3340ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3350ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3360ff60a45SJamal Hadi Salim last_prim = ifa; 3370ff60a45SJamal Hadi Salim 3381da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3391da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3401da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3411da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3420ff60a45SJamal Hadi Salim prev_prom = ifa; 3431da177e4SLinus Torvalds continue; 3441da177e4SLinus Torvalds } 3451da177e4SLinus Torvalds 3460ff60a45SJamal Hadi Salim if (!do_promote) { 347fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3481da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3491da177e4SLinus Torvalds 35015e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 351e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 352e041c683SAlan Stern NETDEV_DOWN, ifa); 3531da177e4SLinus Torvalds inet_free_ifa(ifa); 3548f937c60SHarald Welte } else { 3558f937c60SHarald Welte promote = ifa; 3568f937c60SHarald Welte break; 3578f937c60SHarald Welte } 3581da177e4SLinus Torvalds } 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds 3612d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 3622d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 3632d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 3642d230e2bSJulian Anastasov * while all addresses are on the device list. 3652d230e2bSJulian Anastasov */ 3662d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 3672d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 3682d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 3692d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 3702d230e2bSJulian Anastasov } 3712d230e2bSJulian Anastasov 3721da177e4SLinus Torvalds /* 2. Unlink it */ 3731da177e4SLinus Torvalds 3741da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 375fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3761da177e4SLinus Torvalds 3771da177e4SLinus Torvalds /* 3. Announce address deletion */ 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds /* Send message first, then call notifier. 3801da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 3811da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 3821da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 3831da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 3841da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 3851da177e4SLinus Torvalds So that, this order is correct. 3861da177e4SLinus Torvalds */ 38715e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 388e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 3890ff60a45SJamal Hadi Salim 3900ff60a45SJamal Hadi Salim if (promote) { 39104024b93SJulian Anastasov struct in_ifaddr *next_sec = promote->ifa_next; 3920ff60a45SJamal Hadi Salim 3930ff60a45SJamal Hadi Salim if (prev_prom) { 3940ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 3950ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 3960ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 3970ff60a45SJamal Hadi Salim } 3980ff60a45SJamal Hadi Salim 3990ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 40015e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 401e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 402e041c683SAlan Stern NETDEV_UP, promote); 40304024b93SJulian Anastasov for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { 4040ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4050ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4060ff60a45SJamal Hadi Salim continue; 4070ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4080ff60a45SJamal Hadi Salim } 4090ff60a45SJamal Hadi Salim 4100ff60a45SJamal Hadi Salim } 4116363097cSHerbert Xu if (destroy) 4121da177e4SLinus Torvalds inet_free_ifa(ifa1); 4131da177e4SLinus Torvalds } 4141da177e4SLinus Torvalds 415d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 416d6062cbbSThomas Graf int destroy) 417d6062cbbSThomas Graf { 418d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 419d6062cbbSThomas Graf } 420d6062cbbSThomas Graf 4215c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4225c766d64SJiri Pirko 4235c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4245c766d64SJiri Pirko 425d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 42615e47304SEric W. Biederman u32 portid) 4271da177e4SLinus Torvalds { 4281da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4291da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4301da177e4SLinus Torvalds 4311da177e4SLinus Torvalds ASSERT_RTNL(); 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds if (!ifa->ifa_local) { 4341da177e4SLinus Torvalds inet_free_ifa(ifa); 4351da177e4SLinus Torvalds return 0; 4361da177e4SLinus Torvalds } 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4391da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4421da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4431da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4441da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4451da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4461da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4471da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4481da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4491da177e4SLinus Torvalds inet_free_ifa(ifa); 4501da177e4SLinus Torvalds return -EEXIST; 4511da177e4SLinus Torvalds } 4521da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4531da177e4SLinus Torvalds inet_free_ifa(ifa); 4541da177e4SLinus Torvalds return -EINVAL; 4551da177e4SLinus Torvalds } 4561da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4571da177e4SLinus Torvalds } 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 4611da177e4SLinus Torvalds net_srandom(ifa->ifa_local); 4621da177e4SLinus Torvalds ifap = last_primary; 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds 4651da177e4SLinus Torvalds ifa->ifa_next = *ifap; 4661da177e4SLinus Torvalds *ifap = ifa; 4671da177e4SLinus Torvalds 468fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 469fd23c3b3SDavid S. Miller 4705c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 4715c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, 0); 4725c766d64SJiri Pirko 4731da177e4SLinus Torvalds /* Send message first, then call notifier. 4741da177e4SLinus Torvalds Notifier will trigger FIB update, so that 4751da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 47615e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 477e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 4781da177e4SLinus Torvalds 4791da177e4SLinus Torvalds return 0; 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds 482d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 483d6062cbbSThomas Graf { 484d6062cbbSThomas Graf return __inet_insert_ifa(ifa, NULL, 0); 485d6062cbbSThomas Graf } 486d6062cbbSThomas Graf 4871da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 4881da177e4SLinus Torvalds { 489e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds ASSERT_RTNL(); 4921da177e4SLinus Torvalds 4931da177e4SLinus Torvalds if (!in_dev) { 4941da177e4SLinus Torvalds inet_free_ifa(ifa); 4951da177e4SLinus Torvalds return -ENOBUFS; 4961da177e4SLinus Torvalds } 49771e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 4981da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 499547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 5001da177e4SLinus Torvalds in_dev_hold(in_dev); 5011da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5021da177e4SLinus Torvalds } 503f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5041da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5051da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds 5088723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5098723e1b4SEric Dumazet * We dont take a reference on found in_device 5108723e1b4SEric Dumazet */ 5117fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5121da177e4SLinus Torvalds { 5131da177e4SLinus Torvalds struct net_device *dev; 5141da177e4SLinus Torvalds struct in_device *in_dev = NULL; 515c148fc2eSEric Dumazet 516c148fc2eSEric Dumazet rcu_read_lock(); 517c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5181da177e4SLinus Torvalds if (dev) 5198723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 520c148fc2eSEric Dumazet rcu_read_unlock(); 5211da177e4SLinus Torvalds return in_dev; 5221da177e4SLinus Torvalds } 5239f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5261da177e4SLinus Torvalds 52760cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 52860cad5daSAl Viro __be32 mask) 5291da177e4SLinus Torvalds { 5301da177e4SLinus Torvalds ASSERT_RTNL(); 5311da177e4SLinus Torvalds 5321da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5331da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5341da177e4SLinus Torvalds return ifa; 5351da177e4SLinus Torvalds } endfor_ifa(in_dev); 5361da177e4SLinus Torvalds return NULL; 5371da177e4SLinus Torvalds } 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 5401da177e4SLinus Torvalds { 5413b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 542dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5431da177e4SLinus Torvalds struct in_device *in_dev; 544dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 5451da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 546dfdd5fd4SThomas Graf int err = -EINVAL; 5471da177e4SLinus Torvalds 5481da177e4SLinus Torvalds ASSERT_RTNL(); 5491da177e4SLinus Torvalds 550dfdd5fd4SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 551dfdd5fd4SThomas Graf if (err < 0) 552dfdd5fd4SThomas Graf goto errout; 553dfdd5fd4SThomas Graf 554dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 5557fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 556dfdd5fd4SThomas Graf if (in_dev == NULL) { 557dfdd5fd4SThomas Graf err = -ENODEV; 558dfdd5fd4SThomas Graf goto errout; 559dfdd5fd4SThomas Graf } 560dfdd5fd4SThomas Graf 5611da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 5621da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 563dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 564a7a628c4SAl Viro ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) 5651da177e4SLinus Torvalds continue; 566dfdd5fd4SThomas Graf 567dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 568dfdd5fd4SThomas Graf continue; 569dfdd5fd4SThomas Graf 570dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 571dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 572a7a628c4SAl Viro !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 573dfdd5fd4SThomas Graf continue; 574dfdd5fd4SThomas Graf 57515e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 5761da177e4SLinus Torvalds return 0; 5771da177e4SLinus Torvalds } 578dfdd5fd4SThomas Graf 579dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 580dfdd5fd4SThomas Graf errout: 581dfdd5fd4SThomas Graf return err; 5821da177e4SLinus Torvalds } 5831da177e4SLinus Torvalds 5845c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 5855c766d64SJiri Pirko 5865c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 5875c766d64SJiri Pirko { 5885c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 5895c766d64SJiri Pirko struct in_ifaddr *ifa; 5905c766d64SJiri Pirko int i; 5915c766d64SJiri Pirko 5925c766d64SJiri Pirko now = jiffies; 5935c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 5945c766d64SJiri Pirko 5955c766d64SJiri Pirko rcu_read_lock(); 5965c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 597b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 5985c766d64SJiri Pirko unsigned long age; 5995c766d64SJiri Pirko 6005c766d64SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 6015c766d64SJiri Pirko continue; 6025c766d64SJiri Pirko 6035c766d64SJiri Pirko /* We try to batch several events at once. */ 6045c766d64SJiri Pirko age = (now - ifa->ifa_tstamp + 6055c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 6065c766d64SJiri Pirko 6075c766d64SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 6085c766d64SJiri Pirko age >= ifa->ifa_valid_lft) { 6095c766d64SJiri Pirko struct in_ifaddr **ifap ; 6105c766d64SJiri Pirko 6115c766d64SJiri Pirko rtnl_lock(); 6125c766d64SJiri Pirko for (ifap = &ifa->ifa_dev->ifa_list; 6135c766d64SJiri Pirko *ifap != NULL; ifap = &ifa->ifa_next) { 6145c766d64SJiri Pirko if (*ifap == ifa) 6155c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 6165c766d64SJiri Pirko ifap, 1); 6175c766d64SJiri Pirko } 6185c766d64SJiri Pirko rtnl_unlock(); 6195c766d64SJiri Pirko } else if (ifa->ifa_preferred_lft == 6205c766d64SJiri Pirko INFINITY_LIFE_TIME) { 6215c766d64SJiri Pirko continue; 6225c766d64SJiri Pirko } else if (age >= ifa->ifa_preferred_lft) { 6235c766d64SJiri Pirko if (time_before(ifa->ifa_tstamp + 6245c766d64SJiri Pirko ifa->ifa_valid_lft * HZ, next)) 6255c766d64SJiri Pirko next = ifa->ifa_tstamp + 6265c766d64SJiri Pirko ifa->ifa_valid_lft * HZ; 6275c766d64SJiri Pirko 6285c766d64SJiri Pirko if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) { 6295c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 6305c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 6315c766d64SJiri Pirko } 6325c766d64SJiri Pirko } else if (time_before(ifa->ifa_tstamp + 6335c766d64SJiri Pirko ifa->ifa_preferred_lft * HZ, 6345c766d64SJiri Pirko next)) { 6355c766d64SJiri Pirko next = ifa->ifa_tstamp + 6365c766d64SJiri Pirko ifa->ifa_preferred_lft * HZ; 6375c766d64SJiri Pirko } 6385c766d64SJiri Pirko } 6395c766d64SJiri Pirko } 6405c766d64SJiri Pirko rcu_read_unlock(); 6415c766d64SJiri Pirko 6425c766d64SJiri Pirko next_sec = round_jiffies_up(next); 6435c766d64SJiri Pirko next_sched = next; 6445c766d64SJiri Pirko 6455c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 6465c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 6475c766d64SJiri Pirko next_sched = next_sec; 6485c766d64SJiri Pirko 6495c766d64SJiri Pirko now = jiffies; 6505c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 6515c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 6525c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 6535c766d64SJiri Pirko 6545c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, next_sched - now); 6555c766d64SJiri Pirko } 6565c766d64SJiri Pirko 6575c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 6585c766d64SJiri Pirko __u32 prefered_lft) 6595c766d64SJiri Pirko { 6605c766d64SJiri Pirko unsigned long timeout; 6615c766d64SJiri Pirko 6625c766d64SJiri Pirko ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 6635c766d64SJiri Pirko 6645c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 6655c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 6665c766d64SJiri Pirko ifa->ifa_valid_lft = timeout; 6675c766d64SJiri Pirko else 6685c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_PERMANENT; 6695c766d64SJiri Pirko 6705c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 6715c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 6725c766d64SJiri Pirko if (timeout == 0) 6735c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 6745c766d64SJiri Pirko ifa->ifa_preferred_lft = timeout; 6755c766d64SJiri Pirko } 6765c766d64SJiri Pirko ifa->ifa_tstamp = jiffies; 6775c766d64SJiri Pirko if (!ifa->ifa_cstamp) 6785c766d64SJiri Pirko ifa->ifa_cstamp = ifa->ifa_tstamp; 6795c766d64SJiri Pirko } 6805c766d64SJiri Pirko 6815c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 6825c766d64SJiri Pirko __u32 *pvalid_lft, __u32 *pprefered_lft) 6831da177e4SLinus Torvalds { 6845c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 6855c753978SThomas Graf struct in_ifaddr *ifa; 6865c753978SThomas Graf struct ifaddrmsg *ifm; 6871da177e4SLinus Torvalds struct net_device *dev; 6881da177e4SLinus Torvalds struct in_device *in_dev; 6897b218574SDenis V. Lunev int err; 6901da177e4SLinus Torvalds 6915c753978SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 6925c753978SThomas Graf if (err < 0) 6935c753978SThomas Graf goto errout; 6941da177e4SLinus Torvalds 6955c753978SThomas Graf ifm = nlmsg_data(nlh); 696c4e38f41SEvgeniy Polyakov err = -EINVAL; 6977b218574SDenis V. Lunev if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) 6985c753978SThomas Graf goto errout; 6991da177e4SLinus Torvalds 7004b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 7015c753978SThomas Graf err = -ENODEV; 7027b218574SDenis V. Lunev if (dev == NULL) 7035c753978SThomas Graf goto errout; 7041da177e4SLinus Torvalds 7055c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 7065c753978SThomas Graf err = -ENOBUFS; 7077b218574SDenis V. Lunev if (in_dev == NULL) 7085c753978SThomas Graf goto errout; 70971e27da9SHerbert Xu 7105c753978SThomas Graf ifa = inet_alloc_ifa(); 7117b218574SDenis V. Lunev if (ifa == NULL) 7125c753978SThomas Graf /* 7135c753978SThomas Graf * A potential indev allocation can be left alive, it stays 7145c753978SThomas Graf * assigned to its device and is destroy with it. 7155c753978SThomas Graf */ 7165c753978SThomas Graf goto errout; 7175c753978SThomas Graf 718a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 7195c753978SThomas Graf in_dev_hold(in_dev); 7205c753978SThomas Graf 7215c753978SThomas Graf if (tb[IFA_ADDRESS] == NULL) 7225c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 7235c753978SThomas Graf 724fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 7251da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 7261da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 7271da177e4SLinus Torvalds ifa->ifa_flags = ifm->ifa_flags; 7281da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 7291da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 7305c753978SThomas Graf 731a7a628c4SAl Viro ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); 732a7a628c4SAl Viro ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); 7335c753978SThomas Graf 7345c753978SThomas Graf if (tb[IFA_BROADCAST]) 735a7a628c4SAl Viro ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); 7365c753978SThomas Graf 7375c753978SThomas Graf if (tb[IFA_LABEL]) 7385c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 7391da177e4SLinus Torvalds else 7401da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 7411da177e4SLinus Torvalds 7425c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 7435c766d64SJiri Pirko struct ifa_cacheinfo *ci; 7445c766d64SJiri Pirko 7455c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 7465c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 7475c766d64SJiri Pirko err = -EINVAL; 7485c766d64SJiri Pirko goto errout; 7495c766d64SJiri Pirko } 7505c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 7515c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 7525c766d64SJiri Pirko } 7535c766d64SJiri Pirko 7545c753978SThomas Graf return ifa; 7555c753978SThomas Graf 7565c753978SThomas Graf errout: 7575c753978SThomas Graf return ERR_PTR(err); 7585c753978SThomas Graf } 7595c753978SThomas Graf 7605c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 7615c766d64SJiri Pirko { 7625c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 7635c766d64SJiri Pirko struct in_ifaddr *ifa1, **ifap; 7645c766d64SJiri Pirko 7655c766d64SJiri Pirko if (!ifa->ifa_local) 7665c766d64SJiri Pirko return NULL; 7675c766d64SJiri Pirko 7685c766d64SJiri Pirko for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 7695c766d64SJiri Pirko ifap = &ifa1->ifa_next) { 7705c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 7715c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 7725c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 7735c766d64SJiri Pirko return ifa1; 7745c766d64SJiri Pirko } 7755c766d64SJiri Pirko return NULL; 7765c766d64SJiri Pirko } 7775c766d64SJiri Pirko 7785c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 7795c753978SThomas Graf { 7803b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 7815c753978SThomas Graf struct in_ifaddr *ifa; 7825c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 7835c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 7845c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 7855c753978SThomas Graf 7865c753978SThomas Graf ASSERT_RTNL(); 7875c753978SThomas Graf 7885c766d64SJiri Pirko ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft); 7895c753978SThomas Graf if (IS_ERR(ifa)) 7905c753978SThomas Graf return PTR_ERR(ifa); 7915c753978SThomas Graf 7925c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 7935c766d64SJiri Pirko if (!ifa_existing) { 7945c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 7955c766d64SJiri Pirko * userspace alreay relies on not having to provide this. 7965c766d64SJiri Pirko */ 7975c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 79815e47304SEric W. Biederman return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); 7995c766d64SJiri Pirko } else { 8005c766d64SJiri Pirko inet_free_ifa(ifa); 8015c766d64SJiri Pirko 8025c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 8035c766d64SJiri Pirko !(nlh->nlmsg_flags & NLM_F_REPLACE)) 8045c766d64SJiri Pirko return -EEXIST; 8055c766d64SJiri Pirko 8065c766d64SJiri Pirko set_ifa_lifetime(ifa_existing, valid_lft, prefered_lft); 8075c766d64SJiri Pirko } 8085c766d64SJiri Pirko return 0; 8091da177e4SLinus Torvalds } 8101da177e4SLinus Torvalds 8111da177e4SLinus Torvalds /* 8121da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 8131da177e4SLinus Torvalds */ 8141da177e4SLinus Torvalds 81540384999SEric Dumazet static int inet_abc_len(__be32 addr) 8161da177e4SLinus Torvalds { 8171da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 8181da177e4SLinus Torvalds 819f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 8201da177e4SLinus Torvalds rc = 0; 8211da177e4SLinus Torvalds else { 822714e85beSAl Viro __u32 haddr = ntohl(addr); 8231da177e4SLinus Torvalds 824714e85beSAl Viro if (IN_CLASSA(haddr)) 8251da177e4SLinus Torvalds rc = 8; 826714e85beSAl Viro else if (IN_CLASSB(haddr)) 8271da177e4SLinus Torvalds rc = 16; 828714e85beSAl Viro else if (IN_CLASSC(haddr)) 8291da177e4SLinus Torvalds rc = 24; 8301da177e4SLinus Torvalds } 8311da177e4SLinus Torvalds 8321da177e4SLinus Torvalds return rc; 8331da177e4SLinus Torvalds } 8341da177e4SLinus Torvalds 8351da177e4SLinus Torvalds 836e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) 8371da177e4SLinus Torvalds { 8381da177e4SLinus Torvalds struct ifreq ifr; 8391da177e4SLinus Torvalds struct sockaddr_in sin_orig; 8401da177e4SLinus Torvalds struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 8411da177e4SLinus Torvalds struct in_device *in_dev; 8421da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 8431da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 8441da177e4SLinus Torvalds struct net_device *dev; 8451da177e4SLinus Torvalds char *colon; 8461da177e4SLinus Torvalds int ret = -EFAULT; 8471da177e4SLinus Torvalds int tryaddrmatch = 0; 8481da177e4SLinus Torvalds 8491da177e4SLinus Torvalds /* 8501da177e4SLinus Torvalds * Fetch the caller's info block into kernel space 8511da177e4SLinus Torvalds */ 8521da177e4SLinus Torvalds 8531da177e4SLinus Torvalds if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 8541da177e4SLinus Torvalds goto out; 8551da177e4SLinus Torvalds ifr.ifr_name[IFNAMSIZ - 1] = 0; 8561da177e4SLinus Torvalds 8571da177e4SLinus Torvalds /* save original address for comparison */ 8581da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 8591da177e4SLinus Torvalds 8601da177e4SLinus Torvalds colon = strchr(ifr.ifr_name, ':'); 8611da177e4SLinus Torvalds if (colon) 8621da177e4SLinus Torvalds *colon = 0; 8631da177e4SLinus Torvalds 864e5b13cb1SDenis V. Lunev dev_load(net, ifr.ifr_name); 8651da177e4SLinus Torvalds 8661da177e4SLinus Torvalds switch (cmd) { 8671da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 8681da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 8691da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 8701da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 8711da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 8721da177e4SLinus Torvalds so that we do not impose a lock. 8731da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 8741da177e4SLinus Torvalds */ 8751da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 8761da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 8771da177e4SLinus Torvalds sin->sin_family = AF_INET; 8781da177e4SLinus Torvalds break; 8791da177e4SLinus Torvalds 8801da177e4SLinus Torvalds case SIOCSIFFLAGS: 881bf5b30b8SZhao Hongjiang ret = -EPERM; 88252e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 8831da177e4SLinus Torvalds goto out; 8841da177e4SLinus Torvalds break; 8851da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 8861da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 8871da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 8881da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 889bf5b30b8SZhao Hongjiang ret = -EPERM; 89052e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 8911da177e4SLinus Torvalds goto out; 8921da177e4SLinus Torvalds ret = -EINVAL; 8931da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 8941da177e4SLinus Torvalds goto out; 8951da177e4SLinus Torvalds break; 8961da177e4SLinus Torvalds default: 8971da177e4SLinus Torvalds ret = -EINVAL; 8981da177e4SLinus Torvalds goto out; 8991da177e4SLinus Torvalds } 9001da177e4SLinus Torvalds 9011da177e4SLinus Torvalds rtnl_lock(); 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds ret = -ENODEV; 9049f9354b9SEric Dumazet dev = __dev_get_by_name(net, ifr.ifr_name); 9059f9354b9SEric Dumazet if (!dev) 9061da177e4SLinus Torvalds goto done; 9071da177e4SLinus Torvalds 9081da177e4SLinus Torvalds if (colon) 9091da177e4SLinus Torvalds *colon = ':'; 9101da177e4SLinus Torvalds 9119f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 9129f9354b9SEric Dumazet if (in_dev) { 9131da177e4SLinus Torvalds if (tryaddrmatch) { 9141da177e4SLinus Torvalds /* Matthias Andree */ 9151da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 9161da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 9171da177e4SLinus Torvalds and only if the original address family was AF_INET. 9181da177e4SLinus Torvalds This is checked above. */ 9191da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 9201da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 9211da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label) && 9221da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 9236c91afe1SDavid S. Miller ifa->ifa_local) { 9241da177e4SLinus Torvalds break; /* found */ 9251da177e4SLinus Torvalds } 9261da177e4SLinus Torvalds } 9271da177e4SLinus Torvalds } 9281da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 9291da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 9301da177e4SLinus Torvalds comparing just the label */ 9311da177e4SLinus Torvalds if (!ifa) { 9321da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 9331da177e4SLinus Torvalds ifap = &ifa->ifa_next) 9341da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label)) 9351da177e4SLinus Torvalds break; 9361da177e4SLinus Torvalds } 9371da177e4SLinus Torvalds } 9381da177e4SLinus Torvalds 9391da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 9401da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 9411da177e4SLinus Torvalds goto done; 9421da177e4SLinus Torvalds 9431da177e4SLinus Torvalds switch (cmd) { 9441da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9451da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 9461da177e4SLinus Torvalds goto rarok; 9471da177e4SLinus Torvalds 9481da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9491da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 9501da177e4SLinus Torvalds goto rarok; 9511da177e4SLinus Torvalds 9521da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9531da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 9541da177e4SLinus Torvalds goto rarok; 9551da177e4SLinus Torvalds 9561da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 9571da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 9581da177e4SLinus Torvalds goto rarok; 9591da177e4SLinus Torvalds 9601da177e4SLinus Torvalds case SIOCSIFFLAGS: 9611da177e4SLinus Torvalds if (colon) { 9621da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 9631da177e4SLinus Torvalds if (!ifa) 9641da177e4SLinus Torvalds break; 9651da177e4SLinus Torvalds ret = 0; 9661da177e4SLinus Torvalds if (!(ifr.ifr_flags & IFF_UP)) 9671da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 9681da177e4SLinus Torvalds break; 9691da177e4SLinus Torvalds } 9701da177e4SLinus Torvalds ret = dev_change_flags(dev, ifr.ifr_flags); 9711da177e4SLinus Torvalds break; 9721da177e4SLinus Torvalds 9731da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 9741da177e4SLinus Torvalds ret = -EINVAL; 9751da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 9761da177e4SLinus Torvalds break; 9771da177e4SLinus Torvalds 9781da177e4SLinus Torvalds if (!ifa) { 9791da177e4SLinus Torvalds ret = -ENOBUFS; 9809f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 9819f9354b9SEric Dumazet if (!ifa) 9821da177e4SLinus Torvalds break; 983c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 9841da177e4SLinus Torvalds if (colon) 9851da177e4SLinus Torvalds memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); 9861da177e4SLinus Torvalds else 9871da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 9881da177e4SLinus Torvalds } else { 9891da177e4SLinus Torvalds ret = 0; 9901da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 9911da177e4SLinus Torvalds break; 9921da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 9931da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 994148f9729SBjorn Mork ifa->ifa_scope = 0; 9951da177e4SLinus Torvalds } 9961da177e4SLinus Torvalds 9971da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 9981da177e4SLinus Torvalds 9991da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 10001da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 10011da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 10021da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 10031da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 10041da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 10051da177e4SLinus Torvalds ~ifa->ifa_mask; 10061da177e4SLinus Torvalds } else { 10071da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 10081da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 10091da177e4SLinus Torvalds } 10105c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 10111da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 10121da177e4SLinus Torvalds break; 10131da177e4SLinus Torvalds 10141da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10151da177e4SLinus Torvalds ret = 0; 10161da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 10171da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10181da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 10191da177e4SLinus Torvalds inet_insert_ifa(ifa); 10201da177e4SLinus Torvalds } 10211da177e4SLinus Torvalds break; 10221da177e4SLinus Torvalds 10231da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 10241da177e4SLinus Torvalds ret = 0; 10251da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 10261da177e4SLinus Torvalds break; 10271da177e4SLinus Torvalds ret = -EINVAL; 10281da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 10291da177e4SLinus Torvalds break; 10301da177e4SLinus Torvalds ret = 0; 10311da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10321da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 10331da177e4SLinus Torvalds inet_insert_ifa(ifa); 10341da177e4SLinus Torvalds break; 10351da177e4SLinus Torvalds 10361da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 10371da177e4SLinus Torvalds 10381da177e4SLinus Torvalds /* 10391da177e4SLinus Torvalds * The mask we set must be legal. 10401da177e4SLinus Torvalds */ 10411da177e4SLinus Torvalds ret = -EINVAL; 10421da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 10431da177e4SLinus Torvalds break; 10441da177e4SLinus Torvalds ret = 0; 10451da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1046a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 10471da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10481da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 10491da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds /* See if current broadcast address matches 10521da177e4SLinus Torvalds * with current netmask, then recalculate 10531da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 10541da177e4SLinus Torvalds * funny address, so don't touch it since 10551da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 10561da177e4SLinus Torvalds */ 10571da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 10581da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 10591da177e4SLinus Torvalds (ifa->ifa_broadcast == 1060dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 10611da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 10621da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 10631da177e4SLinus Torvalds } 10641da177e4SLinus Torvalds inet_insert_ifa(ifa); 10651da177e4SLinus Torvalds } 10661da177e4SLinus Torvalds break; 10671da177e4SLinus Torvalds } 10681da177e4SLinus Torvalds done: 10691da177e4SLinus Torvalds rtnl_unlock(); 10701da177e4SLinus Torvalds out: 10711da177e4SLinus Torvalds return ret; 10721da177e4SLinus Torvalds rarok: 10731da177e4SLinus Torvalds rtnl_unlock(); 10741da177e4SLinus Torvalds ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; 10751da177e4SLinus Torvalds goto out; 10761da177e4SLinus Torvalds } 10771da177e4SLinus Torvalds 10781da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 10791da177e4SLinus Torvalds { 1080e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 10811da177e4SLinus Torvalds struct in_ifaddr *ifa; 10821da177e4SLinus Torvalds struct ifreq ifr; 10831da177e4SLinus Torvalds int done = 0; 10841da177e4SLinus Torvalds 10859f9354b9SEric Dumazet if (!in_dev) 10861da177e4SLinus Torvalds goto out; 10871da177e4SLinus Torvalds 10889f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 10891da177e4SLinus Torvalds if (!buf) { 10901da177e4SLinus Torvalds done += sizeof(ifr); 10911da177e4SLinus Torvalds continue; 10921da177e4SLinus Torvalds } 10931da177e4SLinus Torvalds if (len < (int) sizeof(ifr)) 10941da177e4SLinus Torvalds break; 10951da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 10961da177e4SLinus Torvalds if (ifa->ifa_label) 10971da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 10981da177e4SLinus Torvalds else 10991da177e4SLinus Torvalds strcpy(ifr.ifr_name, dev->name); 11001da177e4SLinus Torvalds 11011da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 11021da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 11031da177e4SLinus Torvalds ifa->ifa_local; 11041da177e4SLinus Torvalds 11051da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 11061da177e4SLinus Torvalds done = -EFAULT; 11071da177e4SLinus Torvalds break; 11081da177e4SLinus Torvalds } 11091da177e4SLinus Torvalds buf += sizeof(struct ifreq); 11101da177e4SLinus Torvalds len -= sizeof(struct ifreq); 11111da177e4SLinus Torvalds done += sizeof(struct ifreq); 11121da177e4SLinus Torvalds } 11131da177e4SLinus Torvalds out: 11141da177e4SLinus Torvalds return done; 11151da177e4SLinus Torvalds } 11161da177e4SLinus Torvalds 1117a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 11181da177e4SLinus Torvalds { 1119a61ced5dSAl Viro __be32 addr = 0; 11201da177e4SLinus Torvalds struct in_device *in_dev; 1121c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 11221da177e4SLinus Torvalds 11231da177e4SLinus Torvalds rcu_read_lock(); 1124e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 11251da177e4SLinus Torvalds if (!in_dev) 11261da177e4SLinus Torvalds goto no_in_dev; 11271da177e4SLinus Torvalds 11281da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11291da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 11301da177e4SLinus Torvalds continue; 11311da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 11321da177e4SLinus Torvalds addr = ifa->ifa_local; 11331da177e4SLinus Torvalds break; 11341da177e4SLinus Torvalds } 11351da177e4SLinus Torvalds if (!addr) 11361da177e4SLinus Torvalds addr = ifa->ifa_local; 11371da177e4SLinus Torvalds } endfor_ifa(in_dev); 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds if (addr) 1140c6d14c84SEric Dumazet goto out_unlock; 11419f9354b9SEric Dumazet no_in_dev: 11421da177e4SLinus Torvalds 11431da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 11441da177e4SLinus Torvalds in this case. It is importnat that lo is the first interface 11451da177e4SLinus Torvalds in dev_base list. 11461da177e4SLinus Torvalds */ 1147c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 11489f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 11499f9354b9SEric Dumazet if (!in_dev) 11501da177e4SLinus Torvalds continue; 11511da177e4SLinus Torvalds 11521da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11531da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 11541da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 11551da177e4SLinus Torvalds addr = ifa->ifa_local; 1156c6d14c84SEric Dumazet goto out_unlock; 11571da177e4SLinus Torvalds } 11581da177e4SLinus Torvalds } endfor_ifa(in_dev); 11591da177e4SLinus Torvalds } 1160c6d14c84SEric Dumazet out_unlock: 11611da177e4SLinus Torvalds rcu_read_unlock(); 11621da177e4SLinus Torvalds return addr; 11631da177e4SLinus Torvalds } 11649f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 11651da177e4SLinus Torvalds 116660cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 116760cad5daSAl Viro __be32 local, int scope) 11681da177e4SLinus Torvalds { 11691da177e4SLinus Torvalds int same = 0; 1170a144ea4bSAl Viro __be32 addr = 0; 11711da177e4SLinus Torvalds 11721da177e4SLinus Torvalds for_ifa(in_dev) { 11731da177e4SLinus Torvalds if (!addr && 11741da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 11751da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 11761da177e4SLinus Torvalds addr = ifa->ifa_local; 11771da177e4SLinus Torvalds if (same) 11781da177e4SLinus Torvalds break; 11791da177e4SLinus Torvalds } 11801da177e4SLinus Torvalds if (!same) { 11811da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 11821da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 11831da177e4SLinus Torvalds if (same && addr) { 11841da177e4SLinus Torvalds if (local || !dst) 11851da177e4SLinus Torvalds break; 11861da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 11871da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 11881da177e4SLinus Torvalds break; 11891da177e4SLinus Torvalds /* No, then can we use new local src? */ 11901da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 11911da177e4SLinus Torvalds addr = ifa->ifa_local; 11921da177e4SLinus Torvalds break; 11931da177e4SLinus Torvalds } 11941da177e4SLinus Torvalds /* search for large dst subnet for addr */ 11951da177e4SLinus Torvalds same = 0; 11961da177e4SLinus Torvalds } 11971da177e4SLinus Torvalds } 11981da177e4SLinus Torvalds } endfor_ifa(in_dev); 11991da177e4SLinus Torvalds 12001da177e4SLinus Torvalds return same ? addr : 0; 12011da177e4SLinus Torvalds } 12021da177e4SLinus Torvalds 12031da177e4SLinus Torvalds /* 12041da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 12059bd85e32SDenis V. Lunev * - in_dev: only on this interface, 0=any interface 12061da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 12071da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 12081da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 12091da177e4SLinus Torvalds */ 12109bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev, 12119bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 12121da177e4SLinus Torvalds { 121360cad5daSAl Viro __be32 addr = 0; 12149bd85e32SDenis V. Lunev struct net_device *dev; 121539a6d063SDenis V. Lunev struct net *net; 12161da177e4SLinus Torvalds 121739a6d063SDenis V. Lunev if (scope != RT_SCOPE_LINK) 12189bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 12191da177e4SLinus Torvalds 1220c346dca1SYOSHIFUJI Hideaki net = dev_net(in_dev->dev); 12211da177e4SLinus Torvalds rcu_read_lock(); 1222c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 12239f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12249f9354b9SEric Dumazet if (in_dev) { 12251da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 12261da177e4SLinus Torvalds if (addr) 12271da177e4SLinus Torvalds break; 12281da177e4SLinus Torvalds } 12291da177e4SLinus Torvalds } 12301da177e4SLinus Torvalds rcu_read_unlock(); 12311da177e4SLinus Torvalds 12321da177e4SLinus Torvalds return addr; 12331da177e4SLinus Torvalds } 1234eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 12351da177e4SLinus Torvalds 12361da177e4SLinus Torvalds /* 12371da177e4SLinus Torvalds * Device notifier 12381da177e4SLinus Torvalds */ 12391da177e4SLinus Torvalds 12401da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 12411da177e4SLinus Torvalds { 1242e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 12431da177e4SLinus Torvalds } 12449f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 12451da177e4SLinus Torvalds 12461da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 12471da177e4SLinus Torvalds { 1248e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 12491da177e4SLinus Torvalds } 12509f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 12511da177e4SLinus Torvalds 12529f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 12539f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 12541da177e4SLinus Torvalds */ 12551da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 12561da177e4SLinus Torvalds { 12571da177e4SLinus Torvalds struct in_ifaddr *ifa; 12581da177e4SLinus Torvalds int named = 0; 12591da177e4SLinus Torvalds 12601da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 12611da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 12621da177e4SLinus Torvalds 12631da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 12641da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 12651da177e4SLinus Torvalds if (named++ == 0) 1266573bf470SThomas Graf goto skip; 126744344b2aSMark McLoughlin dot = strchr(old, ':'); 12681da177e4SLinus Torvalds if (dot == NULL) { 12691da177e4SLinus Torvalds sprintf(old, ":%d", named); 12701da177e4SLinus Torvalds dot = old; 12711da177e4SLinus Torvalds } 12729f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 12731da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 12749f9354b9SEric Dumazet else 12751da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1276573bf470SThomas Graf skip: 1277573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 12781da177e4SLinus Torvalds } 12791da177e4SLinus Torvalds } 12801da177e4SLinus Torvalds 128140384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu) 128206770843SBreno Leitao { 128306770843SBreno Leitao return mtu >= 68; 128406770843SBreno Leitao } 128506770843SBreno Leitao 1286d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1287d11327adSIan Campbell struct in_device *in_dev) 1288d11327adSIan Campbell 1289d11327adSIan Campbell { 1290b76d0789SZoltan Kiss struct in_ifaddr *ifa; 1291d11327adSIan Campbell 1292b76d0789SZoltan Kiss for (ifa = in_dev->ifa_list; ifa; 1293b76d0789SZoltan Kiss ifa = ifa->ifa_next) { 1294d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 12956c91afe1SDavid S. Miller ifa->ifa_local, dev, 12966c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1297d11327adSIan Campbell dev->dev_addr, NULL); 1298d11327adSIan Campbell } 1299b76d0789SZoltan Kiss } 1300d11327adSIan Campbell 13011da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 13021da177e4SLinus Torvalds 13031da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 13041da177e4SLinus Torvalds void *ptr) 13051da177e4SLinus Torvalds { 13061da177e4SLinus Torvalds struct net_device *dev = ptr; 1307748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13081da177e4SLinus Torvalds 13091da177e4SLinus Torvalds ASSERT_RTNL(); 13101da177e4SLinus Torvalds 13111da177e4SLinus Torvalds if (!in_dev) { 13128030f544SHerbert Xu if (event == NETDEV_REGISTER) { 13131da177e4SLinus Torvalds in_dev = inetdev_init(dev); 13148d76527eSHerbert Xu if (!in_dev) 1315b217d616SHerbert Xu return notifier_from_errno(-ENOMEM); 13160cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 131742f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 131842f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 13191da177e4SLinus Torvalds } 132006770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 132106770843SBreno Leitao /* Re-enabling IP */ 132206770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 132306770843SBreno Leitao in_dev = inetdev_init(dev); 13248030f544SHerbert Xu } 13251da177e4SLinus Torvalds goto out; 13261da177e4SLinus Torvalds } 13271da177e4SLinus Torvalds 13281da177e4SLinus Torvalds switch (event) { 13291da177e4SLinus Torvalds case NETDEV_REGISTER: 133091df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1331a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 13321da177e4SLinus Torvalds break; 13331da177e4SLinus Torvalds case NETDEV_UP: 133406770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 13351da177e4SLinus Torvalds break; 13360cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 13379f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 13389f9354b9SEric Dumazet 13399f9354b9SEric Dumazet if (ifa) { 1340fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 13411da177e4SLinus Torvalds ifa->ifa_local = 13421da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 13431da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 13441da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 13451da177e4SLinus Torvalds in_dev_hold(in_dev); 13461da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 13471da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 13481da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 13495c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 13505c766d64SJiri Pirko INFINITY_LIFE_TIME); 13511da177e4SLinus Torvalds inet_insert_ifa(ifa); 13521da177e4SLinus Torvalds } 13531da177e4SLinus Torvalds } 13541da177e4SLinus Torvalds ip_mc_up(in_dev); 1355eefef1cfSStephen Hemminger /* fall through */ 1356eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1357d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1358d11327adSIan Campbell break; 1359d11327adSIan Campbell /* fall through */ 1360d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1361a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1362d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 13631da177e4SLinus Torvalds break; 13641da177e4SLinus Torvalds case NETDEV_DOWN: 13651da177e4SLinus Torvalds ip_mc_down(in_dev); 13661da177e4SLinus Torvalds break; 136793d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 136875c78500SMoni Shoua ip_mc_unmap(in_dev); 136975c78500SMoni Shoua break; 137093d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 137175c78500SMoni Shoua ip_mc_remap(in_dev); 137275c78500SMoni Shoua break; 13731da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 137406770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 13751da177e4SLinus Torvalds break; 137606770843SBreno Leitao /* disable IP when MTU is not enough */ 13771da177e4SLinus Torvalds case NETDEV_UNREGISTER: 13781da177e4SLinus Torvalds inetdev_destroy(in_dev); 13791da177e4SLinus Torvalds break; 13801da177e4SLinus Torvalds case NETDEV_CHANGENAME: 13811da177e4SLinus Torvalds /* Do not notify about label change, this event is 13821da177e4SLinus Torvalds * not interesting to applications using netlink. 13831da177e4SLinus Torvalds */ 13841da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 13851da177e4SLinus Torvalds 138651602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 138766f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 13881da177e4SLinus Torvalds break; 13891da177e4SLinus Torvalds } 13901da177e4SLinus Torvalds out: 13911da177e4SLinus Torvalds return NOTIFY_DONE; 13921da177e4SLinus Torvalds } 13931da177e4SLinus Torvalds 13941da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 13951da177e4SLinus Torvalds .notifier_call = inetdev_event, 13961da177e4SLinus Torvalds }; 13971da177e4SLinus Torvalds 139840384999SEric Dumazet static size_t inet_nlmsg_size(void) 1399339bf98fSThomas Graf { 1400339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1401339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1402339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1403339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1404339bf98fSThomas Graf + nla_total_size(IFNAMSIZ); /* IFA_LABEL */ 1405339bf98fSThomas Graf } 1406339bf98fSThomas Graf 14075c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 14085c766d64SJiri Pirko { 14095c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 14105c766d64SJiri Pirko } 14115c766d64SJiri Pirko 14125c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 14135c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 14145c766d64SJiri Pirko { 14155c766d64SJiri Pirko struct ifa_cacheinfo ci; 14165c766d64SJiri Pirko 14175c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 14185c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 14195c766d64SJiri Pirko ci.ifa_prefered = preferred; 14205c766d64SJiri Pirko ci.ifa_valid = valid; 14215c766d64SJiri Pirko 14225c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 14235c766d64SJiri Pirko } 14245c766d64SJiri Pirko 14251da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 142615e47304SEric W. Biederman u32 portid, u32 seq, int event, unsigned int flags) 14271da177e4SLinus Torvalds { 14281da177e4SLinus Torvalds struct ifaddrmsg *ifm; 14291da177e4SLinus Torvalds struct nlmsghdr *nlh; 14305c766d64SJiri Pirko u32 preferred, valid; 14311da177e4SLinus Torvalds 143215e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); 143347f68512SThomas Graf if (nlh == NULL) 143426932566SPatrick McHardy return -EMSGSIZE; 143547f68512SThomas Graf 143647f68512SThomas Graf ifm = nlmsg_data(nlh); 14371da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 14381da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 14395c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 14401da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 14411da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 14421da177e4SLinus Torvalds 14435c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 14445c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 14455c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 14465c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 14475c766d64SJiri Pirko long tval = (jiffies - ifa->ifa_tstamp) / HZ; 14485c766d64SJiri Pirko 14495c766d64SJiri Pirko if (preferred > tval) 14505c766d64SJiri Pirko preferred -= tval; 14515c766d64SJiri Pirko else 14525c766d64SJiri Pirko preferred = 0; 14535c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 14545c766d64SJiri Pirko if (valid > tval) 14555c766d64SJiri Pirko valid -= tval; 14565c766d64SJiri Pirko else 14575c766d64SJiri Pirko valid = 0; 14585c766d64SJiri Pirko } 14595c766d64SJiri Pirko } 14605c766d64SJiri Pirko } else { 14615c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 14625c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 14635c766d64SJiri Pirko } 1464f3756b79SDavid S. Miller if ((ifa->ifa_address && 1465f3756b79SDavid S. Miller nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) || 1466f3756b79SDavid S. Miller (ifa->ifa_local && 1467f3756b79SDavid S. Miller nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) || 1468f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1469f3756b79SDavid S. Miller nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1470f3756b79SDavid S. Miller (ifa->ifa_label[0] && 14715c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 14725c766d64SJiri Pirko put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 14735c766d64SJiri Pirko preferred, valid)) 1474f3756b79SDavid S. Miller goto nla_put_failure; 147547f68512SThomas Graf 147647f68512SThomas Graf return nlmsg_end(skb, nlh); 147747f68512SThomas Graf 147847f68512SThomas Graf nla_put_failure: 147926932566SPatrick McHardy nlmsg_cancel(skb, nlh); 148026932566SPatrick McHardy return -EMSGSIZE; 14811da177e4SLinus Torvalds } 14821da177e4SLinus Torvalds 14831da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 14841da177e4SLinus Torvalds { 14853b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1486eec4df98SEric Dumazet int h, s_h; 1487eec4df98SEric Dumazet int idx, s_idx; 1488eec4df98SEric Dumazet int ip_idx, s_ip_idx; 14891da177e4SLinus Torvalds struct net_device *dev; 14901da177e4SLinus Torvalds struct in_device *in_dev; 14911da177e4SLinus Torvalds struct in_ifaddr *ifa; 1492eec4df98SEric Dumazet struct hlist_head *head; 14931da177e4SLinus Torvalds 1494eec4df98SEric Dumazet s_h = cb->args[0]; 1495eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1496eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1497eec4df98SEric Dumazet 1498eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 14997562f876SPavel Emelianov idx = 0; 1500eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1501eec4df98SEric Dumazet rcu_read_lock(); 1502b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 15031da177e4SLinus Torvalds if (idx < s_idx) 15047562f876SPavel Emelianov goto cont; 15054b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 15061da177e4SLinus Torvalds s_ip_idx = 0; 1507eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 15089f9354b9SEric Dumazet if (!in_dev) 15097562f876SPavel Emelianov goto cont; 15101da177e4SLinus Torvalds 15111da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 15121da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 15131da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1514596e4150SStephen Hemminger continue; 1515eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 151615e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 15171da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1518eec4df98SEric Dumazet RTM_NEWADDR, NLM_F_MULTI) <= 0) { 1519eec4df98SEric Dumazet rcu_read_unlock(); 15201da177e4SLinus Torvalds goto done; 15211da177e4SLinus Torvalds } 1522eec4df98SEric Dumazet } 15237562f876SPavel Emelianov cont: 15247562f876SPavel Emelianov idx++; 15251da177e4SLinus Torvalds } 1526eec4df98SEric Dumazet rcu_read_unlock(); 1527eec4df98SEric Dumazet } 15281da177e4SLinus Torvalds 15291da177e4SLinus Torvalds done: 1530eec4df98SEric Dumazet cb->args[0] = h; 1531eec4df98SEric Dumazet cb->args[1] = idx; 1532eec4df98SEric Dumazet cb->args[2] = ip_idx; 15331da177e4SLinus Torvalds 15341da177e4SLinus Torvalds return skb->len; 15351da177e4SLinus Torvalds } 15361da177e4SLinus Torvalds 1537d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 153815e47304SEric W. Biederman u32 portid) 15391da177e4SLinus Torvalds { 154047f68512SThomas Graf struct sk_buff *skb; 1541d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1542d6062cbbSThomas Graf int err = -ENOBUFS; 15434b8aa9abSDenis V. Lunev struct net *net; 15441da177e4SLinus Torvalds 1545c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1546339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 154747f68512SThomas Graf if (skb == NULL) 1548d6062cbbSThomas Graf goto errout; 1549d6062cbbSThomas Graf 155015e47304SEric W. Biederman err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0); 155126932566SPatrick McHardy if (err < 0) { 155226932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 155326932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 155426932566SPatrick McHardy kfree_skb(skb); 155526932566SPatrick McHardy goto errout; 155626932566SPatrick McHardy } 155715e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 15581ce85fe4SPablo Neira Ayuso return; 1559d6062cbbSThomas Graf errout: 1560d6062cbbSThomas Graf if (err < 0) 15614b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 15621da177e4SLinus Torvalds } 15631da177e4SLinus Torvalds 15649f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 15659f0f7272SThomas Graf { 15661fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 15679f0f7272SThomas Graf 15689f0f7272SThomas Graf if (!in_dev) 15699f0f7272SThomas Graf return 0; 15709f0f7272SThomas Graf 15719f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 15729f0f7272SThomas Graf } 15739f0f7272SThomas Graf 15749f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 15759f0f7272SThomas Graf { 15761fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 15779f0f7272SThomas Graf struct nlattr *nla; 15789f0f7272SThomas Graf int i; 15799f0f7272SThomas Graf 15809f0f7272SThomas Graf if (!in_dev) 15819f0f7272SThomas Graf return -ENODATA; 15829f0f7272SThomas Graf 15839f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 15849f0f7272SThomas Graf if (nla == NULL) 15859f0f7272SThomas Graf return -EMSGSIZE; 15869f0f7272SThomas Graf 15879f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 15889f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 15899f0f7272SThomas Graf 15909f0f7272SThomas Graf return 0; 15919f0f7272SThomas Graf } 15929f0f7272SThomas Graf 15939f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 15949f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 15959f0f7272SThomas Graf }; 15969f0f7272SThomas Graf 1597cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1598cf7afbfeSThomas Graf const struct nlattr *nla) 15999f0f7272SThomas Graf { 16009f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 16019f0f7272SThomas Graf int err, rem; 16029f0f7272SThomas Graf 1603f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1604cf7afbfeSThomas Graf return -EAFNOSUPPORT; 16059f0f7272SThomas Graf 16069f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 16079f0f7272SThomas Graf if (err < 0) 16089f0f7272SThomas Graf return err; 16099f0f7272SThomas Graf 16109f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16119f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 16129f0f7272SThomas Graf int cfgid = nla_type(a); 16139f0f7272SThomas Graf 16149f0f7272SThomas Graf if (nla_len(a) < 4) 16159f0f7272SThomas Graf return -EINVAL; 16169f0f7272SThomas Graf 16179f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 16189f0f7272SThomas Graf return -EINVAL; 16199f0f7272SThomas Graf } 16209f0f7272SThomas Graf } 16219f0f7272SThomas Graf 1622cf7afbfeSThomas Graf return 0; 1623cf7afbfeSThomas Graf } 1624cf7afbfeSThomas Graf 1625cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1626cf7afbfeSThomas Graf { 1627f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1628cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1629cf7afbfeSThomas Graf int rem; 1630cf7afbfeSThomas Graf 1631cf7afbfeSThomas Graf if (!in_dev) 1632cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1633cf7afbfeSThomas Graf 1634cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1635cf7afbfeSThomas Graf BUG(); 1636cf7afbfeSThomas Graf 16379f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16389f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 16399f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 16409f0f7272SThomas Graf } 16419f0f7272SThomas Graf 16429f0f7272SThomas Graf return 0; 16439f0f7272SThomas Graf } 16449f0f7272SThomas Graf 1645edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 1646edc9e748SNicolas Dichtel { 1647edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 1648edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 1649edc9e748SNicolas Dichtel 16509e551110SNicolas Dichtel /* type -1 is used for ALL */ 16519e551110SNicolas Dichtel if (type == -1 || type == NETCONFA_FORWARDING) 1652edc9e748SNicolas Dichtel size += nla_total_size(4); 1653cc535dfbSNicolas Dichtel if (type == -1 || type == NETCONFA_RP_FILTER) 1654cc535dfbSNicolas Dichtel size += nla_total_size(4); 1655d67b8c61SNicolas Dichtel if (type == -1 || type == NETCONFA_MC_FORWARDING) 1656d67b8c61SNicolas Dichtel size += nla_total_size(4); 1657edc9e748SNicolas Dichtel 1658edc9e748SNicolas Dichtel return size; 1659edc9e748SNicolas Dichtel } 1660edc9e748SNicolas Dichtel 1661edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 1662edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 1663edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 1664edc9e748SNicolas Dichtel int type) 1665edc9e748SNicolas Dichtel { 1666edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 1667edc9e748SNicolas Dichtel struct netconfmsg *ncm; 1668edc9e748SNicolas Dichtel 1669edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 1670edc9e748SNicolas Dichtel flags); 1671edc9e748SNicolas Dichtel if (nlh == NULL) 1672edc9e748SNicolas Dichtel return -EMSGSIZE; 1673edc9e748SNicolas Dichtel 1674edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 1675edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 1676edc9e748SNicolas Dichtel 1677edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 1678edc9e748SNicolas Dichtel goto nla_put_failure; 1679edc9e748SNicolas Dichtel 16809e551110SNicolas Dichtel /* type -1 is used for ALL */ 16819e551110SNicolas Dichtel if ((type == -1 || type == NETCONFA_FORWARDING) && 1682edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 1683edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 1684edc9e748SNicolas Dichtel goto nla_put_failure; 1685cc535dfbSNicolas Dichtel if ((type == -1 || type == NETCONFA_RP_FILTER) && 1686cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 1687cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 1688cc535dfbSNicolas Dichtel goto nla_put_failure; 1689d67b8c61SNicolas Dichtel if ((type == -1 || type == NETCONFA_MC_FORWARDING) && 1690d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 1691d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 1692d67b8c61SNicolas Dichtel goto nla_put_failure; 1693edc9e748SNicolas Dichtel 1694edc9e748SNicolas Dichtel return nlmsg_end(skb, nlh); 1695edc9e748SNicolas Dichtel 1696edc9e748SNicolas Dichtel nla_put_failure: 1697edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 1698edc9e748SNicolas Dichtel return -EMSGSIZE; 1699edc9e748SNicolas Dichtel } 1700edc9e748SNicolas Dichtel 1701d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, 1702edc9e748SNicolas Dichtel struct ipv4_devconf *devconf) 1703edc9e748SNicolas Dichtel { 1704edc9e748SNicolas Dichtel struct sk_buff *skb; 1705edc9e748SNicolas Dichtel int err = -ENOBUFS; 1706edc9e748SNicolas Dichtel 1707edc9e748SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC); 1708edc9e748SNicolas Dichtel if (skb == NULL) 1709edc9e748SNicolas Dichtel goto errout; 1710edc9e748SNicolas Dichtel 1711edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 1712edc9e748SNicolas Dichtel RTM_NEWNETCONF, 0, type); 1713edc9e748SNicolas Dichtel if (err < 0) { 1714edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 1715edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 1716edc9e748SNicolas Dichtel kfree_skb(skb); 1717edc9e748SNicolas Dichtel goto errout; 1718edc9e748SNicolas Dichtel } 1719edc9e748SNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC); 1720edc9e748SNicolas Dichtel return; 1721edc9e748SNicolas Dichtel errout: 1722edc9e748SNicolas Dichtel if (err < 0) 1723edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 1724edc9e748SNicolas Dichtel } 1725edc9e748SNicolas Dichtel 17269e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 17279e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 17289e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 1729cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 17309e551110SNicolas Dichtel }; 17319e551110SNicolas Dichtel 17329e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 17339e551110SNicolas Dichtel struct nlmsghdr *nlh, 17349e551110SNicolas Dichtel void *arg) 17359e551110SNicolas Dichtel { 17369e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 17379e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 17389e551110SNicolas Dichtel struct netconfmsg *ncm; 17399e551110SNicolas Dichtel struct sk_buff *skb; 17409e551110SNicolas Dichtel struct ipv4_devconf *devconf; 17419e551110SNicolas Dichtel struct in_device *in_dev; 17429e551110SNicolas Dichtel struct net_device *dev; 17439e551110SNicolas Dichtel int ifindex; 17449e551110SNicolas Dichtel int err; 17459e551110SNicolas Dichtel 17469e551110SNicolas Dichtel err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 17479e551110SNicolas Dichtel devconf_ipv4_policy); 17489e551110SNicolas Dichtel if (err < 0) 17499e551110SNicolas Dichtel goto errout; 17509e551110SNicolas Dichtel 17519e551110SNicolas Dichtel err = EINVAL; 17529e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 17539e551110SNicolas Dichtel goto errout; 17549e551110SNicolas Dichtel 17559e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 17569e551110SNicolas Dichtel switch (ifindex) { 17579e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 17589e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 17599e551110SNicolas Dichtel break; 17609e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 17619e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 17629e551110SNicolas Dichtel break; 17639e551110SNicolas Dichtel default: 17649e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 17659e551110SNicolas Dichtel if (dev == NULL) 17669e551110SNicolas Dichtel goto errout; 17679e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 17689e551110SNicolas Dichtel if (in_dev == NULL) 17699e551110SNicolas Dichtel goto errout; 17709e551110SNicolas Dichtel devconf = &in_dev->cnf; 17719e551110SNicolas Dichtel break; 17729e551110SNicolas Dichtel } 17739e551110SNicolas Dichtel 17749e551110SNicolas Dichtel err = -ENOBUFS; 17759e551110SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC); 17769e551110SNicolas Dichtel if (skb == NULL) 17779e551110SNicolas Dichtel goto errout; 17789e551110SNicolas Dichtel 17799e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 17809e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 17819e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 17829e551110SNicolas Dichtel -1); 17839e551110SNicolas Dichtel if (err < 0) { 17849e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 17859e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 17869e551110SNicolas Dichtel kfree_skb(skb); 17879e551110SNicolas Dichtel goto errout; 17889e551110SNicolas Dichtel } 17899e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 17909e551110SNicolas Dichtel errout: 17919e551110SNicolas Dichtel return err; 17929e551110SNicolas Dichtel } 17939e551110SNicolas Dichtel 17941da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 17951da177e4SLinus Torvalds 1796c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 179731be3085SHerbert Xu { 179831be3085SHerbert Xu struct net_device *dev; 179931be3085SHerbert Xu 180031be3085SHerbert Xu rcu_read_lock(); 1801c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1802c6d14c84SEric Dumazet struct in_device *in_dev; 1803c6d14c84SEric Dumazet 180431be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 180531be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 18069355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1807c6d14c84SEric Dumazet } 180831be3085SHerbert Xu rcu_read_unlock(); 180931be3085SHerbert Xu } 181031be3085SHerbert Xu 1811c6d14c84SEric Dumazet /* called with RTNL locked */ 1812c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 181368dd299bSPavel Emelyanov { 181468dd299bSPavel Emelyanov struct net_device *dev; 1815586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 181668dd299bSPavel Emelyanov 1817586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 18189355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 1819edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1820edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1821edc9e748SNicolas Dichtel net->ipv4.devconf_all); 1822edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1823edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 1824edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 182568dd299bSPavel Emelyanov 1826c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 182768dd299bSPavel Emelyanov struct in_device *in_dev; 18280187bdfbSBen Hutchings if (on) 18290187bdfbSBen Hutchings dev_disable_lro(dev); 183068dd299bSPavel Emelyanov rcu_read_lock(); 183168dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 1832edc9e748SNicolas Dichtel if (in_dev) { 183368dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 1834edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1835edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 1836edc9e748SNicolas Dichtel } 183768dd299bSPavel Emelyanov rcu_read_unlock(); 183868dd299bSPavel Emelyanov } 183968dd299bSPavel Emelyanov } 184068dd299bSPavel Emelyanov 184131be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write, 18428d65af78SAlexey Dobriyan void __user *buffer, 184331be3085SHerbert Xu size_t *lenp, loff_t *ppos) 184431be3085SHerbert Xu { 1845d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 18468d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 1847d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 184831be3085SHerbert Xu 184931be3085SHerbert Xu if (write) { 185031be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 1851c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 185231be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 185331be3085SHerbert Xu 185431be3085SHerbert Xu set_bit(i, cnf->state); 185531be3085SHerbert Xu 18569355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 1857c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 1858d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 1859d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 1860d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 18614ccfe6d4SNicolas Dichtel rt_cache_flush(net); 1862cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 1863cc535dfbSNicolas Dichtel new_value != old_value) { 1864cc535dfbSNicolas Dichtel int ifindex; 1865cc535dfbSNicolas Dichtel 1866cc535dfbSNicolas Dichtel if (cnf == net->ipv4.devconf_dflt) 1867cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_DEFAULT; 1868cc535dfbSNicolas Dichtel else if (cnf == net->ipv4.devconf_all) 1869cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_ALL; 1870cc535dfbSNicolas Dichtel else { 1871cc535dfbSNicolas Dichtel struct in_device *idev = 1872cc535dfbSNicolas Dichtel container_of(cnf, struct in_device, 1873cc535dfbSNicolas Dichtel cnf); 1874cc535dfbSNicolas Dichtel ifindex = idev->dev->ifindex; 1875cc535dfbSNicolas Dichtel } 1876cc535dfbSNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER, 1877cc535dfbSNicolas Dichtel ifindex, cnf); 1878cc535dfbSNicolas Dichtel } 187931be3085SHerbert Xu } 188031be3085SHerbert Xu 188131be3085SHerbert Xu return ret; 188231be3085SHerbert Xu } 188331be3085SHerbert Xu 18841da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write, 18858d65af78SAlexey Dobriyan void __user *buffer, 18861da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 18871da177e4SLinus Torvalds { 18881da177e4SLinus Torvalds int *valp = ctl->data; 18891da177e4SLinus Torvalds int val = *valp; 189088af182eSEric W. Biederman loff_t pos = *ppos; 18918d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 18921da177e4SLinus Torvalds 18931da177e4SLinus Torvalds if (write && *valp != val) { 1894c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 1895c0ce9fb3SPavel Emelyanov 18960187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 189788af182eSEric W. Biederman if (!rtnl_trylock()) { 189888af182eSEric W. Biederman /* Restore the original values before restarting */ 189988af182eSEric W. Biederman *valp = val; 190088af182eSEric W. Biederman *ppos = pos; 19019b8adb5eSEric W. Biederman return restart_syscall(); 190288af182eSEric W. Biederman } 19030187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 1904c0ce9fb3SPavel Emelyanov inet_forward_change(net); 1905edc9e748SNicolas Dichtel } else { 19060187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 19070187bdfbSBen Hutchings struct in_device *idev = 19080187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 1909edc9e748SNicolas Dichtel if (*valp) 19100187bdfbSBen Hutchings dev_disable_lro(idev->dev); 1911edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, 1912edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 1913edc9e748SNicolas Dichtel idev->dev->ifindex, 1914edc9e748SNicolas Dichtel cnf); 19150187bdfbSBen Hutchings } 19160187bdfbSBen Hutchings rtnl_unlock(); 19174ccfe6d4SNicolas Dichtel rt_cache_flush(net); 1918edc9e748SNicolas Dichtel } else 1919edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1920edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 1921edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 19220187bdfbSBen Hutchings } 19231da177e4SLinus Torvalds 19241da177e4SLinus Torvalds return ret; 19251da177e4SLinus Torvalds } 19261da177e4SLinus Torvalds 1927323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write, 19288d65af78SAlexey Dobriyan void __user *buffer, 19291da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 19301da177e4SLinus Torvalds { 19311da177e4SLinus Torvalds int *valp = ctl->data; 19321da177e4SLinus Torvalds int val = *valp; 19338d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 193476e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 19351da177e4SLinus Torvalds 19361da177e4SLinus Torvalds if (write && *valp != val) 19374ccfe6d4SNicolas Dichtel rt_cache_flush(net); 19381da177e4SLinus Torvalds 19391da177e4SLinus Torvalds return ret; 19401da177e4SLinus Torvalds } 19411da177e4SLinus Torvalds 1942f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 194342f811b8SHerbert Xu { \ 194442f811b8SHerbert Xu .procname = name, \ 194542f811b8SHerbert Xu .data = ipv4_devconf.data + \ 194602291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 194742f811b8SHerbert Xu .maxlen = sizeof(int), \ 194842f811b8SHerbert Xu .mode = mval, \ 194942f811b8SHerbert Xu .proc_handler = proc, \ 195031be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 195142f811b8SHerbert Xu } 195242f811b8SHerbert Xu 195342f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 1954f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 195542f811b8SHerbert Xu 195642f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 1957f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 195842f811b8SHerbert Xu 1959f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 1960f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 196142f811b8SHerbert Xu 196242f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 1963f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 196442f811b8SHerbert Xu 19651da177e4SLinus Torvalds static struct devinet_sysctl_table { 19661da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 196702291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 19681da177e4SLinus Torvalds } devinet_sysctl = { 19691da177e4SLinus Torvalds .devinet_vars = { 197042f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 1971f8572d8fSEric W. Biederman devinet_sysctl_forward), 197242f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 197342f811b8SHerbert Xu 197442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 197542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 197642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 197742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 197842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 197942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 198042f811b8SHerbert Xu "accept_source_route"), 19818153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 198228f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 198342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 198442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 198542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 198642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 198742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 198842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 198942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 199042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 199142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 1992eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 199365324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 199442f811b8SHerbert Xu 199542f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 199642f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 199742f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION, 199842f811b8SHerbert Xu "force_igmp_version"), 199942f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 200042f811b8SHerbert Xu "promote_secondaries"), 2001d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2002d0daebc3SThomas Graf "route_localnet"), 20031da177e4SLinus Torvalds }, 20041da177e4SLinus Torvalds }; 20051da177e4SLinus Torvalds 2006ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 2007f8572d8fSEric W. Biederman struct ipv4_devconf *p) 20081da177e4SLinus Torvalds { 20091da177e4SLinus Torvalds int i; 20109fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 20118607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2012bfada697SPavel Emelyanov 20139fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 20141da177e4SLinus Torvalds if (!t) 20159fa89642SPavel Emelyanov goto out; 20169fa89642SPavel Emelyanov 20171da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 20181da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 201931be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2020c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 20211da177e4SLinus Torvalds } 20221da177e4SLinus Torvalds 20238607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 20241da177e4SLinus Torvalds 20258607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 20261da177e4SLinus Torvalds if (!t->sysctl_header) 20278607ddb8SEric W. Biederman goto free; 20281da177e4SLinus Torvalds 20291da177e4SLinus Torvalds p->sysctl = t; 2030ea40b324SPavel Emelyanov return 0; 20311da177e4SLinus Torvalds 20321da177e4SLinus Torvalds free: 20331da177e4SLinus Torvalds kfree(t); 20349fa89642SPavel Emelyanov out: 2035ea40b324SPavel Emelyanov return -ENOBUFS; 20361da177e4SLinus Torvalds } 20371da177e4SLinus Torvalds 203851602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 203966f27a52SPavel Emelyanov { 204051602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 204166f27a52SPavel Emelyanov 204251602b2aSPavel Emelyanov if (t == NULL) 204351602b2aSPavel Emelyanov return; 204451602b2aSPavel Emelyanov 204551602b2aSPavel Emelyanov cnf->sysctl = NULL; 2046ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 20471da177e4SLinus Torvalds kfree(t); 20481da177e4SLinus Torvalds } 204951602b2aSPavel Emelyanov 205051602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev) 205151602b2aSPavel Emelyanov { 205254716e3bSEric W. Biederman neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL); 2053c346dca1SYOSHIFUJI Hideaki __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 2054f8572d8fSEric W. Biederman &idev->cnf); 205551602b2aSPavel Emelyanov } 205651602b2aSPavel Emelyanov 205751602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 205851602b2aSPavel Emelyanov { 205951602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 206051602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 20611da177e4SLinus Torvalds } 20621da177e4SLinus Torvalds 206368dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 206468dd299bSPavel Emelyanov { 206568dd299bSPavel Emelyanov .procname = "ip_forward", 206668dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 206702291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 206868dd299bSPavel Emelyanov .maxlen = sizeof(int), 206968dd299bSPavel Emelyanov .mode = 0644, 207068dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 207168dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2072c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 207368dd299bSPavel Emelyanov }, 207468dd299bSPavel Emelyanov { }, 207568dd299bSPavel Emelyanov }; 20762a75de0cSEric Dumazet #endif 207768dd299bSPavel Emelyanov 2078752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2079752d14dcSPavel Emelyanov { 2080752d14dcSPavel Emelyanov int err; 2081752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 20822a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 20832a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 2084752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 20852a75de0cSEric Dumazet #endif 2086752d14dcSPavel Emelyanov 2087752d14dcSPavel Emelyanov err = -ENOMEM; 2088752d14dcSPavel Emelyanov all = &ipv4_devconf; 2089752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 2090752d14dcSPavel Emelyanov 209109ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 2092752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 2093752d14dcSPavel Emelyanov if (all == NULL) 2094752d14dcSPavel Emelyanov goto err_alloc_all; 2095752d14dcSPavel Emelyanov 2096752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 2097752d14dcSPavel Emelyanov if (dflt == NULL) 2098752d14dcSPavel Emelyanov goto err_alloc_dflt; 2099752d14dcSPavel Emelyanov 21002a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2101752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 2102752d14dcSPavel Emelyanov if (tbl == NULL) 2103752d14dcSPavel Emelyanov goto err_alloc_ctl; 2104752d14dcSPavel Emelyanov 210502291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2106752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2107752d14dcSPavel Emelyanov tbl[0].extra2 = net; 21082a75de0cSEric Dumazet #endif 2109752d14dcSPavel Emelyanov } 2110752d14dcSPavel Emelyanov 2111752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2112f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 2113752d14dcSPavel Emelyanov if (err < 0) 2114752d14dcSPavel Emelyanov goto err_reg_all; 2115752d14dcSPavel Emelyanov 2116f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 2117752d14dcSPavel Emelyanov if (err < 0) 2118752d14dcSPavel Emelyanov goto err_reg_dflt; 2119752d14dcSPavel Emelyanov 2120752d14dcSPavel Emelyanov err = -ENOMEM; 21218607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 2122752d14dcSPavel Emelyanov if (forw_hdr == NULL) 2123752d14dcSPavel Emelyanov goto err_reg_ctl; 21242a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2125752d14dcSPavel Emelyanov #endif 2126752d14dcSPavel Emelyanov 2127752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2128752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2129752d14dcSPavel Emelyanov return 0; 2130752d14dcSPavel Emelyanov 2131752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2132752d14dcSPavel Emelyanov err_reg_ctl: 2133752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 2134752d14dcSPavel Emelyanov err_reg_dflt: 2135752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 2136752d14dcSPavel Emelyanov err_reg_all: 2137752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 2138752d14dcSPavel Emelyanov kfree(tbl); 2139752d14dcSPavel Emelyanov err_alloc_ctl: 21402a75de0cSEric Dumazet #endif 2141752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 2142752d14dcSPavel Emelyanov kfree(dflt); 2143752d14dcSPavel Emelyanov err_alloc_dflt: 2144752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 2145752d14dcSPavel Emelyanov kfree(all); 2146752d14dcSPavel Emelyanov err_alloc_all: 2147752d14dcSPavel Emelyanov return err; 2148752d14dcSPavel Emelyanov } 2149752d14dcSPavel Emelyanov 2150752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2151752d14dcSPavel Emelyanov { 21522a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2153752d14dcSPavel Emelyanov struct ctl_table *tbl; 2154752d14dcSPavel Emelyanov 2155752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2156752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2157752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 2158752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 2159752d14dcSPavel Emelyanov kfree(tbl); 21602a75de0cSEric Dumazet #endif 2161752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2162752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2163752d14dcSPavel Emelyanov } 2164752d14dcSPavel Emelyanov 2165752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2166752d14dcSPavel Emelyanov .init = devinet_init_net, 2167752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2168752d14dcSPavel Emelyanov }; 2169752d14dcSPavel Emelyanov 21709f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = { 21719f0f7272SThomas Graf .family = AF_INET, 21729f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 21739f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2174cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2175cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 21769f0f7272SThomas Graf }; 21779f0f7272SThomas Graf 21781da177e4SLinus Torvalds void __init devinet_init(void) 21791da177e4SLinus Torvalds { 2180fd23c3b3SDavid S. Miller int i; 2181fd23c3b3SDavid S. Miller 2182fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2183fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2184fd23c3b3SDavid S. Miller 2185752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 2186752d14dcSPavel Emelyanov 21871da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 21881da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 218963f3444fSThomas Graf 21905c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, 0); 21915c766d64SJiri Pirko 21929f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 21939f0f7272SThomas Graf 2194c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); 2195c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); 2196c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); 21979e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 21989e551110SNicolas Dichtel NULL, NULL); 21991da177e4SLinus Torvalds } 22001da177e4SLinus Torvalds 2201