11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * NET3 IP device support routines. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 51da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 61da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 71da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Derived from the IP parts of dev.c 1.0.19 1002c30a84SJesper Juhl * Authors: Ross Biro 111da177e4SLinus Torvalds * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> 121da177e4SLinus Torvalds * Mark Evans, <evansmp@uhura.aston.ac.uk> 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * Additional Authors: 151da177e4SLinus Torvalds * Alan Cox, <gw4pts@gw4pts.ampr.org> 161da177e4SLinus Torvalds * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * Changes: 191da177e4SLinus Torvalds * Alexey Kuznetsov: pa_* fields are replaced with ifaddr 201da177e4SLinus Torvalds * lists. 211da177e4SLinus Torvalds * Cyrus Durgin: updated for kmod 221da177e4SLinus Torvalds * Matthias Andree: in devinet_ioctl, compare label and 231da177e4SLinus Torvalds * address (4.4BSD alias style support), 241da177e4SLinus Torvalds * fall back to comparing just the label 251da177e4SLinus Torvalds * if no match found. 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #include <asm/uaccess.h> 301da177e4SLinus Torvalds #include <linux/bitops.h> 314fc268d2SRandy Dunlap #include <linux/capability.h> 321da177e4SLinus Torvalds #include <linux/module.h> 331da177e4SLinus Torvalds #include <linux/types.h> 341da177e4SLinus Torvalds #include <linux/kernel.h> 351da177e4SLinus Torvalds #include <linux/string.h> 361da177e4SLinus Torvalds #include <linux/mm.h> 371da177e4SLinus Torvalds #include <linux/socket.h> 381da177e4SLinus Torvalds #include <linux/sockios.h> 391da177e4SLinus Torvalds #include <linux/in.h> 401da177e4SLinus Torvalds #include <linux/errno.h> 411da177e4SLinus Torvalds #include <linux/interrupt.h> 421823730fSThomas Graf #include <linux/if_addr.h> 431da177e4SLinus Torvalds #include <linux/if_ether.h> 441da177e4SLinus Torvalds #include <linux/inet.h> 451da177e4SLinus Torvalds #include <linux/netdevice.h> 461da177e4SLinus Torvalds #include <linux/etherdevice.h> 471da177e4SLinus Torvalds #include <linux/skbuff.h> 481da177e4SLinus Torvalds #include <linux/init.h> 491da177e4SLinus Torvalds #include <linux/notifier.h> 501da177e4SLinus Torvalds #include <linux/inetdevice.h> 511da177e4SLinus Torvalds #include <linux/igmp.h> 525a0e3ad6STejun Heo #include <linux/slab.h> 53fd23c3b3SDavid S. Miller #include <linux/hash.h> 541da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 551da177e4SLinus Torvalds #include <linux/sysctl.h> 561da177e4SLinus Torvalds #endif 571da177e4SLinus Torvalds #include <linux/kmod.h> 58edc9e748SNicolas Dichtel #include <linux/netconf.h> 591da177e4SLinus Torvalds 6014c85021SArnaldo Carvalho de Melo #include <net/arp.h> 611da177e4SLinus Torvalds #include <net/ip.h> 621da177e4SLinus Torvalds #include <net/route.h> 631da177e4SLinus Torvalds #include <net/ip_fib.h> 6463f3444fSThomas Graf #include <net/rtnetlink.h> 65752d14dcSPavel Emelyanov #include <net/net_namespace.h> 665c766d64SJiri Pirko #include <net/addrconf.h> 671da177e4SLinus Torvalds 68406b6f97SDavid S. Miller #include "fib_lookup.h" 69406b6f97SDavid S. Miller 700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 7142f811b8SHerbert Xu .data = { 7202291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7302291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7402291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7502291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 762690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 772690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 7842f811b8SHerbert Xu }, 791da177e4SLinus Torvalds }; 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 8242f811b8SHerbert Xu .data = { 8302291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8402291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8502291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8602291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8702291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 882690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 892690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 9042f811b8SHerbert Xu }, 911da177e4SLinus Torvalds }; 921da177e4SLinus Torvalds 939355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 949355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9542f811b8SHerbert Xu 96ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 975c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 985c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 995c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 1005176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 1015c766d64SJiri Pirko [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 1025c753978SThomas Graf }; 1035c753978SThomas Graf 10440384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT 8 10540384999SEric Dumazet #define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 10640384999SEric Dumazet 107fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 108fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock); 109fd23c3b3SDavid S. Miller 11040384999SEric Dumazet static u32 inet_addr_hash(struct net *net, __be32 addr) 111fd23c3b3SDavid S. Miller { 11240384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 113fd23c3b3SDavid S. Miller 11440384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 115fd23c3b3SDavid S. Miller } 116fd23c3b3SDavid S. Miller 117fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 118fd23c3b3SDavid S. Miller { 11940384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 120fd23c3b3SDavid S. Miller 121fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 122fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 123fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 124fd23c3b3SDavid S. Miller } 125fd23c3b3SDavid S. Miller 126fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 127fd23c3b3SDavid S. Miller { 128fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 129fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 130fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 131fd23c3b3SDavid S. Miller } 132fd23c3b3SDavid S. Miller 1339435eb1cSDavid S. Miller /** 1349435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1359435eb1cSDavid S. Miller * @net: the net namespace 1369435eb1cSDavid S. Miller * @addr: the source address 1379435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1389435eb1cSDavid S. Miller * 1399435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1409435eb1cSDavid S. Miller */ 1419435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1429435eb1cSDavid S. Miller { 14340384999SEric Dumazet u32 hash = inet_addr_hash(net, addr); 1449435eb1cSDavid S. Miller struct net_device *result = NULL; 1459435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1469435eb1cSDavid S. Miller 1479435eb1cSDavid S. Miller rcu_read_lock(); 148b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) { 14940384999SEric Dumazet if (ifa->ifa_local == addr) { 1509435eb1cSDavid S. Miller struct net_device *dev = ifa->ifa_dev->dev; 1519435eb1cSDavid S. Miller 1529435eb1cSDavid S. Miller if (!net_eq(dev_net(dev), net)) 1539435eb1cSDavid S. Miller continue; 1549435eb1cSDavid S. Miller result = dev; 1559435eb1cSDavid S. Miller break; 1569435eb1cSDavid S. Miller } 1579435eb1cSDavid S. Miller } 158406b6f97SDavid S. Miller if (!result) { 159406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 160406b6f97SDavid S. Miller struct fib_result res = { 0 }; 161406b6f97SDavid S. Miller struct fib_table *local; 162406b6f97SDavid S. Miller 163406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 164406b6f97SDavid S. Miller * over loopback subnets work. 165406b6f97SDavid S. Miller */ 166406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 167406b6f97SDavid S. Miller if (local && 168406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 169406b6f97SDavid S. Miller res.type == RTN_LOCAL) 170406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 171406b6f97SDavid S. Miller } 1729435eb1cSDavid S. Miller if (result && devref) 1739435eb1cSDavid S. Miller dev_hold(result); 1749435eb1cSDavid S. Miller rcu_read_unlock(); 1759435eb1cSDavid S. Miller return result; 1769435eb1cSDavid S. Miller } 1779435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1789435eb1cSDavid S. Miller 179d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1801da177e4SLinus Torvalds 181e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1821da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1831da177e4SLinus Torvalds int destroy); 1841da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 18566f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev); 18651602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 18751602b2aSPavel Emelyanov #else 18840384999SEric Dumazet static void devinet_sysctl_register(struct in_device *idev) 18951602b2aSPavel Emelyanov { 19051602b2aSPavel Emelyanov } 19140384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 19251602b2aSPavel Emelyanov { 19351602b2aSPavel Emelyanov } 1941da177e4SLinus Torvalds #endif 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds /* Locks all the inet devices. */ 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 1991da177e4SLinus Torvalds { 20093adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds 2031da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2041da177e4SLinus Torvalds { 2051da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2061da177e4SLinus Torvalds if (ifa->ifa_dev) 2071da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2081da177e4SLinus Torvalds kfree(ifa); 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 21140384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2121da177e4SLinus Torvalds { 2131da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2171da177e4SLinus Torvalds { 2181da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2191da177e4SLinus Torvalds 220547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 221547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 222e9897071SEric Dumazet kfree(rcu_dereference_protected(idev->mc_hash, 1)); 2231da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 22491df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2251da177e4SLinus Torvalds #endif 2261da177e4SLinus Torvalds dev_put(dev); 2271da177e4SLinus Torvalds if (!idev->dead) 2289f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2299f9354b9SEric Dumazet else 2301da177e4SLinus Torvalds kfree(idev); 2311da177e4SLinus Torvalds } 2329f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2331da177e4SLinus Torvalds 23471e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2351da177e4SLinus Torvalds { 2361da177e4SLinus Torvalds struct in_device *in_dev; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds ASSERT_RTNL(); 2391da177e4SLinus Torvalds 2400da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2411da177e4SLinus Torvalds if (!in_dev) 2421da177e4SLinus Torvalds goto out; 243c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2449355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2451da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2461da177e4SLinus Torvalds in_dev->dev = dev; 2479f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2489f9354b9SEric Dumazet if (!in_dev->arp_parms) 2491da177e4SLinus Torvalds goto out_kfree; 2500187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2510187bdfbSBen Hutchings dev_disable_lro(dev); 2521da177e4SLinus Torvalds /* Reference in_dev->dev */ 2531da177e4SLinus Torvalds dev_hold(dev); 25430c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2551da177e4SLinus Torvalds in_dev_hold(in_dev); 2561da177e4SLinus Torvalds 25766f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 2581da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2591da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2601da177e4SLinus Torvalds ip_mc_up(in_dev); 261483479ecSJarek Poplawski 26230c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 263cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 264483479ecSJarek Poplawski out: 2651da177e4SLinus Torvalds return in_dev; 2661da177e4SLinus Torvalds out_kfree: 2671da177e4SLinus Torvalds kfree(in_dev); 2681da177e4SLinus Torvalds in_dev = NULL; 2691da177e4SLinus Torvalds goto out; 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2751da177e4SLinus Torvalds in_dev_put(idev); 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2791da177e4SLinus Torvalds { 2801da177e4SLinus Torvalds struct in_ifaddr *ifa; 2811da177e4SLinus Torvalds struct net_device *dev; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds ASSERT_RTNL(); 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds dev = in_dev->dev; 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds in_dev->dead = 1; 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 2921da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 2931da177e4SLinus Torvalds inet_free_ifa(ifa); 2941da177e4SLinus Torvalds } 2951da177e4SLinus Torvalds 296a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 2971da177e4SLinus Torvalds 29851602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 2991da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 3001da177e4SLinus Torvalds arp_ifdown(dev); 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds 305ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3061da177e4SLinus Torvalds { 3071da177e4SLinus Torvalds rcu_read_lock(); 3081da177e4SLinus Torvalds for_primary_ifa(in_dev) { 3091da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3101da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3111da177e4SLinus Torvalds rcu_read_unlock(); 3121da177e4SLinus Torvalds return 1; 3131da177e4SLinus Torvalds } 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds } endfor_ifa(in_dev); 3161da177e4SLinus Torvalds rcu_read_unlock(); 3171da177e4SLinus Torvalds return 0; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 320d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 32115e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3221da177e4SLinus Torvalds { 3238f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3240ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3250ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3260ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3270ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds ASSERT_RTNL(); 3301da177e4SLinus Torvalds 3318f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3328f937c60SHarald Welte * unless alias promotion is set 3338f937c60SHarald Welte **/ 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3361da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3371da177e4SLinus Torvalds 3381da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3390ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3400ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3410ff60a45SJamal Hadi Salim last_prim = ifa; 3420ff60a45SJamal Hadi Salim 3431da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3441da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3451da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3461da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3470ff60a45SJamal Hadi Salim prev_prom = ifa; 3481da177e4SLinus Torvalds continue; 3491da177e4SLinus Torvalds } 3501da177e4SLinus Torvalds 3510ff60a45SJamal Hadi Salim if (!do_promote) { 352fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3531da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3541da177e4SLinus Torvalds 35515e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 356e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 357e041c683SAlan Stern NETDEV_DOWN, ifa); 3581da177e4SLinus Torvalds inet_free_ifa(ifa); 3598f937c60SHarald Welte } else { 3608f937c60SHarald Welte promote = ifa; 3618f937c60SHarald Welte break; 3628f937c60SHarald Welte } 3631da177e4SLinus Torvalds } 3641da177e4SLinus Torvalds } 3651da177e4SLinus Torvalds 3662d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 3672d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 3682d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 3692d230e2bSJulian Anastasov * while all addresses are on the device list. 3702d230e2bSJulian Anastasov */ 3712d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 3722d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 3732d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 3742d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 3752d230e2bSJulian Anastasov } 3762d230e2bSJulian Anastasov 3771da177e4SLinus Torvalds /* 2. Unlink it */ 3781da177e4SLinus Torvalds 3791da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 380fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3811da177e4SLinus Torvalds 3821da177e4SLinus Torvalds /* 3. Announce address deletion */ 3831da177e4SLinus Torvalds 3841da177e4SLinus Torvalds /* Send message first, then call notifier. 3851da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 3861da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 3871da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 3881da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 3891da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 3901da177e4SLinus Torvalds So that, this order is correct. 3911da177e4SLinus Torvalds */ 39215e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 393e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 3940ff60a45SJamal Hadi Salim 3950ff60a45SJamal Hadi Salim if (promote) { 39604024b93SJulian Anastasov struct in_ifaddr *next_sec = promote->ifa_next; 3970ff60a45SJamal Hadi Salim 3980ff60a45SJamal Hadi Salim if (prev_prom) { 3990ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 4000ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 4010ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 4020ff60a45SJamal Hadi Salim } 4030ff60a45SJamal Hadi Salim 4040ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 40515e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 406e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 407e041c683SAlan Stern NETDEV_UP, promote); 40804024b93SJulian Anastasov for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { 4090ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4100ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4110ff60a45SJamal Hadi Salim continue; 4120ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4130ff60a45SJamal Hadi Salim } 4140ff60a45SJamal Hadi Salim 4150ff60a45SJamal Hadi Salim } 4166363097cSHerbert Xu if (destroy) 4171da177e4SLinus Torvalds inet_free_ifa(ifa1); 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds 420d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 421d6062cbbSThomas Graf int destroy) 422d6062cbbSThomas Graf { 423d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 424d6062cbbSThomas Graf } 425d6062cbbSThomas Graf 4265c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4275c766d64SJiri Pirko 4285c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4295c766d64SJiri Pirko 430d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 43115e47304SEric W. Biederman u32 portid) 4321da177e4SLinus Torvalds { 4331da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4341da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds ASSERT_RTNL(); 4371da177e4SLinus Torvalds 4381da177e4SLinus Torvalds if (!ifa->ifa_local) { 4391da177e4SLinus Torvalds inet_free_ifa(ifa); 4401da177e4SLinus Torvalds return 0; 4411da177e4SLinus Torvalds } 4421da177e4SLinus Torvalds 4431da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4441da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4451da177e4SLinus Torvalds 4461da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4471da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4481da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4491da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4501da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4511da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4521da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4531da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4541da177e4SLinus Torvalds inet_free_ifa(ifa); 4551da177e4SLinus Torvalds return -EEXIST; 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4581da177e4SLinus Torvalds inet_free_ifa(ifa); 4591da177e4SLinus Torvalds return -EINVAL; 4601da177e4SLinus Torvalds } 4611da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds 4651da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 4661da177e4SLinus Torvalds net_srandom(ifa->ifa_local); 4671da177e4SLinus Torvalds ifap = last_primary; 4681da177e4SLinus Torvalds } 4691da177e4SLinus Torvalds 4701da177e4SLinus Torvalds ifa->ifa_next = *ifap; 4711da177e4SLinus Torvalds *ifap = ifa; 4721da177e4SLinus Torvalds 473fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 474fd23c3b3SDavid S. Miller 4755c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 4765c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, 0); 4775c766d64SJiri Pirko 4781da177e4SLinus Torvalds /* Send message first, then call notifier. 4791da177e4SLinus Torvalds Notifier will trigger FIB update, so that 4801da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 48115e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 482e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 4831da177e4SLinus Torvalds 4841da177e4SLinus Torvalds return 0; 4851da177e4SLinus Torvalds } 4861da177e4SLinus Torvalds 487d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 488d6062cbbSThomas Graf { 489d6062cbbSThomas Graf return __inet_insert_ifa(ifa, NULL, 0); 490d6062cbbSThomas Graf } 491d6062cbbSThomas Graf 4921da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 4931da177e4SLinus Torvalds { 494e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 4951da177e4SLinus Torvalds 4961da177e4SLinus Torvalds ASSERT_RTNL(); 4971da177e4SLinus Torvalds 4981da177e4SLinus Torvalds if (!in_dev) { 4991da177e4SLinus Torvalds inet_free_ifa(ifa); 5001da177e4SLinus Torvalds return -ENOBUFS; 5011da177e4SLinus Torvalds } 50271e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 5031da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 504547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 5051da177e4SLinus Torvalds in_dev_hold(in_dev); 5061da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5071da177e4SLinus Torvalds } 508f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5091da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5101da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5111da177e4SLinus Torvalds } 5121da177e4SLinus Torvalds 5138723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5148723e1b4SEric Dumazet * We dont take a reference on found in_device 5158723e1b4SEric Dumazet */ 5167fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5171da177e4SLinus Torvalds { 5181da177e4SLinus Torvalds struct net_device *dev; 5191da177e4SLinus Torvalds struct in_device *in_dev = NULL; 520c148fc2eSEric Dumazet 521c148fc2eSEric Dumazet rcu_read_lock(); 522c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5231da177e4SLinus Torvalds if (dev) 5248723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 525c148fc2eSEric Dumazet rcu_read_unlock(); 5261da177e4SLinus Torvalds return in_dev; 5271da177e4SLinus Torvalds } 5289f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5291da177e4SLinus Torvalds 5301da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5311da177e4SLinus Torvalds 53260cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 53360cad5daSAl Viro __be32 mask) 5341da177e4SLinus Torvalds { 5351da177e4SLinus Torvalds ASSERT_RTNL(); 5361da177e4SLinus Torvalds 5371da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5381da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5391da177e4SLinus Torvalds return ifa; 5401da177e4SLinus Torvalds } endfor_ifa(in_dev); 5411da177e4SLinus Torvalds return NULL; 5421da177e4SLinus Torvalds } 5431da177e4SLinus Torvalds 544661d2967SThomas Graf static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) 5451da177e4SLinus Torvalds { 5463b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 547dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5481da177e4SLinus Torvalds struct in_device *in_dev; 549dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 5501da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 551dfdd5fd4SThomas Graf int err = -EINVAL; 5521da177e4SLinus Torvalds 5531da177e4SLinus Torvalds ASSERT_RTNL(); 5541da177e4SLinus Torvalds 555dfdd5fd4SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 556dfdd5fd4SThomas Graf if (err < 0) 557dfdd5fd4SThomas Graf goto errout; 558dfdd5fd4SThomas Graf 559dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 5607fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 561dfdd5fd4SThomas Graf if (in_dev == NULL) { 562dfdd5fd4SThomas Graf err = -ENODEV; 563dfdd5fd4SThomas Graf goto errout; 564dfdd5fd4SThomas Graf } 565dfdd5fd4SThomas Graf 5661da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 5671da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 568dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 569a7a628c4SAl Viro ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) 5701da177e4SLinus Torvalds continue; 571dfdd5fd4SThomas Graf 572dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 573dfdd5fd4SThomas Graf continue; 574dfdd5fd4SThomas Graf 575dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 576dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 577a7a628c4SAl Viro !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 578dfdd5fd4SThomas Graf continue; 579dfdd5fd4SThomas Graf 58015e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 5811da177e4SLinus Torvalds return 0; 5821da177e4SLinus Torvalds } 583dfdd5fd4SThomas Graf 584dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 585dfdd5fd4SThomas Graf errout: 586dfdd5fd4SThomas Graf return err; 5871da177e4SLinus Torvalds } 5881da177e4SLinus Torvalds 5895c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 5905c766d64SJiri Pirko 5915c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 5925c766d64SJiri Pirko { 5935c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 5945c766d64SJiri Pirko struct in_ifaddr *ifa; 595c988d1e8SJiri Pirko struct hlist_node *n; 5965c766d64SJiri Pirko int i; 5975c766d64SJiri Pirko 5985c766d64SJiri Pirko now = jiffies; 5995c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 6005c766d64SJiri Pirko 6015c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 602c988d1e8SJiri Pirko bool change_needed = false; 603c988d1e8SJiri Pirko 604c988d1e8SJiri Pirko rcu_read_lock(); 605b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 6065c766d64SJiri Pirko unsigned long age; 6075c766d64SJiri Pirko 6085c766d64SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 6095c766d64SJiri Pirko continue; 6105c766d64SJiri Pirko 6115c766d64SJiri Pirko /* We try to batch several events at once. */ 6125c766d64SJiri Pirko age = (now - ifa->ifa_tstamp + 6135c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 6145c766d64SJiri Pirko 6155c766d64SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 6165c766d64SJiri Pirko age >= ifa->ifa_valid_lft) { 617c988d1e8SJiri Pirko change_needed = true; 618c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft == 619c988d1e8SJiri Pirko INFINITY_LIFE_TIME) { 620c988d1e8SJiri Pirko continue; 621c988d1e8SJiri Pirko } else if (age >= ifa->ifa_preferred_lft) { 622c988d1e8SJiri Pirko if (time_before(ifa->ifa_tstamp + 623c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ, next)) 624c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 625c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ; 626c988d1e8SJiri Pirko 627c988d1e8SJiri Pirko if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) 628c988d1e8SJiri Pirko change_needed = true; 629c988d1e8SJiri Pirko } else if (time_before(ifa->ifa_tstamp + 630c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ, 631c988d1e8SJiri Pirko next)) { 632c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 633c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ; 634c988d1e8SJiri Pirko } 635c988d1e8SJiri Pirko } 636c988d1e8SJiri Pirko rcu_read_unlock(); 637c988d1e8SJiri Pirko if (!change_needed) 638c988d1e8SJiri Pirko continue; 639c988d1e8SJiri Pirko rtnl_lock(); 640c988d1e8SJiri Pirko hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 641c988d1e8SJiri Pirko unsigned long age; 642c988d1e8SJiri Pirko 643c988d1e8SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 644c988d1e8SJiri Pirko continue; 645c988d1e8SJiri Pirko 646c988d1e8SJiri Pirko /* We try to batch several events at once. */ 647c988d1e8SJiri Pirko age = (now - ifa->ifa_tstamp + 648c988d1e8SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 649c988d1e8SJiri Pirko 650c988d1e8SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 651c988d1e8SJiri Pirko age >= ifa->ifa_valid_lft) { 6525c766d64SJiri Pirko struct in_ifaddr **ifap; 6535c766d64SJiri Pirko 6545c766d64SJiri Pirko for (ifap = &ifa->ifa_dev->ifa_list; 655c988d1e8SJiri Pirko *ifap != NULL; ifap = &(*ifap)->ifa_next) { 656c988d1e8SJiri Pirko if (*ifap == ifa) { 6575c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 6585c766d64SJiri Pirko ifap, 1); 659c988d1e8SJiri Pirko break; 6605c766d64SJiri Pirko } 661c988d1e8SJiri Pirko } 662c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft != 663c988d1e8SJiri Pirko INFINITY_LIFE_TIME && 664c988d1e8SJiri Pirko age >= ifa->ifa_preferred_lft && 665c988d1e8SJiri Pirko !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 6665c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 6675c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 6685c766d64SJiri Pirko } 6695c766d64SJiri Pirko } 670c988d1e8SJiri Pirko rtnl_unlock(); 6715c766d64SJiri Pirko } 6725c766d64SJiri Pirko 6735c766d64SJiri Pirko next_sec = round_jiffies_up(next); 6745c766d64SJiri Pirko next_sched = next; 6755c766d64SJiri Pirko 6765c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 6775c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 6785c766d64SJiri Pirko next_sched = next_sec; 6795c766d64SJiri Pirko 6805c766d64SJiri Pirko now = jiffies; 6815c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 6825c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 6835c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 6845c766d64SJiri Pirko 6855c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, next_sched - now); 6865c766d64SJiri Pirko } 6875c766d64SJiri Pirko 6885c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 6895c766d64SJiri Pirko __u32 prefered_lft) 6905c766d64SJiri Pirko { 6915c766d64SJiri Pirko unsigned long timeout; 6925c766d64SJiri Pirko 6935c766d64SJiri Pirko ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 6945c766d64SJiri Pirko 6955c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 6965c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 6975c766d64SJiri Pirko ifa->ifa_valid_lft = timeout; 6985c766d64SJiri Pirko else 6995c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_PERMANENT; 7005c766d64SJiri Pirko 7015c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 7025c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 7035c766d64SJiri Pirko if (timeout == 0) 7045c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7055c766d64SJiri Pirko ifa->ifa_preferred_lft = timeout; 7065c766d64SJiri Pirko } 7075c766d64SJiri Pirko ifa->ifa_tstamp = jiffies; 7085c766d64SJiri Pirko if (!ifa->ifa_cstamp) 7095c766d64SJiri Pirko ifa->ifa_cstamp = ifa->ifa_tstamp; 7105c766d64SJiri Pirko } 7115c766d64SJiri Pirko 7125c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 7135c766d64SJiri Pirko __u32 *pvalid_lft, __u32 *pprefered_lft) 7141da177e4SLinus Torvalds { 7155c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 7165c753978SThomas Graf struct in_ifaddr *ifa; 7175c753978SThomas Graf struct ifaddrmsg *ifm; 7181da177e4SLinus Torvalds struct net_device *dev; 7191da177e4SLinus Torvalds struct in_device *in_dev; 7207b218574SDenis V. Lunev int err; 7211da177e4SLinus Torvalds 7225c753978SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 7235c753978SThomas Graf if (err < 0) 7245c753978SThomas Graf goto errout; 7251da177e4SLinus Torvalds 7265c753978SThomas Graf ifm = nlmsg_data(nlh); 727c4e38f41SEvgeniy Polyakov err = -EINVAL; 7287b218574SDenis V. Lunev if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) 7295c753978SThomas Graf goto errout; 7301da177e4SLinus Torvalds 7314b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 7325c753978SThomas Graf err = -ENODEV; 7337b218574SDenis V. Lunev if (dev == NULL) 7345c753978SThomas Graf goto errout; 7351da177e4SLinus Torvalds 7365c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 7375c753978SThomas Graf err = -ENOBUFS; 7387b218574SDenis V. Lunev if (in_dev == NULL) 7395c753978SThomas Graf goto errout; 74071e27da9SHerbert Xu 7415c753978SThomas Graf ifa = inet_alloc_ifa(); 7427b218574SDenis V. Lunev if (ifa == NULL) 7435c753978SThomas Graf /* 7445c753978SThomas Graf * A potential indev allocation can be left alive, it stays 7455c753978SThomas Graf * assigned to its device and is destroy with it. 7465c753978SThomas Graf */ 7475c753978SThomas Graf goto errout; 7485c753978SThomas Graf 749a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 7505c753978SThomas Graf in_dev_hold(in_dev); 7515c753978SThomas Graf 7525c753978SThomas Graf if (tb[IFA_ADDRESS] == NULL) 7535c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 7545c753978SThomas Graf 755fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 7561da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 7571da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 7581da177e4SLinus Torvalds ifa->ifa_flags = ifm->ifa_flags; 7591da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 7601da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 7615c753978SThomas Graf 762a7a628c4SAl Viro ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); 763a7a628c4SAl Viro ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); 7645c753978SThomas Graf 7655c753978SThomas Graf if (tb[IFA_BROADCAST]) 766a7a628c4SAl Viro ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); 7675c753978SThomas Graf 7685c753978SThomas Graf if (tb[IFA_LABEL]) 7695c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 7701da177e4SLinus Torvalds else 7711da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 7721da177e4SLinus Torvalds 7735c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 7745c766d64SJiri Pirko struct ifa_cacheinfo *ci; 7755c766d64SJiri Pirko 7765c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 7775c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 7785c766d64SJiri Pirko err = -EINVAL; 779446266b0SDaniel Borkmann goto errout_free; 7805c766d64SJiri Pirko } 7815c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 7825c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 7835c766d64SJiri Pirko } 7845c766d64SJiri Pirko 7855c753978SThomas Graf return ifa; 7865c753978SThomas Graf 787446266b0SDaniel Borkmann errout_free: 788446266b0SDaniel Borkmann inet_free_ifa(ifa); 7895c753978SThomas Graf errout: 7905c753978SThomas Graf return ERR_PTR(err); 7915c753978SThomas Graf } 7925c753978SThomas Graf 7935c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 7945c766d64SJiri Pirko { 7955c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 7965c766d64SJiri Pirko struct in_ifaddr *ifa1, **ifap; 7975c766d64SJiri Pirko 7985c766d64SJiri Pirko if (!ifa->ifa_local) 7995c766d64SJiri Pirko return NULL; 8005c766d64SJiri Pirko 8015c766d64SJiri Pirko for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 8025c766d64SJiri Pirko ifap = &ifa1->ifa_next) { 8035c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 8045c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 8055c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 8065c766d64SJiri Pirko return ifa1; 8075c766d64SJiri Pirko } 8085c766d64SJiri Pirko return NULL; 8095c766d64SJiri Pirko } 8105c766d64SJiri Pirko 811661d2967SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) 8125c753978SThomas Graf { 8133b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 8145c753978SThomas Graf struct in_ifaddr *ifa; 8155c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 8165c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 8175c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 8185c753978SThomas Graf 8195c753978SThomas Graf ASSERT_RTNL(); 8205c753978SThomas Graf 8215c766d64SJiri Pirko ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft); 8225c753978SThomas Graf if (IS_ERR(ifa)) 8235c753978SThomas Graf return PTR_ERR(ifa); 8245c753978SThomas Graf 8255c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 8265c766d64SJiri Pirko if (!ifa_existing) { 8275c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 8285c766d64SJiri Pirko * userspace alreay relies on not having to provide this. 8295c766d64SJiri Pirko */ 8305c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 83115e47304SEric W. Biederman return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); 8325c766d64SJiri Pirko } else { 8335c766d64SJiri Pirko inet_free_ifa(ifa); 8345c766d64SJiri Pirko 8355c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 8365c766d64SJiri Pirko !(nlh->nlmsg_flags & NLM_F_REPLACE)) 8375c766d64SJiri Pirko return -EEXIST; 83834e2ed34SJiri Pirko ifa = ifa_existing; 83934e2ed34SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 84005a324b9SJiri Pirko cancel_delayed_work(&check_lifetime_work); 84105a324b9SJiri Pirko schedule_delayed_work(&check_lifetime_work, 0); 84234e2ed34SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 84334e2ed34SJiri Pirko blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 8445c766d64SJiri Pirko } 8455c766d64SJiri Pirko return 0; 8461da177e4SLinus Torvalds } 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds /* 8491da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 8501da177e4SLinus Torvalds */ 8511da177e4SLinus Torvalds 85240384999SEric Dumazet static int inet_abc_len(__be32 addr) 8531da177e4SLinus Torvalds { 8541da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 8551da177e4SLinus Torvalds 856f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 8571da177e4SLinus Torvalds rc = 0; 8581da177e4SLinus Torvalds else { 859714e85beSAl Viro __u32 haddr = ntohl(addr); 8601da177e4SLinus Torvalds 861714e85beSAl Viro if (IN_CLASSA(haddr)) 8621da177e4SLinus Torvalds rc = 8; 863714e85beSAl Viro else if (IN_CLASSB(haddr)) 8641da177e4SLinus Torvalds rc = 16; 865714e85beSAl Viro else if (IN_CLASSC(haddr)) 8661da177e4SLinus Torvalds rc = 24; 8671da177e4SLinus Torvalds } 8681da177e4SLinus Torvalds 8691da177e4SLinus Torvalds return rc; 8701da177e4SLinus Torvalds } 8711da177e4SLinus Torvalds 8721da177e4SLinus Torvalds 873e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) 8741da177e4SLinus Torvalds { 8751da177e4SLinus Torvalds struct ifreq ifr; 8761da177e4SLinus Torvalds struct sockaddr_in sin_orig; 8771da177e4SLinus Torvalds struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 8781da177e4SLinus Torvalds struct in_device *in_dev; 8791da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 8801da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 8811da177e4SLinus Torvalds struct net_device *dev; 8821da177e4SLinus Torvalds char *colon; 8831da177e4SLinus Torvalds int ret = -EFAULT; 8841da177e4SLinus Torvalds int tryaddrmatch = 0; 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds /* 8871da177e4SLinus Torvalds * Fetch the caller's info block into kernel space 8881da177e4SLinus Torvalds */ 8891da177e4SLinus Torvalds 8901da177e4SLinus Torvalds if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 8911da177e4SLinus Torvalds goto out; 8921da177e4SLinus Torvalds ifr.ifr_name[IFNAMSIZ - 1] = 0; 8931da177e4SLinus Torvalds 8941da177e4SLinus Torvalds /* save original address for comparison */ 8951da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 8961da177e4SLinus Torvalds 8971da177e4SLinus Torvalds colon = strchr(ifr.ifr_name, ':'); 8981da177e4SLinus Torvalds if (colon) 8991da177e4SLinus Torvalds *colon = 0; 9001da177e4SLinus Torvalds 901e5b13cb1SDenis V. Lunev dev_load(net, ifr.ifr_name); 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds switch (cmd) { 9041da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9051da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9061da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9071da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 9081da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 9091da177e4SLinus Torvalds so that we do not impose a lock. 9101da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 9111da177e4SLinus Torvalds */ 9121da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 9131da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 9141da177e4SLinus Torvalds sin->sin_family = AF_INET; 9151da177e4SLinus Torvalds break; 9161da177e4SLinus Torvalds 9171da177e4SLinus Torvalds case SIOCSIFFLAGS: 918bf5b30b8SZhao Hongjiang ret = -EPERM; 91952e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 9201da177e4SLinus Torvalds goto out; 9211da177e4SLinus Torvalds break; 9221da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 9231da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 9241da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 9251da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 926bf5b30b8SZhao Hongjiang ret = -EPERM; 92752e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 9281da177e4SLinus Torvalds goto out; 9291da177e4SLinus Torvalds ret = -EINVAL; 9301da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 9311da177e4SLinus Torvalds goto out; 9321da177e4SLinus Torvalds break; 9331da177e4SLinus Torvalds default: 9341da177e4SLinus Torvalds ret = -EINVAL; 9351da177e4SLinus Torvalds goto out; 9361da177e4SLinus Torvalds } 9371da177e4SLinus Torvalds 9381da177e4SLinus Torvalds rtnl_lock(); 9391da177e4SLinus Torvalds 9401da177e4SLinus Torvalds ret = -ENODEV; 9419f9354b9SEric Dumazet dev = __dev_get_by_name(net, ifr.ifr_name); 9429f9354b9SEric Dumazet if (!dev) 9431da177e4SLinus Torvalds goto done; 9441da177e4SLinus Torvalds 9451da177e4SLinus Torvalds if (colon) 9461da177e4SLinus Torvalds *colon = ':'; 9471da177e4SLinus Torvalds 9489f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 9499f9354b9SEric Dumazet if (in_dev) { 9501da177e4SLinus Torvalds if (tryaddrmatch) { 9511da177e4SLinus Torvalds /* Matthias Andree */ 9521da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 9531da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 9541da177e4SLinus Torvalds and only if the original address family was AF_INET. 9551da177e4SLinus Torvalds This is checked above. */ 9561da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 9571da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 9581da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label) && 9591da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 9606c91afe1SDavid S. Miller ifa->ifa_local) { 9611da177e4SLinus Torvalds break; /* found */ 9621da177e4SLinus Torvalds } 9631da177e4SLinus Torvalds } 9641da177e4SLinus Torvalds } 9651da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 9661da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 9671da177e4SLinus Torvalds comparing just the label */ 9681da177e4SLinus Torvalds if (!ifa) { 9691da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 9701da177e4SLinus Torvalds ifap = &ifa->ifa_next) 9711da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label)) 9721da177e4SLinus Torvalds break; 9731da177e4SLinus Torvalds } 9741da177e4SLinus Torvalds } 9751da177e4SLinus Torvalds 9761da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 9771da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 9781da177e4SLinus Torvalds goto done; 9791da177e4SLinus Torvalds 9801da177e4SLinus Torvalds switch (cmd) { 9811da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9821da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 9831da177e4SLinus Torvalds goto rarok; 9841da177e4SLinus Torvalds 9851da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9861da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 9871da177e4SLinus Torvalds goto rarok; 9881da177e4SLinus Torvalds 9891da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9901da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 9911da177e4SLinus Torvalds goto rarok; 9921da177e4SLinus Torvalds 9931da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 9941da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 9951da177e4SLinus Torvalds goto rarok; 9961da177e4SLinus Torvalds 9971da177e4SLinus Torvalds case SIOCSIFFLAGS: 9981da177e4SLinus Torvalds if (colon) { 9991da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 10001da177e4SLinus Torvalds if (!ifa) 10011da177e4SLinus Torvalds break; 10021da177e4SLinus Torvalds ret = 0; 10031da177e4SLinus Torvalds if (!(ifr.ifr_flags & IFF_UP)) 10041da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 10051da177e4SLinus Torvalds break; 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds ret = dev_change_flags(dev, ifr.ifr_flags); 10081da177e4SLinus Torvalds break; 10091da177e4SLinus Torvalds 10101da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10111da177e4SLinus Torvalds ret = -EINVAL; 10121da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 10131da177e4SLinus Torvalds break; 10141da177e4SLinus Torvalds 10151da177e4SLinus Torvalds if (!ifa) { 10161da177e4SLinus Torvalds ret = -ENOBUFS; 10179f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 10189f9354b9SEric Dumazet if (!ifa) 10191da177e4SLinus Torvalds break; 1020c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 10211da177e4SLinus Torvalds if (colon) 10221da177e4SLinus Torvalds memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); 10231da177e4SLinus Torvalds else 10241da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 10251da177e4SLinus Torvalds } else { 10261da177e4SLinus Torvalds ret = 0; 10271da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 10281da177e4SLinus Torvalds break; 10291da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10301da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 1031148f9729SBjorn Mork ifa->ifa_scope = 0; 10321da177e4SLinus Torvalds } 10331da177e4SLinus Torvalds 10341da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 10351da177e4SLinus Torvalds 10361da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 10371da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 10381da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 10391da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 10401da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 10411da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 10421da177e4SLinus Torvalds ~ifa->ifa_mask; 10431da177e4SLinus Torvalds } else { 10441da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 10451da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 10461da177e4SLinus Torvalds } 10475c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 10481da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 10491da177e4SLinus Torvalds break; 10501da177e4SLinus Torvalds 10511da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10521da177e4SLinus Torvalds ret = 0; 10531da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 10541da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10551da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 10561da177e4SLinus Torvalds inet_insert_ifa(ifa); 10571da177e4SLinus Torvalds } 10581da177e4SLinus Torvalds break; 10591da177e4SLinus Torvalds 10601da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 10611da177e4SLinus Torvalds ret = 0; 10621da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 10631da177e4SLinus Torvalds break; 10641da177e4SLinus Torvalds ret = -EINVAL; 10651da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 10661da177e4SLinus Torvalds break; 10671da177e4SLinus Torvalds ret = 0; 10681da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10691da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 10701da177e4SLinus Torvalds inet_insert_ifa(ifa); 10711da177e4SLinus Torvalds break; 10721da177e4SLinus Torvalds 10731da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 10741da177e4SLinus Torvalds 10751da177e4SLinus Torvalds /* 10761da177e4SLinus Torvalds * The mask we set must be legal. 10771da177e4SLinus Torvalds */ 10781da177e4SLinus Torvalds ret = -EINVAL; 10791da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 10801da177e4SLinus Torvalds break; 10811da177e4SLinus Torvalds ret = 0; 10821da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1083a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 10841da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10851da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 10861da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 10871da177e4SLinus Torvalds 10881da177e4SLinus Torvalds /* See if current broadcast address matches 10891da177e4SLinus Torvalds * with current netmask, then recalculate 10901da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 10911da177e4SLinus Torvalds * funny address, so don't touch it since 10921da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 10931da177e4SLinus Torvalds */ 10941da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 10951da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 10961da177e4SLinus Torvalds (ifa->ifa_broadcast == 1097dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 10981da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 10991da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 11001da177e4SLinus Torvalds } 11011da177e4SLinus Torvalds inet_insert_ifa(ifa); 11021da177e4SLinus Torvalds } 11031da177e4SLinus Torvalds break; 11041da177e4SLinus Torvalds } 11051da177e4SLinus Torvalds done: 11061da177e4SLinus Torvalds rtnl_unlock(); 11071da177e4SLinus Torvalds out: 11081da177e4SLinus Torvalds return ret; 11091da177e4SLinus Torvalds rarok: 11101da177e4SLinus Torvalds rtnl_unlock(); 11111da177e4SLinus Torvalds ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; 11121da177e4SLinus Torvalds goto out; 11131da177e4SLinus Torvalds } 11141da177e4SLinus Torvalds 11151da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 11161da177e4SLinus Torvalds { 1117e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 11181da177e4SLinus Torvalds struct in_ifaddr *ifa; 11191da177e4SLinus Torvalds struct ifreq ifr; 11201da177e4SLinus Torvalds int done = 0; 11211da177e4SLinus Torvalds 11229f9354b9SEric Dumazet if (!in_dev) 11231da177e4SLinus Torvalds goto out; 11241da177e4SLinus Torvalds 11259f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 11261da177e4SLinus Torvalds if (!buf) { 11271da177e4SLinus Torvalds done += sizeof(ifr); 11281da177e4SLinus Torvalds continue; 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds if (len < (int) sizeof(ifr)) 11311da177e4SLinus Torvalds break; 11321da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 11331da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 11361da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 11371da177e4SLinus Torvalds ifa->ifa_local; 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 11401da177e4SLinus Torvalds done = -EFAULT; 11411da177e4SLinus Torvalds break; 11421da177e4SLinus Torvalds } 11431da177e4SLinus Torvalds buf += sizeof(struct ifreq); 11441da177e4SLinus Torvalds len -= sizeof(struct ifreq); 11451da177e4SLinus Torvalds done += sizeof(struct ifreq); 11461da177e4SLinus Torvalds } 11471da177e4SLinus Torvalds out: 11481da177e4SLinus Torvalds return done; 11491da177e4SLinus Torvalds } 11501da177e4SLinus Torvalds 1151a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 11521da177e4SLinus Torvalds { 1153a61ced5dSAl Viro __be32 addr = 0; 11541da177e4SLinus Torvalds struct in_device *in_dev; 1155c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 11561da177e4SLinus Torvalds 11571da177e4SLinus Torvalds rcu_read_lock(); 1158e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 11591da177e4SLinus Torvalds if (!in_dev) 11601da177e4SLinus Torvalds goto no_in_dev; 11611da177e4SLinus Torvalds 11621da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11631da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 11641da177e4SLinus Torvalds continue; 11651da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 11661da177e4SLinus Torvalds addr = ifa->ifa_local; 11671da177e4SLinus Torvalds break; 11681da177e4SLinus Torvalds } 11691da177e4SLinus Torvalds if (!addr) 11701da177e4SLinus Torvalds addr = ifa->ifa_local; 11711da177e4SLinus Torvalds } endfor_ifa(in_dev); 11721da177e4SLinus Torvalds 11731da177e4SLinus Torvalds if (addr) 1174c6d14c84SEric Dumazet goto out_unlock; 11759f9354b9SEric Dumazet no_in_dev: 11761da177e4SLinus Torvalds 11771da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 11781da177e4SLinus Torvalds in this case. It is importnat that lo is the first interface 11791da177e4SLinus Torvalds in dev_base list. 11801da177e4SLinus Torvalds */ 1181c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 11829f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 11839f9354b9SEric Dumazet if (!in_dev) 11841da177e4SLinus Torvalds continue; 11851da177e4SLinus Torvalds 11861da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11871da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 11881da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 11891da177e4SLinus Torvalds addr = ifa->ifa_local; 1190c6d14c84SEric Dumazet goto out_unlock; 11911da177e4SLinus Torvalds } 11921da177e4SLinus Torvalds } endfor_ifa(in_dev); 11931da177e4SLinus Torvalds } 1194c6d14c84SEric Dumazet out_unlock: 11951da177e4SLinus Torvalds rcu_read_unlock(); 11961da177e4SLinus Torvalds return addr; 11971da177e4SLinus Torvalds } 11989f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 11991da177e4SLinus Torvalds 120060cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 120160cad5daSAl Viro __be32 local, int scope) 12021da177e4SLinus Torvalds { 12031da177e4SLinus Torvalds int same = 0; 1204a144ea4bSAl Viro __be32 addr = 0; 12051da177e4SLinus Torvalds 12061da177e4SLinus Torvalds for_ifa(in_dev) { 12071da177e4SLinus Torvalds if (!addr && 12081da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 12091da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 12101da177e4SLinus Torvalds addr = ifa->ifa_local; 12111da177e4SLinus Torvalds if (same) 12121da177e4SLinus Torvalds break; 12131da177e4SLinus Torvalds } 12141da177e4SLinus Torvalds if (!same) { 12151da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 12161da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 12171da177e4SLinus Torvalds if (same && addr) { 12181da177e4SLinus Torvalds if (local || !dst) 12191da177e4SLinus Torvalds break; 12201da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 12211da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 12221da177e4SLinus Torvalds break; 12231da177e4SLinus Torvalds /* No, then can we use new local src? */ 12241da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 12251da177e4SLinus Torvalds addr = ifa->ifa_local; 12261da177e4SLinus Torvalds break; 12271da177e4SLinus Torvalds } 12281da177e4SLinus Torvalds /* search for large dst subnet for addr */ 12291da177e4SLinus Torvalds same = 0; 12301da177e4SLinus Torvalds } 12311da177e4SLinus Torvalds } 12321da177e4SLinus Torvalds } endfor_ifa(in_dev); 12331da177e4SLinus Torvalds 12341da177e4SLinus Torvalds return same ? addr : 0; 12351da177e4SLinus Torvalds } 12361da177e4SLinus Torvalds 12371da177e4SLinus Torvalds /* 12381da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 12399bd85e32SDenis V. Lunev * - in_dev: only on this interface, 0=any interface 12401da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 12411da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 12421da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 12431da177e4SLinus Torvalds */ 12449bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev, 12459bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 12461da177e4SLinus Torvalds { 124760cad5daSAl Viro __be32 addr = 0; 12489bd85e32SDenis V. Lunev struct net_device *dev; 124939a6d063SDenis V. Lunev struct net *net; 12501da177e4SLinus Torvalds 125139a6d063SDenis V. Lunev if (scope != RT_SCOPE_LINK) 12529bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 12531da177e4SLinus Torvalds 1254c346dca1SYOSHIFUJI Hideaki net = dev_net(in_dev->dev); 12551da177e4SLinus Torvalds rcu_read_lock(); 1256c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 12579f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12589f9354b9SEric Dumazet if (in_dev) { 12591da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 12601da177e4SLinus Torvalds if (addr) 12611da177e4SLinus Torvalds break; 12621da177e4SLinus Torvalds } 12631da177e4SLinus Torvalds } 12641da177e4SLinus Torvalds rcu_read_unlock(); 12651da177e4SLinus Torvalds 12661da177e4SLinus Torvalds return addr; 12671da177e4SLinus Torvalds } 1268eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 12691da177e4SLinus Torvalds 12701da177e4SLinus Torvalds /* 12711da177e4SLinus Torvalds * Device notifier 12721da177e4SLinus Torvalds */ 12731da177e4SLinus Torvalds 12741da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 12751da177e4SLinus Torvalds { 1276e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 12771da177e4SLinus Torvalds } 12789f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 12791da177e4SLinus Torvalds 12801da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 12811da177e4SLinus Torvalds { 1282e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 12831da177e4SLinus Torvalds } 12849f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 12851da177e4SLinus Torvalds 12869f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 12879f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 12881da177e4SLinus Torvalds */ 12891da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 12901da177e4SLinus Torvalds { 12911da177e4SLinus Torvalds struct in_ifaddr *ifa; 12921da177e4SLinus Torvalds int named = 0; 12931da177e4SLinus Torvalds 12941da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 12951da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 12961da177e4SLinus Torvalds 12971da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 12981da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 12991da177e4SLinus Torvalds if (named++ == 0) 1300573bf470SThomas Graf goto skip; 130144344b2aSMark McLoughlin dot = strchr(old, ':'); 13021da177e4SLinus Torvalds if (dot == NULL) { 13031da177e4SLinus Torvalds sprintf(old, ":%d", named); 13041da177e4SLinus Torvalds dot = old; 13051da177e4SLinus Torvalds } 13069f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 13071da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 13089f9354b9SEric Dumazet else 13091da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1310573bf470SThomas Graf skip: 1311573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 13121da177e4SLinus Torvalds } 13131da177e4SLinus Torvalds } 13141da177e4SLinus Torvalds 131540384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu) 131606770843SBreno Leitao { 131706770843SBreno Leitao return mtu >= 68; 131806770843SBreno Leitao } 131906770843SBreno Leitao 1320d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1321d11327adSIan Campbell struct in_device *in_dev) 1322d11327adSIan Campbell 1323d11327adSIan Campbell { 1324b76d0789SZoltan Kiss struct in_ifaddr *ifa; 1325d11327adSIan Campbell 1326b76d0789SZoltan Kiss for (ifa = in_dev->ifa_list; ifa; 1327b76d0789SZoltan Kiss ifa = ifa->ifa_next) { 1328d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 13296c91afe1SDavid S. Miller ifa->ifa_local, dev, 13306c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1331d11327adSIan Campbell dev->dev_addr, NULL); 1332d11327adSIan Campbell } 1333b76d0789SZoltan Kiss } 1334d11327adSIan Campbell 13351da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 13361da177e4SLinus Torvalds 13371da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 13381da177e4SLinus Torvalds void *ptr) 13391da177e4SLinus Torvalds { 1340351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1341748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13421da177e4SLinus Torvalds 13431da177e4SLinus Torvalds ASSERT_RTNL(); 13441da177e4SLinus Torvalds 13451da177e4SLinus Torvalds if (!in_dev) { 13468030f544SHerbert Xu if (event == NETDEV_REGISTER) { 13471da177e4SLinus Torvalds in_dev = inetdev_init(dev); 13488d76527eSHerbert Xu if (!in_dev) 1349b217d616SHerbert Xu return notifier_from_errno(-ENOMEM); 13500cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 135142f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 135242f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 13531da177e4SLinus Torvalds } 135406770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 135506770843SBreno Leitao /* Re-enabling IP */ 135606770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 135706770843SBreno Leitao in_dev = inetdev_init(dev); 13588030f544SHerbert Xu } 13591da177e4SLinus Torvalds goto out; 13601da177e4SLinus Torvalds } 13611da177e4SLinus Torvalds 13621da177e4SLinus Torvalds switch (event) { 13631da177e4SLinus Torvalds case NETDEV_REGISTER: 136491df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1365a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 13661da177e4SLinus Torvalds break; 13671da177e4SLinus Torvalds case NETDEV_UP: 136806770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 13691da177e4SLinus Torvalds break; 13700cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 13719f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 13729f9354b9SEric Dumazet 13739f9354b9SEric Dumazet if (ifa) { 1374fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 13751da177e4SLinus Torvalds ifa->ifa_local = 13761da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 13771da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 13781da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 13791da177e4SLinus Torvalds in_dev_hold(in_dev); 13801da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 13811da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 13821da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 13835c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 13845c766d64SJiri Pirko INFINITY_LIFE_TIME); 13851da177e4SLinus Torvalds inet_insert_ifa(ifa); 13861da177e4SLinus Torvalds } 13871da177e4SLinus Torvalds } 13881da177e4SLinus Torvalds ip_mc_up(in_dev); 1389eefef1cfSStephen Hemminger /* fall through */ 1390eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1391d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1392d11327adSIan Campbell break; 1393d11327adSIan Campbell /* fall through */ 1394d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1395a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1396d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 13971da177e4SLinus Torvalds break; 13981da177e4SLinus Torvalds case NETDEV_DOWN: 13991da177e4SLinus Torvalds ip_mc_down(in_dev); 14001da177e4SLinus Torvalds break; 140193d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 140275c78500SMoni Shoua ip_mc_unmap(in_dev); 140375c78500SMoni Shoua break; 140493d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 140575c78500SMoni Shoua ip_mc_remap(in_dev); 140675c78500SMoni Shoua break; 14071da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 140806770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 14091da177e4SLinus Torvalds break; 141006770843SBreno Leitao /* disable IP when MTU is not enough */ 14111da177e4SLinus Torvalds case NETDEV_UNREGISTER: 14121da177e4SLinus Torvalds inetdev_destroy(in_dev); 14131da177e4SLinus Torvalds break; 14141da177e4SLinus Torvalds case NETDEV_CHANGENAME: 14151da177e4SLinus Torvalds /* Do not notify about label change, this event is 14161da177e4SLinus Torvalds * not interesting to applications using netlink. 14171da177e4SLinus Torvalds */ 14181da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 14191da177e4SLinus Torvalds 142051602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 142166f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 14221da177e4SLinus Torvalds break; 14231da177e4SLinus Torvalds } 14241da177e4SLinus Torvalds out: 14251da177e4SLinus Torvalds return NOTIFY_DONE; 14261da177e4SLinus Torvalds } 14271da177e4SLinus Torvalds 14281da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 14291da177e4SLinus Torvalds .notifier_call = inetdev_event, 14301da177e4SLinus Torvalds }; 14311da177e4SLinus Torvalds 143240384999SEric Dumazet static size_t inet_nlmsg_size(void) 1433339bf98fSThomas Graf { 1434339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1435339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1436339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1437339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1438339bf98fSThomas Graf + nla_total_size(IFNAMSIZ); /* IFA_LABEL */ 1439339bf98fSThomas Graf } 1440339bf98fSThomas Graf 14415c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 14425c766d64SJiri Pirko { 14435c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 14445c766d64SJiri Pirko } 14455c766d64SJiri Pirko 14465c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 14475c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 14485c766d64SJiri Pirko { 14495c766d64SJiri Pirko struct ifa_cacheinfo ci; 14505c766d64SJiri Pirko 14515c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 14525c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 14535c766d64SJiri Pirko ci.ifa_prefered = preferred; 14545c766d64SJiri Pirko ci.ifa_valid = valid; 14555c766d64SJiri Pirko 14565c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 14575c766d64SJiri Pirko } 14585c766d64SJiri Pirko 14591da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 146015e47304SEric W. Biederman u32 portid, u32 seq, int event, unsigned int flags) 14611da177e4SLinus Torvalds { 14621da177e4SLinus Torvalds struct ifaddrmsg *ifm; 14631da177e4SLinus Torvalds struct nlmsghdr *nlh; 14645c766d64SJiri Pirko u32 preferred, valid; 14651da177e4SLinus Torvalds 146615e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); 146747f68512SThomas Graf if (nlh == NULL) 146826932566SPatrick McHardy return -EMSGSIZE; 146947f68512SThomas Graf 147047f68512SThomas Graf ifm = nlmsg_data(nlh); 14711da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 14721da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 14735c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 14741da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 14751da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 14761da177e4SLinus Torvalds 14775c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 14785c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 14795c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 14805c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 14815c766d64SJiri Pirko long tval = (jiffies - ifa->ifa_tstamp) / HZ; 14825c766d64SJiri Pirko 14835c766d64SJiri Pirko if (preferred > tval) 14845c766d64SJiri Pirko preferred -= tval; 14855c766d64SJiri Pirko else 14865c766d64SJiri Pirko preferred = 0; 14875c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 14885c766d64SJiri Pirko if (valid > tval) 14895c766d64SJiri Pirko valid -= tval; 14905c766d64SJiri Pirko else 14915c766d64SJiri Pirko valid = 0; 14925c766d64SJiri Pirko } 14935c766d64SJiri Pirko } 14945c766d64SJiri Pirko } else { 14955c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 14965c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 14975c766d64SJiri Pirko } 1498f3756b79SDavid S. Miller if ((ifa->ifa_address && 1499f3756b79SDavid S. Miller nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) || 1500f3756b79SDavid S. Miller (ifa->ifa_local && 1501f3756b79SDavid S. Miller nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) || 1502f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1503f3756b79SDavid S. Miller nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1504f3756b79SDavid S. Miller (ifa->ifa_label[0] && 15055c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 15065c766d64SJiri Pirko put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 15075c766d64SJiri Pirko preferred, valid)) 1508f3756b79SDavid S. Miller goto nla_put_failure; 150947f68512SThomas Graf 151047f68512SThomas Graf return nlmsg_end(skb, nlh); 151147f68512SThomas Graf 151247f68512SThomas Graf nla_put_failure: 151326932566SPatrick McHardy nlmsg_cancel(skb, nlh); 151426932566SPatrick McHardy return -EMSGSIZE; 15151da177e4SLinus Torvalds } 15161da177e4SLinus Torvalds 15171da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 15181da177e4SLinus Torvalds { 15193b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1520eec4df98SEric Dumazet int h, s_h; 1521eec4df98SEric Dumazet int idx, s_idx; 1522eec4df98SEric Dumazet int ip_idx, s_ip_idx; 15231da177e4SLinus Torvalds struct net_device *dev; 15241da177e4SLinus Torvalds struct in_device *in_dev; 15251da177e4SLinus Torvalds struct in_ifaddr *ifa; 1526eec4df98SEric Dumazet struct hlist_head *head; 15271da177e4SLinus Torvalds 1528eec4df98SEric Dumazet s_h = cb->args[0]; 1529eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1530eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1531eec4df98SEric Dumazet 1532eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 15337562f876SPavel Emelianov idx = 0; 1534eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1535eec4df98SEric Dumazet rcu_read_lock(); 15360465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 15370465277fSNicolas Dichtel net->dev_base_seq; 1538b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 15391da177e4SLinus Torvalds if (idx < s_idx) 15407562f876SPavel Emelianov goto cont; 15414b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 15421da177e4SLinus Torvalds s_ip_idx = 0; 1543eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 15449f9354b9SEric Dumazet if (!in_dev) 15457562f876SPavel Emelianov goto cont; 15461da177e4SLinus Torvalds 15471da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 15481da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 15491da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1550596e4150SStephen Hemminger continue; 1551eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 155215e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 15531da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1554eec4df98SEric Dumazet RTM_NEWADDR, NLM_F_MULTI) <= 0) { 1555eec4df98SEric Dumazet rcu_read_unlock(); 15561da177e4SLinus Torvalds goto done; 15571da177e4SLinus Torvalds } 15580465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1559eec4df98SEric Dumazet } 15607562f876SPavel Emelianov cont: 15617562f876SPavel Emelianov idx++; 15621da177e4SLinus Torvalds } 1563eec4df98SEric Dumazet rcu_read_unlock(); 1564eec4df98SEric Dumazet } 15651da177e4SLinus Torvalds 15661da177e4SLinus Torvalds done: 1567eec4df98SEric Dumazet cb->args[0] = h; 1568eec4df98SEric Dumazet cb->args[1] = idx; 1569eec4df98SEric Dumazet cb->args[2] = ip_idx; 15701da177e4SLinus Torvalds 15711da177e4SLinus Torvalds return skb->len; 15721da177e4SLinus Torvalds } 15731da177e4SLinus Torvalds 1574d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 157515e47304SEric W. Biederman u32 portid) 15761da177e4SLinus Torvalds { 157747f68512SThomas Graf struct sk_buff *skb; 1578d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1579d6062cbbSThomas Graf int err = -ENOBUFS; 15804b8aa9abSDenis V. Lunev struct net *net; 15811da177e4SLinus Torvalds 1582c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1583339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 158447f68512SThomas Graf if (skb == NULL) 1585d6062cbbSThomas Graf goto errout; 1586d6062cbbSThomas Graf 158715e47304SEric W. Biederman err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0); 158826932566SPatrick McHardy if (err < 0) { 158926932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 159026932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 159126932566SPatrick McHardy kfree_skb(skb); 159226932566SPatrick McHardy goto errout; 159326932566SPatrick McHardy } 159415e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 15951ce85fe4SPablo Neira Ayuso return; 1596d6062cbbSThomas Graf errout: 1597d6062cbbSThomas Graf if (err < 0) 15984b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 15991da177e4SLinus Torvalds } 16001da177e4SLinus Torvalds 16019f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 16029f0f7272SThomas Graf { 16031fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16049f0f7272SThomas Graf 16059f0f7272SThomas Graf if (!in_dev) 16069f0f7272SThomas Graf return 0; 16079f0f7272SThomas Graf 16089f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 16099f0f7272SThomas Graf } 16109f0f7272SThomas Graf 16119f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 16129f0f7272SThomas Graf { 16131fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16149f0f7272SThomas Graf struct nlattr *nla; 16159f0f7272SThomas Graf int i; 16169f0f7272SThomas Graf 16179f0f7272SThomas Graf if (!in_dev) 16189f0f7272SThomas Graf return -ENODATA; 16199f0f7272SThomas Graf 16209f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 16219f0f7272SThomas Graf if (nla == NULL) 16229f0f7272SThomas Graf return -EMSGSIZE; 16239f0f7272SThomas Graf 16249f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 16259f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 16269f0f7272SThomas Graf 16279f0f7272SThomas Graf return 0; 16289f0f7272SThomas Graf } 16299f0f7272SThomas Graf 16309f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 16319f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 16329f0f7272SThomas Graf }; 16339f0f7272SThomas Graf 1634cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1635cf7afbfeSThomas Graf const struct nlattr *nla) 16369f0f7272SThomas Graf { 16379f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 16389f0f7272SThomas Graf int err, rem; 16399f0f7272SThomas Graf 1640f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1641cf7afbfeSThomas Graf return -EAFNOSUPPORT; 16429f0f7272SThomas Graf 16439f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 16449f0f7272SThomas Graf if (err < 0) 16459f0f7272SThomas Graf return err; 16469f0f7272SThomas Graf 16479f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16489f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 16499f0f7272SThomas Graf int cfgid = nla_type(a); 16509f0f7272SThomas Graf 16519f0f7272SThomas Graf if (nla_len(a) < 4) 16529f0f7272SThomas Graf return -EINVAL; 16539f0f7272SThomas Graf 16549f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 16559f0f7272SThomas Graf return -EINVAL; 16569f0f7272SThomas Graf } 16579f0f7272SThomas Graf } 16589f0f7272SThomas Graf 1659cf7afbfeSThomas Graf return 0; 1660cf7afbfeSThomas Graf } 1661cf7afbfeSThomas Graf 1662cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1663cf7afbfeSThomas Graf { 1664f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1665cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1666cf7afbfeSThomas Graf int rem; 1667cf7afbfeSThomas Graf 1668cf7afbfeSThomas Graf if (!in_dev) 1669cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1670cf7afbfeSThomas Graf 1671cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1672cf7afbfeSThomas Graf BUG(); 1673cf7afbfeSThomas Graf 16749f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16759f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 16769f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 16779f0f7272SThomas Graf } 16789f0f7272SThomas Graf 16799f0f7272SThomas Graf return 0; 16809f0f7272SThomas Graf } 16819f0f7272SThomas Graf 1682edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 1683edc9e748SNicolas Dichtel { 1684edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 1685edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 1686edc9e748SNicolas Dichtel 16879e551110SNicolas Dichtel /* type -1 is used for ALL */ 16889e551110SNicolas Dichtel if (type == -1 || type == NETCONFA_FORWARDING) 1689edc9e748SNicolas Dichtel size += nla_total_size(4); 1690cc535dfbSNicolas Dichtel if (type == -1 || type == NETCONFA_RP_FILTER) 1691cc535dfbSNicolas Dichtel size += nla_total_size(4); 1692d67b8c61SNicolas Dichtel if (type == -1 || type == NETCONFA_MC_FORWARDING) 1693d67b8c61SNicolas Dichtel size += nla_total_size(4); 1694edc9e748SNicolas Dichtel 1695edc9e748SNicolas Dichtel return size; 1696edc9e748SNicolas Dichtel } 1697edc9e748SNicolas Dichtel 1698edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 1699edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 1700edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 1701edc9e748SNicolas Dichtel int type) 1702edc9e748SNicolas Dichtel { 1703edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 1704edc9e748SNicolas Dichtel struct netconfmsg *ncm; 1705edc9e748SNicolas Dichtel 1706edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 1707edc9e748SNicolas Dichtel flags); 1708edc9e748SNicolas Dichtel if (nlh == NULL) 1709edc9e748SNicolas Dichtel return -EMSGSIZE; 1710edc9e748SNicolas Dichtel 1711edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 1712edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 1713edc9e748SNicolas Dichtel 1714edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 1715edc9e748SNicolas Dichtel goto nla_put_failure; 1716edc9e748SNicolas Dichtel 17179e551110SNicolas Dichtel /* type -1 is used for ALL */ 17189e551110SNicolas Dichtel if ((type == -1 || type == NETCONFA_FORWARDING) && 1719edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 1720edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 1721edc9e748SNicolas Dichtel goto nla_put_failure; 1722cc535dfbSNicolas Dichtel if ((type == -1 || type == NETCONFA_RP_FILTER) && 1723cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 1724cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 1725cc535dfbSNicolas Dichtel goto nla_put_failure; 1726d67b8c61SNicolas Dichtel if ((type == -1 || type == NETCONFA_MC_FORWARDING) && 1727d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 1728d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 1729d67b8c61SNicolas Dichtel goto nla_put_failure; 1730edc9e748SNicolas Dichtel 1731edc9e748SNicolas Dichtel return nlmsg_end(skb, nlh); 1732edc9e748SNicolas Dichtel 1733edc9e748SNicolas Dichtel nla_put_failure: 1734edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 1735edc9e748SNicolas Dichtel return -EMSGSIZE; 1736edc9e748SNicolas Dichtel } 1737edc9e748SNicolas Dichtel 1738d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, 1739edc9e748SNicolas Dichtel struct ipv4_devconf *devconf) 1740edc9e748SNicolas Dichtel { 1741edc9e748SNicolas Dichtel struct sk_buff *skb; 1742edc9e748SNicolas Dichtel int err = -ENOBUFS; 1743edc9e748SNicolas Dichtel 1744edc9e748SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC); 1745edc9e748SNicolas Dichtel if (skb == NULL) 1746edc9e748SNicolas Dichtel goto errout; 1747edc9e748SNicolas Dichtel 1748edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 1749edc9e748SNicolas Dichtel RTM_NEWNETCONF, 0, type); 1750edc9e748SNicolas Dichtel if (err < 0) { 1751edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 1752edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 1753edc9e748SNicolas Dichtel kfree_skb(skb); 1754edc9e748SNicolas Dichtel goto errout; 1755edc9e748SNicolas Dichtel } 1756edc9e748SNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC); 1757edc9e748SNicolas Dichtel return; 1758edc9e748SNicolas Dichtel errout: 1759edc9e748SNicolas Dichtel if (err < 0) 1760edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 1761edc9e748SNicolas Dichtel } 1762edc9e748SNicolas Dichtel 17639e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 17649e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 17659e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 1766cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 17679e551110SNicolas Dichtel }; 17689e551110SNicolas Dichtel 17699e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 1770661d2967SThomas Graf struct nlmsghdr *nlh) 17719e551110SNicolas Dichtel { 17729e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 17739e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 17749e551110SNicolas Dichtel struct netconfmsg *ncm; 17759e551110SNicolas Dichtel struct sk_buff *skb; 17769e551110SNicolas Dichtel struct ipv4_devconf *devconf; 17779e551110SNicolas Dichtel struct in_device *in_dev; 17789e551110SNicolas Dichtel struct net_device *dev; 17799e551110SNicolas Dichtel int ifindex; 17809e551110SNicolas Dichtel int err; 17819e551110SNicolas Dichtel 17829e551110SNicolas Dichtel err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 17839e551110SNicolas Dichtel devconf_ipv4_policy); 17849e551110SNicolas Dichtel if (err < 0) 17859e551110SNicolas Dichtel goto errout; 17869e551110SNicolas Dichtel 17879e551110SNicolas Dichtel err = EINVAL; 17889e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 17899e551110SNicolas Dichtel goto errout; 17909e551110SNicolas Dichtel 17919e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 17929e551110SNicolas Dichtel switch (ifindex) { 17939e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 17949e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 17959e551110SNicolas Dichtel break; 17969e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 17979e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 17989e551110SNicolas Dichtel break; 17999e551110SNicolas Dichtel default: 18009e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 18019e551110SNicolas Dichtel if (dev == NULL) 18029e551110SNicolas Dichtel goto errout; 18039e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 18049e551110SNicolas Dichtel if (in_dev == NULL) 18059e551110SNicolas Dichtel goto errout; 18069e551110SNicolas Dichtel devconf = &in_dev->cnf; 18079e551110SNicolas Dichtel break; 18089e551110SNicolas Dichtel } 18099e551110SNicolas Dichtel 18109e551110SNicolas Dichtel err = -ENOBUFS; 18119e551110SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC); 18129e551110SNicolas Dichtel if (skb == NULL) 18139e551110SNicolas Dichtel goto errout; 18149e551110SNicolas Dichtel 18159e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 18169e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 18179e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 18189e551110SNicolas Dichtel -1); 18199e551110SNicolas Dichtel if (err < 0) { 18209e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 18219e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 18229e551110SNicolas Dichtel kfree_skb(skb); 18239e551110SNicolas Dichtel goto errout; 18249e551110SNicolas Dichtel } 18259e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 18269e551110SNicolas Dichtel errout: 18279e551110SNicolas Dichtel return err; 18289e551110SNicolas Dichtel } 18299e551110SNicolas Dichtel 18307a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 18317a674200SNicolas Dichtel struct netlink_callback *cb) 18327a674200SNicolas Dichtel { 18337a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 18347a674200SNicolas Dichtel int h, s_h; 18357a674200SNicolas Dichtel int idx, s_idx; 18367a674200SNicolas Dichtel struct net_device *dev; 18377a674200SNicolas Dichtel struct in_device *in_dev; 18387a674200SNicolas Dichtel struct hlist_head *head; 18397a674200SNicolas Dichtel 18407a674200SNicolas Dichtel s_h = cb->args[0]; 18417a674200SNicolas Dichtel s_idx = idx = cb->args[1]; 18427a674200SNicolas Dichtel 18437a674200SNicolas Dichtel for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 18447a674200SNicolas Dichtel idx = 0; 18457a674200SNicolas Dichtel head = &net->dev_index_head[h]; 18467a674200SNicolas Dichtel rcu_read_lock(); 18470465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 18480465277fSNicolas Dichtel net->dev_base_seq; 18497a674200SNicolas Dichtel hlist_for_each_entry_rcu(dev, head, index_hlist) { 18507a674200SNicolas Dichtel if (idx < s_idx) 18517a674200SNicolas Dichtel goto cont; 18527a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 18537a674200SNicolas Dichtel if (!in_dev) 18547a674200SNicolas Dichtel goto cont; 18557a674200SNicolas Dichtel 18567a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, dev->ifindex, 18577a674200SNicolas Dichtel &in_dev->cnf, 18587a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18597a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18607a674200SNicolas Dichtel RTM_NEWNETCONF, 18617a674200SNicolas Dichtel NLM_F_MULTI, 18627a674200SNicolas Dichtel -1) <= 0) { 18637a674200SNicolas Dichtel rcu_read_unlock(); 18647a674200SNicolas Dichtel goto done; 18657a674200SNicolas Dichtel } 18660465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 18677a674200SNicolas Dichtel cont: 18687a674200SNicolas Dichtel idx++; 18697a674200SNicolas Dichtel } 18707a674200SNicolas Dichtel rcu_read_unlock(); 18717a674200SNicolas Dichtel } 18727a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES) { 18737a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 18747a674200SNicolas Dichtel net->ipv4.devconf_all, 18757a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18767a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18777a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 18787a674200SNicolas Dichtel -1) <= 0) 18797a674200SNicolas Dichtel goto done; 18807a674200SNicolas Dichtel else 18817a674200SNicolas Dichtel h++; 18827a674200SNicolas Dichtel } 18837a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES + 1) { 18847a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 18857a674200SNicolas Dichtel net->ipv4.devconf_dflt, 18867a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18877a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18887a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 18897a674200SNicolas Dichtel -1) <= 0) 18907a674200SNicolas Dichtel goto done; 18917a674200SNicolas Dichtel else 18927a674200SNicolas Dichtel h++; 18937a674200SNicolas Dichtel } 18947a674200SNicolas Dichtel done: 18957a674200SNicolas Dichtel cb->args[0] = h; 18967a674200SNicolas Dichtel cb->args[1] = idx; 18977a674200SNicolas Dichtel 18987a674200SNicolas Dichtel return skb->len; 18997a674200SNicolas Dichtel } 19007a674200SNicolas Dichtel 19011da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 19021da177e4SLinus Torvalds 1903c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 190431be3085SHerbert Xu { 190531be3085SHerbert Xu struct net_device *dev; 190631be3085SHerbert Xu 190731be3085SHerbert Xu rcu_read_lock(); 1908c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1909c6d14c84SEric Dumazet struct in_device *in_dev; 1910c6d14c84SEric Dumazet 191131be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 191231be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 19139355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1914c6d14c84SEric Dumazet } 191531be3085SHerbert Xu rcu_read_unlock(); 191631be3085SHerbert Xu } 191731be3085SHerbert Xu 1918c6d14c84SEric Dumazet /* called with RTNL locked */ 1919c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 192068dd299bSPavel Emelyanov { 192168dd299bSPavel Emelyanov struct net_device *dev; 1922586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 192368dd299bSPavel Emelyanov 1924586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 19259355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 1926edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1927edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1928edc9e748SNicolas Dichtel net->ipv4.devconf_all); 1929edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1930edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 1931edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 193268dd299bSPavel Emelyanov 1933c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 193468dd299bSPavel Emelyanov struct in_device *in_dev; 19350187bdfbSBen Hutchings if (on) 19360187bdfbSBen Hutchings dev_disable_lro(dev); 193768dd299bSPavel Emelyanov rcu_read_lock(); 193868dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 1939edc9e748SNicolas Dichtel if (in_dev) { 194068dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 1941edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1942edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 1943edc9e748SNicolas Dichtel } 194468dd299bSPavel Emelyanov rcu_read_unlock(); 194568dd299bSPavel Emelyanov } 194668dd299bSPavel Emelyanov } 194768dd299bSPavel Emelyanov 1948fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write, 19498d65af78SAlexey Dobriyan void __user *buffer, 195031be3085SHerbert Xu size_t *lenp, loff_t *ppos) 195131be3085SHerbert Xu { 1952d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 19538d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 1954d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 195531be3085SHerbert Xu 195631be3085SHerbert Xu if (write) { 195731be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 1958c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 195931be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 196031be3085SHerbert Xu 196131be3085SHerbert Xu set_bit(i, cnf->state); 196231be3085SHerbert Xu 19639355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 1964c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 1965d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 1966d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 1967d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 19684ccfe6d4SNicolas Dichtel rt_cache_flush(net); 1969cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 1970cc535dfbSNicolas Dichtel new_value != old_value) { 1971cc535dfbSNicolas Dichtel int ifindex; 1972cc535dfbSNicolas Dichtel 1973cc535dfbSNicolas Dichtel if (cnf == net->ipv4.devconf_dflt) 1974cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_DEFAULT; 1975cc535dfbSNicolas Dichtel else if (cnf == net->ipv4.devconf_all) 1976cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_ALL; 1977cc535dfbSNicolas Dichtel else { 1978cc535dfbSNicolas Dichtel struct in_device *idev = 1979cc535dfbSNicolas Dichtel container_of(cnf, struct in_device, 1980cc535dfbSNicolas Dichtel cnf); 1981cc535dfbSNicolas Dichtel ifindex = idev->dev->ifindex; 1982cc535dfbSNicolas Dichtel } 1983cc535dfbSNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER, 1984cc535dfbSNicolas Dichtel ifindex, cnf); 1985cc535dfbSNicolas Dichtel } 198631be3085SHerbert Xu } 198731be3085SHerbert Xu 198831be3085SHerbert Xu return ret; 198931be3085SHerbert Xu } 199031be3085SHerbert Xu 1991fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write, 19928d65af78SAlexey Dobriyan void __user *buffer, 19931da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 19941da177e4SLinus Torvalds { 19951da177e4SLinus Torvalds int *valp = ctl->data; 19961da177e4SLinus Torvalds int val = *valp; 199788af182eSEric W. Biederman loff_t pos = *ppos; 19988d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 19991da177e4SLinus Torvalds 20001da177e4SLinus Torvalds if (write && *valp != val) { 2001c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 2002c0ce9fb3SPavel Emelyanov 20030187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 200488af182eSEric W. Biederman if (!rtnl_trylock()) { 200588af182eSEric W. Biederman /* Restore the original values before restarting */ 200688af182eSEric W. Biederman *valp = val; 200788af182eSEric W. Biederman *ppos = pos; 20089b8adb5eSEric W. Biederman return restart_syscall(); 200988af182eSEric W. Biederman } 20100187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2011c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2012edc9e748SNicolas Dichtel } else { 20130187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 20140187bdfbSBen Hutchings struct in_device *idev = 20150187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2016edc9e748SNicolas Dichtel if (*valp) 20170187bdfbSBen Hutchings dev_disable_lro(idev->dev); 2018edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, 2019edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2020edc9e748SNicolas Dichtel idev->dev->ifindex, 2021edc9e748SNicolas Dichtel cnf); 20220187bdfbSBen Hutchings } 20230187bdfbSBen Hutchings rtnl_unlock(); 20244ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2025edc9e748SNicolas Dichtel } else 2026edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 2027edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2028edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 20290187bdfbSBen Hutchings } 20301da177e4SLinus Torvalds 20311da177e4SLinus Torvalds return ret; 20321da177e4SLinus Torvalds } 20331da177e4SLinus Torvalds 2034fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 20358d65af78SAlexey Dobriyan void __user *buffer, 20361da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 20371da177e4SLinus Torvalds { 20381da177e4SLinus Torvalds int *valp = ctl->data; 20391da177e4SLinus Torvalds int val = *valp; 20408d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 204176e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 20421da177e4SLinus Torvalds 20431da177e4SLinus Torvalds if (write && *valp != val) 20444ccfe6d4SNicolas Dichtel rt_cache_flush(net); 20451da177e4SLinus Torvalds 20461da177e4SLinus Torvalds return ret; 20471da177e4SLinus Torvalds } 20481da177e4SLinus Torvalds 2049f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 205042f811b8SHerbert Xu { \ 205142f811b8SHerbert Xu .procname = name, \ 205242f811b8SHerbert Xu .data = ipv4_devconf.data + \ 205302291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 205442f811b8SHerbert Xu .maxlen = sizeof(int), \ 205542f811b8SHerbert Xu .mode = mval, \ 205642f811b8SHerbert Xu .proc_handler = proc, \ 205731be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 205842f811b8SHerbert Xu } 205942f811b8SHerbert Xu 206042f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2061f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 206242f811b8SHerbert Xu 206342f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2064f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 206542f811b8SHerbert Xu 2066f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2067f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 206842f811b8SHerbert Xu 206942f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2070f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 207142f811b8SHerbert Xu 20721da177e4SLinus Torvalds static struct devinet_sysctl_table { 20731da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 207402291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 20751da177e4SLinus Torvalds } devinet_sysctl = { 20761da177e4SLinus Torvalds .devinet_vars = { 207742f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2078f8572d8fSEric W. Biederman devinet_sysctl_forward), 207942f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 208042f811b8SHerbert Xu 208142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 208242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 208342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 208442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 208542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 208642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 208742f811b8SHerbert Xu "accept_source_route"), 20888153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 208928f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 209042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 209142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 209242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 209342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 209442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 209542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 209642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 209742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 209842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2099eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 210065324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 21015c6fe01cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 21025c6fe01cSWilliam Manley "force_igmp_version"), 21032690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, 21042690048cSWilliam Manley "igmpv2_unsolicited_report_interval"), 21052690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, 21062690048cSWilliam Manley "igmpv3_unsolicited_report_interval"), 210742f811b8SHerbert Xu 210842f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 210942f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 211042f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 211142f811b8SHerbert Xu "promote_secondaries"), 2112d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2113d0daebc3SThomas Graf "route_localnet"), 21141da177e4SLinus Torvalds }, 21151da177e4SLinus Torvalds }; 21161da177e4SLinus Torvalds 2117ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 2118f8572d8fSEric W. Biederman struct ipv4_devconf *p) 21191da177e4SLinus Torvalds { 21201da177e4SLinus Torvalds int i; 21219fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 21228607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2123bfada697SPavel Emelyanov 21249fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 21251da177e4SLinus Torvalds if (!t) 21269fa89642SPavel Emelyanov goto out; 21279fa89642SPavel Emelyanov 21281da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 21291da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 213031be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2131c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 21321da177e4SLinus Torvalds } 21331da177e4SLinus Torvalds 21348607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 21351da177e4SLinus Torvalds 21368607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 21371da177e4SLinus Torvalds if (!t->sysctl_header) 21388607ddb8SEric W. Biederman goto free; 21391da177e4SLinus Torvalds 21401da177e4SLinus Torvalds p->sysctl = t; 2141ea40b324SPavel Emelyanov return 0; 21421da177e4SLinus Torvalds 21431da177e4SLinus Torvalds free: 21441da177e4SLinus Torvalds kfree(t); 21459fa89642SPavel Emelyanov out: 2146ea40b324SPavel Emelyanov return -ENOBUFS; 21471da177e4SLinus Torvalds } 21481da177e4SLinus Torvalds 214951602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 215066f27a52SPavel Emelyanov { 215151602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 215266f27a52SPavel Emelyanov 215351602b2aSPavel Emelyanov if (t == NULL) 215451602b2aSPavel Emelyanov return; 215551602b2aSPavel Emelyanov 215651602b2aSPavel Emelyanov cnf->sysctl = NULL; 2157ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 21581da177e4SLinus Torvalds kfree(t); 21591da177e4SLinus Torvalds } 216051602b2aSPavel Emelyanov 216151602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev) 216251602b2aSPavel Emelyanov { 216373af614aSJiri Pirko neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); 2164c346dca1SYOSHIFUJI Hideaki __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 2165f8572d8fSEric W. Biederman &idev->cnf); 216651602b2aSPavel Emelyanov } 216751602b2aSPavel Emelyanov 216851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 216951602b2aSPavel Emelyanov { 217051602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 217151602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 21721da177e4SLinus Torvalds } 21731da177e4SLinus Torvalds 217468dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 217568dd299bSPavel Emelyanov { 217668dd299bSPavel Emelyanov .procname = "ip_forward", 217768dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 217802291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 217968dd299bSPavel Emelyanov .maxlen = sizeof(int), 218068dd299bSPavel Emelyanov .mode = 0644, 218168dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 218268dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2183c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 218468dd299bSPavel Emelyanov }, 218568dd299bSPavel Emelyanov { }, 218668dd299bSPavel Emelyanov }; 21872a75de0cSEric Dumazet #endif 218868dd299bSPavel Emelyanov 2189752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2190752d14dcSPavel Emelyanov { 2191752d14dcSPavel Emelyanov int err; 2192752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 21932a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 21942a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 2195752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 21962a75de0cSEric Dumazet #endif 2197752d14dcSPavel Emelyanov 2198752d14dcSPavel Emelyanov err = -ENOMEM; 2199752d14dcSPavel Emelyanov all = &ipv4_devconf; 2200752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 2201752d14dcSPavel Emelyanov 220209ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 2203752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 2204752d14dcSPavel Emelyanov if (all == NULL) 2205752d14dcSPavel Emelyanov goto err_alloc_all; 2206752d14dcSPavel Emelyanov 2207752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 2208752d14dcSPavel Emelyanov if (dflt == NULL) 2209752d14dcSPavel Emelyanov goto err_alloc_dflt; 2210752d14dcSPavel Emelyanov 22112a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2212752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 2213752d14dcSPavel Emelyanov if (tbl == NULL) 2214752d14dcSPavel Emelyanov goto err_alloc_ctl; 2215752d14dcSPavel Emelyanov 221602291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2217752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2218752d14dcSPavel Emelyanov tbl[0].extra2 = net; 22192a75de0cSEric Dumazet #endif 2220752d14dcSPavel Emelyanov } 2221752d14dcSPavel Emelyanov 2222752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2223f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 2224752d14dcSPavel Emelyanov if (err < 0) 2225752d14dcSPavel Emelyanov goto err_reg_all; 2226752d14dcSPavel Emelyanov 2227f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 2228752d14dcSPavel Emelyanov if (err < 0) 2229752d14dcSPavel Emelyanov goto err_reg_dflt; 2230752d14dcSPavel Emelyanov 2231752d14dcSPavel Emelyanov err = -ENOMEM; 22328607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 2233752d14dcSPavel Emelyanov if (forw_hdr == NULL) 2234752d14dcSPavel Emelyanov goto err_reg_ctl; 22352a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2236752d14dcSPavel Emelyanov #endif 2237752d14dcSPavel Emelyanov 2238752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2239752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2240752d14dcSPavel Emelyanov return 0; 2241752d14dcSPavel Emelyanov 2242752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2243752d14dcSPavel Emelyanov err_reg_ctl: 2244752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 2245752d14dcSPavel Emelyanov err_reg_dflt: 2246752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 2247752d14dcSPavel Emelyanov err_reg_all: 2248752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 2249752d14dcSPavel Emelyanov kfree(tbl); 2250752d14dcSPavel Emelyanov err_alloc_ctl: 22512a75de0cSEric Dumazet #endif 2252752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 2253752d14dcSPavel Emelyanov kfree(dflt); 2254752d14dcSPavel Emelyanov err_alloc_dflt: 2255752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 2256752d14dcSPavel Emelyanov kfree(all); 2257752d14dcSPavel Emelyanov err_alloc_all: 2258752d14dcSPavel Emelyanov return err; 2259752d14dcSPavel Emelyanov } 2260752d14dcSPavel Emelyanov 2261752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2262752d14dcSPavel Emelyanov { 22632a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2264752d14dcSPavel Emelyanov struct ctl_table *tbl; 2265752d14dcSPavel Emelyanov 2266752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2267752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2268752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 2269752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 2270752d14dcSPavel Emelyanov kfree(tbl); 22712a75de0cSEric Dumazet #endif 2272752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2273752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2274752d14dcSPavel Emelyanov } 2275752d14dcSPavel Emelyanov 2276752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2277752d14dcSPavel Emelyanov .init = devinet_init_net, 2278752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2279752d14dcSPavel Emelyanov }; 2280752d14dcSPavel Emelyanov 22819f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = { 22829f0f7272SThomas Graf .family = AF_INET, 22839f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 22849f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2285cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2286cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 22879f0f7272SThomas Graf }; 22889f0f7272SThomas Graf 22891da177e4SLinus Torvalds void __init devinet_init(void) 22901da177e4SLinus Torvalds { 2291fd23c3b3SDavid S. Miller int i; 2292fd23c3b3SDavid S. Miller 2293fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2294fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2295fd23c3b3SDavid S. Miller 2296752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 2297752d14dcSPavel Emelyanov 22981da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 22991da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 230063f3444fSThomas Graf 23015c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, 0); 23025c766d64SJiri Pirko 23039f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 23049f0f7272SThomas Graf 2305c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); 2306c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); 2307c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); 23089e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 23097a674200SNicolas Dichtel inet_netconf_dump_devconf, NULL); 23101da177e4SLinus Torvalds } 23111da177e4SLinus Torvalds 2312