11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * NET3 IP device support routines. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 51da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 61da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 71da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Derived from the IP parts of dev.c 1.0.19 1002c30a84SJesper Juhl * Authors: Ross Biro 111da177e4SLinus Torvalds * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> 121da177e4SLinus Torvalds * Mark Evans, <evansmp@uhura.aston.ac.uk> 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * Additional Authors: 151da177e4SLinus Torvalds * Alan Cox, <gw4pts@gw4pts.ampr.org> 161da177e4SLinus Torvalds * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * Changes: 191da177e4SLinus Torvalds * Alexey Kuznetsov: pa_* fields are replaced with ifaddr 201da177e4SLinus Torvalds * lists. 211da177e4SLinus Torvalds * Cyrus Durgin: updated for kmod 221da177e4SLinus Torvalds * Matthias Andree: in devinet_ioctl, compare label and 231da177e4SLinus Torvalds * address (4.4BSD alias style support), 241da177e4SLinus Torvalds * fall back to comparing just the label 251da177e4SLinus Torvalds * if no match found. 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #include <asm/uaccess.h> 301da177e4SLinus Torvalds #include <linux/bitops.h> 314fc268d2SRandy Dunlap #include <linux/capability.h> 321da177e4SLinus Torvalds #include <linux/module.h> 331da177e4SLinus Torvalds #include <linux/types.h> 341da177e4SLinus Torvalds #include <linux/kernel.h> 351da177e4SLinus Torvalds #include <linux/string.h> 361da177e4SLinus Torvalds #include <linux/mm.h> 371da177e4SLinus Torvalds #include <linux/socket.h> 381da177e4SLinus Torvalds #include <linux/sockios.h> 391da177e4SLinus Torvalds #include <linux/in.h> 401da177e4SLinus Torvalds #include <linux/errno.h> 411da177e4SLinus Torvalds #include <linux/interrupt.h> 421823730fSThomas Graf #include <linux/if_addr.h> 431da177e4SLinus Torvalds #include <linux/if_ether.h> 441da177e4SLinus Torvalds #include <linux/inet.h> 451da177e4SLinus Torvalds #include <linux/netdevice.h> 461da177e4SLinus Torvalds #include <linux/etherdevice.h> 471da177e4SLinus Torvalds #include <linux/skbuff.h> 481da177e4SLinus Torvalds #include <linux/init.h> 491da177e4SLinus Torvalds #include <linux/notifier.h> 501da177e4SLinus Torvalds #include <linux/inetdevice.h> 511da177e4SLinus Torvalds #include <linux/igmp.h> 525a0e3ad6STejun Heo #include <linux/slab.h> 53fd23c3b3SDavid S. Miller #include <linux/hash.h> 541da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 551da177e4SLinus Torvalds #include <linux/sysctl.h> 561da177e4SLinus Torvalds #endif 571da177e4SLinus Torvalds #include <linux/kmod.h> 58edc9e748SNicolas Dichtel #include <linux/netconf.h> 591da177e4SLinus Torvalds 6014c85021SArnaldo Carvalho de Melo #include <net/arp.h> 611da177e4SLinus Torvalds #include <net/ip.h> 621da177e4SLinus Torvalds #include <net/route.h> 631da177e4SLinus Torvalds #include <net/ip_fib.h> 6463f3444fSThomas Graf #include <net/rtnetlink.h> 65752d14dcSPavel Emelyanov #include <net/net_namespace.h> 665c766d64SJiri Pirko #include <net/addrconf.h> 671da177e4SLinus Torvalds 68406b6f97SDavid S. Miller #include "fib_lookup.h" 69406b6f97SDavid S. Miller 700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 7142f811b8SHerbert Xu .data = { 7202291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7302291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7402291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7502291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 7642f811b8SHerbert Xu }, 771da177e4SLinus Torvalds }; 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 8042f811b8SHerbert Xu .data = { 8102291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8202291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8302291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8402291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8502291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 8642f811b8SHerbert Xu }, 871da177e4SLinus Torvalds }; 881da177e4SLinus Torvalds 899355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 909355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9142f811b8SHerbert Xu 92ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 935c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 945c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 955c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 965176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 975c766d64SJiri Pirko [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 985c753978SThomas Graf }; 995c753978SThomas Graf 10040384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT 8 10140384999SEric Dumazet #define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 10240384999SEric Dumazet 103fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 104fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock); 105fd23c3b3SDavid S. Miller 10640384999SEric Dumazet static u32 inet_addr_hash(struct net *net, __be32 addr) 107fd23c3b3SDavid S. Miller { 10840384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 109fd23c3b3SDavid S. Miller 11040384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 111fd23c3b3SDavid S. Miller } 112fd23c3b3SDavid S. Miller 113fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 114fd23c3b3SDavid S. Miller { 11540384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 116fd23c3b3SDavid S. Miller 117fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 118fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 119fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 120fd23c3b3SDavid S. Miller } 121fd23c3b3SDavid S. Miller 122fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 123fd23c3b3SDavid S. Miller { 124fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 125fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 126fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 127fd23c3b3SDavid S. Miller } 128fd23c3b3SDavid S. Miller 1299435eb1cSDavid S. Miller /** 1309435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1319435eb1cSDavid S. Miller * @net: the net namespace 1329435eb1cSDavid S. Miller * @addr: the source address 1339435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1349435eb1cSDavid S. Miller * 1359435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1369435eb1cSDavid S. Miller */ 1379435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1389435eb1cSDavid S. Miller { 13940384999SEric Dumazet u32 hash = inet_addr_hash(net, addr); 1409435eb1cSDavid S. Miller struct net_device *result = NULL; 1419435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1429435eb1cSDavid S. Miller 1439435eb1cSDavid S. Miller rcu_read_lock(); 144b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) { 14540384999SEric Dumazet if (ifa->ifa_local == addr) { 1469435eb1cSDavid S. Miller struct net_device *dev = ifa->ifa_dev->dev; 1479435eb1cSDavid S. Miller 1489435eb1cSDavid S. Miller if (!net_eq(dev_net(dev), net)) 1499435eb1cSDavid S. Miller continue; 1509435eb1cSDavid S. Miller result = dev; 1519435eb1cSDavid S. Miller break; 1529435eb1cSDavid S. Miller } 1539435eb1cSDavid S. Miller } 154406b6f97SDavid S. Miller if (!result) { 155406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 156406b6f97SDavid S. Miller struct fib_result res = { 0 }; 157406b6f97SDavid S. Miller struct fib_table *local; 158406b6f97SDavid S. Miller 159406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 160406b6f97SDavid S. Miller * over loopback subnets work. 161406b6f97SDavid S. Miller */ 162406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 163406b6f97SDavid S. Miller if (local && 164406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 165406b6f97SDavid S. Miller res.type == RTN_LOCAL) 166406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 167406b6f97SDavid S. Miller } 1689435eb1cSDavid S. Miller if (result && devref) 1699435eb1cSDavid S. Miller dev_hold(result); 1709435eb1cSDavid S. Miller rcu_read_unlock(); 1719435eb1cSDavid S. Miller return result; 1729435eb1cSDavid S. Miller } 1739435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1749435eb1cSDavid S. Miller 175d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1761da177e4SLinus Torvalds 177e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1781da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1791da177e4SLinus Torvalds int destroy); 1801da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 18166f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev); 18251602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 18351602b2aSPavel Emelyanov #else 18440384999SEric Dumazet static void devinet_sysctl_register(struct in_device *idev) 18551602b2aSPavel Emelyanov { 18651602b2aSPavel Emelyanov } 18740384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 18851602b2aSPavel Emelyanov { 18951602b2aSPavel Emelyanov } 1901da177e4SLinus Torvalds #endif 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds /* Locks all the inet devices. */ 1931da177e4SLinus Torvalds 1941da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 1951da177e4SLinus Torvalds { 19693adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 1971da177e4SLinus Torvalds } 1981da177e4SLinus Torvalds 1991da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2001da177e4SLinus Torvalds { 2011da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2021da177e4SLinus Torvalds if (ifa->ifa_dev) 2031da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2041da177e4SLinus Torvalds kfree(ifa); 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 20740384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2081da177e4SLinus Torvalds { 2091da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2131da177e4SLinus Torvalds { 2141da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2151da177e4SLinus Torvalds 216547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 217547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 218e9897071SEric Dumazet kfree(rcu_dereference_protected(idev->mc_hash, 1)); 2191da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 22091df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2211da177e4SLinus Torvalds #endif 2221da177e4SLinus Torvalds dev_put(dev); 2231da177e4SLinus Torvalds if (!idev->dead) 2249f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2259f9354b9SEric Dumazet else 2261da177e4SLinus Torvalds kfree(idev); 2271da177e4SLinus Torvalds } 2289f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2291da177e4SLinus Torvalds 23071e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2311da177e4SLinus Torvalds { 2321da177e4SLinus Torvalds struct in_device *in_dev; 2331da177e4SLinus Torvalds 2341da177e4SLinus Torvalds ASSERT_RTNL(); 2351da177e4SLinus Torvalds 2360da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2371da177e4SLinus Torvalds if (!in_dev) 2381da177e4SLinus Torvalds goto out; 239c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2409355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2411da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2421da177e4SLinus Torvalds in_dev->dev = dev; 2439f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2449f9354b9SEric Dumazet if (!in_dev->arp_parms) 2451da177e4SLinus Torvalds goto out_kfree; 2460187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2470187bdfbSBen Hutchings dev_disable_lro(dev); 2481da177e4SLinus Torvalds /* Reference in_dev->dev */ 2491da177e4SLinus Torvalds dev_hold(dev); 25030c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2511da177e4SLinus Torvalds in_dev_hold(in_dev); 2521da177e4SLinus Torvalds 25366f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 2541da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2551da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2561da177e4SLinus Torvalds ip_mc_up(in_dev); 257483479ecSJarek Poplawski 25830c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 259cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 260483479ecSJarek Poplawski out: 2611da177e4SLinus Torvalds return in_dev; 2621da177e4SLinus Torvalds out_kfree: 2631da177e4SLinus Torvalds kfree(in_dev); 2641da177e4SLinus Torvalds in_dev = NULL; 2651da177e4SLinus Torvalds goto out; 2661da177e4SLinus Torvalds } 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2691da177e4SLinus Torvalds { 2701da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2711da177e4SLinus Torvalds in_dev_put(idev); 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2751da177e4SLinus Torvalds { 2761da177e4SLinus Torvalds struct in_ifaddr *ifa; 2771da177e4SLinus Torvalds struct net_device *dev; 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds ASSERT_RTNL(); 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds dev = in_dev->dev; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds in_dev->dead = 1; 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 2861da177e4SLinus Torvalds 2871da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 2881da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 2891da177e4SLinus Torvalds inet_free_ifa(ifa); 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 292a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 2931da177e4SLinus Torvalds 29451602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 2951da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 2961da177e4SLinus Torvalds arp_ifdown(dev); 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 2991da177e4SLinus Torvalds } 3001da177e4SLinus Torvalds 301ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3021da177e4SLinus Torvalds { 3031da177e4SLinus Torvalds rcu_read_lock(); 3041da177e4SLinus Torvalds for_primary_ifa(in_dev) { 3051da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3061da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3071da177e4SLinus Torvalds rcu_read_unlock(); 3081da177e4SLinus Torvalds return 1; 3091da177e4SLinus Torvalds } 3101da177e4SLinus Torvalds } 3111da177e4SLinus Torvalds } endfor_ifa(in_dev); 3121da177e4SLinus Torvalds rcu_read_unlock(); 3131da177e4SLinus Torvalds return 0; 3141da177e4SLinus Torvalds } 3151da177e4SLinus Torvalds 316d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 31715e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3181da177e4SLinus Torvalds { 3198f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3200ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3210ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3220ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3230ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds ASSERT_RTNL(); 3261da177e4SLinus Torvalds 3278f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3288f937c60SHarald Welte * unless alias promotion is set 3298f937c60SHarald Welte **/ 3301da177e4SLinus Torvalds 3311da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3321da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3331da177e4SLinus Torvalds 3341da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3350ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3360ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3370ff60a45SJamal Hadi Salim last_prim = ifa; 3380ff60a45SJamal Hadi Salim 3391da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3401da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3411da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3421da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3430ff60a45SJamal Hadi Salim prev_prom = ifa; 3441da177e4SLinus Torvalds continue; 3451da177e4SLinus Torvalds } 3461da177e4SLinus Torvalds 3470ff60a45SJamal Hadi Salim if (!do_promote) { 348fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3491da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3501da177e4SLinus Torvalds 35115e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 352e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 353e041c683SAlan Stern NETDEV_DOWN, ifa); 3541da177e4SLinus Torvalds inet_free_ifa(ifa); 3558f937c60SHarald Welte } else { 3568f937c60SHarald Welte promote = ifa; 3578f937c60SHarald Welte break; 3588f937c60SHarald Welte } 3591da177e4SLinus Torvalds } 3601da177e4SLinus Torvalds } 3611da177e4SLinus Torvalds 3622d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 3632d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 3642d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 3652d230e2bSJulian Anastasov * while all addresses are on the device list. 3662d230e2bSJulian Anastasov */ 3672d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 3682d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 3692d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 3702d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 3712d230e2bSJulian Anastasov } 3722d230e2bSJulian Anastasov 3731da177e4SLinus Torvalds /* 2. Unlink it */ 3741da177e4SLinus Torvalds 3751da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 376fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds /* 3. Announce address deletion */ 3791da177e4SLinus Torvalds 3801da177e4SLinus Torvalds /* Send message first, then call notifier. 3811da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 3821da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 3831da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 3841da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 3851da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 3861da177e4SLinus Torvalds So that, this order is correct. 3871da177e4SLinus Torvalds */ 38815e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 389e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 3900ff60a45SJamal Hadi Salim 3910ff60a45SJamal Hadi Salim if (promote) { 39204024b93SJulian Anastasov struct in_ifaddr *next_sec = promote->ifa_next; 3930ff60a45SJamal Hadi Salim 3940ff60a45SJamal Hadi Salim if (prev_prom) { 3950ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 3960ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 3970ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 3980ff60a45SJamal Hadi Salim } 3990ff60a45SJamal Hadi Salim 4000ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 40115e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 402e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 403e041c683SAlan Stern NETDEV_UP, promote); 40404024b93SJulian Anastasov for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { 4050ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4060ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4070ff60a45SJamal Hadi Salim continue; 4080ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4090ff60a45SJamal Hadi Salim } 4100ff60a45SJamal Hadi Salim 4110ff60a45SJamal Hadi Salim } 4126363097cSHerbert Xu if (destroy) 4131da177e4SLinus Torvalds inet_free_ifa(ifa1); 4141da177e4SLinus Torvalds } 4151da177e4SLinus Torvalds 416d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 417d6062cbbSThomas Graf int destroy) 418d6062cbbSThomas Graf { 419d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 420d6062cbbSThomas Graf } 421d6062cbbSThomas Graf 4225c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4235c766d64SJiri Pirko 4245c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4255c766d64SJiri Pirko 426d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 42715e47304SEric W. Biederman u32 portid) 4281da177e4SLinus Torvalds { 4291da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4301da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4311da177e4SLinus Torvalds 4321da177e4SLinus Torvalds ASSERT_RTNL(); 4331da177e4SLinus Torvalds 4341da177e4SLinus Torvalds if (!ifa->ifa_local) { 4351da177e4SLinus Torvalds inet_free_ifa(ifa); 4361da177e4SLinus Torvalds return 0; 4371da177e4SLinus Torvalds } 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4401da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4411da177e4SLinus Torvalds 4421da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4431da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4441da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4451da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4461da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4471da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4481da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4491da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4501da177e4SLinus Torvalds inet_free_ifa(ifa); 4511da177e4SLinus Torvalds return -EEXIST; 4521da177e4SLinus Torvalds } 4531da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4541da177e4SLinus Torvalds inet_free_ifa(ifa); 4551da177e4SLinus Torvalds return -EINVAL; 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds } 4601da177e4SLinus Torvalds 4611da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 4621da177e4SLinus Torvalds net_srandom(ifa->ifa_local); 4631da177e4SLinus Torvalds ifap = last_primary; 4641da177e4SLinus Torvalds } 4651da177e4SLinus Torvalds 4661da177e4SLinus Torvalds ifa->ifa_next = *ifap; 4671da177e4SLinus Torvalds *ifap = ifa; 4681da177e4SLinus Torvalds 469fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 470fd23c3b3SDavid S. Miller 4715c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 4725c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, 0); 4735c766d64SJiri Pirko 4741da177e4SLinus Torvalds /* Send message first, then call notifier. 4751da177e4SLinus Torvalds Notifier will trigger FIB update, so that 4761da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 47715e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 478e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 4791da177e4SLinus Torvalds 4801da177e4SLinus Torvalds return 0; 4811da177e4SLinus Torvalds } 4821da177e4SLinus Torvalds 483d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 484d6062cbbSThomas Graf { 485d6062cbbSThomas Graf return __inet_insert_ifa(ifa, NULL, 0); 486d6062cbbSThomas Graf } 487d6062cbbSThomas Graf 4881da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 4891da177e4SLinus Torvalds { 490e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 4911da177e4SLinus Torvalds 4921da177e4SLinus Torvalds ASSERT_RTNL(); 4931da177e4SLinus Torvalds 4941da177e4SLinus Torvalds if (!in_dev) { 4951da177e4SLinus Torvalds inet_free_ifa(ifa); 4961da177e4SLinus Torvalds return -ENOBUFS; 4971da177e4SLinus Torvalds } 49871e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 4991da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 500547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 5011da177e4SLinus Torvalds in_dev_hold(in_dev); 5021da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5031da177e4SLinus Torvalds } 504f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5051da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5061da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5071da177e4SLinus Torvalds } 5081da177e4SLinus Torvalds 5098723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5108723e1b4SEric Dumazet * We dont take a reference on found in_device 5118723e1b4SEric Dumazet */ 5127fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5131da177e4SLinus Torvalds { 5141da177e4SLinus Torvalds struct net_device *dev; 5151da177e4SLinus Torvalds struct in_device *in_dev = NULL; 516c148fc2eSEric Dumazet 517c148fc2eSEric Dumazet rcu_read_lock(); 518c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5191da177e4SLinus Torvalds if (dev) 5208723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 521c148fc2eSEric Dumazet rcu_read_unlock(); 5221da177e4SLinus Torvalds return in_dev; 5231da177e4SLinus Torvalds } 5249f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5251da177e4SLinus Torvalds 5261da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5271da177e4SLinus Torvalds 52860cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 52960cad5daSAl Viro __be32 mask) 5301da177e4SLinus Torvalds { 5311da177e4SLinus Torvalds ASSERT_RTNL(); 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5341da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5351da177e4SLinus Torvalds return ifa; 5361da177e4SLinus Torvalds } endfor_ifa(in_dev); 5371da177e4SLinus Torvalds return NULL; 5381da177e4SLinus Torvalds } 5391da177e4SLinus Torvalds 540661d2967SThomas Graf static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) 5411da177e4SLinus Torvalds { 5423b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 543dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5441da177e4SLinus Torvalds struct in_device *in_dev; 545dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 5461da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 547dfdd5fd4SThomas Graf int err = -EINVAL; 5481da177e4SLinus Torvalds 5491da177e4SLinus Torvalds ASSERT_RTNL(); 5501da177e4SLinus Torvalds 551dfdd5fd4SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 552dfdd5fd4SThomas Graf if (err < 0) 553dfdd5fd4SThomas Graf goto errout; 554dfdd5fd4SThomas Graf 555dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 5567fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 557dfdd5fd4SThomas Graf if (in_dev == NULL) { 558dfdd5fd4SThomas Graf err = -ENODEV; 559dfdd5fd4SThomas Graf goto errout; 560dfdd5fd4SThomas Graf } 561dfdd5fd4SThomas Graf 5621da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 5631da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 564dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 565a7a628c4SAl Viro ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) 5661da177e4SLinus Torvalds continue; 567dfdd5fd4SThomas Graf 568dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 569dfdd5fd4SThomas Graf continue; 570dfdd5fd4SThomas Graf 571dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 572dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 573a7a628c4SAl Viro !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 574dfdd5fd4SThomas Graf continue; 575dfdd5fd4SThomas Graf 57615e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 5771da177e4SLinus Torvalds return 0; 5781da177e4SLinus Torvalds } 579dfdd5fd4SThomas Graf 580dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 581dfdd5fd4SThomas Graf errout: 582dfdd5fd4SThomas Graf return err; 5831da177e4SLinus Torvalds } 5841da177e4SLinus Torvalds 5855c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 5865c766d64SJiri Pirko 5875c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 5885c766d64SJiri Pirko { 5895c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 5905c766d64SJiri Pirko struct in_ifaddr *ifa; 591c988d1e8SJiri Pirko struct hlist_node *n; 5925c766d64SJiri Pirko int i; 5935c766d64SJiri Pirko 5945c766d64SJiri Pirko now = jiffies; 5955c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 5965c766d64SJiri Pirko 5975c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 598c988d1e8SJiri Pirko bool change_needed = false; 599c988d1e8SJiri Pirko 600c988d1e8SJiri Pirko rcu_read_lock(); 601b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 6025c766d64SJiri Pirko unsigned long age; 6035c766d64SJiri Pirko 6045c766d64SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 6055c766d64SJiri Pirko continue; 6065c766d64SJiri Pirko 6075c766d64SJiri Pirko /* We try to batch several events at once. */ 6085c766d64SJiri Pirko age = (now - ifa->ifa_tstamp + 6095c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 6105c766d64SJiri Pirko 6115c766d64SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 6125c766d64SJiri Pirko age >= ifa->ifa_valid_lft) { 613c988d1e8SJiri Pirko change_needed = true; 614c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft == 615c988d1e8SJiri Pirko INFINITY_LIFE_TIME) { 616c988d1e8SJiri Pirko continue; 617c988d1e8SJiri Pirko } else if (age >= ifa->ifa_preferred_lft) { 618c988d1e8SJiri Pirko if (time_before(ifa->ifa_tstamp + 619c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ, next)) 620c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 621c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ; 622c988d1e8SJiri Pirko 623c988d1e8SJiri Pirko if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) 624c988d1e8SJiri Pirko change_needed = true; 625c988d1e8SJiri Pirko } else if (time_before(ifa->ifa_tstamp + 626c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ, 627c988d1e8SJiri Pirko next)) { 628c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 629c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ; 630c988d1e8SJiri Pirko } 631c988d1e8SJiri Pirko } 632c988d1e8SJiri Pirko rcu_read_unlock(); 633c988d1e8SJiri Pirko if (!change_needed) 634c988d1e8SJiri Pirko continue; 635c988d1e8SJiri Pirko rtnl_lock(); 636c988d1e8SJiri Pirko hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 637c988d1e8SJiri Pirko unsigned long age; 638c988d1e8SJiri Pirko 639c988d1e8SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 640c988d1e8SJiri Pirko continue; 641c988d1e8SJiri Pirko 642c988d1e8SJiri Pirko /* We try to batch several events at once. */ 643c988d1e8SJiri Pirko age = (now - ifa->ifa_tstamp + 644c988d1e8SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 645c988d1e8SJiri Pirko 646c988d1e8SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 647c988d1e8SJiri Pirko age >= ifa->ifa_valid_lft) { 6485c766d64SJiri Pirko struct in_ifaddr **ifap; 6495c766d64SJiri Pirko 6505c766d64SJiri Pirko for (ifap = &ifa->ifa_dev->ifa_list; 651c988d1e8SJiri Pirko *ifap != NULL; ifap = &(*ifap)->ifa_next) { 652c988d1e8SJiri Pirko if (*ifap == ifa) { 6535c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 6545c766d64SJiri Pirko ifap, 1); 655c988d1e8SJiri Pirko break; 6565c766d64SJiri Pirko } 657c988d1e8SJiri Pirko } 658c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft != 659c988d1e8SJiri Pirko INFINITY_LIFE_TIME && 660c988d1e8SJiri Pirko age >= ifa->ifa_preferred_lft && 661c988d1e8SJiri Pirko !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 6625c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 6635c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 6645c766d64SJiri Pirko } 6655c766d64SJiri Pirko } 666c988d1e8SJiri Pirko rtnl_unlock(); 6675c766d64SJiri Pirko } 6685c766d64SJiri Pirko 6695c766d64SJiri Pirko next_sec = round_jiffies_up(next); 6705c766d64SJiri Pirko next_sched = next; 6715c766d64SJiri Pirko 6725c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 6735c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 6745c766d64SJiri Pirko next_sched = next_sec; 6755c766d64SJiri Pirko 6765c766d64SJiri Pirko now = jiffies; 6775c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 6785c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 6795c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 6805c766d64SJiri Pirko 6815c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, next_sched - now); 6825c766d64SJiri Pirko } 6835c766d64SJiri Pirko 6845c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 6855c766d64SJiri Pirko __u32 prefered_lft) 6865c766d64SJiri Pirko { 6875c766d64SJiri Pirko unsigned long timeout; 6885c766d64SJiri Pirko 6895c766d64SJiri Pirko ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 6905c766d64SJiri Pirko 6915c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 6925c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 6935c766d64SJiri Pirko ifa->ifa_valid_lft = timeout; 6945c766d64SJiri Pirko else 6955c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_PERMANENT; 6965c766d64SJiri Pirko 6975c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 6985c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 6995c766d64SJiri Pirko if (timeout == 0) 7005c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7015c766d64SJiri Pirko ifa->ifa_preferred_lft = timeout; 7025c766d64SJiri Pirko } 7035c766d64SJiri Pirko ifa->ifa_tstamp = jiffies; 7045c766d64SJiri Pirko if (!ifa->ifa_cstamp) 7055c766d64SJiri Pirko ifa->ifa_cstamp = ifa->ifa_tstamp; 7065c766d64SJiri Pirko } 7075c766d64SJiri Pirko 7085c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 7095c766d64SJiri Pirko __u32 *pvalid_lft, __u32 *pprefered_lft) 7101da177e4SLinus Torvalds { 7115c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 7125c753978SThomas Graf struct in_ifaddr *ifa; 7135c753978SThomas Graf struct ifaddrmsg *ifm; 7141da177e4SLinus Torvalds struct net_device *dev; 7151da177e4SLinus Torvalds struct in_device *in_dev; 7167b218574SDenis V. Lunev int err; 7171da177e4SLinus Torvalds 7185c753978SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 7195c753978SThomas Graf if (err < 0) 7205c753978SThomas Graf goto errout; 7211da177e4SLinus Torvalds 7225c753978SThomas Graf ifm = nlmsg_data(nlh); 723c4e38f41SEvgeniy Polyakov err = -EINVAL; 7247b218574SDenis V. Lunev if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) 7255c753978SThomas Graf goto errout; 7261da177e4SLinus Torvalds 7274b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 7285c753978SThomas Graf err = -ENODEV; 7297b218574SDenis V. Lunev if (dev == NULL) 7305c753978SThomas Graf goto errout; 7311da177e4SLinus Torvalds 7325c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 7335c753978SThomas Graf err = -ENOBUFS; 7347b218574SDenis V. Lunev if (in_dev == NULL) 7355c753978SThomas Graf goto errout; 73671e27da9SHerbert Xu 7375c753978SThomas Graf ifa = inet_alloc_ifa(); 7387b218574SDenis V. Lunev if (ifa == NULL) 7395c753978SThomas Graf /* 7405c753978SThomas Graf * A potential indev allocation can be left alive, it stays 7415c753978SThomas Graf * assigned to its device and is destroy with it. 7425c753978SThomas Graf */ 7435c753978SThomas Graf goto errout; 7445c753978SThomas Graf 745a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 7465c753978SThomas Graf in_dev_hold(in_dev); 7475c753978SThomas Graf 7485c753978SThomas Graf if (tb[IFA_ADDRESS] == NULL) 7495c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 7505c753978SThomas Graf 751fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 7521da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 7531da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 7541da177e4SLinus Torvalds ifa->ifa_flags = ifm->ifa_flags; 7551da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 7561da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 7575c753978SThomas Graf 758a7a628c4SAl Viro ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); 759a7a628c4SAl Viro ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); 7605c753978SThomas Graf 7615c753978SThomas Graf if (tb[IFA_BROADCAST]) 762a7a628c4SAl Viro ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); 7635c753978SThomas Graf 7645c753978SThomas Graf if (tb[IFA_LABEL]) 7655c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 7661da177e4SLinus Torvalds else 7671da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 7681da177e4SLinus Torvalds 7695c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 7705c766d64SJiri Pirko struct ifa_cacheinfo *ci; 7715c766d64SJiri Pirko 7725c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 7735c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 7745c766d64SJiri Pirko err = -EINVAL; 775446266b0SDaniel Borkmann goto errout_free; 7765c766d64SJiri Pirko } 7775c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 7785c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 7795c766d64SJiri Pirko } 7805c766d64SJiri Pirko 7815c753978SThomas Graf return ifa; 7825c753978SThomas Graf 783446266b0SDaniel Borkmann errout_free: 784446266b0SDaniel Borkmann inet_free_ifa(ifa); 7855c753978SThomas Graf errout: 7865c753978SThomas Graf return ERR_PTR(err); 7875c753978SThomas Graf } 7885c753978SThomas Graf 7895c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 7905c766d64SJiri Pirko { 7915c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 7925c766d64SJiri Pirko struct in_ifaddr *ifa1, **ifap; 7935c766d64SJiri Pirko 7945c766d64SJiri Pirko if (!ifa->ifa_local) 7955c766d64SJiri Pirko return NULL; 7965c766d64SJiri Pirko 7975c766d64SJiri Pirko for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 7985c766d64SJiri Pirko ifap = &ifa1->ifa_next) { 7995c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 8005c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 8015c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 8025c766d64SJiri Pirko return ifa1; 8035c766d64SJiri Pirko } 8045c766d64SJiri Pirko return NULL; 8055c766d64SJiri Pirko } 8065c766d64SJiri Pirko 807661d2967SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) 8085c753978SThomas Graf { 8093b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 8105c753978SThomas Graf struct in_ifaddr *ifa; 8115c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 8125c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 8135c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 8145c753978SThomas Graf 8155c753978SThomas Graf ASSERT_RTNL(); 8165c753978SThomas Graf 8175c766d64SJiri Pirko ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft); 8185c753978SThomas Graf if (IS_ERR(ifa)) 8195c753978SThomas Graf return PTR_ERR(ifa); 8205c753978SThomas Graf 8215c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 8225c766d64SJiri Pirko if (!ifa_existing) { 8235c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 8245c766d64SJiri Pirko * userspace alreay relies on not having to provide this. 8255c766d64SJiri Pirko */ 8265c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 82715e47304SEric W. Biederman return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); 8285c766d64SJiri Pirko } else { 8295c766d64SJiri Pirko inet_free_ifa(ifa); 8305c766d64SJiri Pirko 8315c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 8325c766d64SJiri Pirko !(nlh->nlmsg_flags & NLM_F_REPLACE)) 8335c766d64SJiri Pirko return -EEXIST; 83434e2ed34SJiri Pirko ifa = ifa_existing; 83534e2ed34SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 83605a324b9SJiri Pirko cancel_delayed_work(&check_lifetime_work); 83705a324b9SJiri Pirko schedule_delayed_work(&check_lifetime_work, 0); 83834e2ed34SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 83934e2ed34SJiri Pirko blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 8405c766d64SJiri Pirko } 8415c766d64SJiri Pirko return 0; 8421da177e4SLinus Torvalds } 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds /* 8451da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 8461da177e4SLinus Torvalds */ 8471da177e4SLinus Torvalds 84840384999SEric Dumazet static int inet_abc_len(__be32 addr) 8491da177e4SLinus Torvalds { 8501da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 8511da177e4SLinus Torvalds 852f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 8531da177e4SLinus Torvalds rc = 0; 8541da177e4SLinus Torvalds else { 855714e85beSAl Viro __u32 haddr = ntohl(addr); 8561da177e4SLinus Torvalds 857714e85beSAl Viro if (IN_CLASSA(haddr)) 8581da177e4SLinus Torvalds rc = 8; 859714e85beSAl Viro else if (IN_CLASSB(haddr)) 8601da177e4SLinus Torvalds rc = 16; 861714e85beSAl Viro else if (IN_CLASSC(haddr)) 8621da177e4SLinus Torvalds rc = 24; 8631da177e4SLinus Torvalds } 8641da177e4SLinus Torvalds 8651da177e4SLinus Torvalds return rc; 8661da177e4SLinus Torvalds } 8671da177e4SLinus Torvalds 8681da177e4SLinus Torvalds 869e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) 8701da177e4SLinus Torvalds { 8711da177e4SLinus Torvalds struct ifreq ifr; 8721da177e4SLinus Torvalds struct sockaddr_in sin_orig; 8731da177e4SLinus Torvalds struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 8741da177e4SLinus Torvalds struct in_device *in_dev; 8751da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 8761da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 8771da177e4SLinus Torvalds struct net_device *dev; 8781da177e4SLinus Torvalds char *colon; 8791da177e4SLinus Torvalds int ret = -EFAULT; 8801da177e4SLinus Torvalds int tryaddrmatch = 0; 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds /* 8831da177e4SLinus Torvalds * Fetch the caller's info block into kernel space 8841da177e4SLinus Torvalds */ 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 8871da177e4SLinus Torvalds goto out; 8881da177e4SLinus Torvalds ifr.ifr_name[IFNAMSIZ - 1] = 0; 8891da177e4SLinus Torvalds 8901da177e4SLinus Torvalds /* save original address for comparison */ 8911da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 8921da177e4SLinus Torvalds 8931da177e4SLinus Torvalds colon = strchr(ifr.ifr_name, ':'); 8941da177e4SLinus Torvalds if (colon) 8951da177e4SLinus Torvalds *colon = 0; 8961da177e4SLinus Torvalds 897e5b13cb1SDenis V. Lunev dev_load(net, ifr.ifr_name); 8981da177e4SLinus Torvalds 8991da177e4SLinus Torvalds switch (cmd) { 9001da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9011da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9021da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9031da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 9041da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 9051da177e4SLinus Torvalds so that we do not impose a lock. 9061da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 9071da177e4SLinus Torvalds */ 9081da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 9091da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 9101da177e4SLinus Torvalds sin->sin_family = AF_INET; 9111da177e4SLinus Torvalds break; 9121da177e4SLinus Torvalds 9131da177e4SLinus Torvalds case SIOCSIFFLAGS: 914bf5b30b8SZhao Hongjiang ret = -EPERM; 91552e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 9161da177e4SLinus Torvalds goto out; 9171da177e4SLinus Torvalds break; 9181da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 9191da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 9201da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 9211da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 922bf5b30b8SZhao Hongjiang ret = -EPERM; 92352e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 9241da177e4SLinus Torvalds goto out; 9251da177e4SLinus Torvalds ret = -EINVAL; 9261da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 9271da177e4SLinus Torvalds goto out; 9281da177e4SLinus Torvalds break; 9291da177e4SLinus Torvalds default: 9301da177e4SLinus Torvalds ret = -EINVAL; 9311da177e4SLinus Torvalds goto out; 9321da177e4SLinus Torvalds } 9331da177e4SLinus Torvalds 9341da177e4SLinus Torvalds rtnl_lock(); 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds ret = -ENODEV; 9379f9354b9SEric Dumazet dev = __dev_get_by_name(net, ifr.ifr_name); 9389f9354b9SEric Dumazet if (!dev) 9391da177e4SLinus Torvalds goto done; 9401da177e4SLinus Torvalds 9411da177e4SLinus Torvalds if (colon) 9421da177e4SLinus Torvalds *colon = ':'; 9431da177e4SLinus Torvalds 9449f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 9459f9354b9SEric Dumazet if (in_dev) { 9461da177e4SLinus Torvalds if (tryaddrmatch) { 9471da177e4SLinus Torvalds /* Matthias Andree */ 9481da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 9491da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 9501da177e4SLinus Torvalds and only if the original address family was AF_INET. 9511da177e4SLinus Torvalds This is checked above. */ 9521da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 9531da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 9541da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label) && 9551da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 9566c91afe1SDavid S. Miller ifa->ifa_local) { 9571da177e4SLinus Torvalds break; /* found */ 9581da177e4SLinus Torvalds } 9591da177e4SLinus Torvalds } 9601da177e4SLinus Torvalds } 9611da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 9621da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 9631da177e4SLinus Torvalds comparing just the label */ 9641da177e4SLinus Torvalds if (!ifa) { 9651da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 9661da177e4SLinus Torvalds ifap = &ifa->ifa_next) 9671da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label)) 9681da177e4SLinus Torvalds break; 9691da177e4SLinus Torvalds } 9701da177e4SLinus Torvalds } 9711da177e4SLinus Torvalds 9721da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 9731da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 9741da177e4SLinus Torvalds goto done; 9751da177e4SLinus Torvalds 9761da177e4SLinus Torvalds switch (cmd) { 9771da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9781da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 9791da177e4SLinus Torvalds goto rarok; 9801da177e4SLinus Torvalds 9811da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9821da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 9831da177e4SLinus Torvalds goto rarok; 9841da177e4SLinus Torvalds 9851da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9861da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 9871da177e4SLinus Torvalds goto rarok; 9881da177e4SLinus Torvalds 9891da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 9901da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 9911da177e4SLinus Torvalds goto rarok; 9921da177e4SLinus Torvalds 9931da177e4SLinus Torvalds case SIOCSIFFLAGS: 9941da177e4SLinus Torvalds if (colon) { 9951da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 9961da177e4SLinus Torvalds if (!ifa) 9971da177e4SLinus Torvalds break; 9981da177e4SLinus Torvalds ret = 0; 9991da177e4SLinus Torvalds if (!(ifr.ifr_flags & IFF_UP)) 10001da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 10011da177e4SLinus Torvalds break; 10021da177e4SLinus Torvalds } 10031da177e4SLinus Torvalds ret = dev_change_flags(dev, ifr.ifr_flags); 10041da177e4SLinus Torvalds break; 10051da177e4SLinus Torvalds 10061da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10071da177e4SLinus Torvalds ret = -EINVAL; 10081da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 10091da177e4SLinus Torvalds break; 10101da177e4SLinus Torvalds 10111da177e4SLinus Torvalds if (!ifa) { 10121da177e4SLinus Torvalds ret = -ENOBUFS; 10139f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 10149f9354b9SEric Dumazet if (!ifa) 10151da177e4SLinus Torvalds break; 1016c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 10171da177e4SLinus Torvalds if (colon) 10181da177e4SLinus Torvalds memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); 10191da177e4SLinus Torvalds else 10201da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 10211da177e4SLinus Torvalds } else { 10221da177e4SLinus Torvalds ret = 0; 10231da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 10241da177e4SLinus Torvalds break; 10251da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10261da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 1027148f9729SBjorn Mork ifa->ifa_scope = 0; 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds 10301da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 10311da177e4SLinus Torvalds 10321da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 10331da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 10341da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 10351da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 10361da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 10371da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 10381da177e4SLinus Torvalds ~ifa->ifa_mask; 10391da177e4SLinus Torvalds } else { 10401da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 10411da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 10421da177e4SLinus Torvalds } 10435c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 10441da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 10451da177e4SLinus Torvalds break; 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10481da177e4SLinus Torvalds ret = 0; 10491da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 10501da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10511da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 10521da177e4SLinus Torvalds inet_insert_ifa(ifa); 10531da177e4SLinus Torvalds } 10541da177e4SLinus Torvalds break; 10551da177e4SLinus Torvalds 10561da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 10571da177e4SLinus Torvalds ret = 0; 10581da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 10591da177e4SLinus Torvalds break; 10601da177e4SLinus Torvalds ret = -EINVAL; 10611da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 10621da177e4SLinus Torvalds break; 10631da177e4SLinus Torvalds ret = 0; 10641da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10651da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 10661da177e4SLinus Torvalds inet_insert_ifa(ifa); 10671da177e4SLinus Torvalds break; 10681da177e4SLinus Torvalds 10691da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 10701da177e4SLinus Torvalds 10711da177e4SLinus Torvalds /* 10721da177e4SLinus Torvalds * The mask we set must be legal. 10731da177e4SLinus Torvalds */ 10741da177e4SLinus Torvalds ret = -EINVAL; 10751da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 10761da177e4SLinus Torvalds break; 10771da177e4SLinus Torvalds ret = 0; 10781da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1079a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 10801da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10811da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 10821da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds /* See if current broadcast address matches 10851da177e4SLinus Torvalds * with current netmask, then recalculate 10861da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 10871da177e4SLinus Torvalds * funny address, so don't touch it since 10881da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 10891da177e4SLinus Torvalds */ 10901da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 10911da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 10921da177e4SLinus Torvalds (ifa->ifa_broadcast == 1093dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 10941da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 10951da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds inet_insert_ifa(ifa); 10981da177e4SLinus Torvalds } 10991da177e4SLinus Torvalds break; 11001da177e4SLinus Torvalds } 11011da177e4SLinus Torvalds done: 11021da177e4SLinus Torvalds rtnl_unlock(); 11031da177e4SLinus Torvalds out: 11041da177e4SLinus Torvalds return ret; 11051da177e4SLinus Torvalds rarok: 11061da177e4SLinus Torvalds rtnl_unlock(); 11071da177e4SLinus Torvalds ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; 11081da177e4SLinus Torvalds goto out; 11091da177e4SLinus Torvalds } 11101da177e4SLinus Torvalds 11111da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 11121da177e4SLinus Torvalds { 1113e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 11141da177e4SLinus Torvalds struct in_ifaddr *ifa; 11151da177e4SLinus Torvalds struct ifreq ifr; 11161da177e4SLinus Torvalds int done = 0; 11171da177e4SLinus Torvalds 11189f9354b9SEric Dumazet if (!in_dev) 11191da177e4SLinus Torvalds goto out; 11201da177e4SLinus Torvalds 11219f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 11221da177e4SLinus Torvalds if (!buf) { 11231da177e4SLinus Torvalds done += sizeof(ifr); 11241da177e4SLinus Torvalds continue; 11251da177e4SLinus Torvalds } 11261da177e4SLinus Torvalds if (len < (int) sizeof(ifr)) 11271da177e4SLinus Torvalds break; 11281da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 11291da177e4SLinus Torvalds if (ifa->ifa_label) 11301da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 11311da177e4SLinus Torvalds else 11321da177e4SLinus Torvalds strcpy(ifr.ifr_name, dev->name); 11331da177e4SLinus Torvalds 11341da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 11351da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 11361da177e4SLinus Torvalds ifa->ifa_local; 11371da177e4SLinus Torvalds 11381da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 11391da177e4SLinus Torvalds done = -EFAULT; 11401da177e4SLinus Torvalds break; 11411da177e4SLinus Torvalds } 11421da177e4SLinus Torvalds buf += sizeof(struct ifreq); 11431da177e4SLinus Torvalds len -= sizeof(struct ifreq); 11441da177e4SLinus Torvalds done += sizeof(struct ifreq); 11451da177e4SLinus Torvalds } 11461da177e4SLinus Torvalds out: 11471da177e4SLinus Torvalds return done; 11481da177e4SLinus Torvalds } 11491da177e4SLinus Torvalds 1150a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 11511da177e4SLinus Torvalds { 1152a61ced5dSAl Viro __be32 addr = 0; 11531da177e4SLinus Torvalds struct in_device *in_dev; 1154c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 11551da177e4SLinus Torvalds 11561da177e4SLinus Torvalds rcu_read_lock(); 1157e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 11581da177e4SLinus Torvalds if (!in_dev) 11591da177e4SLinus Torvalds goto no_in_dev; 11601da177e4SLinus Torvalds 11611da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11621da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 11631da177e4SLinus Torvalds continue; 11641da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 11651da177e4SLinus Torvalds addr = ifa->ifa_local; 11661da177e4SLinus Torvalds break; 11671da177e4SLinus Torvalds } 11681da177e4SLinus Torvalds if (!addr) 11691da177e4SLinus Torvalds addr = ifa->ifa_local; 11701da177e4SLinus Torvalds } endfor_ifa(in_dev); 11711da177e4SLinus Torvalds 11721da177e4SLinus Torvalds if (addr) 1173c6d14c84SEric Dumazet goto out_unlock; 11749f9354b9SEric Dumazet no_in_dev: 11751da177e4SLinus Torvalds 11761da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 11771da177e4SLinus Torvalds in this case. It is importnat that lo is the first interface 11781da177e4SLinus Torvalds in dev_base list. 11791da177e4SLinus Torvalds */ 1180c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 11819f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 11829f9354b9SEric Dumazet if (!in_dev) 11831da177e4SLinus Torvalds continue; 11841da177e4SLinus Torvalds 11851da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11861da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 11871da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 11881da177e4SLinus Torvalds addr = ifa->ifa_local; 1189c6d14c84SEric Dumazet goto out_unlock; 11901da177e4SLinus Torvalds } 11911da177e4SLinus Torvalds } endfor_ifa(in_dev); 11921da177e4SLinus Torvalds } 1193c6d14c84SEric Dumazet out_unlock: 11941da177e4SLinus Torvalds rcu_read_unlock(); 11951da177e4SLinus Torvalds return addr; 11961da177e4SLinus Torvalds } 11979f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 11981da177e4SLinus Torvalds 119960cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 120060cad5daSAl Viro __be32 local, int scope) 12011da177e4SLinus Torvalds { 12021da177e4SLinus Torvalds int same = 0; 1203a144ea4bSAl Viro __be32 addr = 0; 12041da177e4SLinus Torvalds 12051da177e4SLinus Torvalds for_ifa(in_dev) { 12061da177e4SLinus Torvalds if (!addr && 12071da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 12081da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 12091da177e4SLinus Torvalds addr = ifa->ifa_local; 12101da177e4SLinus Torvalds if (same) 12111da177e4SLinus Torvalds break; 12121da177e4SLinus Torvalds } 12131da177e4SLinus Torvalds if (!same) { 12141da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 12151da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 12161da177e4SLinus Torvalds if (same && addr) { 12171da177e4SLinus Torvalds if (local || !dst) 12181da177e4SLinus Torvalds break; 12191da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 12201da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 12211da177e4SLinus Torvalds break; 12221da177e4SLinus Torvalds /* No, then can we use new local src? */ 12231da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 12241da177e4SLinus Torvalds addr = ifa->ifa_local; 12251da177e4SLinus Torvalds break; 12261da177e4SLinus Torvalds } 12271da177e4SLinus Torvalds /* search for large dst subnet for addr */ 12281da177e4SLinus Torvalds same = 0; 12291da177e4SLinus Torvalds } 12301da177e4SLinus Torvalds } 12311da177e4SLinus Torvalds } endfor_ifa(in_dev); 12321da177e4SLinus Torvalds 12331da177e4SLinus Torvalds return same ? addr : 0; 12341da177e4SLinus Torvalds } 12351da177e4SLinus Torvalds 12361da177e4SLinus Torvalds /* 12371da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 12389bd85e32SDenis V. Lunev * - in_dev: only on this interface, 0=any interface 12391da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 12401da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 12411da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 12421da177e4SLinus Torvalds */ 12439bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev, 12449bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 12451da177e4SLinus Torvalds { 124660cad5daSAl Viro __be32 addr = 0; 12479bd85e32SDenis V. Lunev struct net_device *dev; 124839a6d063SDenis V. Lunev struct net *net; 12491da177e4SLinus Torvalds 125039a6d063SDenis V. Lunev if (scope != RT_SCOPE_LINK) 12519bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 12521da177e4SLinus Torvalds 1253c346dca1SYOSHIFUJI Hideaki net = dev_net(in_dev->dev); 12541da177e4SLinus Torvalds rcu_read_lock(); 1255c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 12569f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12579f9354b9SEric Dumazet if (in_dev) { 12581da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 12591da177e4SLinus Torvalds if (addr) 12601da177e4SLinus Torvalds break; 12611da177e4SLinus Torvalds } 12621da177e4SLinus Torvalds } 12631da177e4SLinus Torvalds rcu_read_unlock(); 12641da177e4SLinus Torvalds 12651da177e4SLinus Torvalds return addr; 12661da177e4SLinus Torvalds } 1267eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 12681da177e4SLinus Torvalds 12691da177e4SLinus Torvalds /* 12701da177e4SLinus Torvalds * Device notifier 12711da177e4SLinus Torvalds */ 12721da177e4SLinus Torvalds 12731da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 12741da177e4SLinus Torvalds { 1275e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 12761da177e4SLinus Torvalds } 12779f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 12801da177e4SLinus Torvalds { 1281e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 12821da177e4SLinus Torvalds } 12839f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 12841da177e4SLinus Torvalds 12859f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 12869f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 12871da177e4SLinus Torvalds */ 12881da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 12891da177e4SLinus Torvalds { 12901da177e4SLinus Torvalds struct in_ifaddr *ifa; 12911da177e4SLinus Torvalds int named = 0; 12921da177e4SLinus Torvalds 12931da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 12941da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 12951da177e4SLinus Torvalds 12961da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 12971da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 12981da177e4SLinus Torvalds if (named++ == 0) 1299573bf470SThomas Graf goto skip; 130044344b2aSMark McLoughlin dot = strchr(old, ':'); 13011da177e4SLinus Torvalds if (dot == NULL) { 13021da177e4SLinus Torvalds sprintf(old, ":%d", named); 13031da177e4SLinus Torvalds dot = old; 13041da177e4SLinus Torvalds } 13059f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 13061da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 13079f9354b9SEric Dumazet else 13081da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1309573bf470SThomas Graf skip: 1310573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 13111da177e4SLinus Torvalds } 13121da177e4SLinus Torvalds } 13131da177e4SLinus Torvalds 131440384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu) 131506770843SBreno Leitao { 131606770843SBreno Leitao return mtu >= 68; 131706770843SBreno Leitao } 131806770843SBreno Leitao 1319d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1320d11327adSIan Campbell struct in_device *in_dev) 1321d11327adSIan Campbell 1322d11327adSIan Campbell { 1323b76d0789SZoltan Kiss struct in_ifaddr *ifa; 1324d11327adSIan Campbell 1325b76d0789SZoltan Kiss for (ifa = in_dev->ifa_list; ifa; 1326b76d0789SZoltan Kiss ifa = ifa->ifa_next) { 1327d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 13286c91afe1SDavid S. Miller ifa->ifa_local, dev, 13296c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1330d11327adSIan Campbell dev->dev_addr, NULL); 1331d11327adSIan Campbell } 1332b76d0789SZoltan Kiss } 1333d11327adSIan Campbell 13341da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 13351da177e4SLinus Torvalds 13361da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 13371da177e4SLinus Torvalds void *ptr) 13381da177e4SLinus Torvalds { 1339351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1340748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13411da177e4SLinus Torvalds 13421da177e4SLinus Torvalds ASSERT_RTNL(); 13431da177e4SLinus Torvalds 13441da177e4SLinus Torvalds if (!in_dev) { 13458030f544SHerbert Xu if (event == NETDEV_REGISTER) { 13461da177e4SLinus Torvalds in_dev = inetdev_init(dev); 13478d76527eSHerbert Xu if (!in_dev) 1348b217d616SHerbert Xu return notifier_from_errno(-ENOMEM); 13490cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 135042f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 135142f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 13521da177e4SLinus Torvalds } 135306770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 135406770843SBreno Leitao /* Re-enabling IP */ 135506770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 135606770843SBreno Leitao in_dev = inetdev_init(dev); 13578030f544SHerbert Xu } 13581da177e4SLinus Torvalds goto out; 13591da177e4SLinus Torvalds } 13601da177e4SLinus Torvalds 13611da177e4SLinus Torvalds switch (event) { 13621da177e4SLinus Torvalds case NETDEV_REGISTER: 136391df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1364a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 13651da177e4SLinus Torvalds break; 13661da177e4SLinus Torvalds case NETDEV_UP: 136706770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 13681da177e4SLinus Torvalds break; 13690cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 13709f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 13719f9354b9SEric Dumazet 13729f9354b9SEric Dumazet if (ifa) { 1373fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 13741da177e4SLinus Torvalds ifa->ifa_local = 13751da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 13761da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 13771da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 13781da177e4SLinus Torvalds in_dev_hold(in_dev); 13791da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 13801da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 13811da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 13825c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 13835c766d64SJiri Pirko INFINITY_LIFE_TIME); 13841da177e4SLinus Torvalds inet_insert_ifa(ifa); 13851da177e4SLinus Torvalds } 13861da177e4SLinus Torvalds } 13871da177e4SLinus Torvalds ip_mc_up(in_dev); 1388eefef1cfSStephen Hemminger /* fall through */ 1389eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1390d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1391d11327adSIan Campbell break; 1392d11327adSIan Campbell /* fall through */ 1393d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1394a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1395d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 13961da177e4SLinus Torvalds break; 13971da177e4SLinus Torvalds case NETDEV_DOWN: 13981da177e4SLinus Torvalds ip_mc_down(in_dev); 13991da177e4SLinus Torvalds break; 140093d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 140175c78500SMoni Shoua ip_mc_unmap(in_dev); 140275c78500SMoni Shoua break; 140393d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 140475c78500SMoni Shoua ip_mc_remap(in_dev); 140575c78500SMoni Shoua break; 14061da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 140706770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 14081da177e4SLinus Torvalds break; 140906770843SBreno Leitao /* disable IP when MTU is not enough */ 14101da177e4SLinus Torvalds case NETDEV_UNREGISTER: 14111da177e4SLinus Torvalds inetdev_destroy(in_dev); 14121da177e4SLinus Torvalds break; 14131da177e4SLinus Torvalds case NETDEV_CHANGENAME: 14141da177e4SLinus Torvalds /* Do not notify about label change, this event is 14151da177e4SLinus Torvalds * not interesting to applications using netlink. 14161da177e4SLinus Torvalds */ 14171da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 14181da177e4SLinus Torvalds 141951602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 142066f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 14211da177e4SLinus Torvalds break; 14221da177e4SLinus Torvalds } 14231da177e4SLinus Torvalds out: 14241da177e4SLinus Torvalds return NOTIFY_DONE; 14251da177e4SLinus Torvalds } 14261da177e4SLinus Torvalds 14271da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 14281da177e4SLinus Torvalds .notifier_call = inetdev_event, 14291da177e4SLinus Torvalds }; 14301da177e4SLinus Torvalds 143140384999SEric Dumazet static size_t inet_nlmsg_size(void) 1432339bf98fSThomas Graf { 1433339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1434339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1435339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1436339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1437339bf98fSThomas Graf + nla_total_size(IFNAMSIZ); /* IFA_LABEL */ 1438339bf98fSThomas Graf } 1439339bf98fSThomas Graf 14405c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 14415c766d64SJiri Pirko { 14425c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 14435c766d64SJiri Pirko } 14445c766d64SJiri Pirko 14455c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 14465c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 14475c766d64SJiri Pirko { 14485c766d64SJiri Pirko struct ifa_cacheinfo ci; 14495c766d64SJiri Pirko 14505c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 14515c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 14525c766d64SJiri Pirko ci.ifa_prefered = preferred; 14535c766d64SJiri Pirko ci.ifa_valid = valid; 14545c766d64SJiri Pirko 14555c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 14565c766d64SJiri Pirko } 14575c766d64SJiri Pirko 14581da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 145915e47304SEric W. Biederman u32 portid, u32 seq, int event, unsigned int flags) 14601da177e4SLinus Torvalds { 14611da177e4SLinus Torvalds struct ifaddrmsg *ifm; 14621da177e4SLinus Torvalds struct nlmsghdr *nlh; 14635c766d64SJiri Pirko u32 preferred, valid; 14641da177e4SLinus Torvalds 146515e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); 146647f68512SThomas Graf if (nlh == NULL) 146726932566SPatrick McHardy return -EMSGSIZE; 146847f68512SThomas Graf 146947f68512SThomas Graf ifm = nlmsg_data(nlh); 14701da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 14711da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 14725c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 14731da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 14741da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 14751da177e4SLinus Torvalds 14765c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 14775c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 14785c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 14795c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 14805c766d64SJiri Pirko long tval = (jiffies - ifa->ifa_tstamp) / HZ; 14815c766d64SJiri Pirko 14825c766d64SJiri Pirko if (preferred > tval) 14835c766d64SJiri Pirko preferred -= tval; 14845c766d64SJiri Pirko else 14855c766d64SJiri Pirko preferred = 0; 14865c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 14875c766d64SJiri Pirko if (valid > tval) 14885c766d64SJiri Pirko valid -= tval; 14895c766d64SJiri Pirko else 14905c766d64SJiri Pirko valid = 0; 14915c766d64SJiri Pirko } 14925c766d64SJiri Pirko } 14935c766d64SJiri Pirko } else { 14945c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 14955c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 14965c766d64SJiri Pirko } 1497f3756b79SDavid S. Miller if ((ifa->ifa_address && 1498f3756b79SDavid S. Miller nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) || 1499f3756b79SDavid S. Miller (ifa->ifa_local && 1500f3756b79SDavid S. Miller nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) || 1501f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1502f3756b79SDavid S. Miller nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1503f3756b79SDavid S. Miller (ifa->ifa_label[0] && 15045c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 15055c766d64SJiri Pirko put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 15065c766d64SJiri Pirko preferred, valid)) 1507f3756b79SDavid S. Miller goto nla_put_failure; 150847f68512SThomas Graf 150947f68512SThomas Graf return nlmsg_end(skb, nlh); 151047f68512SThomas Graf 151147f68512SThomas Graf nla_put_failure: 151226932566SPatrick McHardy nlmsg_cancel(skb, nlh); 151326932566SPatrick McHardy return -EMSGSIZE; 15141da177e4SLinus Torvalds } 15151da177e4SLinus Torvalds 15161da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 15171da177e4SLinus Torvalds { 15183b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1519eec4df98SEric Dumazet int h, s_h; 1520eec4df98SEric Dumazet int idx, s_idx; 1521eec4df98SEric Dumazet int ip_idx, s_ip_idx; 15221da177e4SLinus Torvalds struct net_device *dev; 15231da177e4SLinus Torvalds struct in_device *in_dev; 15241da177e4SLinus Torvalds struct in_ifaddr *ifa; 1525eec4df98SEric Dumazet struct hlist_head *head; 15261da177e4SLinus Torvalds 1527eec4df98SEric Dumazet s_h = cb->args[0]; 1528eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1529eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1530eec4df98SEric Dumazet 1531eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 15327562f876SPavel Emelianov idx = 0; 1533eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1534eec4df98SEric Dumazet rcu_read_lock(); 15350465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 15360465277fSNicolas Dichtel net->dev_base_seq; 1537b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 15381da177e4SLinus Torvalds if (idx < s_idx) 15397562f876SPavel Emelianov goto cont; 15404b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 15411da177e4SLinus Torvalds s_ip_idx = 0; 1542eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 15439f9354b9SEric Dumazet if (!in_dev) 15447562f876SPavel Emelianov goto cont; 15451da177e4SLinus Torvalds 15461da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 15471da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 15481da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1549596e4150SStephen Hemminger continue; 1550eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 155115e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 15521da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1553eec4df98SEric Dumazet RTM_NEWADDR, NLM_F_MULTI) <= 0) { 1554eec4df98SEric Dumazet rcu_read_unlock(); 15551da177e4SLinus Torvalds goto done; 15561da177e4SLinus Torvalds } 15570465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1558eec4df98SEric Dumazet } 15597562f876SPavel Emelianov cont: 15607562f876SPavel Emelianov idx++; 15611da177e4SLinus Torvalds } 1562eec4df98SEric Dumazet rcu_read_unlock(); 1563eec4df98SEric Dumazet } 15641da177e4SLinus Torvalds 15651da177e4SLinus Torvalds done: 1566eec4df98SEric Dumazet cb->args[0] = h; 1567eec4df98SEric Dumazet cb->args[1] = idx; 1568eec4df98SEric Dumazet cb->args[2] = ip_idx; 15691da177e4SLinus Torvalds 15701da177e4SLinus Torvalds return skb->len; 15711da177e4SLinus Torvalds } 15721da177e4SLinus Torvalds 1573d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 157415e47304SEric W. Biederman u32 portid) 15751da177e4SLinus Torvalds { 157647f68512SThomas Graf struct sk_buff *skb; 1577d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1578d6062cbbSThomas Graf int err = -ENOBUFS; 15794b8aa9abSDenis V. Lunev struct net *net; 15801da177e4SLinus Torvalds 1581c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1582339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 158347f68512SThomas Graf if (skb == NULL) 1584d6062cbbSThomas Graf goto errout; 1585d6062cbbSThomas Graf 158615e47304SEric W. Biederman err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0); 158726932566SPatrick McHardy if (err < 0) { 158826932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 158926932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 159026932566SPatrick McHardy kfree_skb(skb); 159126932566SPatrick McHardy goto errout; 159226932566SPatrick McHardy } 159315e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 15941ce85fe4SPablo Neira Ayuso return; 1595d6062cbbSThomas Graf errout: 1596d6062cbbSThomas Graf if (err < 0) 15974b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 15981da177e4SLinus Torvalds } 15991da177e4SLinus Torvalds 16009f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 16019f0f7272SThomas Graf { 16021fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16039f0f7272SThomas Graf 16049f0f7272SThomas Graf if (!in_dev) 16059f0f7272SThomas Graf return 0; 16069f0f7272SThomas Graf 16079f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 16089f0f7272SThomas Graf } 16099f0f7272SThomas Graf 16109f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 16119f0f7272SThomas Graf { 16121fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16139f0f7272SThomas Graf struct nlattr *nla; 16149f0f7272SThomas Graf int i; 16159f0f7272SThomas Graf 16169f0f7272SThomas Graf if (!in_dev) 16179f0f7272SThomas Graf return -ENODATA; 16189f0f7272SThomas Graf 16199f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 16209f0f7272SThomas Graf if (nla == NULL) 16219f0f7272SThomas Graf return -EMSGSIZE; 16229f0f7272SThomas Graf 16239f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 16249f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 16259f0f7272SThomas Graf 16269f0f7272SThomas Graf return 0; 16279f0f7272SThomas Graf } 16289f0f7272SThomas Graf 16299f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 16309f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 16319f0f7272SThomas Graf }; 16329f0f7272SThomas Graf 1633cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1634cf7afbfeSThomas Graf const struct nlattr *nla) 16359f0f7272SThomas Graf { 16369f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 16379f0f7272SThomas Graf int err, rem; 16389f0f7272SThomas Graf 1639f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1640cf7afbfeSThomas Graf return -EAFNOSUPPORT; 16419f0f7272SThomas Graf 16429f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 16439f0f7272SThomas Graf if (err < 0) 16449f0f7272SThomas Graf return err; 16459f0f7272SThomas Graf 16469f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16479f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 16489f0f7272SThomas Graf int cfgid = nla_type(a); 16499f0f7272SThomas Graf 16509f0f7272SThomas Graf if (nla_len(a) < 4) 16519f0f7272SThomas Graf return -EINVAL; 16529f0f7272SThomas Graf 16539f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 16549f0f7272SThomas Graf return -EINVAL; 16559f0f7272SThomas Graf } 16569f0f7272SThomas Graf } 16579f0f7272SThomas Graf 1658cf7afbfeSThomas Graf return 0; 1659cf7afbfeSThomas Graf } 1660cf7afbfeSThomas Graf 1661cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1662cf7afbfeSThomas Graf { 1663f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1664cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1665cf7afbfeSThomas Graf int rem; 1666cf7afbfeSThomas Graf 1667cf7afbfeSThomas Graf if (!in_dev) 1668cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1669cf7afbfeSThomas Graf 1670cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1671cf7afbfeSThomas Graf BUG(); 1672cf7afbfeSThomas Graf 16739f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16749f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 16759f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 16769f0f7272SThomas Graf } 16779f0f7272SThomas Graf 16789f0f7272SThomas Graf return 0; 16799f0f7272SThomas Graf } 16809f0f7272SThomas Graf 1681edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 1682edc9e748SNicolas Dichtel { 1683edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 1684edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 1685edc9e748SNicolas Dichtel 16869e551110SNicolas Dichtel /* type -1 is used for ALL */ 16879e551110SNicolas Dichtel if (type == -1 || type == NETCONFA_FORWARDING) 1688edc9e748SNicolas Dichtel size += nla_total_size(4); 1689cc535dfbSNicolas Dichtel if (type == -1 || type == NETCONFA_RP_FILTER) 1690cc535dfbSNicolas Dichtel size += nla_total_size(4); 1691d67b8c61SNicolas Dichtel if (type == -1 || type == NETCONFA_MC_FORWARDING) 1692d67b8c61SNicolas Dichtel size += nla_total_size(4); 1693edc9e748SNicolas Dichtel 1694edc9e748SNicolas Dichtel return size; 1695edc9e748SNicolas Dichtel } 1696edc9e748SNicolas Dichtel 1697edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 1698edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 1699edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 1700edc9e748SNicolas Dichtel int type) 1701edc9e748SNicolas Dichtel { 1702edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 1703edc9e748SNicolas Dichtel struct netconfmsg *ncm; 1704edc9e748SNicolas Dichtel 1705edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 1706edc9e748SNicolas Dichtel flags); 1707edc9e748SNicolas Dichtel if (nlh == NULL) 1708edc9e748SNicolas Dichtel return -EMSGSIZE; 1709edc9e748SNicolas Dichtel 1710edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 1711edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 1712edc9e748SNicolas Dichtel 1713edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 1714edc9e748SNicolas Dichtel goto nla_put_failure; 1715edc9e748SNicolas Dichtel 17169e551110SNicolas Dichtel /* type -1 is used for ALL */ 17179e551110SNicolas Dichtel if ((type == -1 || type == NETCONFA_FORWARDING) && 1718edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 1719edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 1720edc9e748SNicolas Dichtel goto nla_put_failure; 1721cc535dfbSNicolas Dichtel if ((type == -1 || type == NETCONFA_RP_FILTER) && 1722cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 1723cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 1724cc535dfbSNicolas Dichtel goto nla_put_failure; 1725d67b8c61SNicolas Dichtel if ((type == -1 || type == NETCONFA_MC_FORWARDING) && 1726d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 1727d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 1728d67b8c61SNicolas Dichtel goto nla_put_failure; 1729edc9e748SNicolas Dichtel 1730edc9e748SNicolas Dichtel return nlmsg_end(skb, nlh); 1731edc9e748SNicolas Dichtel 1732edc9e748SNicolas Dichtel nla_put_failure: 1733edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 1734edc9e748SNicolas Dichtel return -EMSGSIZE; 1735edc9e748SNicolas Dichtel } 1736edc9e748SNicolas Dichtel 1737d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, 1738edc9e748SNicolas Dichtel struct ipv4_devconf *devconf) 1739edc9e748SNicolas Dichtel { 1740edc9e748SNicolas Dichtel struct sk_buff *skb; 1741edc9e748SNicolas Dichtel int err = -ENOBUFS; 1742edc9e748SNicolas Dichtel 1743edc9e748SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC); 1744edc9e748SNicolas Dichtel if (skb == NULL) 1745edc9e748SNicolas Dichtel goto errout; 1746edc9e748SNicolas Dichtel 1747edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 1748edc9e748SNicolas Dichtel RTM_NEWNETCONF, 0, type); 1749edc9e748SNicolas Dichtel if (err < 0) { 1750edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 1751edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 1752edc9e748SNicolas Dichtel kfree_skb(skb); 1753edc9e748SNicolas Dichtel goto errout; 1754edc9e748SNicolas Dichtel } 1755edc9e748SNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC); 1756edc9e748SNicolas Dichtel return; 1757edc9e748SNicolas Dichtel errout: 1758edc9e748SNicolas Dichtel if (err < 0) 1759edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 1760edc9e748SNicolas Dichtel } 1761edc9e748SNicolas Dichtel 17629e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 17639e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 17649e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 1765cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 17669e551110SNicolas Dichtel }; 17679e551110SNicolas Dichtel 17689e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 1769661d2967SThomas Graf struct nlmsghdr *nlh) 17709e551110SNicolas Dichtel { 17719e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 17729e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 17739e551110SNicolas Dichtel struct netconfmsg *ncm; 17749e551110SNicolas Dichtel struct sk_buff *skb; 17759e551110SNicolas Dichtel struct ipv4_devconf *devconf; 17769e551110SNicolas Dichtel struct in_device *in_dev; 17779e551110SNicolas Dichtel struct net_device *dev; 17789e551110SNicolas Dichtel int ifindex; 17799e551110SNicolas Dichtel int err; 17809e551110SNicolas Dichtel 17819e551110SNicolas Dichtel err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 17829e551110SNicolas Dichtel devconf_ipv4_policy); 17839e551110SNicolas Dichtel if (err < 0) 17849e551110SNicolas Dichtel goto errout; 17859e551110SNicolas Dichtel 17869e551110SNicolas Dichtel err = EINVAL; 17879e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 17889e551110SNicolas Dichtel goto errout; 17899e551110SNicolas Dichtel 17909e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 17919e551110SNicolas Dichtel switch (ifindex) { 17929e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 17939e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 17949e551110SNicolas Dichtel break; 17959e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 17969e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 17979e551110SNicolas Dichtel break; 17989e551110SNicolas Dichtel default: 17999e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 18009e551110SNicolas Dichtel if (dev == NULL) 18019e551110SNicolas Dichtel goto errout; 18029e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 18039e551110SNicolas Dichtel if (in_dev == NULL) 18049e551110SNicolas Dichtel goto errout; 18059e551110SNicolas Dichtel devconf = &in_dev->cnf; 18069e551110SNicolas Dichtel break; 18079e551110SNicolas Dichtel } 18089e551110SNicolas Dichtel 18099e551110SNicolas Dichtel err = -ENOBUFS; 18109e551110SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC); 18119e551110SNicolas Dichtel if (skb == NULL) 18129e551110SNicolas Dichtel goto errout; 18139e551110SNicolas Dichtel 18149e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 18159e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 18169e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 18179e551110SNicolas Dichtel -1); 18189e551110SNicolas Dichtel if (err < 0) { 18199e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 18209e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 18219e551110SNicolas Dichtel kfree_skb(skb); 18229e551110SNicolas Dichtel goto errout; 18239e551110SNicolas Dichtel } 18249e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 18259e551110SNicolas Dichtel errout: 18269e551110SNicolas Dichtel return err; 18279e551110SNicolas Dichtel } 18289e551110SNicolas Dichtel 18297a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 18307a674200SNicolas Dichtel struct netlink_callback *cb) 18317a674200SNicolas Dichtel { 18327a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 18337a674200SNicolas Dichtel int h, s_h; 18347a674200SNicolas Dichtel int idx, s_idx; 18357a674200SNicolas Dichtel struct net_device *dev; 18367a674200SNicolas Dichtel struct in_device *in_dev; 18377a674200SNicolas Dichtel struct hlist_head *head; 18387a674200SNicolas Dichtel 18397a674200SNicolas Dichtel s_h = cb->args[0]; 18407a674200SNicolas Dichtel s_idx = idx = cb->args[1]; 18417a674200SNicolas Dichtel 18427a674200SNicolas Dichtel for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 18437a674200SNicolas Dichtel idx = 0; 18447a674200SNicolas Dichtel head = &net->dev_index_head[h]; 18457a674200SNicolas Dichtel rcu_read_lock(); 18460465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 18470465277fSNicolas Dichtel net->dev_base_seq; 18487a674200SNicolas Dichtel hlist_for_each_entry_rcu(dev, head, index_hlist) { 18497a674200SNicolas Dichtel if (idx < s_idx) 18507a674200SNicolas Dichtel goto cont; 18517a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 18527a674200SNicolas Dichtel if (!in_dev) 18537a674200SNicolas Dichtel goto cont; 18547a674200SNicolas Dichtel 18557a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, dev->ifindex, 18567a674200SNicolas Dichtel &in_dev->cnf, 18577a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18587a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18597a674200SNicolas Dichtel RTM_NEWNETCONF, 18607a674200SNicolas Dichtel NLM_F_MULTI, 18617a674200SNicolas Dichtel -1) <= 0) { 18627a674200SNicolas Dichtel rcu_read_unlock(); 18637a674200SNicolas Dichtel goto done; 18647a674200SNicolas Dichtel } 18650465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 18667a674200SNicolas Dichtel cont: 18677a674200SNicolas Dichtel idx++; 18687a674200SNicolas Dichtel } 18697a674200SNicolas Dichtel rcu_read_unlock(); 18707a674200SNicolas Dichtel } 18717a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES) { 18727a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 18737a674200SNicolas Dichtel net->ipv4.devconf_all, 18747a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18757a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18767a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 18777a674200SNicolas Dichtel -1) <= 0) 18787a674200SNicolas Dichtel goto done; 18797a674200SNicolas Dichtel else 18807a674200SNicolas Dichtel h++; 18817a674200SNicolas Dichtel } 18827a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES + 1) { 18837a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 18847a674200SNicolas Dichtel net->ipv4.devconf_dflt, 18857a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18867a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18877a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 18887a674200SNicolas Dichtel -1) <= 0) 18897a674200SNicolas Dichtel goto done; 18907a674200SNicolas Dichtel else 18917a674200SNicolas Dichtel h++; 18927a674200SNicolas Dichtel } 18937a674200SNicolas Dichtel done: 18947a674200SNicolas Dichtel cb->args[0] = h; 18957a674200SNicolas Dichtel cb->args[1] = idx; 18967a674200SNicolas Dichtel 18977a674200SNicolas Dichtel return skb->len; 18987a674200SNicolas Dichtel } 18997a674200SNicolas Dichtel 19001da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 19011da177e4SLinus Torvalds 1902c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 190331be3085SHerbert Xu { 190431be3085SHerbert Xu struct net_device *dev; 190531be3085SHerbert Xu 190631be3085SHerbert Xu rcu_read_lock(); 1907c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1908c6d14c84SEric Dumazet struct in_device *in_dev; 1909c6d14c84SEric Dumazet 191031be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 191131be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 19129355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1913c6d14c84SEric Dumazet } 191431be3085SHerbert Xu rcu_read_unlock(); 191531be3085SHerbert Xu } 191631be3085SHerbert Xu 1917c6d14c84SEric Dumazet /* called with RTNL locked */ 1918c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 191968dd299bSPavel Emelyanov { 192068dd299bSPavel Emelyanov struct net_device *dev; 1921586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 192268dd299bSPavel Emelyanov 1923586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 19249355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 1925edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1926edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1927edc9e748SNicolas Dichtel net->ipv4.devconf_all); 1928edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1929edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 1930edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 193168dd299bSPavel Emelyanov 1932c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 193368dd299bSPavel Emelyanov struct in_device *in_dev; 19340187bdfbSBen Hutchings if (on) 19350187bdfbSBen Hutchings dev_disable_lro(dev); 193668dd299bSPavel Emelyanov rcu_read_lock(); 193768dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 1938edc9e748SNicolas Dichtel if (in_dev) { 193968dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 1940edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1941edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 1942edc9e748SNicolas Dichtel } 194368dd299bSPavel Emelyanov rcu_read_unlock(); 194468dd299bSPavel Emelyanov } 194568dd299bSPavel Emelyanov } 194668dd299bSPavel Emelyanov 1947fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write, 19488d65af78SAlexey Dobriyan void __user *buffer, 194931be3085SHerbert Xu size_t *lenp, loff_t *ppos) 195031be3085SHerbert Xu { 1951d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 19528d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 1953d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 195431be3085SHerbert Xu 195531be3085SHerbert Xu if (write) { 195631be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 1957c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 195831be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 195931be3085SHerbert Xu 196031be3085SHerbert Xu set_bit(i, cnf->state); 196131be3085SHerbert Xu 19629355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 1963c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 1964d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 1965d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 1966d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 19674ccfe6d4SNicolas Dichtel rt_cache_flush(net); 1968cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 1969cc535dfbSNicolas Dichtel new_value != old_value) { 1970cc535dfbSNicolas Dichtel int ifindex; 1971cc535dfbSNicolas Dichtel 1972cc535dfbSNicolas Dichtel if (cnf == net->ipv4.devconf_dflt) 1973cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_DEFAULT; 1974cc535dfbSNicolas Dichtel else if (cnf == net->ipv4.devconf_all) 1975cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_ALL; 1976cc535dfbSNicolas Dichtel else { 1977cc535dfbSNicolas Dichtel struct in_device *idev = 1978cc535dfbSNicolas Dichtel container_of(cnf, struct in_device, 1979cc535dfbSNicolas Dichtel cnf); 1980cc535dfbSNicolas Dichtel ifindex = idev->dev->ifindex; 1981cc535dfbSNicolas Dichtel } 1982cc535dfbSNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER, 1983cc535dfbSNicolas Dichtel ifindex, cnf); 1984cc535dfbSNicolas Dichtel } 198531be3085SHerbert Xu } 198631be3085SHerbert Xu 198731be3085SHerbert Xu return ret; 198831be3085SHerbert Xu } 198931be3085SHerbert Xu 1990fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write, 19918d65af78SAlexey Dobriyan void __user *buffer, 19921da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 19931da177e4SLinus Torvalds { 19941da177e4SLinus Torvalds int *valp = ctl->data; 19951da177e4SLinus Torvalds int val = *valp; 199688af182eSEric W. Biederman loff_t pos = *ppos; 19978d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 19981da177e4SLinus Torvalds 19991da177e4SLinus Torvalds if (write && *valp != val) { 2000c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 2001c0ce9fb3SPavel Emelyanov 20020187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 200388af182eSEric W. Biederman if (!rtnl_trylock()) { 200488af182eSEric W. Biederman /* Restore the original values before restarting */ 200588af182eSEric W. Biederman *valp = val; 200688af182eSEric W. Biederman *ppos = pos; 20079b8adb5eSEric W. Biederman return restart_syscall(); 200888af182eSEric W. Biederman } 20090187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2010c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2011edc9e748SNicolas Dichtel } else { 20120187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 20130187bdfbSBen Hutchings struct in_device *idev = 20140187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2015edc9e748SNicolas Dichtel if (*valp) 20160187bdfbSBen Hutchings dev_disable_lro(idev->dev); 2017edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, 2018edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2019edc9e748SNicolas Dichtel idev->dev->ifindex, 2020edc9e748SNicolas Dichtel cnf); 20210187bdfbSBen Hutchings } 20220187bdfbSBen Hutchings rtnl_unlock(); 20234ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2024edc9e748SNicolas Dichtel } else 2025edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 2026edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2027edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 20280187bdfbSBen Hutchings } 20291da177e4SLinus Torvalds 20301da177e4SLinus Torvalds return ret; 20311da177e4SLinus Torvalds } 20321da177e4SLinus Torvalds 2033fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 20348d65af78SAlexey Dobriyan void __user *buffer, 20351da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 20361da177e4SLinus Torvalds { 20371da177e4SLinus Torvalds int *valp = ctl->data; 20381da177e4SLinus Torvalds int val = *valp; 20398d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 204076e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 20411da177e4SLinus Torvalds 20421da177e4SLinus Torvalds if (write && *valp != val) 20434ccfe6d4SNicolas Dichtel rt_cache_flush(net); 20441da177e4SLinus Torvalds 20451da177e4SLinus Torvalds return ret; 20461da177e4SLinus Torvalds } 20471da177e4SLinus Torvalds 2048f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 204942f811b8SHerbert Xu { \ 205042f811b8SHerbert Xu .procname = name, \ 205142f811b8SHerbert Xu .data = ipv4_devconf.data + \ 205202291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 205342f811b8SHerbert Xu .maxlen = sizeof(int), \ 205442f811b8SHerbert Xu .mode = mval, \ 205542f811b8SHerbert Xu .proc_handler = proc, \ 205631be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 205742f811b8SHerbert Xu } 205842f811b8SHerbert Xu 205942f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2060f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 206142f811b8SHerbert Xu 206242f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2063f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 206442f811b8SHerbert Xu 2065f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2066f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 206742f811b8SHerbert Xu 206842f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2069f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 207042f811b8SHerbert Xu 20711da177e4SLinus Torvalds static struct devinet_sysctl_table { 20721da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 207302291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 20741da177e4SLinus Torvalds } devinet_sysctl = { 20751da177e4SLinus Torvalds .devinet_vars = { 207642f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2077f8572d8fSEric W. Biederman devinet_sysctl_forward), 207842f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 207942f811b8SHerbert Xu 208042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 208142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 208242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 208342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 208442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 208542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 208642f811b8SHerbert Xu "accept_source_route"), 20878153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 208828f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 208942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 209042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 209142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 209242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 209342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 209442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 209542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 209642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 209742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2098eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 209965324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 210042f811b8SHerbert Xu 210142f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 210242f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 210342f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION, 210442f811b8SHerbert Xu "force_igmp_version"), 210542f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 210642f811b8SHerbert Xu "promote_secondaries"), 2107d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2108d0daebc3SThomas Graf "route_localnet"), 21091da177e4SLinus Torvalds }, 21101da177e4SLinus Torvalds }; 21111da177e4SLinus Torvalds 2112ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 2113f8572d8fSEric W. Biederman struct ipv4_devconf *p) 21141da177e4SLinus Torvalds { 21151da177e4SLinus Torvalds int i; 21169fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 21178607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2118bfada697SPavel Emelyanov 21199fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 21201da177e4SLinus Torvalds if (!t) 21219fa89642SPavel Emelyanov goto out; 21229fa89642SPavel Emelyanov 21231da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 21241da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 212531be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2126c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 21271da177e4SLinus Torvalds } 21281da177e4SLinus Torvalds 21298607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 21301da177e4SLinus Torvalds 21318607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 21321da177e4SLinus Torvalds if (!t->sysctl_header) 21338607ddb8SEric W. Biederman goto free; 21341da177e4SLinus Torvalds 21351da177e4SLinus Torvalds p->sysctl = t; 2136ea40b324SPavel Emelyanov return 0; 21371da177e4SLinus Torvalds 21381da177e4SLinus Torvalds free: 21391da177e4SLinus Torvalds kfree(t); 21409fa89642SPavel Emelyanov out: 2141ea40b324SPavel Emelyanov return -ENOBUFS; 21421da177e4SLinus Torvalds } 21431da177e4SLinus Torvalds 214451602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 214566f27a52SPavel Emelyanov { 214651602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 214766f27a52SPavel Emelyanov 214851602b2aSPavel Emelyanov if (t == NULL) 214951602b2aSPavel Emelyanov return; 215051602b2aSPavel Emelyanov 215151602b2aSPavel Emelyanov cnf->sysctl = NULL; 2152ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 21531da177e4SLinus Torvalds kfree(t); 21541da177e4SLinus Torvalds } 215551602b2aSPavel Emelyanov 215651602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev) 215751602b2aSPavel Emelyanov { 215854716e3bSEric W. Biederman neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL); 2159c346dca1SYOSHIFUJI Hideaki __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 2160f8572d8fSEric W. Biederman &idev->cnf); 216151602b2aSPavel Emelyanov } 216251602b2aSPavel Emelyanov 216351602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 216451602b2aSPavel Emelyanov { 216551602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 216651602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 21671da177e4SLinus Torvalds } 21681da177e4SLinus Torvalds 216968dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 217068dd299bSPavel Emelyanov { 217168dd299bSPavel Emelyanov .procname = "ip_forward", 217268dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 217302291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 217468dd299bSPavel Emelyanov .maxlen = sizeof(int), 217568dd299bSPavel Emelyanov .mode = 0644, 217668dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 217768dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2178c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 217968dd299bSPavel Emelyanov }, 218068dd299bSPavel Emelyanov { }, 218168dd299bSPavel Emelyanov }; 21822a75de0cSEric Dumazet #endif 218368dd299bSPavel Emelyanov 2184752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2185752d14dcSPavel Emelyanov { 2186752d14dcSPavel Emelyanov int err; 2187752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 21882a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 21892a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 2190752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 21912a75de0cSEric Dumazet #endif 2192752d14dcSPavel Emelyanov 2193752d14dcSPavel Emelyanov err = -ENOMEM; 2194752d14dcSPavel Emelyanov all = &ipv4_devconf; 2195752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 2196752d14dcSPavel Emelyanov 219709ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 2198752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 2199752d14dcSPavel Emelyanov if (all == NULL) 2200752d14dcSPavel Emelyanov goto err_alloc_all; 2201752d14dcSPavel Emelyanov 2202752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 2203752d14dcSPavel Emelyanov if (dflt == NULL) 2204752d14dcSPavel Emelyanov goto err_alloc_dflt; 2205752d14dcSPavel Emelyanov 22062a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2207752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 2208752d14dcSPavel Emelyanov if (tbl == NULL) 2209752d14dcSPavel Emelyanov goto err_alloc_ctl; 2210752d14dcSPavel Emelyanov 221102291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2212752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2213752d14dcSPavel Emelyanov tbl[0].extra2 = net; 22142a75de0cSEric Dumazet #endif 2215752d14dcSPavel Emelyanov } 2216752d14dcSPavel Emelyanov 2217752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2218f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 2219752d14dcSPavel Emelyanov if (err < 0) 2220752d14dcSPavel Emelyanov goto err_reg_all; 2221752d14dcSPavel Emelyanov 2222f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 2223752d14dcSPavel Emelyanov if (err < 0) 2224752d14dcSPavel Emelyanov goto err_reg_dflt; 2225752d14dcSPavel Emelyanov 2226752d14dcSPavel Emelyanov err = -ENOMEM; 22278607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 2228752d14dcSPavel Emelyanov if (forw_hdr == NULL) 2229752d14dcSPavel Emelyanov goto err_reg_ctl; 22302a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2231752d14dcSPavel Emelyanov #endif 2232752d14dcSPavel Emelyanov 2233752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2234752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2235752d14dcSPavel Emelyanov return 0; 2236752d14dcSPavel Emelyanov 2237752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2238752d14dcSPavel Emelyanov err_reg_ctl: 2239752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 2240752d14dcSPavel Emelyanov err_reg_dflt: 2241752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 2242752d14dcSPavel Emelyanov err_reg_all: 2243752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 2244752d14dcSPavel Emelyanov kfree(tbl); 2245752d14dcSPavel Emelyanov err_alloc_ctl: 22462a75de0cSEric Dumazet #endif 2247752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 2248752d14dcSPavel Emelyanov kfree(dflt); 2249752d14dcSPavel Emelyanov err_alloc_dflt: 2250752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 2251752d14dcSPavel Emelyanov kfree(all); 2252752d14dcSPavel Emelyanov err_alloc_all: 2253752d14dcSPavel Emelyanov return err; 2254752d14dcSPavel Emelyanov } 2255752d14dcSPavel Emelyanov 2256752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2257752d14dcSPavel Emelyanov { 22582a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2259752d14dcSPavel Emelyanov struct ctl_table *tbl; 2260752d14dcSPavel Emelyanov 2261752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2262752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2263752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 2264752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 2265752d14dcSPavel Emelyanov kfree(tbl); 22662a75de0cSEric Dumazet #endif 2267752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2268752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2269752d14dcSPavel Emelyanov } 2270752d14dcSPavel Emelyanov 2271752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2272752d14dcSPavel Emelyanov .init = devinet_init_net, 2273752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2274752d14dcSPavel Emelyanov }; 2275752d14dcSPavel Emelyanov 22769f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = { 22779f0f7272SThomas Graf .family = AF_INET, 22789f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 22799f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2280cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2281cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 22829f0f7272SThomas Graf }; 22839f0f7272SThomas Graf 22841da177e4SLinus Torvalds void __init devinet_init(void) 22851da177e4SLinus Torvalds { 2286fd23c3b3SDavid S. Miller int i; 2287fd23c3b3SDavid S. Miller 2288fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2289fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2290fd23c3b3SDavid S. Miller 2291752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 2292752d14dcSPavel Emelyanov 22931da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 22941da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 229563f3444fSThomas Graf 22965c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, 0); 22975c766d64SJiri Pirko 22989f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 22999f0f7272SThomas Graf 2300c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); 2301c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); 2302c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); 23039e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 23047a674200SNicolas Dichtel inet_netconf_dump_devconf, NULL); 23051da177e4SLinus Torvalds } 23061da177e4SLinus Torvalds 2307