11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * NET3 IP device support routines. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or 51da177e4SLinus Torvalds * modify it under the terms of the GNU General Public License 61da177e4SLinus Torvalds * as published by the Free Software Foundation; either version 71da177e4SLinus Torvalds * 2 of the License, or (at your option) any later version. 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Derived from the IP parts of dev.c 1.0.19 1002c30a84SJesper Juhl * Authors: Ross Biro 111da177e4SLinus Torvalds * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> 121da177e4SLinus Torvalds * Mark Evans, <evansmp@uhura.aston.ac.uk> 131da177e4SLinus Torvalds * 141da177e4SLinus Torvalds * Additional Authors: 151da177e4SLinus Torvalds * Alan Cox, <gw4pts@gw4pts.ampr.org> 161da177e4SLinus Torvalds * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 171da177e4SLinus Torvalds * 181da177e4SLinus Torvalds * Changes: 191da177e4SLinus Torvalds * Alexey Kuznetsov: pa_* fields are replaced with ifaddr 201da177e4SLinus Torvalds * lists. 211da177e4SLinus Torvalds * Cyrus Durgin: updated for kmod 221da177e4SLinus Torvalds * Matthias Andree: in devinet_ioctl, compare label and 231da177e4SLinus Torvalds * address (4.4BSD alias style support), 241da177e4SLinus Torvalds * fall back to comparing just the label 251da177e4SLinus Torvalds * if no match found. 261da177e4SLinus Torvalds */ 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds 291da177e4SLinus Torvalds #include <asm/uaccess.h> 301da177e4SLinus Torvalds #include <linux/bitops.h> 314fc268d2SRandy Dunlap #include <linux/capability.h> 321da177e4SLinus Torvalds #include <linux/module.h> 331da177e4SLinus Torvalds #include <linux/types.h> 341da177e4SLinus Torvalds #include <linux/kernel.h> 351da177e4SLinus Torvalds #include <linux/string.h> 361da177e4SLinus Torvalds #include <linux/mm.h> 371da177e4SLinus Torvalds #include <linux/socket.h> 381da177e4SLinus Torvalds #include <linux/sockios.h> 391da177e4SLinus Torvalds #include <linux/in.h> 401da177e4SLinus Torvalds #include <linux/errno.h> 411da177e4SLinus Torvalds #include <linux/interrupt.h> 421823730fSThomas Graf #include <linux/if_addr.h> 431da177e4SLinus Torvalds #include <linux/if_ether.h> 441da177e4SLinus Torvalds #include <linux/inet.h> 451da177e4SLinus Torvalds #include <linux/netdevice.h> 461da177e4SLinus Torvalds #include <linux/etherdevice.h> 471da177e4SLinus Torvalds #include <linux/skbuff.h> 481da177e4SLinus Torvalds #include <linux/init.h> 491da177e4SLinus Torvalds #include <linux/notifier.h> 501da177e4SLinus Torvalds #include <linux/inetdevice.h> 511da177e4SLinus Torvalds #include <linux/igmp.h> 525a0e3ad6STejun Heo #include <linux/slab.h> 53fd23c3b3SDavid S. Miller #include <linux/hash.h> 541da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 551da177e4SLinus Torvalds #include <linux/sysctl.h> 561da177e4SLinus Torvalds #endif 571da177e4SLinus Torvalds #include <linux/kmod.h> 58edc9e748SNicolas Dichtel #include <linux/netconf.h> 591da177e4SLinus Torvalds 6014c85021SArnaldo Carvalho de Melo #include <net/arp.h> 611da177e4SLinus Torvalds #include <net/ip.h> 621da177e4SLinus Torvalds #include <net/route.h> 631da177e4SLinus Torvalds #include <net/ip_fib.h> 6463f3444fSThomas Graf #include <net/rtnetlink.h> 65752d14dcSPavel Emelyanov #include <net/net_namespace.h> 665c766d64SJiri Pirko #include <net/addrconf.h> 671da177e4SLinus Torvalds 68406b6f97SDavid S. Miller #include "fib_lookup.h" 69406b6f97SDavid S. Miller 700027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 7142f811b8SHerbert Xu .data = { 7202291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7302291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7402291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7502291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 762690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 772690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 7842f811b8SHerbert Xu }, 791da177e4SLinus Torvalds }; 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 8242f811b8SHerbert Xu .data = { 8302291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8402291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8502291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8602291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8702291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 882690048cSWilliam Manley [IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL - 1] = 10000 /*ms*/, 892690048cSWilliam Manley [IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL - 1] = 1000 /*ms*/, 9042f811b8SHerbert Xu }, 911da177e4SLinus Torvalds }; 921da177e4SLinus Torvalds 939355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 949355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9542f811b8SHerbert Xu 96ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 975c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 985c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 995c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 1005176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 1015c766d64SJiri Pirko [IFA_CACHEINFO] = { .len = sizeof(struct ifa_cacheinfo) }, 102ad6c8135SJiri Pirko [IFA_FLAGS] = { .type = NLA_U32 }, 1035c753978SThomas Graf }; 1045c753978SThomas Graf 10540384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT 8 10640384999SEric Dumazet #define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 10740384999SEric Dumazet 108fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 109fd23c3b3SDavid S. Miller 1106eada011SEric Dumazet static u32 inet_addr_hash(const struct net *net, __be32 addr) 111fd23c3b3SDavid S. Miller { 11240384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 113fd23c3b3SDavid S. Miller 11440384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 115fd23c3b3SDavid S. Miller } 116fd23c3b3SDavid S. Miller 117fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 118fd23c3b3SDavid S. Miller { 11940384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 120fd23c3b3SDavid S. Miller 12132a4be48SWANG Cong ASSERT_RTNL(); 122fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 123fd23c3b3SDavid S. Miller } 124fd23c3b3SDavid S. Miller 125fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 126fd23c3b3SDavid S. Miller { 12732a4be48SWANG Cong ASSERT_RTNL(); 128fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 129fd23c3b3SDavid S. Miller } 130fd23c3b3SDavid S. Miller 1319435eb1cSDavid S. Miller /** 1329435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1339435eb1cSDavid S. Miller * @net: the net namespace 1349435eb1cSDavid S. Miller * @addr: the source address 1359435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1369435eb1cSDavid S. Miller * 1379435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1389435eb1cSDavid S. Miller */ 1399435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1409435eb1cSDavid S. Miller { 14140384999SEric Dumazet u32 hash = inet_addr_hash(net, addr); 1429435eb1cSDavid S. Miller struct net_device *result = NULL; 1439435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1449435eb1cSDavid S. Miller 1459435eb1cSDavid S. Miller rcu_read_lock(); 146b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[hash], hash) { 14740384999SEric Dumazet if (ifa->ifa_local == addr) { 1489435eb1cSDavid S. Miller struct net_device *dev = ifa->ifa_dev->dev; 1499435eb1cSDavid S. Miller 1509435eb1cSDavid S. Miller if (!net_eq(dev_net(dev), net)) 1519435eb1cSDavid S. Miller continue; 1529435eb1cSDavid S. Miller result = dev; 1539435eb1cSDavid S. Miller break; 1549435eb1cSDavid S. Miller } 1559435eb1cSDavid S. Miller } 156406b6f97SDavid S. Miller if (!result) { 157406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 158406b6f97SDavid S. Miller struct fib_result res = { 0 }; 159406b6f97SDavid S. Miller struct fib_table *local; 160406b6f97SDavid S. Miller 161406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 162406b6f97SDavid S. Miller * over loopback subnets work. 163406b6f97SDavid S. Miller */ 164406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 165406b6f97SDavid S. Miller if (local && 166406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 167406b6f97SDavid S. Miller res.type == RTN_LOCAL) 168406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 169406b6f97SDavid S. Miller } 1709435eb1cSDavid S. Miller if (result && devref) 1719435eb1cSDavid S. Miller dev_hold(result); 1729435eb1cSDavid S. Miller rcu_read_unlock(); 1739435eb1cSDavid S. Miller return result; 1749435eb1cSDavid S. Miller } 1759435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1769435eb1cSDavid S. Miller 177d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1781da177e4SLinus Torvalds 179e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1801da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1811da177e4SLinus Torvalds int destroy); 1821da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 18320e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev); 18451602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 18551602b2aSPavel Emelyanov #else 18620e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 18751602b2aSPavel Emelyanov { 18820e61da7SWANG Cong return 0; 18951602b2aSPavel Emelyanov } 19040384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 19151602b2aSPavel Emelyanov { 19251602b2aSPavel Emelyanov } 1931da177e4SLinus Torvalds #endif 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds /* Locks all the inet devices. */ 1961da177e4SLinus Torvalds 1971da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 1981da177e4SLinus Torvalds { 19993adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds 2021da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 2031da177e4SLinus Torvalds { 2041da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2051da177e4SLinus Torvalds if (ifa->ifa_dev) 2061da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2071da177e4SLinus Torvalds kfree(ifa); 2081da177e4SLinus Torvalds } 2091da177e4SLinus Torvalds 21040384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2111da177e4SLinus Torvalds { 2121da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2131da177e4SLinus Torvalds } 2141da177e4SLinus Torvalds 2151da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2161da177e4SLinus Torvalds { 2171da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2181da177e4SLinus Torvalds 219547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 220547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 221e9897071SEric Dumazet kfree(rcu_dereference_protected(idev->mc_hash, 1)); 2221da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 22391df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2241da177e4SLinus Torvalds #endif 2251da177e4SLinus Torvalds dev_put(dev); 2261da177e4SLinus Torvalds if (!idev->dead) 2279f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2289f9354b9SEric Dumazet else 2291da177e4SLinus Torvalds kfree(idev); 2301da177e4SLinus Torvalds } 2319f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2321da177e4SLinus Torvalds 23371e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2341da177e4SLinus Torvalds { 2351da177e4SLinus Torvalds struct in_device *in_dev; 23620e61da7SWANG Cong int err = -ENOMEM; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds ASSERT_RTNL(); 2391da177e4SLinus Torvalds 2400da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2411da177e4SLinus Torvalds if (!in_dev) 2421da177e4SLinus Torvalds goto out; 243c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2449355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2451da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2461da177e4SLinus Torvalds in_dev->dev = dev; 2479f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2489f9354b9SEric Dumazet if (!in_dev->arp_parms) 2491da177e4SLinus Torvalds goto out_kfree; 2500187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2510187bdfbSBen Hutchings dev_disable_lro(dev); 2521da177e4SLinus Torvalds /* Reference in_dev->dev */ 2531da177e4SLinus Torvalds dev_hold(dev); 25430c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2551da177e4SLinus Torvalds in_dev_hold(in_dev); 2561da177e4SLinus Torvalds 25720e61da7SWANG Cong err = devinet_sysctl_register(in_dev); 25820e61da7SWANG Cong if (err) { 25920e61da7SWANG Cong in_dev->dead = 1; 26020e61da7SWANG Cong in_dev_put(in_dev); 26120e61da7SWANG Cong in_dev = NULL; 26220e61da7SWANG Cong goto out; 26320e61da7SWANG Cong } 2641da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2651da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2661da177e4SLinus Torvalds ip_mc_up(in_dev); 267483479ecSJarek Poplawski 26830c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 269cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 270483479ecSJarek Poplawski out: 27120e61da7SWANG Cong return in_dev ?: ERR_PTR(err); 2721da177e4SLinus Torvalds out_kfree: 2731da177e4SLinus Torvalds kfree(in_dev); 2741da177e4SLinus Torvalds in_dev = NULL; 2751da177e4SLinus Torvalds goto out; 2761da177e4SLinus Torvalds } 2771da177e4SLinus Torvalds 2781da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2791da177e4SLinus Torvalds { 2801da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2811da177e4SLinus Torvalds in_dev_put(idev); 2821da177e4SLinus Torvalds } 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2851da177e4SLinus Torvalds { 2861da177e4SLinus Torvalds struct in_ifaddr *ifa; 2871da177e4SLinus Torvalds struct net_device *dev; 2881da177e4SLinus Torvalds 2891da177e4SLinus Torvalds ASSERT_RTNL(); 2901da177e4SLinus Torvalds 2911da177e4SLinus Torvalds dev = in_dev->dev; 2921da177e4SLinus Torvalds 2931da177e4SLinus Torvalds in_dev->dead = 1; 2941da177e4SLinus Torvalds 2951da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 2981da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 2991da177e4SLinus Torvalds inet_free_ifa(ifa); 3001da177e4SLinus Torvalds } 3011da177e4SLinus Torvalds 302a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 3031da177e4SLinus Torvalds 30451602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 3051da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 3061da177e4SLinus Torvalds arp_ifdown(dev); 3071da177e4SLinus Torvalds 3081da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 3091da177e4SLinus Torvalds } 3101da177e4SLinus Torvalds 311ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3121da177e4SLinus Torvalds { 3131da177e4SLinus Torvalds rcu_read_lock(); 3141da177e4SLinus Torvalds for_primary_ifa(in_dev) { 3151da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3161da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3171da177e4SLinus Torvalds rcu_read_unlock(); 3181da177e4SLinus Torvalds return 1; 3191da177e4SLinus Torvalds } 3201da177e4SLinus Torvalds } 3211da177e4SLinus Torvalds } endfor_ifa(in_dev); 3221da177e4SLinus Torvalds rcu_read_unlock(); 3231da177e4SLinus Torvalds return 0; 3241da177e4SLinus Torvalds } 3251da177e4SLinus Torvalds 326d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 32715e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3281da177e4SLinus Torvalds { 3298f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3300ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3310ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3320ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3330ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3341da177e4SLinus Torvalds 3351da177e4SLinus Torvalds ASSERT_RTNL(); 3361da177e4SLinus Torvalds 3378f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3388f937c60SHarald Welte * unless alias promotion is set 3398f937c60SHarald Welte **/ 3401da177e4SLinus Torvalds 3411da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3421da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3431da177e4SLinus Torvalds 3441da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3450ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3460ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3470ff60a45SJamal Hadi Salim last_prim = ifa; 3480ff60a45SJamal Hadi Salim 3491da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3501da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3511da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3521da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3530ff60a45SJamal Hadi Salim prev_prom = ifa; 3541da177e4SLinus Torvalds continue; 3551da177e4SLinus Torvalds } 3561da177e4SLinus Torvalds 3570ff60a45SJamal Hadi Salim if (!do_promote) { 358fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3591da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3601da177e4SLinus Torvalds 36115e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 362e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 363e041c683SAlan Stern NETDEV_DOWN, ifa); 3641da177e4SLinus Torvalds inet_free_ifa(ifa); 3658f937c60SHarald Welte } else { 3668f937c60SHarald Welte promote = ifa; 3678f937c60SHarald Welte break; 3688f937c60SHarald Welte } 3691da177e4SLinus Torvalds } 3701da177e4SLinus Torvalds } 3711da177e4SLinus Torvalds 3722d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 3732d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 3742d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 3752d230e2bSJulian Anastasov * while all addresses are on the device list. 3762d230e2bSJulian Anastasov */ 3772d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 3782d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 3792d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 3802d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 3812d230e2bSJulian Anastasov } 3822d230e2bSJulian Anastasov 3831da177e4SLinus Torvalds /* 2. Unlink it */ 3841da177e4SLinus Torvalds 3851da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 386fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3871da177e4SLinus Torvalds 3881da177e4SLinus Torvalds /* 3. Announce address deletion */ 3891da177e4SLinus Torvalds 3901da177e4SLinus Torvalds /* Send message first, then call notifier. 3911da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 3921da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 3931da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 3941da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 3951da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 3961da177e4SLinus Torvalds So that, this order is correct. 3971da177e4SLinus Torvalds */ 39815e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 399e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 4000ff60a45SJamal Hadi Salim 4010ff60a45SJamal Hadi Salim if (promote) { 40204024b93SJulian Anastasov struct in_ifaddr *next_sec = promote->ifa_next; 4030ff60a45SJamal Hadi Salim 4040ff60a45SJamal Hadi Salim if (prev_prom) { 4050ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 4060ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 4070ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 4080ff60a45SJamal Hadi Salim } 4090ff60a45SJamal Hadi Salim 4100ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 41115e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 412e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 413e041c683SAlan Stern NETDEV_UP, promote); 41404024b93SJulian Anastasov for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { 4150ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4160ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4170ff60a45SJamal Hadi Salim continue; 4180ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4190ff60a45SJamal Hadi Salim } 4200ff60a45SJamal Hadi Salim 4210ff60a45SJamal Hadi Salim } 4226363097cSHerbert Xu if (destroy) 4231da177e4SLinus Torvalds inet_free_ifa(ifa1); 4241da177e4SLinus Torvalds } 4251da177e4SLinus Torvalds 426d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 427d6062cbbSThomas Graf int destroy) 428d6062cbbSThomas Graf { 429d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 430d6062cbbSThomas Graf } 431d6062cbbSThomas Graf 4325c766d64SJiri Pirko static void check_lifetime(struct work_struct *work); 4335c766d64SJiri Pirko 4345c766d64SJiri Pirko static DECLARE_DELAYED_WORK(check_lifetime_work, check_lifetime); 4355c766d64SJiri Pirko 436d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 43715e47304SEric W. Biederman u32 portid) 4381da177e4SLinus Torvalds { 4391da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4401da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4411da177e4SLinus Torvalds 4421da177e4SLinus Torvalds ASSERT_RTNL(); 4431da177e4SLinus Torvalds 4441da177e4SLinus Torvalds if (!ifa->ifa_local) { 4451da177e4SLinus Torvalds inet_free_ifa(ifa); 4461da177e4SLinus Torvalds return 0; 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds 4491da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4501da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4511da177e4SLinus Torvalds 4521da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4531da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4541da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4551da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4561da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4571da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4581da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4591da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4601da177e4SLinus Torvalds inet_free_ifa(ifa); 4611da177e4SLinus Torvalds return -EEXIST; 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4641da177e4SLinus Torvalds inet_free_ifa(ifa); 4651da177e4SLinus Torvalds return -EINVAL; 4661da177e4SLinus Torvalds } 4671da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4681da177e4SLinus Torvalds } 4691da177e4SLinus Torvalds } 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 47263862b5bSAruna-Hewapathirane prandom_seed((__force u32) ifa->ifa_local); 4731da177e4SLinus Torvalds ifap = last_primary; 4741da177e4SLinus Torvalds } 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds ifa->ifa_next = *ifap; 4771da177e4SLinus Torvalds *ifap = ifa; 4781da177e4SLinus Torvalds 479fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 480fd23c3b3SDavid S. Miller 4815c766d64SJiri Pirko cancel_delayed_work(&check_lifetime_work); 482906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 4835c766d64SJiri Pirko 4841da177e4SLinus Torvalds /* Send message first, then call notifier. 4851da177e4SLinus Torvalds Notifier will trigger FIB update, so that 4861da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 48715e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 488e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 4891da177e4SLinus Torvalds 4901da177e4SLinus Torvalds return 0; 4911da177e4SLinus Torvalds } 4921da177e4SLinus Torvalds 493d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 494d6062cbbSThomas Graf { 495d6062cbbSThomas Graf return __inet_insert_ifa(ifa, NULL, 0); 496d6062cbbSThomas Graf } 497d6062cbbSThomas Graf 4981da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 4991da177e4SLinus Torvalds { 500e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 5011da177e4SLinus Torvalds 5021da177e4SLinus Torvalds ASSERT_RTNL(); 5031da177e4SLinus Torvalds 5041da177e4SLinus Torvalds if (!in_dev) { 5051da177e4SLinus Torvalds inet_free_ifa(ifa); 5061da177e4SLinus Torvalds return -ENOBUFS; 5071da177e4SLinus Torvalds } 50871e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 5091d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 5101da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 511547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 5121da177e4SLinus Torvalds in_dev_hold(in_dev); 5131da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 5141da177e4SLinus Torvalds } 515f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 5161da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 5171da177e4SLinus Torvalds return inet_insert_ifa(ifa); 5181da177e4SLinus Torvalds } 5191da177e4SLinus Torvalds 5208723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5218723e1b4SEric Dumazet * We dont take a reference on found in_device 5228723e1b4SEric Dumazet */ 5237fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5241da177e4SLinus Torvalds { 5251da177e4SLinus Torvalds struct net_device *dev; 5261da177e4SLinus Torvalds struct in_device *in_dev = NULL; 527c148fc2eSEric Dumazet 528c148fc2eSEric Dumazet rcu_read_lock(); 529c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5301da177e4SLinus Torvalds if (dev) 5318723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 532c148fc2eSEric Dumazet rcu_read_unlock(); 5331da177e4SLinus Torvalds return in_dev; 5341da177e4SLinus Torvalds } 5359f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5361da177e4SLinus Torvalds 5371da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5381da177e4SLinus Torvalds 53960cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 54060cad5daSAl Viro __be32 mask) 5411da177e4SLinus Torvalds { 5421da177e4SLinus Torvalds ASSERT_RTNL(); 5431da177e4SLinus Torvalds 5441da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5451da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5461da177e4SLinus Torvalds return ifa; 5471da177e4SLinus Torvalds } endfor_ifa(in_dev); 5481da177e4SLinus Torvalds return NULL; 5491da177e4SLinus Torvalds } 5501da177e4SLinus Torvalds 55193a714d6SMadhu Challa static int ip_mc_config(struct sock *sk, bool join, const struct in_ifaddr *ifa) 55293a714d6SMadhu Challa { 55393a714d6SMadhu Challa struct ip_mreqn mreq = { 55493a714d6SMadhu Challa .imr_multiaddr.s_addr = ifa->ifa_address, 55593a714d6SMadhu Challa .imr_ifindex = ifa->ifa_dev->dev->ifindex, 55693a714d6SMadhu Challa }; 55793a714d6SMadhu Challa int ret; 55893a714d6SMadhu Challa 55993a714d6SMadhu Challa ASSERT_RTNL(); 56093a714d6SMadhu Challa 56193a714d6SMadhu Challa lock_sock(sk); 56293a714d6SMadhu Challa if (join) 56354ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_join_group(sk, &mreq); 56493a714d6SMadhu Challa else 56554ff9ef3SMarcelo Ricardo Leitner ret = ip_mc_leave_group(sk, &mreq); 56693a714d6SMadhu Challa release_sock(sk); 56793a714d6SMadhu Challa 56893a714d6SMadhu Challa return ret; 56993a714d6SMadhu Challa } 57093a714d6SMadhu Challa 571661d2967SThomas Graf static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh) 5721da177e4SLinus Torvalds { 5733b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 574dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5751da177e4SLinus Torvalds struct in_device *in_dev; 576dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 5771da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 578dfdd5fd4SThomas Graf int err = -EINVAL; 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds ASSERT_RTNL(); 5811da177e4SLinus Torvalds 582dfdd5fd4SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 583dfdd5fd4SThomas Graf if (err < 0) 584dfdd5fd4SThomas Graf goto errout; 585dfdd5fd4SThomas Graf 586dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 5877fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 588dfdd5fd4SThomas Graf if (in_dev == NULL) { 589dfdd5fd4SThomas Graf err = -ENODEV; 590dfdd5fd4SThomas Graf goto errout; 591dfdd5fd4SThomas Graf } 592dfdd5fd4SThomas Graf 5931da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 5941da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 595dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 596a7a628c4SAl Viro ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) 5971da177e4SLinus Torvalds continue; 598dfdd5fd4SThomas Graf 599dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 600dfdd5fd4SThomas Graf continue; 601dfdd5fd4SThomas Graf 602dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 603dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 604a7a628c4SAl Viro !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 605dfdd5fd4SThomas Graf continue; 606dfdd5fd4SThomas Graf 60793a714d6SMadhu Challa if (ipv4_is_multicast(ifa->ifa_address)) 60893a714d6SMadhu Challa ip_mc_config(net->ipv4.mc_autojoin_sk, false, ifa); 60915e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 6101da177e4SLinus Torvalds return 0; 6111da177e4SLinus Torvalds } 612dfdd5fd4SThomas Graf 613dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 614dfdd5fd4SThomas Graf errout: 615dfdd5fd4SThomas Graf return err; 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds 6185c766d64SJiri Pirko #define INFINITY_LIFE_TIME 0xFFFFFFFF 6195c766d64SJiri Pirko 6205c766d64SJiri Pirko static void check_lifetime(struct work_struct *work) 6215c766d64SJiri Pirko { 6225c766d64SJiri Pirko unsigned long now, next, next_sec, next_sched; 6235c766d64SJiri Pirko struct in_ifaddr *ifa; 624c988d1e8SJiri Pirko struct hlist_node *n; 6255c766d64SJiri Pirko int i; 6265c766d64SJiri Pirko 6275c766d64SJiri Pirko now = jiffies; 6285c766d64SJiri Pirko next = round_jiffies_up(now + ADDR_CHECK_FREQUENCY); 6295c766d64SJiri Pirko 6305c766d64SJiri Pirko for (i = 0; i < IN4_ADDR_HSIZE; i++) { 631c988d1e8SJiri Pirko bool change_needed = false; 632c988d1e8SJiri Pirko 633c988d1e8SJiri Pirko rcu_read_lock(); 634b67bfe0dSSasha Levin hlist_for_each_entry_rcu(ifa, &inet_addr_lst[i], hash) { 6355c766d64SJiri Pirko unsigned long age; 6365c766d64SJiri Pirko 6375c766d64SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 6385c766d64SJiri Pirko continue; 6395c766d64SJiri Pirko 6405c766d64SJiri Pirko /* We try to batch several events at once. */ 6415c766d64SJiri Pirko age = (now - ifa->ifa_tstamp + 6425c766d64SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 6435c766d64SJiri Pirko 6445c766d64SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 6455c766d64SJiri Pirko age >= ifa->ifa_valid_lft) { 646c988d1e8SJiri Pirko change_needed = true; 647c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft == 648c988d1e8SJiri Pirko INFINITY_LIFE_TIME) { 649c988d1e8SJiri Pirko continue; 650c988d1e8SJiri Pirko } else if (age >= ifa->ifa_preferred_lft) { 651c988d1e8SJiri Pirko if (time_before(ifa->ifa_tstamp + 652c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ, next)) 653c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 654c988d1e8SJiri Pirko ifa->ifa_valid_lft * HZ; 655c988d1e8SJiri Pirko 656c988d1e8SJiri Pirko if (!(ifa->ifa_flags & IFA_F_DEPRECATED)) 657c988d1e8SJiri Pirko change_needed = true; 658c988d1e8SJiri Pirko } else if (time_before(ifa->ifa_tstamp + 659c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ, 660c988d1e8SJiri Pirko next)) { 661c988d1e8SJiri Pirko next = ifa->ifa_tstamp + 662c988d1e8SJiri Pirko ifa->ifa_preferred_lft * HZ; 663c988d1e8SJiri Pirko } 664c988d1e8SJiri Pirko } 665c988d1e8SJiri Pirko rcu_read_unlock(); 666c988d1e8SJiri Pirko if (!change_needed) 667c988d1e8SJiri Pirko continue; 668c988d1e8SJiri Pirko rtnl_lock(); 669c988d1e8SJiri Pirko hlist_for_each_entry_safe(ifa, n, &inet_addr_lst[i], hash) { 670c988d1e8SJiri Pirko unsigned long age; 671c988d1e8SJiri Pirko 672c988d1e8SJiri Pirko if (ifa->ifa_flags & IFA_F_PERMANENT) 673c988d1e8SJiri Pirko continue; 674c988d1e8SJiri Pirko 675c988d1e8SJiri Pirko /* We try to batch several events at once. */ 676c988d1e8SJiri Pirko age = (now - ifa->ifa_tstamp + 677c988d1e8SJiri Pirko ADDRCONF_TIMER_FUZZ_MINUS) / HZ; 678c988d1e8SJiri Pirko 679c988d1e8SJiri Pirko if (ifa->ifa_valid_lft != INFINITY_LIFE_TIME && 680c988d1e8SJiri Pirko age >= ifa->ifa_valid_lft) { 6815c766d64SJiri Pirko struct in_ifaddr **ifap; 6825c766d64SJiri Pirko 6835c766d64SJiri Pirko for (ifap = &ifa->ifa_dev->ifa_list; 684c988d1e8SJiri Pirko *ifap != NULL; ifap = &(*ifap)->ifa_next) { 685c988d1e8SJiri Pirko if (*ifap == ifa) { 6865c766d64SJiri Pirko inet_del_ifa(ifa->ifa_dev, 6875c766d64SJiri Pirko ifap, 1); 688c988d1e8SJiri Pirko break; 6895c766d64SJiri Pirko } 690c988d1e8SJiri Pirko } 691c988d1e8SJiri Pirko } else if (ifa->ifa_preferred_lft != 692c988d1e8SJiri Pirko INFINITY_LIFE_TIME && 693c988d1e8SJiri Pirko age >= ifa->ifa_preferred_lft && 694c988d1e8SJiri Pirko !(ifa->ifa_flags & IFA_F_DEPRECATED)) { 6955c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 6965c766d64SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 6975c766d64SJiri Pirko } 6985c766d64SJiri Pirko } 699c988d1e8SJiri Pirko rtnl_unlock(); 7005c766d64SJiri Pirko } 7015c766d64SJiri Pirko 7025c766d64SJiri Pirko next_sec = round_jiffies_up(next); 7035c766d64SJiri Pirko next_sched = next; 7045c766d64SJiri Pirko 7055c766d64SJiri Pirko /* If rounded timeout is accurate enough, accept it. */ 7065c766d64SJiri Pirko if (time_before(next_sec, next + ADDRCONF_TIMER_FUZZ)) 7075c766d64SJiri Pirko next_sched = next_sec; 7085c766d64SJiri Pirko 7095c766d64SJiri Pirko now = jiffies; 7105c766d64SJiri Pirko /* And minimum interval is ADDRCONF_TIMER_FUZZ_MAX. */ 7115c766d64SJiri Pirko if (time_before(next_sched, now + ADDRCONF_TIMER_FUZZ_MAX)) 7125c766d64SJiri Pirko next_sched = now + ADDRCONF_TIMER_FUZZ_MAX; 7135c766d64SJiri Pirko 714906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 715906e073fSviresh kumar next_sched - now); 7165c766d64SJiri Pirko } 7175c766d64SJiri Pirko 7185c766d64SJiri Pirko static void set_ifa_lifetime(struct in_ifaddr *ifa, __u32 valid_lft, 7195c766d64SJiri Pirko __u32 prefered_lft) 7205c766d64SJiri Pirko { 7215c766d64SJiri Pirko unsigned long timeout; 7225c766d64SJiri Pirko 7235c766d64SJiri Pirko ifa->ifa_flags &= ~(IFA_F_PERMANENT | IFA_F_DEPRECATED); 7245c766d64SJiri Pirko 7255c766d64SJiri Pirko timeout = addrconf_timeout_fixup(valid_lft, HZ); 7265c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) 7275c766d64SJiri Pirko ifa->ifa_valid_lft = timeout; 7285c766d64SJiri Pirko else 7295c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_PERMANENT; 7305c766d64SJiri Pirko 7315c766d64SJiri Pirko timeout = addrconf_timeout_fixup(prefered_lft, HZ); 7325c766d64SJiri Pirko if (addrconf_finite_timeout(timeout)) { 7335c766d64SJiri Pirko if (timeout == 0) 7345c766d64SJiri Pirko ifa->ifa_flags |= IFA_F_DEPRECATED; 7355c766d64SJiri Pirko ifa->ifa_preferred_lft = timeout; 7365c766d64SJiri Pirko } 7375c766d64SJiri Pirko ifa->ifa_tstamp = jiffies; 7385c766d64SJiri Pirko if (!ifa->ifa_cstamp) 7395c766d64SJiri Pirko ifa->ifa_cstamp = ifa->ifa_tstamp; 7405c766d64SJiri Pirko } 7415c766d64SJiri Pirko 7425c766d64SJiri Pirko static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh, 7435c766d64SJiri Pirko __u32 *pvalid_lft, __u32 *pprefered_lft) 7441da177e4SLinus Torvalds { 7455c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 7465c753978SThomas Graf struct in_ifaddr *ifa; 7475c753978SThomas Graf struct ifaddrmsg *ifm; 7481da177e4SLinus Torvalds struct net_device *dev; 7491da177e4SLinus Torvalds struct in_device *in_dev; 7507b218574SDenis V. Lunev int err; 7511da177e4SLinus Torvalds 7525c753978SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 7535c753978SThomas Graf if (err < 0) 7545c753978SThomas Graf goto errout; 7551da177e4SLinus Torvalds 7565c753978SThomas Graf ifm = nlmsg_data(nlh); 757c4e38f41SEvgeniy Polyakov err = -EINVAL; 7587b218574SDenis V. Lunev if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) 7595c753978SThomas Graf goto errout; 7601da177e4SLinus Torvalds 7614b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 7625c753978SThomas Graf err = -ENODEV; 7637b218574SDenis V. Lunev if (dev == NULL) 7645c753978SThomas Graf goto errout; 7651da177e4SLinus Torvalds 7665c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 7675c753978SThomas Graf err = -ENOBUFS; 7687b218574SDenis V. Lunev if (in_dev == NULL) 7695c753978SThomas Graf goto errout; 77071e27da9SHerbert Xu 7715c753978SThomas Graf ifa = inet_alloc_ifa(); 7727b218574SDenis V. Lunev if (ifa == NULL) 7735c753978SThomas Graf /* 7745c753978SThomas Graf * A potential indev allocation can be left alive, it stays 7755c753978SThomas Graf * assigned to its device and is destroy with it. 7765c753978SThomas Graf */ 7775c753978SThomas Graf goto errout; 7785c753978SThomas Graf 779a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 7801d4c8c29SJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 7815c753978SThomas Graf in_dev_hold(in_dev); 7825c753978SThomas Graf 7835c753978SThomas Graf if (tb[IFA_ADDRESS] == NULL) 7845c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 7855c753978SThomas Graf 786fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 7871da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 7881da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 789ad6c8135SJiri Pirko ifa->ifa_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) : 790ad6c8135SJiri Pirko ifm->ifa_flags; 7911da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 7921da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 7935c753978SThomas Graf 794a7a628c4SAl Viro ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); 795a7a628c4SAl Viro ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); 7965c753978SThomas Graf 7975c753978SThomas Graf if (tb[IFA_BROADCAST]) 798a7a628c4SAl Viro ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); 7995c753978SThomas Graf 8005c753978SThomas Graf if (tb[IFA_LABEL]) 8015c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 8021da177e4SLinus Torvalds else 8031da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 8041da177e4SLinus Torvalds 8055c766d64SJiri Pirko if (tb[IFA_CACHEINFO]) { 8065c766d64SJiri Pirko struct ifa_cacheinfo *ci; 8075c766d64SJiri Pirko 8085c766d64SJiri Pirko ci = nla_data(tb[IFA_CACHEINFO]); 8095c766d64SJiri Pirko if (!ci->ifa_valid || ci->ifa_prefered > ci->ifa_valid) { 8105c766d64SJiri Pirko err = -EINVAL; 811446266b0SDaniel Borkmann goto errout_free; 8125c766d64SJiri Pirko } 8135c766d64SJiri Pirko *pvalid_lft = ci->ifa_valid; 8145c766d64SJiri Pirko *pprefered_lft = ci->ifa_prefered; 8155c766d64SJiri Pirko } 8165c766d64SJiri Pirko 8175c753978SThomas Graf return ifa; 8185c753978SThomas Graf 819446266b0SDaniel Borkmann errout_free: 820446266b0SDaniel Borkmann inet_free_ifa(ifa); 8215c753978SThomas Graf errout: 8225c753978SThomas Graf return ERR_PTR(err); 8235c753978SThomas Graf } 8245c753978SThomas Graf 8255c766d64SJiri Pirko static struct in_ifaddr *find_matching_ifa(struct in_ifaddr *ifa) 8265c766d64SJiri Pirko { 8275c766d64SJiri Pirko struct in_device *in_dev = ifa->ifa_dev; 8285c766d64SJiri Pirko struct in_ifaddr *ifa1, **ifap; 8295c766d64SJiri Pirko 8305c766d64SJiri Pirko if (!ifa->ifa_local) 8315c766d64SJiri Pirko return NULL; 8325c766d64SJiri Pirko 8335c766d64SJiri Pirko for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 8345c766d64SJiri Pirko ifap = &ifa1->ifa_next) { 8355c766d64SJiri Pirko if (ifa1->ifa_mask == ifa->ifa_mask && 8365c766d64SJiri Pirko inet_ifa_match(ifa1->ifa_address, ifa) && 8375c766d64SJiri Pirko ifa1->ifa_local == ifa->ifa_local) 8385c766d64SJiri Pirko return ifa1; 8395c766d64SJiri Pirko } 8405c766d64SJiri Pirko return NULL; 8415c766d64SJiri Pirko } 8425c766d64SJiri Pirko 843661d2967SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh) 8445c753978SThomas Graf { 8453b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 8465c753978SThomas Graf struct in_ifaddr *ifa; 8475c766d64SJiri Pirko struct in_ifaddr *ifa_existing; 8485c766d64SJiri Pirko __u32 valid_lft = INFINITY_LIFE_TIME; 8495c766d64SJiri Pirko __u32 prefered_lft = INFINITY_LIFE_TIME; 8505c753978SThomas Graf 8515c753978SThomas Graf ASSERT_RTNL(); 8525c753978SThomas Graf 8535c766d64SJiri Pirko ifa = rtm_to_ifaddr(net, nlh, &valid_lft, &prefered_lft); 8545c753978SThomas Graf if (IS_ERR(ifa)) 8555c753978SThomas Graf return PTR_ERR(ifa); 8565c753978SThomas Graf 8575c766d64SJiri Pirko ifa_existing = find_matching_ifa(ifa); 8585c766d64SJiri Pirko if (!ifa_existing) { 8595c766d64SJiri Pirko /* It would be best to check for !NLM_F_CREATE here but 860614d056cSstephen hemminger * userspace already relies on not having to provide this. 8615c766d64SJiri Pirko */ 8625c766d64SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 86393a714d6SMadhu Challa if (ifa->ifa_flags & IFA_F_MCAUTOJOIN) { 86493a714d6SMadhu Challa int ret = ip_mc_config(net->ipv4.mc_autojoin_sk, 86593a714d6SMadhu Challa true, ifa); 86693a714d6SMadhu Challa 86793a714d6SMadhu Challa if (ret < 0) { 86893a714d6SMadhu Challa inet_free_ifa(ifa); 86993a714d6SMadhu Challa return ret; 87093a714d6SMadhu Challa } 87193a714d6SMadhu Challa } 87215e47304SEric W. Biederman return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); 8735c766d64SJiri Pirko } else { 8745c766d64SJiri Pirko inet_free_ifa(ifa); 8755c766d64SJiri Pirko 8765c766d64SJiri Pirko if (nlh->nlmsg_flags & NLM_F_EXCL || 8775c766d64SJiri Pirko !(nlh->nlmsg_flags & NLM_F_REPLACE)) 8785c766d64SJiri Pirko return -EEXIST; 87934e2ed34SJiri Pirko ifa = ifa_existing; 88034e2ed34SJiri Pirko set_ifa_lifetime(ifa, valid_lft, prefered_lft); 88105a324b9SJiri Pirko cancel_delayed_work(&check_lifetime_work); 882906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, 883906e073fSviresh kumar &check_lifetime_work, 0); 88434e2ed34SJiri Pirko rtmsg_ifa(RTM_NEWADDR, ifa, nlh, NETLINK_CB(skb).portid); 88534e2ed34SJiri Pirko blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 8865c766d64SJiri Pirko } 8875c766d64SJiri Pirko return 0; 8881da177e4SLinus Torvalds } 8891da177e4SLinus Torvalds 8901da177e4SLinus Torvalds /* 8911da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 8921da177e4SLinus Torvalds */ 8931da177e4SLinus Torvalds 89440384999SEric Dumazet static int inet_abc_len(__be32 addr) 8951da177e4SLinus Torvalds { 8961da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 8971da177e4SLinus Torvalds 898f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 8991da177e4SLinus Torvalds rc = 0; 9001da177e4SLinus Torvalds else { 901714e85beSAl Viro __u32 haddr = ntohl(addr); 9021da177e4SLinus Torvalds 903714e85beSAl Viro if (IN_CLASSA(haddr)) 9041da177e4SLinus Torvalds rc = 8; 905714e85beSAl Viro else if (IN_CLASSB(haddr)) 9061da177e4SLinus Torvalds rc = 16; 907714e85beSAl Viro else if (IN_CLASSC(haddr)) 9081da177e4SLinus Torvalds rc = 24; 9091da177e4SLinus Torvalds } 9101da177e4SLinus Torvalds 9111da177e4SLinus Torvalds return rc; 9121da177e4SLinus Torvalds } 9131da177e4SLinus Torvalds 9141da177e4SLinus Torvalds 915e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) 9161da177e4SLinus Torvalds { 9171da177e4SLinus Torvalds struct ifreq ifr; 9181da177e4SLinus Torvalds struct sockaddr_in sin_orig; 9191da177e4SLinus Torvalds struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 9201da177e4SLinus Torvalds struct in_device *in_dev; 9211da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 9221da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 9231da177e4SLinus Torvalds struct net_device *dev; 9241da177e4SLinus Torvalds char *colon; 9251da177e4SLinus Torvalds int ret = -EFAULT; 9261da177e4SLinus Torvalds int tryaddrmatch = 0; 9271da177e4SLinus Torvalds 9281da177e4SLinus Torvalds /* 9291da177e4SLinus Torvalds * Fetch the caller's info block into kernel space 9301da177e4SLinus Torvalds */ 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 9331da177e4SLinus Torvalds goto out; 9341da177e4SLinus Torvalds ifr.ifr_name[IFNAMSIZ - 1] = 0; 9351da177e4SLinus Torvalds 9361da177e4SLinus Torvalds /* save original address for comparison */ 9371da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 9381da177e4SLinus Torvalds 9391da177e4SLinus Torvalds colon = strchr(ifr.ifr_name, ':'); 9401da177e4SLinus Torvalds if (colon) 9411da177e4SLinus Torvalds *colon = 0; 9421da177e4SLinus Torvalds 943e5b13cb1SDenis V. Lunev dev_load(net, ifr.ifr_name); 9441da177e4SLinus Torvalds 9451da177e4SLinus Torvalds switch (cmd) { 9461da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 9471da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 9481da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 9491da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 9501da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 9511da177e4SLinus Torvalds so that we do not impose a lock. 9521da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 9531da177e4SLinus Torvalds */ 9541da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 9551da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 9561da177e4SLinus Torvalds sin->sin_family = AF_INET; 9571da177e4SLinus Torvalds break; 9581da177e4SLinus Torvalds 9591da177e4SLinus Torvalds case SIOCSIFFLAGS: 960bf5b30b8SZhao Hongjiang ret = -EPERM; 96152e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 9621da177e4SLinus Torvalds goto out; 9631da177e4SLinus Torvalds break; 9641da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 9651da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 9661da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 9671da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 968bf5b30b8SZhao Hongjiang ret = -EPERM; 96952e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 9701da177e4SLinus Torvalds goto out; 9711da177e4SLinus Torvalds ret = -EINVAL; 9721da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 9731da177e4SLinus Torvalds goto out; 9741da177e4SLinus Torvalds break; 9751da177e4SLinus Torvalds default: 9761da177e4SLinus Torvalds ret = -EINVAL; 9771da177e4SLinus Torvalds goto out; 9781da177e4SLinus Torvalds } 9791da177e4SLinus Torvalds 9801da177e4SLinus Torvalds rtnl_lock(); 9811da177e4SLinus Torvalds 9821da177e4SLinus Torvalds ret = -ENODEV; 9839f9354b9SEric Dumazet dev = __dev_get_by_name(net, ifr.ifr_name); 9849f9354b9SEric Dumazet if (!dev) 9851da177e4SLinus Torvalds goto done; 9861da177e4SLinus Torvalds 9871da177e4SLinus Torvalds if (colon) 9881da177e4SLinus Torvalds *colon = ':'; 9891da177e4SLinus Torvalds 9909f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 9919f9354b9SEric Dumazet if (in_dev) { 9921da177e4SLinus Torvalds if (tryaddrmatch) { 9931da177e4SLinus Torvalds /* Matthias Andree */ 9941da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 9951da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 9961da177e4SLinus Torvalds and only if the original address family was AF_INET. 9971da177e4SLinus Torvalds This is checked above. */ 9981da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 9991da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 10001da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label) && 10011da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 10026c91afe1SDavid S. Miller ifa->ifa_local) { 10031da177e4SLinus Torvalds break; /* found */ 10041da177e4SLinus Torvalds } 10051da177e4SLinus Torvalds } 10061da177e4SLinus Torvalds } 10071da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 10081da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 10091da177e4SLinus Torvalds comparing just the label */ 10101da177e4SLinus Torvalds if (!ifa) { 10111da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 10121da177e4SLinus Torvalds ifap = &ifa->ifa_next) 10131da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label)) 10141da177e4SLinus Torvalds break; 10151da177e4SLinus Torvalds } 10161da177e4SLinus Torvalds } 10171da177e4SLinus Torvalds 10181da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 10191da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 10201da177e4SLinus Torvalds goto done; 10211da177e4SLinus Torvalds 10221da177e4SLinus Torvalds switch (cmd) { 10231da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 10241da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 10251da177e4SLinus Torvalds goto rarok; 10261da177e4SLinus Torvalds 10271da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 10281da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 10291da177e4SLinus Torvalds goto rarok; 10301da177e4SLinus Torvalds 10311da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 10321da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 10331da177e4SLinus Torvalds goto rarok; 10341da177e4SLinus Torvalds 10351da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 10361da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 10371da177e4SLinus Torvalds goto rarok; 10381da177e4SLinus Torvalds 10391da177e4SLinus Torvalds case SIOCSIFFLAGS: 10401da177e4SLinus Torvalds if (colon) { 10411da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 10421da177e4SLinus Torvalds if (!ifa) 10431da177e4SLinus Torvalds break; 10441da177e4SLinus Torvalds ret = 0; 10451da177e4SLinus Torvalds if (!(ifr.ifr_flags & IFF_UP)) 10461da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 10471da177e4SLinus Torvalds break; 10481da177e4SLinus Torvalds } 10491da177e4SLinus Torvalds ret = dev_change_flags(dev, ifr.ifr_flags); 10501da177e4SLinus Torvalds break; 10511da177e4SLinus Torvalds 10521da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 10531da177e4SLinus Torvalds ret = -EINVAL; 10541da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 10551da177e4SLinus Torvalds break; 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds if (!ifa) { 10581da177e4SLinus Torvalds ret = -ENOBUFS; 10599f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 10609f9354b9SEric Dumazet if (!ifa) 10611da177e4SLinus Torvalds break; 1062c7e2e1d7SXi Wang INIT_HLIST_NODE(&ifa->hash); 10631da177e4SLinus Torvalds if (colon) 10641da177e4SLinus Torvalds memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); 10651da177e4SLinus Torvalds else 10661da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 10671da177e4SLinus Torvalds } else { 10681da177e4SLinus Torvalds ret = 0; 10691da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 10701da177e4SLinus Torvalds break; 10711da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10721da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 1073148f9729SBjorn Mork ifa->ifa_scope = 0; 10741da177e4SLinus Torvalds } 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 10771da177e4SLinus Torvalds 10781da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 10791da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 10801da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 10811da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 10821da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 10831da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 10841da177e4SLinus Torvalds ~ifa->ifa_mask; 10851da177e4SLinus Torvalds } else { 10861da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 10871da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 10881da177e4SLinus Torvalds } 10895c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, INFINITY_LIFE_TIME); 10901da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 10911da177e4SLinus Torvalds break; 10921da177e4SLinus Torvalds 10931da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 10941da177e4SLinus Torvalds ret = 0; 10951da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 10961da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 10971da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 10981da177e4SLinus Torvalds inet_insert_ifa(ifa); 10991da177e4SLinus Torvalds } 11001da177e4SLinus Torvalds break; 11011da177e4SLinus Torvalds 11021da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 11031da177e4SLinus Torvalds ret = 0; 11041da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 11051da177e4SLinus Torvalds break; 11061da177e4SLinus Torvalds ret = -EINVAL; 11071da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 11081da177e4SLinus Torvalds break; 11091da177e4SLinus Torvalds ret = 0; 11101da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11111da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 11121da177e4SLinus Torvalds inet_insert_ifa(ifa); 11131da177e4SLinus Torvalds break; 11141da177e4SLinus Torvalds 11151da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 11161da177e4SLinus Torvalds 11171da177e4SLinus Torvalds /* 11181da177e4SLinus Torvalds * The mask we set must be legal. 11191da177e4SLinus Torvalds */ 11201da177e4SLinus Torvalds ret = -EINVAL; 11211da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 11221da177e4SLinus Torvalds break; 11231da177e4SLinus Torvalds ret = 0; 11241da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 1125a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 11261da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 11271da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 11281da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 11291da177e4SLinus Torvalds 11301da177e4SLinus Torvalds /* See if current broadcast address matches 11311da177e4SLinus Torvalds * with current netmask, then recalculate 11321da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 11331da177e4SLinus Torvalds * funny address, so don't touch it since 11341da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 11351da177e4SLinus Torvalds */ 11361da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 11371da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 11381da177e4SLinus Torvalds (ifa->ifa_broadcast == 1139dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 11401da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 11411da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 11421da177e4SLinus Torvalds } 11431da177e4SLinus Torvalds inet_insert_ifa(ifa); 11441da177e4SLinus Torvalds } 11451da177e4SLinus Torvalds break; 11461da177e4SLinus Torvalds } 11471da177e4SLinus Torvalds done: 11481da177e4SLinus Torvalds rtnl_unlock(); 11491da177e4SLinus Torvalds out: 11501da177e4SLinus Torvalds return ret; 11511da177e4SLinus Torvalds rarok: 11521da177e4SLinus Torvalds rtnl_unlock(); 11531da177e4SLinus Torvalds ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; 11541da177e4SLinus Torvalds goto out; 11551da177e4SLinus Torvalds } 11561da177e4SLinus Torvalds 11571da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 11581da177e4SLinus Torvalds { 1159e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 11601da177e4SLinus Torvalds struct in_ifaddr *ifa; 11611da177e4SLinus Torvalds struct ifreq ifr; 11621da177e4SLinus Torvalds int done = 0; 11631da177e4SLinus Torvalds 11649f9354b9SEric Dumazet if (!in_dev) 11651da177e4SLinus Torvalds goto out; 11661da177e4SLinus Torvalds 11679f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 11681da177e4SLinus Torvalds if (!buf) { 11691da177e4SLinus Torvalds done += sizeof(ifr); 11701da177e4SLinus Torvalds continue; 11711da177e4SLinus Torvalds } 11721da177e4SLinus Torvalds if (len < (int) sizeof(ifr)) 11731da177e4SLinus Torvalds break; 11741da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 11751da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 11761da177e4SLinus Torvalds 11771da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 11781da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 11791da177e4SLinus Torvalds ifa->ifa_local; 11801da177e4SLinus Torvalds 11811da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 11821da177e4SLinus Torvalds done = -EFAULT; 11831da177e4SLinus Torvalds break; 11841da177e4SLinus Torvalds } 11851da177e4SLinus Torvalds buf += sizeof(struct ifreq); 11861da177e4SLinus Torvalds len -= sizeof(struct ifreq); 11871da177e4SLinus Torvalds done += sizeof(struct ifreq); 11881da177e4SLinus Torvalds } 11891da177e4SLinus Torvalds out: 11901da177e4SLinus Torvalds return done; 11911da177e4SLinus Torvalds } 11921da177e4SLinus Torvalds 1193a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 11941da177e4SLinus Torvalds { 1195a61ced5dSAl Viro __be32 addr = 0; 11961da177e4SLinus Torvalds struct in_device *in_dev; 1197c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 11981da177e4SLinus Torvalds 11991da177e4SLinus Torvalds rcu_read_lock(); 1200e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 12011da177e4SLinus Torvalds if (!in_dev) 12021da177e4SLinus Torvalds goto no_in_dev; 12031da177e4SLinus Torvalds 12041da177e4SLinus Torvalds for_primary_ifa(in_dev) { 12051da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 12061da177e4SLinus Torvalds continue; 12071da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 12081da177e4SLinus Torvalds addr = ifa->ifa_local; 12091da177e4SLinus Torvalds break; 12101da177e4SLinus Torvalds } 12111da177e4SLinus Torvalds if (!addr) 12121da177e4SLinus Torvalds addr = ifa->ifa_local; 12131da177e4SLinus Torvalds } endfor_ifa(in_dev); 12141da177e4SLinus Torvalds 12151da177e4SLinus Torvalds if (addr) 1216c6d14c84SEric Dumazet goto out_unlock; 12179f9354b9SEric Dumazet no_in_dev: 12181da177e4SLinus Torvalds 12191da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 1220ca9f1fd2SStephen Hemminger in this case. It is important that lo is the first interface 12211da177e4SLinus Torvalds in dev_base list. 12221da177e4SLinus Torvalds */ 1223c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 12249f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12259f9354b9SEric Dumazet if (!in_dev) 12261da177e4SLinus Torvalds continue; 12271da177e4SLinus Torvalds 12281da177e4SLinus Torvalds for_primary_ifa(in_dev) { 12291da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 12301da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 12311da177e4SLinus Torvalds addr = ifa->ifa_local; 1232c6d14c84SEric Dumazet goto out_unlock; 12331da177e4SLinus Torvalds } 12341da177e4SLinus Torvalds } endfor_ifa(in_dev); 12351da177e4SLinus Torvalds } 1236c6d14c84SEric Dumazet out_unlock: 12371da177e4SLinus Torvalds rcu_read_unlock(); 12381da177e4SLinus Torvalds return addr; 12391da177e4SLinus Torvalds } 12409f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 12411da177e4SLinus Torvalds 124260cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 124360cad5daSAl Viro __be32 local, int scope) 12441da177e4SLinus Torvalds { 12451da177e4SLinus Torvalds int same = 0; 1246a144ea4bSAl Viro __be32 addr = 0; 12471da177e4SLinus Torvalds 12481da177e4SLinus Torvalds for_ifa(in_dev) { 12491da177e4SLinus Torvalds if (!addr && 12501da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 12511da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 12521da177e4SLinus Torvalds addr = ifa->ifa_local; 12531da177e4SLinus Torvalds if (same) 12541da177e4SLinus Torvalds break; 12551da177e4SLinus Torvalds } 12561da177e4SLinus Torvalds if (!same) { 12571da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 12581da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 12591da177e4SLinus Torvalds if (same && addr) { 12601da177e4SLinus Torvalds if (local || !dst) 12611da177e4SLinus Torvalds break; 12621da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 12631da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 12641da177e4SLinus Torvalds break; 12651da177e4SLinus Torvalds /* No, then can we use new local src? */ 12661da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 12671da177e4SLinus Torvalds addr = ifa->ifa_local; 12681da177e4SLinus Torvalds break; 12691da177e4SLinus Torvalds } 12701da177e4SLinus Torvalds /* search for large dst subnet for addr */ 12711da177e4SLinus Torvalds same = 0; 12721da177e4SLinus Torvalds } 12731da177e4SLinus Torvalds } 12741da177e4SLinus Torvalds } endfor_ifa(in_dev); 12751da177e4SLinus Torvalds 12761da177e4SLinus Torvalds return same ? addr : 0; 12771da177e4SLinus Torvalds } 12781da177e4SLinus Torvalds 12791da177e4SLinus Torvalds /* 12801da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 1281b601fa19SNicolas Dichtel * - net: netns to check, cannot be NULL 1282b601fa19SNicolas Dichtel * - in_dev: only on this interface, NULL=any interface 12831da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 12841da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 12851da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 12861da177e4SLinus Torvalds */ 1287b601fa19SNicolas Dichtel __be32 inet_confirm_addr(struct net *net, struct in_device *in_dev, 12889bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 12891da177e4SLinus Torvalds { 129060cad5daSAl Viro __be32 addr = 0; 12919bd85e32SDenis V. Lunev struct net_device *dev; 12921da177e4SLinus Torvalds 1293b601fa19SNicolas Dichtel if (in_dev != NULL) 12949bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 12951da177e4SLinus Torvalds 12961da177e4SLinus Torvalds rcu_read_lock(); 1297c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 12989f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 12999f9354b9SEric Dumazet if (in_dev) { 13001da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 13011da177e4SLinus Torvalds if (addr) 13021da177e4SLinus Torvalds break; 13031da177e4SLinus Torvalds } 13041da177e4SLinus Torvalds } 13051da177e4SLinus Torvalds rcu_read_unlock(); 13061da177e4SLinus Torvalds 13071da177e4SLinus Torvalds return addr; 13081da177e4SLinus Torvalds } 1309eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 13101da177e4SLinus Torvalds 13111da177e4SLinus Torvalds /* 13121da177e4SLinus Torvalds * Device notifier 13131da177e4SLinus Torvalds */ 13141da177e4SLinus Torvalds 13151da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 13161da177e4SLinus Torvalds { 1317e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 13181da177e4SLinus Torvalds } 13199f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 13201da177e4SLinus Torvalds 13211da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 13221da177e4SLinus Torvalds { 1323e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 13241da177e4SLinus Torvalds } 13259f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 13261da177e4SLinus Torvalds 13279f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 13289f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 13291da177e4SLinus Torvalds */ 13301da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 13311da177e4SLinus Torvalds { 13321da177e4SLinus Torvalds struct in_ifaddr *ifa; 13331da177e4SLinus Torvalds int named = 0; 13341da177e4SLinus Torvalds 13351da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 13361da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 13371da177e4SLinus Torvalds 13381da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 13391da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 13401da177e4SLinus Torvalds if (named++ == 0) 1341573bf470SThomas Graf goto skip; 134244344b2aSMark McLoughlin dot = strchr(old, ':'); 13431da177e4SLinus Torvalds if (dot == NULL) { 13441da177e4SLinus Torvalds sprintf(old, ":%d", named); 13451da177e4SLinus Torvalds dot = old; 13461da177e4SLinus Torvalds } 13479f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 13481da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 13499f9354b9SEric Dumazet else 13501da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1351573bf470SThomas Graf skip: 1352573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 13531da177e4SLinus Torvalds } 13541da177e4SLinus Torvalds } 13551da177e4SLinus Torvalds 135640384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu) 135706770843SBreno Leitao { 135806770843SBreno Leitao return mtu >= 68; 135906770843SBreno Leitao } 136006770843SBreno Leitao 1361d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1362d11327adSIan Campbell struct in_device *in_dev) 1363d11327adSIan Campbell 1364d11327adSIan Campbell { 1365b76d0789SZoltan Kiss struct in_ifaddr *ifa; 1366d11327adSIan Campbell 1367b76d0789SZoltan Kiss for (ifa = in_dev->ifa_list; ifa; 1368b76d0789SZoltan Kiss ifa = ifa->ifa_next) { 1369d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 13706c91afe1SDavid S. Miller ifa->ifa_local, dev, 13716c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1372d11327adSIan Campbell dev->dev_addr, NULL); 1373d11327adSIan Campbell } 1374b76d0789SZoltan Kiss } 1375d11327adSIan Campbell 13761da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 13771da177e4SLinus Torvalds 13781da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 13791da177e4SLinus Torvalds void *ptr) 13801da177e4SLinus Torvalds { 1381351638e7SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 1382748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 13831da177e4SLinus Torvalds 13841da177e4SLinus Torvalds ASSERT_RTNL(); 13851da177e4SLinus Torvalds 13861da177e4SLinus Torvalds if (!in_dev) { 13878030f544SHerbert Xu if (event == NETDEV_REGISTER) { 13881da177e4SLinus Torvalds in_dev = inetdev_init(dev); 138920e61da7SWANG Cong if (IS_ERR(in_dev)) 139020e61da7SWANG Cong return notifier_from_errno(PTR_ERR(in_dev)); 13910cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 139242f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 139342f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 13941da177e4SLinus Torvalds } 139506770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 139606770843SBreno Leitao /* Re-enabling IP */ 139706770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 139806770843SBreno Leitao in_dev = inetdev_init(dev); 13998030f544SHerbert Xu } 14001da177e4SLinus Torvalds goto out; 14011da177e4SLinus Torvalds } 14021da177e4SLinus Torvalds 14031da177e4SLinus Torvalds switch (event) { 14041da177e4SLinus Torvalds case NETDEV_REGISTER: 140591df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1406a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 14071da177e4SLinus Torvalds break; 14081da177e4SLinus Torvalds case NETDEV_UP: 140906770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 14101da177e4SLinus Torvalds break; 14110cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 14129f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 14139f9354b9SEric Dumazet 14149f9354b9SEric Dumazet if (ifa) { 1415fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 14161da177e4SLinus Torvalds ifa->ifa_local = 14171da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 14181da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 14191da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 14201da177e4SLinus Torvalds in_dev_hold(in_dev); 14211da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 14221da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 14231da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 14245c766d64SJiri Pirko set_ifa_lifetime(ifa, INFINITY_LIFE_TIME, 14255c766d64SJiri Pirko INFINITY_LIFE_TIME); 1426dfd1582dSJiri Pirko ipv4_devconf_setall(in_dev); 1427dfd1582dSJiri Pirko neigh_parms_data_state_setall(in_dev->arp_parms); 14281da177e4SLinus Torvalds inet_insert_ifa(ifa); 14291da177e4SLinus Torvalds } 14301da177e4SLinus Torvalds } 14311da177e4SLinus Torvalds ip_mc_up(in_dev); 1432eefef1cfSStephen Hemminger /* fall through */ 1433eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1434d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1435d11327adSIan Campbell break; 1436d11327adSIan Campbell /* fall through */ 1437d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1438a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1439d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 14401da177e4SLinus Torvalds break; 14411da177e4SLinus Torvalds case NETDEV_DOWN: 14421da177e4SLinus Torvalds ip_mc_down(in_dev); 14431da177e4SLinus Torvalds break; 144493d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 144575c78500SMoni Shoua ip_mc_unmap(in_dev); 144675c78500SMoni Shoua break; 144793d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 144875c78500SMoni Shoua ip_mc_remap(in_dev); 144975c78500SMoni Shoua break; 14501da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 145106770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 14521da177e4SLinus Torvalds break; 145306770843SBreno Leitao /* disable IP when MTU is not enough */ 14541da177e4SLinus Torvalds case NETDEV_UNREGISTER: 14551da177e4SLinus Torvalds inetdev_destroy(in_dev); 14561da177e4SLinus Torvalds break; 14571da177e4SLinus Torvalds case NETDEV_CHANGENAME: 14581da177e4SLinus Torvalds /* Do not notify about label change, this event is 14591da177e4SLinus Torvalds * not interesting to applications using netlink. 14601da177e4SLinus Torvalds */ 14611da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 14621da177e4SLinus Torvalds 146351602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 146466f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 14651da177e4SLinus Torvalds break; 14661da177e4SLinus Torvalds } 14671da177e4SLinus Torvalds out: 14681da177e4SLinus Torvalds return NOTIFY_DONE; 14691da177e4SLinus Torvalds } 14701da177e4SLinus Torvalds 14711da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 14721da177e4SLinus Torvalds .notifier_call = inetdev_event, 14731da177e4SLinus Torvalds }; 14741da177e4SLinus Torvalds 147540384999SEric Dumazet static size_t inet_nlmsg_size(void) 1476339bf98fSThomas Graf { 1477339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1478339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1479339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1480339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1481ad6c8135SJiri Pirko + nla_total_size(IFNAMSIZ) /* IFA_LABEL */ 148263b5f152SGeert Uytterhoeven + nla_total_size(4) /* IFA_FLAGS */ 148363b5f152SGeert Uytterhoeven + nla_total_size(sizeof(struct ifa_cacheinfo)); /* IFA_CACHEINFO */ 1484339bf98fSThomas Graf } 1485339bf98fSThomas Graf 14865c766d64SJiri Pirko static inline u32 cstamp_delta(unsigned long cstamp) 14875c766d64SJiri Pirko { 14885c766d64SJiri Pirko return (cstamp - INITIAL_JIFFIES) * 100UL / HZ; 14895c766d64SJiri Pirko } 14905c766d64SJiri Pirko 14915c766d64SJiri Pirko static int put_cacheinfo(struct sk_buff *skb, unsigned long cstamp, 14925c766d64SJiri Pirko unsigned long tstamp, u32 preferred, u32 valid) 14935c766d64SJiri Pirko { 14945c766d64SJiri Pirko struct ifa_cacheinfo ci; 14955c766d64SJiri Pirko 14965c766d64SJiri Pirko ci.cstamp = cstamp_delta(cstamp); 14975c766d64SJiri Pirko ci.tstamp = cstamp_delta(tstamp); 14985c766d64SJiri Pirko ci.ifa_prefered = preferred; 14995c766d64SJiri Pirko ci.ifa_valid = valid; 15005c766d64SJiri Pirko 15015c766d64SJiri Pirko return nla_put(skb, IFA_CACHEINFO, sizeof(ci), &ci); 15025c766d64SJiri Pirko } 15035c766d64SJiri Pirko 15041da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 150515e47304SEric W. Biederman u32 portid, u32 seq, int event, unsigned int flags) 15061da177e4SLinus Torvalds { 15071da177e4SLinus Torvalds struct ifaddrmsg *ifm; 15081da177e4SLinus Torvalds struct nlmsghdr *nlh; 15095c766d64SJiri Pirko u32 preferred, valid; 15101da177e4SLinus Torvalds 151115e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); 151247f68512SThomas Graf if (nlh == NULL) 151326932566SPatrick McHardy return -EMSGSIZE; 151447f68512SThomas Graf 151547f68512SThomas Graf ifm = nlmsg_data(nlh); 15161da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 15171da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 15185c766d64SJiri Pirko ifm->ifa_flags = ifa->ifa_flags; 15191da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 15201da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 15211da177e4SLinus Torvalds 15225c766d64SJiri Pirko if (!(ifm->ifa_flags & IFA_F_PERMANENT)) { 15235c766d64SJiri Pirko preferred = ifa->ifa_preferred_lft; 15245c766d64SJiri Pirko valid = ifa->ifa_valid_lft; 15255c766d64SJiri Pirko if (preferred != INFINITY_LIFE_TIME) { 15265c766d64SJiri Pirko long tval = (jiffies - ifa->ifa_tstamp) / HZ; 15275c766d64SJiri Pirko 15285c766d64SJiri Pirko if (preferred > tval) 15295c766d64SJiri Pirko preferred -= tval; 15305c766d64SJiri Pirko else 15315c766d64SJiri Pirko preferred = 0; 15325c766d64SJiri Pirko if (valid != INFINITY_LIFE_TIME) { 15335c766d64SJiri Pirko if (valid > tval) 15345c766d64SJiri Pirko valid -= tval; 15355c766d64SJiri Pirko else 15365c766d64SJiri Pirko valid = 0; 15375c766d64SJiri Pirko } 15385c766d64SJiri Pirko } 15395c766d64SJiri Pirko } else { 15405c766d64SJiri Pirko preferred = INFINITY_LIFE_TIME; 15415c766d64SJiri Pirko valid = INFINITY_LIFE_TIME; 15425c766d64SJiri Pirko } 1543f3756b79SDavid S. Miller if ((ifa->ifa_address && 1544*930345eaSJiri Benc nla_put_in_addr(skb, IFA_ADDRESS, ifa->ifa_address)) || 1545f3756b79SDavid S. Miller (ifa->ifa_local && 1546*930345eaSJiri Benc nla_put_in_addr(skb, IFA_LOCAL, ifa->ifa_local)) || 1547f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1548*930345eaSJiri Benc nla_put_in_addr(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1549f3756b79SDavid S. Miller (ifa->ifa_label[0] && 15505c766d64SJiri Pirko nla_put_string(skb, IFA_LABEL, ifa->ifa_label)) || 1551ad6c8135SJiri Pirko nla_put_u32(skb, IFA_FLAGS, ifa->ifa_flags) || 15525c766d64SJiri Pirko put_cacheinfo(skb, ifa->ifa_cstamp, ifa->ifa_tstamp, 15535c766d64SJiri Pirko preferred, valid)) 1554f3756b79SDavid S. Miller goto nla_put_failure; 155547f68512SThomas Graf 1556053c095aSJohannes Berg nlmsg_end(skb, nlh); 1557053c095aSJohannes Berg return 0; 155847f68512SThomas Graf 155947f68512SThomas Graf nla_put_failure: 156026932566SPatrick McHardy nlmsg_cancel(skb, nlh); 156126932566SPatrick McHardy return -EMSGSIZE; 15621da177e4SLinus Torvalds } 15631da177e4SLinus Torvalds 15641da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 15651da177e4SLinus Torvalds { 15663b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1567eec4df98SEric Dumazet int h, s_h; 1568eec4df98SEric Dumazet int idx, s_idx; 1569eec4df98SEric Dumazet int ip_idx, s_ip_idx; 15701da177e4SLinus Torvalds struct net_device *dev; 15711da177e4SLinus Torvalds struct in_device *in_dev; 15721da177e4SLinus Torvalds struct in_ifaddr *ifa; 1573eec4df98SEric Dumazet struct hlist_head *head; 15741da177e4SLinus Torvalds 1575eec4df98SEric Dumazet s_h = cb->args[0]; 1576eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1577eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1578eec4df98SEric Dumazet 1579eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 15807562f876SPavel Emelianov idx = 0; 1581eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1582eec4df98SEric Dumazet rcu_read_lock(); 15830465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 15840465277fSNicolas Dichtel net->dev_base_seq; 1585b67bfe0dSSasha Levin hlist_for_each_entry_rcu(dev, head, index_hlist) { 15861da177e4SLinus Torvalds if (idx < s_idx) 15877562f876SPavel Emelianov goto cont; 15884b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 15891da177e4SLinus Torvalds s_ip_idx = 0; 1590eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 15919f9354b9SEric Dumazet if (!in_dev) 15927562f876SPavel Emelianov goto cont; 15931da177e4SLinus Torvalds 15941da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 15951da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 15961da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1597596e4150SStephen Hemminger continue; 1598eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 159915e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 16001da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1601053c095aSJohannes Berg RTM_NEWADDR, NLM_F_MULTI) < 0) { 1602eec4df98SEric Dumazet rcu_read_unlock(); 16031da177e4SLinus Torvalds goto done; 16041da177e4SLinus Torvalds } 16050465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 1606eec4df98SEric Dumazet } 16077562f876SPavel Emelianov cont: 16087562f876SPavel Emelianov idx++; 16091da177e4SLinus Torvalds } 1610eec4df98SEric Dumazet rcu_read_unlock(); 1611eec4df98SEric Dumazet } 16121da177e4SLinus Torvalds 16131da177e4SLinus Torvalds done: 1614eec4df98SEric Dumazet cb->args[0] = h; 1615eec4df98SEric Dumazet cb->args[1] = idx; 1616eec4df98SEric Dumazet cb->args[2] = ip_idx; 16171da177e4SLinus Torvalds 16181da177e4SLinus Torvalds return skb->len; 16191da177e4SLinus Torvalds } 16201da177e4SLinus Torvalds 1621d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 162215e47304SEric W. Biederman u32 portid) 16231da177e4SLinus Torvalds { 162447f68512SThomas Graf struct sk_buff *skb; 1625d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1626d6062cbbSThomas Graf int err = -ENOBUFS; 16274b8aa9abSDenis V. Lunev struct net *net; 16281da177e4SLinus Torvalds 1629c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1630339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 163147f68512SThomas Graf if (skb == NULL) 1632d6062cbbSThomas Graf goto errout; 1633d6062cbbSThomas Graf 163415e47304SEric W. Biederman err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0); 163526932566SPatrick McHardy if (err < 0) { 163626932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 163726932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 163826932566SPatrick McHardy kfree_skb(skb); 163926932566SPatrick McHardy goto errout; 164026932566SPatrick McHardy } 164115e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 16421ce85fe4SPablo Neira Ayuso return; 1643d6062cbbSThomas Graf errout: 1644d6062cbbSThomas Graf if (err < 0) 16454b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 16461da177e4SLinus Torvalds } 16471da177e4SLinus Torvalds 16489f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 16499f0f7272SThomas Graf { 16501fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16519f0f7272SThomas Graf 16529f0f7272SThomas Graf if (!in_dev) 16539f0f7272SThomas Graf return 0; 16549f0f7272SThomas Graf 16559f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 16569f0f7272SThomas Graf } 16579f0f7272SThomas Graf 16589f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 16599f0f7272SThomas Graf { 16601fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 16619f0f7272SThomas Graf struct nlattr *nla; 16629f0f7272SThomas Graf int i; 16639f0f7272SThomas Graf 16649f0f7272SThomas Graf if (!in_dev) 16659f0f7272SThomas Graf return -ENODATA; 16669f0f7272SThomas Graf 16679f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 16689f0f7272SThomas Graf if (nla == NULL) 16699f0f7272SThomas Graf return -EMSGSIZE; 16709f0f7272SThomas Graf 16719f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 16729f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 16739f0f7272SThomas Graf 16749f0f7272SThomas Graf return 0; 16759f0f7272SThomas Graf } 16769f0f7272SThomas Graf 16779f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 16789f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 16799f0f7272SThomas Graf }; 16809f0f7272SThomas Graf 1681cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1682cf7afbfeSThomas Graf const struct nlattr *nla) 16839f0f7272SThomas Graf { 16849f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 16859f0f7272SThomas Graf int err, rem; 16869f0f7272SThomas Graf 1687f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1688cf7afbfeSThomas Graf return -EAFNOSUPPORT; 16899f0f7272SThomas Graf 16909f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 16919f0f7272SThomas Graf if (err < 0) 16929f0f7272SThomas Graf return err; 16939f0f7272SThomas Graf 16949f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 16959f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 16969f0f7272SThomas Graf int cfgid = nla_type(a); 16979f0f7272SThomas Graf 16989f0f7272SThomas Graf if (nla_len(a) < 4) 16999f0f7272SThomas Graf return -EINVAL; 17009f0f7272SThomas Graf 17019f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 17029f0f7272SThomas Graf return -EINVAL; 17039f0f7272SThomas Graf } 17049f0f7272SThomas Graf } 17059f0f7272SThomas Graf 1706cf7afbfeSThomas Graf return 0; 1707cf7afbfeSThomas Graf } 1708cf7afbfeSThomas Graf 1709cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1710cf7afbfeSThomas Graf { 1711f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1712cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1713cf7afbfeSThomas Graf int rem; 1714cf7afbfeSThomas Graf 1715cf7afbfeSThomas Graf if (!in_dev) 1716cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1717cf7afbfeSThomas Graf 1718cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1719cf7afbfeSThomas Graf BUG(); 1720cf7afbfeSThomas Graf 17219f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 17229f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 17239f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 17249f0f7272SThomas Graf } 17259f0f7272SThomas Graf 17269f0f7272SThomas Graf return 0; 17279f0f7272SThomas Graf } 17289f0f7272SThomas Graf 1729edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 1730edc9e748SNicolas Dichtel { 1731edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 1732edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 1733edc9e748SNicolas Dichtel 17349e551110SNicolas Dichtel /* type -1 is used for ALL */ 17359e551110SNicolas Dichtel if (type == -1 || type == NETCONFA_FORWARDING) 1736edc9e748SNicolas Dichtel size += nla_total_size(4); 1737cc535dfbSNicolas Dichtel if (type == -1 || type == NETCONFA_RP_FILTER) 1738cc535dfbSNicolas Dichtel size += nla_total_size(4); 1739d67b8c61SNicolas Dichtel if (type == -1 || type == NETCONFA_MC_FORWARDING) 1740d67b8c61SNicolas Dichtel size += nla_total_size(4); 174109aea5dfSstephen hemminger if (type == -1 || type == NETCONFA_PROXY_NEIGH) 1742f085ff1cSstephen hemminger size += nla_total_size(4); 1743edc9e748SNicolas Dichtel 1744edc9e748SNicolas Dichtel return size; 1745edc9e748SNicolas Dichtel } 1746edc9e748SNicolas Dichtel 1747edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 1748edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 1749edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 1750edc9e748SNicolas Dichtel int type) 1751edc9e748SNicolas Dichtel { 1752edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 1753edc9e748SNicolas Dichtel struct netconfmsg *ncm; 1754edc9e748SNicolas Dichtel 1755edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 1756edc9e748SNicolas Dichtel flags); 1757edc9e748SNicolas Dichtel if (nlh == NULL) 1758edc9e748SNicolas Dichtel return -EMSGSIZE; 1759edc9e748SNicolas Dichtel 1760edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 1761edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 1762edc9e748SNicolas Dichtel 1763edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 1764edc9e748SNicolas Dichtel goto nla_put_failure; 1765edc9e748SNicolas Dichtel 17669e551110SNicolas Dichtel /* type -1 is used for ALL */ 17679e551110SNicolas Dichtel if ((type == -1 || type == NETCONFA_FORWARDING) && 1768edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 1769edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 1770edc9e748SNicolas Dichtel goto nla_put_failure; 1771cc535dfbSNicolas Dichtel if ((type == -1 || type == NETCONFA_RP_FILTER) && 1772cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 1773cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 1774cc535dfbSNicolas Dichtel goto nla_put_failure; 1775d67b8c61SNicolas Dichtel if ((type == -1 || type == NETCONFA_MC_FORWARDING) && 1776d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 1777d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 1778d67b8c61SNicolas Dichtel goto nla_put_failure; 177909aea5dfSstephen hemminger if ((type == -1 || type == NETCONFA_PROXY_NEIGH) && 178009aea5dfSstephen hemminger nla_put_s32(skb, NETCONFA_PROXY_NEIGH, 1781f085ff1cSstephen hemminger IPV4_DEVCONF(*devconf, PROXY_ARP)) < 0) 1782f085ff1cSstephen hemminger goto nla_put_failure; 1783edc9e748SNicolas Dichtel 1784053c095aSJohannes Berg nlmsg_end(skb, nlh); 1785053c095aSJohannes Berg return 0; 1786edc9e748SNicolas Dichtel 1787edc9e748SNicolas Dichtel nla_put_failure: 1788edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 1789edc9e748SNicolas Dichtel return -EMSGSIZE; 1790edc9e748SNicolas Dichtel } 1791edc9e748SNicolas Dichtel 1792d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, 1793edc9e748SNicolas Dichtel struct ipv4_devconf *devconf) 1794edc9e748SNicolas Dichtel { 1795edc9e748SNicolas Dichtel struct sk_buff *skb; 1796edc9e748SNicolas Dichtel int err = -ENOBUFS; 1797edc9e748SNicolas Dichtel 1798edc9e748SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC); 1799edc9e748SNicolas Dichtel if (skb == NULL) 1800edc9e748SNicolas Dichtel goto errout; 1801edc9e748SNicolas Dichtel 1802edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 1803edc9e748SNicolas Dichtel RTM_NEWNETCONF, 0, type); 1804edc9e748SNicolas Dichtel if (err < 0) { 1805edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 1806edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 1807edc9e748SNicolas Dichtel kfree_skb(skb); 1808edc9e748SNicolas Dichtel goto errout; 1809edc9e748SNicolas Dichtel } 1810edc9e748SNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC); 1811edc9e748SNicolas Dichtel return; 1812edc9e748SNicolas Dichtel errout: 1813edc9e748SNicolas Dichtel if (err < 0) 1814edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 1815edc9e748SNicolas Dichtel } 1816edc9e748SNicolas Dichtel 18179e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 18189e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 18199e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 1820cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 182109aea5dfSstephen hemminger [NETCONFA_PROXY_NEIGH] = { .len = sizeof(int) }, 18229e551110SNicolas Dichtel }; 18239e551110SNicolas Dichtel 18249e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 1825661d2967SThomas Graf struct nlmsghdr *nlh) 18269e551110SNicolas Dichtel { 18279e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 18289e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 18299e551110SNicolas Dichtel struct netconfmsg *ncm; 18309e551110SNicolas Dichtel struct sk_buff *skb; 18319e551110SNicolas Dichtel struct ipv4_devconf *devconf; 18329e551110SNicolas Dichtel struct in_device *in_dev; 18339e551110SNicolas Dichtel struct net_device *dev; 18349e551110SNicolas Dichtel int ifindex; 18359e551110SNicolas Dichtel int err; 18369e551110SNicolas Dichtel 18379e551110SNicolas Dichtel err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 18389e551110SNicolas Dichtel devconf_ipv4_policy); 18399e551110SNicolas Dichtel if (err < 0) 18409e551110SNicolas Dichtel goto errout; 18419e551110SNicolas Dichtel 18429e551110SNicolas Dichtel err = EINVAL; 18439e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 18449e551110SNicolas Dichtel goto errout; 18459e551110SNicolas Dichtel 18469e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 18479e551110SNicolas Dichtel switch (ifindex) { 18489e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 18499e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 18509e551110SNicolas Dichtel break; 18519e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 18529e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 18539e551110SNicolas Dichtel break; 18549e551110SNicolas Dichtel default: 18559e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 18569e551110SNicolas Dichtel if (dev == NULL) 18579e551110SNicolas Dichtel goto errout; 18589e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 18599e551110SNicolas Dichtel if (in_dev == NULL) 18609e551110SNicolas Dichtel goto errout; 18619e551110SNicolas Dichtel devconf = &in_dev->cnf; 18629e551110SNicolas Dichtel break; 18639e551110SNicolas Dichtel } 18649e551110SNicolas Dichtel 18659e551110SNicolas Dichtel err = -ENOBUFS; 18669e551110SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC); 18679e551110SNicolas Dichtel if (skb == NULL) 18689e551110SNicolas Dichtel goto errout; 18699e551110SNicolas Dichtel 18709e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 18719e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 18729e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 18739e551110SNicolas Dichtel -1); 18749e551110SNicolas Dichtel if (err < 0) { 18759e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 18769e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 18779e551110SNicolas Dichtel kfree_skb(skb); 18789e551110SNicolas Dichtel goto errout; 18799e551110SNicolas Dichtel } 18809e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 18819e551110SNicolas Dichtel errout: 18829e551110SNicolas Dichtel return err; 18839e551110SNicolas Dichtel } 18849e551110SNicolas Dichtel 18857a674200SNicolas Dichtel static int inet_netconf_dump_devconf(struct sk_buff *skb, 18867a674200SNicolas Dichtel struct netlink_callback *cb) 18877a674200SNicolas Dichtel { 18887a674200SNicolas Dichtel struct net *net = sock_net(skb->sk); 18897a674200SNicolas Dichtel int h, s_h; 18907a674200SNicolas Dichtel int idx, s_idx; 18917a674200SNicolas Dichtel struct net_device *dev; 18927a674200SNicolas Dichtel struct in_device *in_dev; 18937a674200SNicolas Dichtel struct hlist_head *head; 18947a674200SNicolas Dichtel 18957a674200SNicolas Dichtel s_h = cb->args[0]; 18967a674200SNicolas Dichtel s_idx = idx = cb->args[1]; 18977a674200SNicolas Dichtel 18987a674200SNicolas Dichtel for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 18997a674200SNicolas Dichtel idx = 0; 19007a674200SNicolas Dichtel head = &net->dev_index_head[h]; 19017a674200SNicolas Dichtel rcu_read_lock(); 19020465277fSNicolas Dichtel cb->seq = atomic_read(&net->ipv4.dev_addr_genid) ^ 19030465277fSNicolas Dichtel net->dev_base_seq; 19047a674200SNicolas Dichtel hlist_for_each_entry_rcu(dev, head, index_hlist) { 19057a674200SNicolas Dichtel if (idx < s_idx) 19067a674200SNicolas Dichtel goto cont; 19077a674200SNicolas Dichtel in_dev = __in_dev_get_rcu(dev); 19087a674200SNicolas Dichtel if (!in_dev) 19097a674200SNicolas Dichtel goto cont; 19107a674200SNicolas Dichtel 19117a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, dev->ifindex, 19127a674200SNicolas Dichtel &in_dev->cnf, 19137a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 19147a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 19157a674200SNicolas Dichtel RTM_NEWNETCONF, 19167a674200SNicolas Dichtel NLM_F_MULTI, 19177b46a644SDavid S. Miller -1) < 0) { 19187a674200SNicolas Dichtel rcu_read_unlock(); 19197a674200SNicolas Dichtel goto done; 19207a674200SNicolas Dichtel } 19210465277fSNicolas Dichtel nl_dump_check_consistent(cb, nlmsg_hdr(skb)); 19227a674200SNicolas Dichtel cont: 19237a674200SNicolas Dichtel idx++; 19247a674200SNicolas Dichtel } 19257a674200SNicolas Dichtel rcu_read_unlock(); 19267a674200SNicolas Dichtel } 19277a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES) { 19287a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_ALL, 19297a674200SNicolas Dichtel net->ipv4.devconf_all, 19307a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 19317a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 19327a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 19337b46a644SDavid S. Miller -1) < 0) 19347a674200SNicolas Dichtel goto done; 19357a674200SNicolas Dichtel else 19367a674200SNicolas Dichtel h++; 19377a674200SNicolas Dichtel } 19387a674200SNicolas Dichtel if (h == NETDEV_HASHENTRIES + 1) { 19397a674200SNicolas Dichtel if (inet_netconf_fill_devconf(skb, NETCONFA_IFINDEX_DEFAULT, 19407a674200SNicolas Dichtel net->ipv4.devconf_dflt, 19417a674200SNicolas Dichtel NETLINK_CB(cb->skb).portid, 19427a674200SNicolas Dichtel cb->nlh->nlmsg_seq, 19437a674200SNicolas Dichtel RTM_NEWNETCONF, NLM_F_MULTI, 19447b46a644SDavid S. Miller -1) < 0) 19457a674200SNicolas Dichtel goto done; 19467a674200SNicolas Dichtel else 19477a674200SNicolas Dichtel h++; 19487a674200SNicolas Dichtel } 19497a674200SNicolas Dichtel done: 19507a674200SNicolas Dichtel cb->args[0] = h; 19517a674200SNicolas Dichtel cb->args[1] = idx; 19527a674200SNicolas Dichtel 19537a674200SNicolas Dichtel return skb->len; 19547a674200SNicolas Dichtel } 19557a674200SNicolas Dichtel 19561da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 19571da177e4SLinus Torvalds 1958c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 195931be3085SHerbert Xu { 196031be3085SHerbert Xu struct net_device *dev; 196131be3085SHerbert Xu 196231be3085SHerbert Xu rcu_read_lock(); 1963c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1964c6d14c84SEric Dumazet struct in_device *in_dev; 1965c6d14c84SEric Dumazet 196631be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 196731be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 19689355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1969c6d14c84SEric Dumazet } 197031be3085SHerbert Xu rcu_read_unlock(); 197131be3085SHerbert Xu } 197231be3085SHerbert Xu 1973c6d14c84SEric Dumazet /* called with RTNL locked */ 1974c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 197568dd299bSPavel Emelyanov { 197668dd299bSPavel Emelyanov struct net_device *dev; 1977586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 197868dd299bSPavel Emelyanov 1979586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 19809355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 1981edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1982edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1983edc9e748SNicolas Dichtel net->ipv4.devconf_all); 1984edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1985edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 1986edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 198768dd299bSPavel Emelyanov 1988c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 198968dd299bSPavel Emelyanov struct in_device *in_dev; 19900187bdfbSBen Hutchings if (on) 19910187bdfbSBen Hutchings dev_disable_lro(dev); 199268dd299bSPavel Emelyanov rcu_read_lock(); 199368dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 1994edc9e748SNicolas Dichtel if (in_dev) { 199568dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 1996edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1997edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 1998edc9e748SNicolas Dichtel } 199968dd299bSPavel Emelyanov rcu_read_unlock(); 200068dd299bSPavel Emelyanov } 200168dd299bSPavel Emelyanov } 200268dd299bSPavel Emelyanov 2003f085ff1cSstephen hemminger static int devinet_conf_ifindex(struct net *net, struct ipv4_devconf *cnf) 2004f085ff1cSstephen hemminger { 2005f085ff1cSstephen hemminger if (cnf == net->ipv4.devconf_dflt) 2006f085ff1cSstephen hemminger return NETCONFA_IFINDEX_DEFAULT; 2007f085ff1cSstephen hemminger else if (cnf == net->ipv4.devconf_all) 2008f085ff1cSstephen hemminger return NETCONFA_IFINDEX_ALL; 2009f085ff1cSstephen hemminger else { 2010f085ff1cSstephen hemminger struct in_device *idev 2011f085ff1cSstephen hemminger = container_of(cnf, struct in_device, cnf); 2012f085ff1cSstephen hemminger return idev->dev->ifindex; 2013f085ff1cSstephen hemminger } 2014f085ff1cSstephen hemminger } 2015f085ff1cSstephen hemminger 2016fe2c6338SJoe Perches static int devinet_conf_proc(struct ctl_table *ctl, int write, 20178d65af78SAlexey Dobriyan void __user *buffer, 201831be3085SHerbert Xu size_t *lenp, loff_t *ppos) 201931be3085SHerbert Xu { 2020d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 20218d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 2022d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 202331be3085SHerbert Xu 202431be3085SHerbert Xu if (write) { 202531be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 2026c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 202731be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 2028f085ff1cSstephen hemminger int ifindex; 202931be3085SHerbert Xu 203031be3085SHerbert Xu set_bit(i, cnf->state); 203131be3085SHerbert Xu 20329355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 2033c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 2034d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 2035d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 2036d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 20374ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2038f085ff1cSstephen hemminger 2039cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 2040cc535dfbSNicolas Dichtel new_value != old_value) { 2041f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 2042cc535dfbSNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER, 2043cc535dfbSNicolas Dichtel ifindex, cnf); 2044cc535dfbSNicolas Dichtel } 2045f085ff1cSstephen hemminger if (i == IPV4_DEVCONF_PROXY_ARP - 1 && 2046f085ff1cSstephen hemminger new_value != old_value) { 2047f085ff1cSstephen hemminger ifindex = devinet_conf_ifindex(net, cnf); 204809aea5dfSstephen hemminger inet_netconf_notify_devconf(net, NETCONFA_PROXY_NEIGH, 2049f085ff1cSstephen hemminger ifindex, cnf); 2050f085ff1cSstephen hemminger } 205131be3085SHerbert Xu } 205231be3085SHerbert Xu 205331be3085SHerbert Xu return ret; 205431be3085SHerbert Xu } 205531be3085SHerbert Xu 2056fe2c6338SJoe Perches static int devinet_sysctl_forward(struct ctl_table *ctl, int write, 20578d65af78SAlexey Dobriyan void __user *buffer, 20581da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 20591da177e4SLinus Torvalds { 20601da177e4SLinus Torvalds int *valp = ctl->data; 20611da177e4SLinus Torvalds int val = *valp; 206288af182eSEric W. Biederman loff_t pos = *ppos; 20638d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 20641da177e4SLinus Torvalds 20651da177e4SLinus Torvalds if (write && *valp != val) { 2066c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 2067c0ce9fb3SPavel Emelyanov 20680187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 206988af182eSEric W. Biederman if (!rtnl_trylock()) { 207088af182eSEric W. Biederman /* Restore the original values before restarting */ 207188af182eSEric W. Biederman *valp = val; 207288af182eSEric W. Biederman *ppos = pos; 20739b8adb5eSEric W. Biederman return restart_syscall(); 207488af182eSEric W. Biederman } 20750187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 2076c0ce9fb3SPavel Emelyanov inet_forward_change(net); 2077edc9e748SNicolas Dichtel } else { 20780187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 20790187bdfbSBen Hutchings struct in_device *idev = 20800187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 2081edc9e748SNicolas Dichtel if (*valp) 20820187bdfbSBen Hutchings dev_disable_lro(idev->dev); 2083edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, 2084edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 2085edc9e748SNicolas Dichtel idev->dev->ifindex, 2086edc9e748SNicolas Dichtel cnf); 20870187bdfbSBen Hutchings } 20880187bdfbSBen Hutchings rtnl_unlock(); 20894ccfe6d4SNicolas Dichtel rt_cache_flush(net); 2090edc9e748SNicolas Dichtel } else 2091edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 2092edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 2093edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 20940187bdfbSBen Hutchings } 20951da177e4SLinus Torvalds 20961da177e4SLinus Torvalds return ret; 20971da177e4SLinus Torvalds } 20981da177e4SLinus Torvalds 2099fe2c6338SJoe Perches static int ipv4_doint_and_flush(struct ctl_table *ctl, int write, 21008d65af78SAlexey Dobriyan void __user *buffer, 21011da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 21021da177e4SLinus Torvalds { 21031da177e4SLinus Torvalds int *valp = ctl->data; 21041da177e4SLinus Torvalds int val = *valp; 21058d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 210676e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 21071da177e4SLinus Torvalds 21081da177e4SLinus Torvalds if (write && *valp != val) 21094ccfe6d4SNicolas Dichtel rt_cache_flush(net); 21101da177e4SLinus Torvalds 21111da177e4SLinus Torvalds return ret; 21121da177e4SLinus Torvalds } 21131da177e4SLinus Torvalds 2114f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 211542f811b8SHerbert Xu { \ 211642f811b8SHerbert Xu .procname = name, \ 211742f811b8SHerbert Xu .data = ipv4_devconf.data + \ 211802291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 211942f811b8SHerbert Xu .maxlen = sizeof(int), \ 212042f811b8SHerbert Xu .mode = mval, \ 212142f811b8SHerbert Xu .proc_handler = proc, \ 212231be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 212342f811b8SHerbert Xu } 212442f811b8SHerbert Xu 212542f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 2126f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 212742f811b8SHerbert Xu 212842f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 2129f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 213042f811b8SHerbert Xu 2131f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 2132f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 213342f811b8SHerbert Xu 213442f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 2135f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 213642f811b8SHerbert Xu 21371da177e4SLinus Torvalds static struct devinet_sysctl_table { 21381da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 213902291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 21401da177e4SLinus Torvalds } devinet_sysctl = { 21411da177e4SLinus Torvalds .devinet_vars = { 214242f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 2143f8572d8fSEric W. Biederman devinet_sysctl_forward), 214442f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 214542f811b8SHerbert Xu 214642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 214742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 214842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 214942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 215042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 215142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 215242f811b8SHerbert Xu "accept_source_route"), 21538153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 215428f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 215542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 215642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 215742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 215842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 215942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 216042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 216142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 216242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 216342f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 2164eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 216565324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 21665c6fe01cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(FORCE_IGMP_VERSION, 21675c6fe01cSWilliam Manley "force_igmp_version"), 21682690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV2_UNSOLICITED_REPORT_INTERVAL, 21692690048cSWilliam Manley "igmpv2_unsolicited_report_interval"), 21702690048cSWilliam Manley DEVINET_SYSCTL_RW_ENTRY(IGMPV3_UNSOLICITED_REPORT_INTERVAL, 21712690048cSWilliam Manley "igmpv3_unsolicited_report_interval"), 217242f811b8SHerbert Xu 217342f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 217442f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 217542f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 217642f811b8SHerbert Xu "promote_secondaries"), 2177d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 2178d0daebc3SThomas Graf "route_localnet"), 21791da177e4SLinus Torvalds }, 21801da177e4SLinus Torvalds }; 21811da177e4SLinus Torvalds 2182ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 2183f8572d8fSEric W. Biederman struct ipv4_devconf *p) 21841da177e4SLinus Torvalds { 21851da177e4SLinus Torvalds int i; 21869fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 21878607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 2188bfada697SPavel Emelyanov 21899fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 21901da177e4SLinus Torvalds if (!t) 21919fa89642SPavel Emelyanov goto out; 21929fa89642SPavel Emelyanov 21931da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 21941da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 219531be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 2196c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 21971da177e4SLinus Torvalds } 21981da177e4SLinus Torvalds 21998607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 22001da177e4SLinus Torvalds 22018607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 22021da177e4SLinus Torvalds if (!t->sysctl_header) 22038607ddb8SEric W. Biederman goto free; 22041da177e4SLinus Torvalds 22051da177e4SLinus Torvalds p->sysctl = t; 2206ea40b324SPavel Emelyanov return 0; 22071da177e4SLinus Torvalds 22081da177e4SLinus Torvalds free: 22091da177e4SLinus Torvalds kfree(t); 22109fa89642SPavel Emelyanov out: 2211ea40b324SPavel Emelyanov return -ENOBUFS; 22121da177e4SLinus Torvalds } 22131da177e4SLinus Torvalds 221451602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 221566f27a52SPavel Emelyanov { 221651602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 221766f27a52SPavel Emelyanov 221851602b2aSPavel Emelyanov if (t == NULL) 221951602b2aSPavel Emelyanov return; 222051602b2aSPavel Emelyanov 222151602b2aSPavel Emelyanov cnf->sysctl = NULL; 2222ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 22231da177e4SLinus Torvalds kfree(t); 22241da177e4SLinus Torvalds } 222551602b2aSPavel Emelyanov 222620e61da7SWANG Cong static int devinet_sysctl_register(struct in_device *idev) 222751602b2aSPavel Emelyanov { 222820e61da7SWANG Cong int err; 222920e61da7SWANG Cong 223020e61da7SWANG Cong if (!sysctl_dev_name_is_allowed(idev->dev->name)) 223120e61da7SWANG Cong return -EINVAL; 223220e61da7SWANG Cong 223320e61da7SWANG Cong err = neigh_sysctl_register(idev->dev, idev->arp_parms, NULL); 223420e61da7SWANG Cong if (err) 223520e61da7SWANG Cong return err; 223620e61da7SWANG Cong err = __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 2237f8572d8fSEric W. Biederman &idev->cnf); 223820e61da7SWANG Cong if (err) 223920e61da7SWANG Cong neigh_sysctl_unregister(idev->arp_parms); 224020e61da7SWANG Cong return err; 224151602b2aSPavel Emelyanov } 224251602b2aSPavel Emelyanov 224351602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 224451602b2aSPavel Emelyanov { 224551602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 224651602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 22471da177e4SLinus Torvalds } 22481da177e4SLinus Torvalds 224968dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 225068dd299bSPavel Emelyanov { 225168dd299bSPavel Emelyanov .procname = "ip_forward", 225268dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 225302291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 225468dd299bSPavel Emelyanov .maxlen = sizeof(int), 225568dd299bSPavel Emelyanov .mode = 0644, 225668dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 225768dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 2258c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 225968dd299bSPavel Emelyanov }, 226068dd299bSPavel Emelyanov { }, 226168dd299bSPavel Emelyanov }; 22622a75de0cSEric Dumazet #endif 226368dd299bSPavel Emelyanov 2264752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 2265752d14dcSPavel Emelyanov { 2266752d14dcSPavel Emelyanov int err; 2267752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 22682a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 22692a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 2270752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 22712a75de0cSEric Dumazet #endif 2272752d14dcSPavel Emelyanov 2273752d14dcSPavel Emelyanov err = -ENOMEM; 2274752d14dcSPavel Emelyanov all = &ipv4_devconf; 2275752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 2276752d14dcSPavel Emelyanov 227709ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 2278752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 2279752d14dcSPavel Emelyanov if (all == NULL) 2280752d14dcSPavel Emelyanov goto err_alloc_all; 2281752d14dcSPavel Emelyanov 2282752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 2283752d14dcSPavel Emelyanov if (dflt == NULL) 2284752d14dcSPavel Emelyanov goto err_alloc_dflt; 2285752d14dcSPavel Emelyanov 22862a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2287752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 2288752d14dcSPavel Emelyanov if (tbl == NULL) 2289752d14dcSPavel Emelyanov goto err_alloc_ctl; 2290752d14dcSPavel Emelyanov 229102291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 2292752d14dcSPavel Emelyanov tbl[0].extra1 = all; 2293752d14dcSPavel Emelyanov tbl[0].extra2 = net; 22942a75de0cSEric Dumazet #endif 2295752d14dcSPavel Emelyanov } 2296752d14dcSPavel Emelyanov 2297752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2298f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 2299752d14dcSPavel Emelyanov if (err < 0) 2300752d14dcSPavel Emelyanov goto err_reg_all; 2301752d14dcSPavel Emelyanov 2302f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 2303752d14dcSPavel Emelyanov if (err < 0) 2304752d14dcSPavel Emelyanov goto err_reg_dflt; 2305752d14dcSPavel Emelyanov 2306752d14dcSPavel Emelyanov err = -ENOMEM; 23078607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 2308752d14dcSPavel Emelyanov if (forw_hdr == NULL) 2309752d14dcSPavel Emelyanov goto err_reg_ctl; 23102a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 2311752d14dcSPavel Emelyanov #endif 2312752d14dcSPavel Emelyanov 2313752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 2314752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 2315752d14dcSPavel Emelyanov return 0; 2316752d14dcSPavel Emelyanov 2317752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 2318752d14dcSPavel Emelyanov err_reg_ctl: 2319752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 2320752d14dcSPavel Emelyanov err_reg_dflt: 2321752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 2322752d14dcSPavel Emelyanov err_reg_all: 2323752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 2324752d14dcSPavel Emelyanov kfree(tbl); 2325752d14dcSPavel Emelyanov err_alloc_ctl: 23262a75de0cSEric Dumazet #endif 2327752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 2328752d14dcSPavel Emelyanov kfree(dflt); 2329752d14dcSPavel Emelyanov err_alloc_dflt: 2330752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 2331752d14dcSPavel Emelyanov kfree(all); 2332752d14dcSPavel Emelyanov err_alloc_all: 2333752d14dcSPavel Emelyanov return err; 2334752d14dcSPavel Emelyanov } 2335752d14dcSPavel Emelyanov 2336752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 2337752d14dcSPavel Emelyanov { 23382a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 2339752d14dcSPavel Emelyanov struct ctl_table *tbl; 2340752d14dcSPavel Emelyanov 2341752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 2342752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 2343752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 2344752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 2345752d14dcSPavel Emelyanov kfree(tbl); 23462a75de0cSEric Dumazet #endif 2347752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 2348752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 2349752d14dcSPavel Emelyanov } 2350752d14dcSPavel Emelyanov 2351752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 2352752d14dcSPavel Emelyanov .init = devinet_init_net, 2353752d14dcSPavel Emelyanov .exit = devinet_exit_net, 2354752d14dcSPavel Emelyanov }; 2355752d14dcSPavel Emelyanov 2356207895fdSDaniel Borkmann static struct rtnl_af_ops inet_af_ops __read_mostly = { 23579f0f7272SThomas Graf .family = AF_INET, 23589f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 23599f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 2360cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 2361cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 23629f0f7272SThomas Graf }; 23639f0f7272SThomas Graf 23641da177e4SLinus Torvalds void __init devinet_init(void) 23651da177e4SLinus Torvalds { 2366fd23c3b3SDavid S. Miller int i; 2367fd23c3b3SDavid S. Miller 2368fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 2369fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 2370fd23c3b3SDavid S. Miller 2371752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 2372752d14dcSPavel Emelyanov 23731da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 23741da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 237563f3444fSThomas Graf 2376906e073fSviresh kumar queue_delayed_work(system_power_efficient_wq, &check_lifetime_work, 0); 23775c766d64SJiri Pirko 23789f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 23799f0f7272SThomas Graf 2380c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); 2381c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); 2382c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); 23839e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 23847a674200SNicolas Dichtel inet_netconf_dump_devconf, NULL); 23851da177e4SLinus Torvalds } 23861da177e4SLinus Torvalds 2387