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 strcpy(ifr.ifr_name, ifa->ifa_label); 11301da177e4SLinus Torvalds 11311da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 11321da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 11331da177e4SLinus Torvalds ifa->ifa_local; 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 11361da177e4SLinus Torvalds done = -EFAULT; 11371da177e4SLinus Torvalds break; 11381da177e4SLinus Torvalds } 11391da177e4SLinus Torvalds buf += sizeof(struct ifreq); 11401da177e4SLinus Torvalds len -= sizeof(struct ifreq); 11411da177e4SLinus Torvalds done += sizeof(struct ifreq); 11421da177e4SLinus Torvalds } 11431da177e4SLinus Torvalds out: 11441da177e4SLinus Torvalds return done; 11451da177e4SLinus Torvalds } 11461da177e4SLinus Torvalds 1147a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 11481da177e4SLinus Torvalds { 1149a61ced5dSAl Viro __be32 addr = 0; 11501da177e4SLinus Torvalds struct in_device *in_dev; 1151c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 11521da177e4SLinus Torvalds 11531da177e4SLinus Torvalds rcu_read_lock(); 1154e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 11551da177e4SLinus Torvalds if (!in_dev) 11561da177e4SLinus Torvalds goto no_in_dev; 11571da177e4SLinus Torvalds 11581da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11591da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 11601da177e4SLinus Torvalds continue; 11611da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 11621da177e4SLinus Torvalds addr = ifa->ifa_local; 11631da177e4SLinus Torvalds break; 11641da177e4SLinus Torvalds } 11651da177e4SLinus Torvalds if (!addr) 11661da177e4SLinus Torvalds addr = ifa->ifa_local; 11671da177e4SLinus Torvalds } endfor_ifa(in_dev); 11681da177e4SLinus Torvalds 11691da177e4SLinus Torvalds if (addr) 1170c6d14c84SEric Dumazet goto out_unlock; 11719f9354b9SEric Dumazet no_in_dev: 11721da177e4SLinus Torvalds 11731da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 11741da177e4SLinus Torvalds in this case. It is importnat that lo is the first interface 11751da177e4SLinus Torvalds in dev_base list. 11761da177e4SLinus Torvalds */ 1177c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 11789f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 11799f9354b9SEric Dumazet if (!in_dev) 11801da177e4SLinus Torvalds continue; 11811da177e4SLinus Torvalds 11821da177e4SLinus Torvalds for_primary_ifa(in_dev) { 11831da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 11841da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 11851da177e4SLinus Torvalds addr = ifa->ifa_local; 1186c6d14c84SEric Dumazet goto out_unlock; 11871da177e4SLinus Torvalds } 11881da177e4SLinus Torvalds } endfor_ifa(in_dev); 11891da177e4SLinus Torvalds } 1190c6d14c84SEric Dumazet out_unlock: 11911da177e4SLinus Torvalds rcu_read_unlock(); 11921da177e4SLinus Torvalds return addr; 11931da177e4SLinus Torvalds } 11949f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 11951da177e4SLinus Torvalds 119660cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 119760cad5daSAl Viro __be32 local, int scope) 11981da177e4SLinus Torvalds { 11991da177e4SLinus Torvalds int same = 0; 1200a144ea4bSAl Viro __be32 addr = 0; 12011da177e4SLinus Torvalds 12021da177e4SLinus Torvalds for_ifa(in_dev) { 12031da177e4SLinus Torvalds if (!addr && 12041da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 12051da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 12061da177e4SLinus Torvalds addr = ifa->ifa_local; 12071da177e4SLinus Torvalds if (same) 12081da177e4SLinus Torvalds break; 12091da177e4SLinus Torvalds } 12101da177e4SLinus Torvalds if (!same) { 12111da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 12121da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 12131da177e4SLinus Torvalds if (same && addr) { 12141da177e4SLinus Torvalds if (local || !dst) 12151da177e4SLinus Torvalds break; 12161da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 12171da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 12181da177e4SLinus Torvalds break; 12191da177e4SLinus Torvalds /* No, then can we use new local src? */ 12201da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 12211da177e4SLinus Torvalds addr = ifa->ifa_local; 12221da177e4SLinus Torvalds break; 12231da177e4SLinus Torvalds } 12241da177e4SLinus Torvalds /* search for large dst subnet for addr */ 12251da177e4SLinus Torvalds same = 0; 12261da177e4SLinus Torvalds } 12271da177e4SLinus Torvalds } 12281da177e4SLinus Torvalds } endfor_ifa(in_dev); 12291da177e4SLinus Torvalds 12301da177e4SLinus Torvalds return same ? addr : 0; 12311da177e4SLinus Torvalds } 12321da177e4SLinus Torvalds 12331da177e4SLinus Torvalds /* 12341da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 12359bd85e32SDenis V. Lunev * - in_dev: only on this interface, 0=any interface 12361da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 12371da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 12381da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 12391da177e4SLinus Torvalds */ 12409bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev, 12419bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 12421da177e4SLinus Torvalds { 124360cad5daSAl Viro __be32 addr = 0; 12449bd85e32SDenis V. Lunev struct net_device *dev; 124539a6d063SDenis V. Lunev struct net *net; 12461da177e4SLinus Torvalds 124739a6d063SDenis V. Lunev if (scope != RT_SCOPE_LINK) 12489bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 12491da177e4SLinus Torvalds 1250c346dca1SYOSHIFUJI Hideaki net = dev_net(in_dev->dev); 12511da177e4SLinus Torvalds rcu_read_lock(); 1252c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 12539f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12549f9354b9SEric Dumazet if (in_dev) { 12551da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 12561da177e4SLinus Torvalds if (addr) 12571da177e4SLinus Torvalds break; 12581da177e4SLinus Torvalds } 12591da177e4SLinus Torvalds } 12601da177e4SLinus Torvalds rcu_read_unlock(); 12611da177e4SLinus Torvalds 12621da177e4SLinus Torvalds return addr; 12631da177e4SLinus Torvalds } 1264eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 12651da177e4SLinus Torvalds 12661da177e4SLinus Torvalds /* 12671da177e4SLinus Torvalds * Device notifier 12681da177e4SLinus Torvalds */ 12691da177e4SLinus Torvalds 12701da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 12711da177e4SLinus Torvalds { 1272e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 12731da177e4SLinus Torvalds } 12749f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 12751da177e4SLinus Torvalds 12761da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 12771da177e4SLinus Torvalds { 1278e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 12791da177e4SLinus Torvalds } 12809f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 12811da177e4SLinus Torvalds 12829f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 12839f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 12841da177e4SLinus Torvalds */ 12851da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 12861da177e4SLinus Torvalds { 12871da177e4SLinus Torvalds struct in_ifaddr *ifa; 12881da177e4SLinus Torvalds int named = 0; 12891da177e4SLinus Torvalds 12901da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 12911da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 12921da177e4SLinus Torvalds 12931da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 12941da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 12951da177e4SLinus Torvalds if (named++ == 0) 1296573bf470SThomas Graf goto skip; 129744344b2aSMark McLoughlin dot = strchr(old, ':'); 12981da177e4SLinus Torvalds if (dot == NULL) { 12991da177e4SLinus Torvalds sprintf(old, ":%d", named); 13001da177e4SLinus Torvalds dot = old; 13011da177e4SLinus Torvalds } 13029f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 13031da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 13049f9354b9SEric Dumazet else 13051da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1306573bf470SThomas Graf skip: 1307573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 13081da177e4SLinus Torvalds } 13091da177e4SLinus Torvalds } 13101da177e4SLinus Torvalds 131140384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu) 131206770843SBreno Leitao { 131306770843SBreno Leitao return mtu >= 68; 131406770843SBreno Leitao } 131506770843SBreno Leitao 1316d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1317d11327adSIan Campbell struct in_device *in_dev) 1318d11327adSIan Campbell 1319d11327adSIan Campbell { 1320b76d0789SZoltan Kiss struct in_ifaddr *ifa; 1321d11327adSIan Campbell 1322b76d0789SZoltan Kiss for (ifa = in_dev->ifa_list; ifa; 1323b76d0789SZoltan Kiss ifa = ifa->ifa_next) { 1324d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 13256c91afe1SDavid S. Miller ifa->ifa_local, dev, 13266c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1327d11327adSIan Campbell dev->dev_addr, NULL); 1328d11327adSIan Campbell } 1329b76d0789SZoltan Kiss } 1330d11327adSIan Campbell 13311da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 13321da177e4SLinus Torvalds 13331da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 13341da177e4SLinus Torvalds void *ptr) 13351da177e4SLinus Torvalds { 1336351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1337748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13381da177e4SLinus Torvalds 13391da177e4SLinus Torvalds ASSERT_RTNL(); 13401da177e4SLinus Torvalds 13411da177e4SLinus Torvalds if (!in_dev) { 13428030f544SHerbert Xu if (event == NETDEV_REGISTER) { 13431da177e4SLinus Torvalds in_dev = inetdev_init(dev); 13448d76527eSHerbert Xu if (!in_dev) 1345b217d616SHerbert Xu return notifier_from_errno(-ENOMEM); 13460cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 134742f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 134842f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 13491da177e4SLinus Torvalds } 135006770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 135106770843SBreno Leitao /* Re-enabling IP */ 135206770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 135306770843SBreno Leitao in_dev = inetdev_init(dev); 13548030f544SHerbert Xu } 13551da177e4SLinus Torvalds goto out; 13561da177e4SLinus Torvalds } 13571da177e4SLinus Torvalds 13581da177e4SLinus Torvalds switch (event) { 13591da177e4SLinus Torvalds case NETDEV_REGISTER: 136091df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1361a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 13621da177e4SLinus Torvalds break; 13631da177e4SLinus Torvalds case NETDEV_UP: 136406770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 13651da177e4SLinus Torvalds break; 13660cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 13679f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 13689f9354b9SEric Dumazet 13699f9354b9SEric Dumazet if (ifa) { 1370fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 13711da177e4SLinus Torvalds ifa->ifa_local = 13721da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 13731da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 13741da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 13751da177e4SLinus Torvalds in_dev_hold(in_dev); 13761da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 13771da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 13781da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 13795c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 13805c766d64SJiri Pirko INFINITY_LIFE_TIME); 13811da177e4SLinus Torvalds inet_insert_ifa(ifa); 13821da177e4SLinus Torvalds } 13831da177e4SLinus Torvalds } 13841da177e4SLinus Torvalds ip_mc_up(in_dev); 1385eefef1cfSStephen Hemminger /* fall through */ 1386eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1387d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1388d11327adSIan Campbell break; 1389d11327adSIan Campbell /* fall through */ 1390d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1391a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1392d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 13931da177e4SLinus Torvalds break; 13941da177e4SLinus Torvalds case NETDEV_DOWN: 13951da177e4SLinus Torvalds ip_mc_down(in_dev); 13961da177e4SLinus Torvalds break; 139793d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 139875c78500SMoni Shoua ip_mc_unmap(in_dev); 139975c78500SMoni Shoua break; 140093d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 140175c78500SMoni Shoua ip_mc_remap(in_dev); 140275c78500SMoni Shoua break; 14031da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 140406770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 14051da177e4SLinus Torvalds break; 140606770843SBreno Leitao /* disable IP when MTU is not enough */ 14071da177e4SLinus Torvalds case NETDEV_UNREGISTER: 14081da177e4SLinus Torvalds inetdev_destroy(in_dev); 14091da177e4SLinus Torvalds break; 14101da177e4SLinus Torvalds case NETDEV_CHANGENAME: 14111da177e4SLinus Torvalds /* Do not notify about label change, this event is 14121da177e4SLinus Torvalds * not interesting to applications using netlink. 14131da177e4SLinus Torvalds */ 14141da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 14151da177e4SLinus Torvalds 141651602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 141766f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 14181da177e4SLinus Torvalds break; 14191da177e4SLinus Torvalds } 14201da177e4SLinus Torvalds out: 14211da177e4SLinus Torvalds return NOTIFY_DONE; 14221da177e4SLinus Torvalds } 14231da177e4SLinus Torvalds 14241da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 14251da177e4SLinus Torvalds .notifier_call = inetdev_event, 14261da177e4SLinus Torvalds }; 14271da177e4SLinus Torvalds 142840384999SEric Dumazet static size_t inet_nlmsg_size(void) 1429339bf98fSThomas Graf { 1430339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1431339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1432339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1433339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1434339bf98fSThomas Graf + nla_total_size(IFNAMSIZ); /* IFA_LABEL */ 1435339bf98fSThomas Graf } 1436339bf98fSThomas Graf 14375c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 14385c766d64SJiri Pirko { 14395c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 14405c766d64SJiri Pirko } 14415c766d64SJiri Pirko 14425c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 14435c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 14445c766d64SJiri Pirko { 14455c766d64SJiri Pirko struct ifa_cacheinfo ci; 14465c766d64SJiri Pirko 14475c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 14485c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 14495c766d64SJiri Pirko ci.ifa_prefered = preferred; 14505c766d64SJiri Pirko ci.ifa_valid = valid; 14515c766d64SJiri Pirko 14525c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 14535c766d64SJiri Pirko } 14545c766d64SJiri Pirko 14551da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 145615e47304SEric W. Biederman u32 portid, u32 seq, int event, unsigned int flags) 14571da177e4SLinus Torvalds { 14581da177e4SLinus Torvalds struct ifaddrmsg *ifm; 14591da177e4SLinus Torvalds struct nlmsghdr *nlh; 14605c766d64SJiri Pirko u32 preferred, valid; 14611da177e4SLinus Torvalds 146215e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); 146347f68512SThomas Graf if (nlh == NULL) 146426932566SPatrick McHardy return -EMSGSIZE; 146547f68512SThomas Graf 146647f68512SThomas Graf ifm = nlmsg_data(nlh); 14671da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 14681da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 14695c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 14701da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 14711da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 14721da177e4SLinus Torvalds 14735c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 14745c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 14755c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 14765c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 14775c766d64SJiri Pirko long tval = (jiffies - ifa->ifa_tstamp) / HZ; 14785c766d64SJiri Pirko 14795c766d64SJiri Pirko if (preferred > tval) 14805c766d64SJiri Pirko preferred -= tval; 14815c766d64SJiri Pirko else 14825c766d64SJiri Pirko preferred = 0; 14835c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 14845c766d64SJiri Pirko if (valid > tval) 14855c766d64SJiri Pirko valid -= tval; 14865c766d64SJiri Pirko else 14875c766d64SJiri Pirko valid = 0; 14885c766d64SJiri Pirko } 14895c766d64SJiri Pirko } 14905c766d64SJiri Pirko } else { 14915c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 14925c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 14935c766d64SJiri Pirko } 1494f3756b79SDavid S. Miller if ((ifa->ifa_address && 1495f3756b79SDavid S. Miller nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) || 1496f3756b79SDavid S. Miller (ifa->ifa_local && 1497f3756b79SDavid S. Miller nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) || 1498f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1499f3756b79SDavid S. Miller nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1500f3756b79SDavid S. Miller (ifa->ifa_label[0] && 15015c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 15025c766d64SJiri Pirko put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 15035c766d64SJiri Pirko preferred, valid)) 1504f3756b79SDavid S. Miller goto nla_put_failure; 150547f68512SThomas Graf 150647f68512SThomas Graf return nlmsg_end(skb, nlh); 150747f68512SThomas Graf 150847f68512SThomas Graf nla_put_failure: 150926932566SPatrick McHardy nlmsg_cancel(skb, nlh); 151026932566SPatrick McHardy return -EMSGSIZE; 15111da177e4SLinus Torvalds } 15121da177e4SLinus Torvalds 15131da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 15141da177e4SLinus Torvalds { 15153b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1516eec4df98SEric Dumazet int h, s_h; 1517eec4df98SEric Dumazet int idx, s_idx; 1518eec4df98SEric Dumazet int ip_idx, s_ip_idx; 15191da177e4SLinus Torvalds struct net_device *dev; 15201da177e4SLinus Torvalds struct in_device *in_dev; 15211da177e4SLinus Torvalds struct in_ifaddr *ifa; 1522eec4df98SEric Dumazet struct hlist_head *head; 15231da177e4SLinus Torvalds 1524eec4df98SEric Dumazet s_h = cb->args[0]; 1525eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1526eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1527eec4df98SEric Dumazet 1528eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 15297562f876SPavel Emelianov idx = 0; 1530eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1531eec4df98SEric Dumazet rcu_read_lock(); 15320465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 15330465277fSNicolas Dichtel net->dev_base_seq; 1534b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 15351da177e4SLinus Torvalds if (idx < s_idx) 15367562f876SPavel Emelianov goto cont; 15374b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 15381da177e4SLinus Torvalds s_ip_idx = 0; 1539eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 15409f9354b9SEric Dumazet if (!in_dev) 15417562f876SPavel Emelianov goto cont; 15421da177e4SLinus Torvalds 15431da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 15441da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 15451da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1546596e4150SStephen Hemminger continue; 1547eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 154815e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 15491da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1550eec4df98SEric Dumazet RTM_NEWADDR, NLM_F_MULTI) <= 0) { 1551eec4df98SEric Dumazet rcu_read_unlock(); 15521da177e4SLinus Torvalds goto done; 15531da177e4SLinus Torvalds } 15540465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1555eec4df98SEric Dumazet } 15567562f876SPavel Emelianov cont: 15577562f876SPavel Emelianov idx++; 15581da177e4SLinus Torvalds } 1559eec4df98SEric Dumazet rcu_read_unlock(); 1560eec4df98SEric Dumazet } 15611da177e4SLinus Torvalds 15621da177e4SLinus Torvalds done: 1563eec4df98SEric Dumazet cb->args[0] = h; 1564eec4df98SEric Dumazet cb->args[1] = idx; 1565eec4df98SEric Dumazet cb->args[2] = ip_idx; 15661da177e4SLinus Torvalds 15671da177e4SLinus Torvalds return skb->len; 15681da177e4SLinus Torvalds } 15691da177e4SLinus Torvalds 1570d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 157115e47304SEric W. Biederman u32 portid) 15721da177e4SLinus Torvalds { 157347f68512SThomas Graf struct sk_buff *skb; 1574d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1575d6062cbbSThomas Graf int err = -ENOBUFS; 15764b8aa9abSDenis V. Lunev struct net *net; 15771da177e4SLinus Torvalds 1578c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1579339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 158047f68512SThomas Graf if (skb == NULL) 1581d6062cbbSThomas Graf goto errout; 1582d6062cbbSThomas Graf 158315e47304SEric W. Biederman err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0); 158426932566SPatrick McHardy if (err < 0) { 158526932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 158626932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 158726932566SPatrick McHardy kfree_skb(skb); 158826932566SPatrick McHardy goto errout; 158926932566SPatrick McHardy } 159015e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 15911ce85fe4SPablo Neira Ayuso return; 1592d6062cbbSThomas Graf errout: 1593d6062cbbSThomas Graf if (err < 0) 15944b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 15951da177e4SLinus Torvalds } 15961da177e4SLinus Torvalds 15979f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 15989f0f7272SThomas Graf { 15991fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16009f0f7272SThomas Graf 16019f0f7272SThomas Graf if (!in_dev) 16029f0f7272SThomas Graf return 0; 16039f0f7272SThomas Graf 16049f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 16059f0f7272SThomas Graf } 16069f0f7272SThomas Graf 16079f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 16089f0f7272SThomas Graf { 16091fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16109f0f7272SThomas Graf struct nlattr *nla; 16119f0f7272SThomas Graf int i; 16129f0f7272SThomas Graf 16139f0f7272SThomas Graf if (!in_dev) 16149f0f7272SThomas Graf return -ENODATA; 16159f0f7272SThomas Graf 16169f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 16179f0f7272SThomas Graf if (nla == NULL) 16189f0f7272SThomas Graf return -EMSGSIZE; 16199f0f7272SThomas Graf 16209f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 16219f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 16229f0f7272SThomas Graf 16239f0f7272SThomas Graf return 0; 16249f0f7272SThomas Graf } 16259f0f7272SThomas Graf 16269f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 16279f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 16289f0f7272SThomas Graf }; 16299f0f7272SThomas Graf 1630cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1631cf7afbfeSThomas Graf const struct nlattr *nla) 16329f0f7272SThomas Graf { 16339f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 16349f0f7272SThomas Graf int err, rem; 16359f0f7272SThomas Graf 1636f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1637cf7afbfeSThomas Graf return -EAFNOSUPPORT; 16389f0f7272SThomas Graf 16399f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 16409f0f7272SThomas Graf if (err < 0) 16419f0f7272SThomas Graf return err; 16429f0f7272SThomas Graf 16439f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16449f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 16459f0f7272SThomas Graf int cfgid = nla_type(a); 16469f0f7272SThomas Graf 16479f0f7272SThomas Graf if (nla_len(a) < 4) 16489f0f7272SThomas Graf return -EINVAL; 16499f0f7272SThomas Graf 16509f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 16519f0f7272SThomas Graf return -EINVAL; 16529f0f7272SThomas Graf } 16539f0f7272SThomas Graf } 16549f0f7272SThomas Graf 1655cf7afbfeSThomas Graf return 0; 1656cf7afbfeSThomas Graf } 1657cf7afbfeSThomas Graf 1658cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1659cf7afbfeSThomas Graf { 1660f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1661cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1662cf7afbfeSThomas Graf int rem; 1663cf7afbfeSThomas Graf 1664cf7afbfeSThomas Graf if (!in_dev) 1665cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1666cf7afbfeSThomas Graf 1667cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1668cf7afbfeSThomas Graf BUG(); 1669cf7afbfeSThomas Graf 16709f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16719f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 16729f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 16739f0f7272SThomas Graf } 16749f0f7272SThomas Graf 16759f0f7272SThomas Graf return 0; 16769f0f7272SThomas Graf } 16779f0f7272SThomas Graf 1678edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 1679edc9e748SNicolas Dichtel { 1680edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 1681edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 1682edc9e748SNicolas Dichtel 16839e551110SNicolas Dichtel /* type -1 is used for ALL */ 16849e551110SNicolas Dichtel if (type == -1 || type == NETCONFA_FORWARDING) 1685edc9e748SNicolas Dichtel size += nla_total_size(4); 1686cc535dfbSNicolas Dichtel if (type == -1 || type == NETCONFA_RP_FILTER) 1687cc535dfbSNicolas Dichtel size += nla_total_size(4); 1688d67b8c61SNicolas Dichtel if (type == -1 || type == NETCONFA_MC_FORWARDING) 1689d67b8c61SNicolas Dichtel size += nla_total_size(4); 1690edc9e748SNicolas Dichtel 1691edc9e748SNicolas Dichtel return size; 1692edc9e748SNicolas Dichtel } 1693edc9e748SNicolas Dichtel 1694edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 1695edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 1696edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 1697edc9e748SNicolas Dichtel int type) 1698edc9e748SNicolas Dichtel { 1699edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 1700edc9e748SNicolas Dichtel struct netconfmsg *ncm; 1701edc9e748SNicolas Dichtel 1702edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 1703edc9e748SNicolas Dichtel flags); 1704edc9e748SNicolas Dichtel if (nlh == NULL) 1705edc9e748SNicolas Dichtel return -EMSGSIZE; 1706edc9e748SNicolas Dichtel 1707edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 1708edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 1709edc9e748SNicolas Dichtel 1710edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 1711edc9e748SNicolas Dichtel goto nla_put_failure; 1712edc9e748SNicolas Dichtel 17139e551110SNicolas Dichtel /* type -1 is used for ALL */ 17149e551110SNicolas Dichtel if ((type == -1 || type == NETCONFA_FORWARDING) && 1715edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 1716edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 1717edc9e748SNicolas Dichtel goto nla_put_failure; 1718cc535dfbSNicolas Dichtel if ((type == -1 || type == NETCONFA_RP_FILTER) && 1719cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 1720cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 1721cc535dfbSNicolas Dichtel goto nla_put_failure; 1722d67b8c61SNicolas Dichtel if ((type == -1 || type == NETCONFA_MC_FORWARDING) && 1723d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 1724d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 1725d67b8c61SNicolas Dichtel goto nla_put_failure; 1726edc9e748SNicolas Dichtel 1727edc9e748SNicolas Dichtel return nlmsg_end(skb, nlh); 1728edc9e748SNicolas Dichtel 1729edc9e748SNicolas Dichtel nla_put_failure: 1730edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 1731edc9e748SNicolas Dichtel return -EMSGSIZE; 1732edc9e748SNicolas Dichtel } 1733edc9e748SNicolas Dichtel 1734d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, 1735edc9e748SNicolas Dichtel struct ipv4_devconf *devconf) 1736edc9e748SNicolas Dichtel { 1737edc9e748SNicolas Dichtel struct sk_buff *skb; 1738edc9e748SNicolas Dichtel int err = -ENOBUFS; 1739edc9e748SNicolas Dichtel 1740edc9e748SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC); 1741edc9e748SNicolas Dichtel if (skb == NULL) 1742edc9e748SNicolas Dichtel goto errout; 1743edc9e748SNicolas Dichtel 1744edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 1745edc9e748SNicolas Dichtel RTM_NEWNETCONF, 0, type); 1746edc9e748SNicolas Dichtel if (err < 0) { 1747edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 1748edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 1749edc9e748SNicolas Dichtel kfree_skb(skb); 1750edc9e748SNicolas Dichtel goto errout; 1751edc9e748SNicolas Dichtel } 1752edc9e748SNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC); 1753edc9e748SNicolas Dichtel return; 1754edc9e748SNicolas Dichtel errout: 1755edc9e748SNicolas Dichtel if (err < 0) 1756edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 1757edc9e748SNicolas Dichtel } 1758edc9e748SNicolas Dichtel 17599e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 17609e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 17619e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 1762cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 17639e551110SNicolas Dichtel }; 17649e551110SNicolas Dichtel 17659e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 1766661d2967SThomas Graf struct nlmsghdr *nlh) 17679e551110SNicolas Dichtel { 17689e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 17699e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 17709e551110SNicolas Dichtel struct netconfmsg *ncm; 17719e551110SNicolas Dichtel struct sk_buff *skb; 17729e551110SNicolas Dichtel struct ipv4_devconf *devconf; 17739e551110SNicolas Dichtel struct in_device *in_dev; 17749e551110SNicolas Dichtel struct net_device *dev; 17759e551110SNicolas Dichtel int ifindex; 17769e551110SNicolas Dichtel int err; 17779e551110SNicolas Dichtel 17789e551110SNicolas Dichtel err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 17799e551110SNicolas Dichtel devconf_ipv4_policy); 17809e551110SNicolas Dichtel if (err < 0) 17819e551110SNicolas Dichtel goto errout; 17829e551110SNicolas Dichtel 17839e551110SNicolas Dichtel err = EINVAL; 17849e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 17859e551110SNicolas Dichtel goto errout; 17869e551110SNicolas Dichtel 17879e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 17889e551110SNicolas Dichtel switch (ifindex) { 17899e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 17909e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 17919e551110SNicolas Dichtel break; 17929e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 17939e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 17949e551110SNicolas Dichtel break; 17959e551110SNicolas Dichtel default: 17969e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 17979e551110SNicolas Dichtel if (dev == NULL) 17989e551110SNicolas Dichtel goto errout; 17999e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 18009e551110SNicolas Dichtel if (in_dev == NULL) 18019e551110SNicolas Dichtel goto errout; 18029e551110SNicolas Dichtel devconf = &in_dev->cnf; 18039e551110SNicolas Dichtel break; 18049e551110SNicolas Dichtel } 18059e551110SNicolas Dichtel 18069e551110SNicolas Dichtel err = -ENOBUFS; 18079e551110SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC); 18089e551110SNicolas Dichtel if (skb == NULL) 18099e551110SNicolas Dichtel goto errout; 18109e551110SNicolas Dichtel 18119e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 18129e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 18139e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 18149e551110SNicolas Dichtel -1); 18159e551110SNicolas Dichtel if (err < 0) { 18169e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 18179e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 18189e551110SNicolas Dichtel kfree_skb(skb); 18199e551110SNicolas Dichtel goto errout; 18209e551110SNicolas Dichtel } 18219e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 18229e551110SNicolas Dichtel errout: 18239e551110SNicolas Dichtel return err; 18249e551110SNicolas Dichtel } 18259e551110SNicolas Dichtel 18267a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 18277a674200SNicolas Dichtel struct netlink_callback *cb) 18287a674200SNicolas Dichtel { 18297a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 18307a674200SNicolas Dichtel int h, s_h; 18317a674200SNicolas Dichtel int idx, s_idx; 18327a674200SNicolas Dichtel struct net_device *dev; 18337a674200SNicolas Dichtel struct in_device *in_dev; 18347a674200SNicolas Dichtel struct hlist_head *head; 18357a674200SNicolas Dichtel 18367a674200SNicolas Dichtel s_h = cb->args[0]; 18377a674200SNicolas Dichtel s_idx = idx = cb->args[1]; 18387a674200SNicolas Dichtel 18397a674200SNicolas Dichtel for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 18407a674200SNicolas Dichtel idx = 0; 18417a674200SNicolas Dichtel head = &net->dev_index_head[h]; 18427a674200SNicolas Dichtel rcu_read_lock(); 18430465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 18440465277fSNicolas Dichtel net->dev_base_seq; 18457a674200SNicolas Dichtel hlist_for_each_entry_rcu(dev, head, index_hlist) { 18467a674200SNicolas Dichtel if (idx < s_idx) 18477a674200SNicolas Dichtel goto cont; 18487a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 18497a674200SNicolas Dichtel if (!in_dev) 18507a674200SNicolas Dichtel goto cont; 18517a674200SNicolas Dichtel 18527a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, dev->ifindex, 18537a674200SNicolas Dichtel &in_dev->cnf, 18547a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18557a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18567a674200SNicolas Dichtel RTM_NEWNETCONF, 18577a674200SNicolas Dichtel NLM_F_MULTI, 18587a674200SNicolas Dichtel -1) <= 0) { 18597a674200SNicolas Dichtel rcu_read_unlock(); 18607a674200SNicolas Dichtel goto done; 18617a674200SNicolas Dichtel } 18620465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 18637a674200SNicolas Dichtel cont: 18647a674200SNicolas Dichtel idx++; 18657a674200SNicolas Dichtel } 18667a674200SNicolas Dichtel rcu_read_unlock(); 18677a674200SNicolas Dichtel } 18687a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES) { 18697a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 18707a674200SNicolas Dichtel net->ipv4.devconf_all, 18717a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18727a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18737a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 18747a674200SNicolas Dichtel -1) <= 0) 18757a674200SNicolas Dichtel goto done; 18767a674200SNicolas Dichtel else 18777a674200SNicolas Dichtel h++; 18787a674200SNicolas Dichtel } 18797a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES + 1) { 18807a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 18817a674200SNicolas Dichtel net->ipv4.devconf_dflt, 18827a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 18837a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 18847a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 18857a674200SNicolas Dichtel -1) <= 0) 18867a674200SNicolas Dichtel goto done; 18877a674200SNicolas Dichtel else 18887a674200SNicolas Dichtel h++; 18897a674200SNicolas Dichtel } 18907a674200SNicolas Dichtel done: 18917a674200SNicolas Dichtel cb->args[0] = h; 18927a674200SNicolas Dichtel cb->args[1] = idx; 18937a674200SNicolas Dichtel 18947a674200SNicolas Dichtel return skb->len; 18957a674200SNicolas Dichtel } 18967a674200SNicolas Dichtel 18971da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 18981da177e4SLinus Torvalds 1899c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 190031be3085SHerbert Xu { 190131be3085SHerbert Xu struct net_device *dev; 190231be3085SHerbert Xu 190331be3085SHerbert Xu rcu_read_lock(); 1904c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1905c6d14c84SEric Dumazet struct in_device *in_dev; 1906c6d14c84SEric Dumazet 190731be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 190831be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 19099355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1910c6d14c84SEric Dumazet } 191131be3085SHerbert Xu rcu_read_unlock(); 191231be3085SHerbert Xu } 191331be3085SHerbert Xu 1914c6d14c84SEric Dumazet /* called with RTNL locked */ 1915c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 191668dd299bSPavel Emelyanov { 191768dd299bSPavel Emelyanov struct net_device *dev; 1918586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 191968dd299bSPavel Emelyanov 1920586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 19219355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 1922edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1923edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1924edc9e748SNicolas Dichtel net->ipv4.devconf_all); 1925edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1926edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 1927edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 192868dd299bSPavel Emelyanov 1929c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 193068dd299bSPavel Emelyanov struct in_device *in_dev; 19310187bdfbSBen Hutchings if (on) 19320187bdfbSBen Hutchings dev_disable_lro(dev); 193368dd299bSPavel Emelyanov rcu_read_lock(); 193468dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 1935edc9e748SNicolas Dichtel if (in_dev) { 193668dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 1937edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1938edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 1939edc9e748SNicolas Dichtel } 194068dd299bSPavel Emelyanov rcu_read_unlock(); 194168dd299bSPavel Emelyanov } 194268dd299bSPavel Emelyanov } 194368dd299bSPavel Emelyanov 1944fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write, 19458d65af78SAlexey Dobriyan void __user *buffer, 194631be3085SHerbert Xu size_t *lenp, loff_t *ppos) 194731be3085SHerbert Xu { 1948d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 19498d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 1950d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 195131be3085SHerbert Xu 195231be3085SHerbert Xu if (write) { 195331be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 1954c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 195531be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 195631be3085SHerbert Xu 195731be3085SHerbert Xu set_bit(i, cnf->state); 195831be3085SHerbert Xu 19599355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 1960c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 1961d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 1962d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 1963d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 19644ccfe6d4SNicolas Dichtel rt_cache_flush(net); 1965cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 1966cc535dfbSNicolas Dichtel new_value != old_value) { 1967cc535dfbSNicolas Dichtel int ifindex; 1968cc535dfbSNicolas Dichtel 1969cc535dfbSNicolas Dichtel if (cnf == net->ipv4.devconf_dflt) 1970cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_DEFAULT; 1971cc535dfbSNicolas Dichtel else if (cnf == net->ipv4.devconf_all) 1972cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_ALL; 1973cc535dfbSNicolas Dichtel else { 1974cc535dfbSNicolas Dichtel struct in_device *idev = 1975cc535dfbSNicolas Dichtel container_of(cnf, struct in_device, 1976cc535dfbSNicolas Dichtel cnf); 1977cc535dfbSNicolas Dichtel ifindex = idev->dev->ifindex; 1978cc535dfbSNicolas Dichtel } 1979cc535dfbSNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER, 1980cc535dfbSNicolas Dichtel ifindex, cnf); 1981cc535dfbSNicolas Dichtel } 198231be3085SHerbert Xu } 198331be3085SHerbert Xu 198431be3085SHerbert Xu return ret; 198531be3085SHerbert Xu } 198631be3085SHerbert Xu 1987fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write, 19888d65af78SAlexey Dobriyan void __user *buffer, 19891da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 19901da177e4SLinus Torvalds { 19911da177e4SLinus Torvalds int *valp = ctl->data; 19921da177e4SLinus Torvalds int val = *valp; 199388af182eSEric W. Biederman loff_t pos = *ppos; 19948d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 19951da177e4SLinus Torvalds 19961da177e4SLinus Torvalds if (write && *valp != val) { 1997c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 1998c0ce9fb3SPavel Emelyanov 19990187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 200088af182eSEric W. Biederman if (!rtnl_trylock()) { 200188af182eSEric W. Biederman /* Restore the original values before restarting */ 200288af182eSEric W. Biederman *valp = val; 200388af182eSEric W. Biederman *ppos = pos; 20049b8adb5eSEric W. Biederman return restart_syscall(); 200588af182eSEric W. Biederman } 20060187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2007c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2008edc9e748SNicolas Dichtel } else { 20090187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 20100187bdfbSBen Hutchings struct in_device *idev = 20110187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2012edc9e748SNicolas Dichtel if (*valp) 20130187bdfbSBen Hutchings dev_disable_lro(idev->dev); 2014edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, 2015edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2016edc9e748SNicolas Dichtel idev->dev->ifindex, 2017edc9e748SNicolas Dichtel cnf); 20180187bdfbSBen Hutchings } 20190187bdfbSBen Hutchings rtnl_unlock(); 20204ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2021edc9e748SNicolas Dichtel } else 2022edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 2023edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2024edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 20250187bdfbSBen Hutchings } 20261da177e4SLinus Torvalds 20271da177e4SLinus Torvalds return ret; 20281da177e4SLinus Torvalds } 20291da177e4SLinus Torvalds 2030fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 20318d65af78SAlexey Dobriyan void __user *buffer, 20321da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 20331da177e4SLinus Torvalds { 20341da177e4SLinus Torvalds int *valp = ctl->data; 20351da177e4SLinus Torvalds int val = *valp; 20368d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 203776e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 20381da177e4SLinus Torvalds 20391da177e4SLinus Torvalds if (write && *valp != val) 20404ccfe6d4SNicolas Dichtel rt_cache_flush(net); 20411da177e4SLinus Torvalds 20421da177e4SLinus Torvalds return ret; 20431da177e4SLinus Torvalds } 20441da177e4SLinus Torvalds 2045f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 204642f811b8SHerbert Xu { \ 204742f811b8SHerbert Xu .procname = name, \ 204842f811b8SHerbert Xu .data = ipv4_devconf.data + \ 204902291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 205042f811b8SHerbert Xu .maxlen = sizeof(int), \ 205142f811b8SHerbert Xu .mode = mval, \ 205242f811b8SHerbert Xu .proc_handler = proc, \ 205331be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 205442f811b8SHerbert Xu } 205542f811b8SHerbert Xu 205642f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2057f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 205842f811b8SHerbert Xu 205942f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2060f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 206142f811b8SHerbert Xu 2062f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2063f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 206442f811b8SHerbert Xu 206542f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2066f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 206742f811b8SHerbert Xu 20681da177e4SLinus Torvalds static struct devinet_sysctl_table { 20691da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 207002291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 20711da177e4SLinus Torvalds } devinet_sysctl = { 20721da177e4SLinus Torvalds .devinet_vars = { 207342f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2074f8572d8fSEric W. Biederman devinet_sysctl_forward), 207542f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 207642f811b8SHerbert Xu 207742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 207842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 207942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 208042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 208142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 208242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 208342f811b8SHerbert Xu "accept_source_route"), 20848153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 208528f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 208642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 208742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 208842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 208942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 209042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 209142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 209242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 209342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 209442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2095eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 209665324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 20975c6fe01cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 20985c6fe01cSWilliam Manley "force_igmp_version"), 209942f811b8SHerbert Xu 210042f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 210142f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 210242f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 210342f811b8SHerbert Xu "promote_secondaries"), 2104d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2105d0daebc3SThomas Graf "route_localnet"), 21061da177e4SLinus Torvalds }, 21071da177e4SLinus Torvalds }; 21081da177e4SLinus Torvalds 2109ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 2110f8572d8fSEric W. Biederman struct ipv4_devconf *p) 21111da177e4SLinus Torvalds { 21121da177e4SLinus Torvalds int i; 21139fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 21148607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2115bfada697SPavel Emelyanov 21169fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 21171da177e4SLinus Torvalds if (!t) 21189fa89642SPavel Emelyanov goto out; 21199fa89642SPavel Emelyanov 21201da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 21211da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 212231be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2123c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 21241da177e4SLinus Torvalds } 21251da177e4SLinus Torvalds 21268607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 21271da177e4SLinus Torvalds 21288607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 21291da177e4SLinus Torvalds if (!t->sysctl_header) 21308607ddb8SEric W. Biederman goto free; 21311da177e4SLinus Torvalds 21321da177e4SLinus Torvalds p->sysctl = t; 2133ea40b324SPavel Emelyanov return 0; 21341da177e4SLinus Torvalds 21351da177e4SLinus Torvalds free: 21361da177e4SLinus Torvalds kfree(t); 21379fa89642SPavel Emelyanov out: 2138ea40b324SPavel Emelyanov return -ENOBUFS; 21391da177e4SLinus Torvalds } 21401da177e4SLinus Torvalds 214151602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 214266f27a52SPavel Emelyanov { 214351602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 214466f27a52SPavel Emelyanov 214551602b2aSPavel Emelyanov if (t == NULL) 214651602b2aSPavel Emelyanov return; 214751602b2aSPavel Emelyanov 214851602b2aSPavel Emelyanov cnf->sysctl = NULL; 2149ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 21501da177e4SLinus Torvalds kfree(t); 21511da177e4SLinus Torvalds } 215251602b2aSPavel Emelyanov 215351602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev) 215451602b2aSPavel Emelyanov { 215554716e3bSEric W. Biederman neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL); 2156c346dca1SYOSHIFUJI Hideaki __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 2157f8572d8fSEric W. Biederman &idev->cnf); 215851602b2aSPavel Emelyanov } 215951602b2aSPavel Emelyanov 216051602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 216151602b2aSPavel Emelyanov { 216251602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 216351602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 21641da177e4SLinus Torvalds } 21651da177e4SLinus Torvalds 216668dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 216768dd299bSPavel Emelyanov { 216868dd299bSPavel Emelyanov .procname = "ip_forward", 216968dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 217002291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 217168dd299bSPavel Emelyanov .maxlen = sizeof(int), 217268dd299bSPavel Emelyanov .mode = 0644, 217368dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 217468dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2175c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 217668dd299bSPavel Emelyanov }, 217768dd299bSPavel Emelyanov { }, 217868dd299bSPavel Emelyanov }; 21792a75de0cSEric Dumazet #endif 218068dd299bSPavel Emelyanov 2181752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2182752d14dcSPavel Emelyanov { 2183752d14dcSPavel Emelyanov int err; 2184752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 21852a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 21862a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 2187752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 21882a75de0cSEric Dumazet #endif 2189752d14dcSPavel Emelyanov 2190752d14dcSPavel Emelyanov err = -ENOMEM; 2191752d14dcSPavel Emelyanov all = &ipv4_devconf; 2192752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 2193752d14dcSPavel Emelyanov 219409ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 2195752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 2196752d14dcSPavel Emelyanov if (all == NULL) 2197752d14dcSPavel Emelyanov goto err_alloc_all; 2198752d14dcSPavel Emelyanov 2199752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 2200752d14dcSPavel Emelyanov if (dflt == NULL) 2201752d14dcSPavel Emelyanov goto err_alloc_dflt; 2202752d14dcSPavel Emelyanov 22032a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2204752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 2205752d14dcSPavel Emelyanov if (tbl == NULL) 2206752d14dcSPavel Emelyanov goto err_alloc_ctl; 2207752d14dcSPavel Emelyanov 220802291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2209752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2210752d14dcSPavel Emelyanov tbl[0].extra2 = net; 22112a75de0cSEric Dumazet #endif 2212752d14dcSPavel Emelyanov } 2213752d14dcSPavel Emelyanov 2214752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2215f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 2216752d14dcSPavel Emelyanov if (err < 0) 2217752d14dcSPavel Emelyanov goto err_reg_all; 2218752d14dcSPavel Emelyanov 2219f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 2220752d14dcSPavel Emelyanov if (err < 0) 2221752d14dcSPavel Emelyanov goto err_reg_dflt; 2222752d14dcSPavel Emelyanov 2223752d14dcSPavel Emelyanov err = -ENOMEM; 22248607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 2225752d14dcSPavel Emelyanov if (forw_hdr == NULL) 2226752d14dcSPavel Emelyanov goto err_reg_ctl; 22272a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2228752d14dcSPavel Emelyanov #endif 2229752d14dcSPavel Emelyanov 2230752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2231752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2232752d14dcSPavel Emelyanov return 0; 2233752d14dcSPavel Emelyanov 2234752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2235752d14dcSPavel Emelyanov err_reg_ctl: 2236752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 2237752d14dcSPavel Emelyanov err_reg_dflt: 2238752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 2239752d14dcSPavel Emelyanov err_reg_all: 2240752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 2241752d14dcSPavel Emelyanov kfree(tbl); 2242752d14dcSPavel Emelyanov err_alloc_ctl: 22432a75de0cSEric Dumazet #endif 2244752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 2245752d14dcSPavel Emelyanov kfree(dflt); 2246752d14dcSPavel Emelyanov err_alloc_dflt: 2247752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 2248752d14dcSPavel Emelyanov kfree(all); 2249752d14dcSPavel Emelyanov err_alloc_all: 2250752d14dcSPavel Emelyanov return err; 2251752d14dcSPavel Emelyanov } 2252752d14dcSPavel Emelyanov 2253752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2254752d14dcSPavel Emelyanov { 22552a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2256752d14dcSPavel Emelyanov struct ctl_table *tbl; 2257752d14dcSPavel Emelyanov 2258752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2259752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2260752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 2261752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 2262752d14dcSPavel Emelyanov kfree(tbl); 22632a75de0cSEric Dumazet #endif 2264752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2265752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2266752d14dcSPavel Emelyanov } 2267752d14dcSPavel Emelyanov 2268752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2269752d14dcSPavel Emelyanov .init = devinet_init_net, 2270752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2271752d14dcSPavel Emelyanov }; 2272752d14dcSPavel Emelyanov 22739f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = { 22749f0f7272SThomas Graf .family = AF_INET, 22759f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 22769f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2277cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2278cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 22799f0f7272SThomas Graf }; 22809f0f7272SThomas Graf 22811da177e4SLinus Torvalds void __init devinet_init(void) 22821da177e4SLinus Torvalds { 2283fd23c3b3SDavid S. Miller int i; 2284fd23c3b3SDavid S. Miller 2285fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2286fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2287fd23c3b3SDavid S. Miller 2288752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 2289752d14dcSPavel Emelyanov 22901da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 22911da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 229263f3444fSThomas Graf 22935c766d64SJiri Pirko schedule_delayed_work(&check_lifetime_work, 0); 22945c766d64SJiri Pirko 22959f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 22969f0f7272SThomas Graf 2297c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); 2298c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); 2299c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); 23009e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 23017a674200SNicolas Dichtel inet_netconf_dump_devconf, NULL); 23021da177e4SLinus Torvalds } 23031da177e4SLinus Torvalds 2304