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> 661da177e4SLinus Torvalds 67406b6f97SDavid S. Miller #include "fib_lookup.h" 68406b6f97SDavid S. Miller 690027ba84SAdrian Bunk static struct ipv4_devconf ipv4_devconf = { 7042f811b8SHerbert Xu .data = { 7102291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 7202291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 7302291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 7402291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 7542f811b8SHerbert Xu }, 761da177e4SLinus Torvalds }; 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds static struct ipv4_devconf ipv4_devconf_dflt = { 7942f811b8SHerbert Xu .data = { 8002291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, 8102291680SEric W. Biederman [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1, 8202291680SEric W. Biederman [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1, 8302291680SEric W. Biederman [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1, 8402291680SEric W. Biederman [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1, 8542f811b8SHerbert Xu }, 861da177e4SLinus Torvalds }; 871da177e4SLinus Torvalds 889355bbd6SPavel Emelyanov #define IPV4_DEVCONF_DFLT(net, attr) \ 899355bbd6SPavel Emelyanov IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr) 9042f811b8SHerbert Xu 91ef7c79edSPatrick McHardy static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { 925c753978SThomas Graf [IFA_LOCAL] = { .type = NLA_U32 }, 935c753978SThomas Graf [IFA_ADDRESS] = { .type = NLA_U32 }, 945c753978SThomas Graf [IFA_BROADCAST] = { .type = NLA_U32 }, 955176f91eSThomas Graf [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, 965c753978SThomas Graf }; 975c753978SThomas Graf 9840384999SEric Dumazet #define IN4_ADDR_HSIZE_SHIFT 8 9940384999SEric Dumazet #define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT) 10040384999SEric Dumazet 101fd23c3b3SDavid S. Miller static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; 102fd23c3b3SDavid S. Miller static DEFINE_SPINLOCK(inet_addr_hash_lock); 103fd23c3b3SDavid S. Miller 10440384999SEric Dumazet static u32 inet_addr_hash(struct net *net, __be32 addr) 105fd23c3b3SDavid S. Miller { 10640384999SEric Dumazet u32 val = (__force u32) addr ^ net_hash_mix(net); 107fd23c3b3SDavid S. Miller 10840384999SEric Dumazet return hash_32(val, IN4_ADDR_HSIZE_SHIFT); 109fd23c3b3SDavid S. Miller } 110fd23c3b3SDavid S. Miller 111fd23c3b3SDavid S. Miller static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) 112fd23c3b3SDavid S. Miller { 11340384999SEric Dumazet u32 hash = inet_addr_hash(net, ifa->ifa_local); 114fd23c3b3SDavid S. Miller 115fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 116fd23c3b3SDavid S. Miller hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); 117fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 118fd23c3b3SDavid S. Miller } 119fd23c3b3SDavid S. Miller 120fd23c3b3SDavid S. Miller static void inet_hash_remove(struct in_ifaddr *ifa) 121fd23c3b3SDavid S. Miller { 122fd23c3b3SDavid S. Miller spin_lock(&inet_addr_hash_lock); 123fd23c3b3SDavid S. Miller hlist_del_init_rcu(&ifa->hash); 124fd23c3b3SDavid S. Miller spin_unlock(&inet_addr_hash_lock); 125fd23c3b3SDavid S. Miller } 126fd23c3b3SDavid S. Miller 1279435eb1cSDavid S. Miller /** 1289435eb1cSDavid S. Miller * __ip_dev_find - find the first device with a given source address. 1299435eb1cSDavid S. Miller * @net: the net namespace 1309435eb1cSDavid S. Miller * @addr: the source address 1319435eb1cSDavid S. Miller * @devref: if true, take a reference on the found device 1329435eb1cSDavid S. Miller * 1339435eb1cSDavid S. Miller * If a caller uses devref=false, it should be protected by RCU, or RTNL 1349435eb1cSDavid S. Miller */ 1359435eb1cSDavid S. Miller struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) 1369435eb1cSDavid S. Miller { 13740384999SEric Dumazet u32 hash = inet_addr_hash(net, addr); 1389435eb1cSDavid S. Miller struct net_device *result = NULL; 1399435eb1cSDavid S. Miller struct in_ifaddr *ifa; 1409435eb1cSDavid S. Miller struct hlist_node *node; 1419435eb1cSDavid S. Miller 1429435eb1cSDavid S. Miller rcu_read_lock(); 1439435eb1cSDavid S. Miller hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) { 14440384999SEric Dumazet if (ifa->ifa_local == addr) { 1459435eb1cSDavid S. Miller struct net_device *dev = ifa->ifa_dev->dev; 1469435eb1cSDavid S. Miller 1479435eb1cSDavid S. Miller if (!net_eq(dev_net(dev), net)) 1489435eb1cSDavid S. Miller continue; 1499435eb1cSDavid S. Miller result = dev; 1509435eb1cSDavid S. Miller break; 1519435eb1cSDavid S. Miller } 1529435eb1cSDavid S. Miller } 153406b6f97SDavid S. Miller if (!result) { 154406b6f97SDavid S. Miller struct flowi4 fl4 = { .daddr = addr }; 155406b6f97SDavid S. Miller struct fib_result res = { 0 }; 156406b6f97SDavid S. Miller struct fib_table *local; 157406b6f97SDavid S. Miller 158406b6f97SDavid S. Miller /* Fallback to FIB local table so that communication 159406b6f97SDavid S. Miller * over loopback subnets work. 160406b6f97SDavid S. Miller */ 161406b6f97SDavid S. Miller local = fib_get_table(net, RT_TABLE_LOCAL); 162406b6f97SDavid S. Miller if (local && 163406b6f97SDavid S. Miller !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && 164406b6f97SDavid S. Miller res.type == RTN_LOCAL) 165406b6f97SDavid S. Miller result = FIB_RES_DEV(res); 166406b6f97SDavid S. Miller } 1679435eb1cSDavid S. Miller if (result && devref) 1689435eb1cSDavid S. Miller dev_hold(result); 1699435eb1cSDavid S. Miller rcu_read_unlock(); 1709435eb1cSDavid S. Miller return result; 1719435eb1cSDavid S. Miller } 1729435eb1cSDavid S. Miller EXPORT_SYMBOL(__ip_dev_find); 1739435eb1cSDavid S. Miller 174d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); 1751da177e4SLinus Torvalds 176e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); 1771da177e4SLinus Torvalds static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 1781da177e4SLinus Torvalds int destroy); 1791da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 18066f27a52SPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev); 18151602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev); 18251602b2aSPavel Emelyanov #else 18340384999SEric Dumazet static void devinet_sysctl_register(struct in_device *idev) 18451602b2aSPavel Emelyanov { 18551602b2aSPavel Emelyanov } 18640384999SEric Dumazet static void devinet_sysctl_unregister(struct in_device *idev) 18751602b2aSPavel Emelyanov { 18851602b2aSPavel Emelyanov } 1891da177e4SLinus Torvalds #endif 1901da177e4SLinus Torvalds 1911da177e4SLinus Torvalds /* Locks all the inet devices. */ 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds static struct in_ifaddr *inet_alloc_ifa(void) 1941da177e4SLinus Torvalds { 19593adcc80SAlexey Dobriyan return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL); 1961da177e4SLinus Torvalds } 1971da177e4SLinus Torvalds 1981da177e4SLinus Torvalds static void inet_rcu_free_ifa(struct rcu_head *head) 1991da177e4SLinus Torvalds { 2001da177e4SLinus Torvalds struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); 2011da177e4SLinus Torvalds if (ifa->ifa_dev) 2021da177e4SLinus Torvalds in_dev_put(ifa->ifa_dev); 2031da177e4SLinus Torvalds kfree(ifa); 2041da177e4SLinus Torvalds } 2051da177e4SLinus Torvalds 20640384999SEric Dumazet static void inet_free_ifa(struct in_ifaddr *ifa) 2071da177e4SLinus Torvalds { 2081da177e4SLinus Torvalds call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); 2091da177e4SLinus Torvalds } 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds void in_dev_finish_destroy(struct in_device *idev) 2121da177e4SLinus Torvalds { 2131da177e4SLinus Torvalds struct net_device *dev = idev->dev; 2141da177e4SLinus Torvalds 215547b792cSIlpo Järvinen WARN_ON(idev->ifa_list); 216547b792cSIlpo Järvinen WARN_ON(idev->mc_list); 2171da177e4SLinus Torvalds #ifdef NET_REFCNT_DEBUG 21891df42beSJoe Perches pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL"); 2191da177e4SLinus Torvalds #endif 2201da177e4SLinus Torvalds dev_put(dev); 2211da177e4SLinus Torvalds if (!idev->dead) 2229f9354b9SEric Dumazet pr_err("Freeing alive in_device %p\n", idev); 2239f9354b9SEric Dumazet else 2241da177e4SLinus Torvalds kfree(idev); 2251da177e4SLinus Torvalds } 2269f9354b9SEric Dumazet EXPORT_SYMBOL(in_dev_finish_destroy); 2271da177e4SLinus Torvalds 22871e27da9SHerbert Xu static struct in_device *inetdev_init(struct net_device *dev) 2291da177e4SLinus Torvalds { 2301da177e4SLinus Torvalds struct in_device *in_dev; 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds ASSERT_RTNL(); 2331da177e4SLinus Torvalds 2340da974f4SPanagiotis Issaris in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL); 2351da177e4SLinus Torvalds if (!in_dev) 2361da177e4SLinus Torvalds goto out; 237c346dca1SYOSHIFUJI Hideaki memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt, 2389355bbd6SPavel Emelyanov sizeof(in_dev->cnf)); 2391da177e4SLinus Torvalds in_dev->cnf.sysctl = NULL; 2401da177e4SLinus Torvalds in_dev->dev = dev; 2419f9354b9SEric Dumazet in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); 2429f9354b9SEric Dumazet if (!in_dev->arp_parms) 2431da177e4SLinus Torvalds goto out_kfree; 2440187bdfbSBen Hutchings if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) 2450187bdfbSBen Hutchings dev_disable_lro(dev); 2461da177e4SLinus Torvalds /* Reference in_dev->dev */ 2471da177e4SLinus Torvalds dev_hold(dev); 24830c4cf57SDavid L Stevens /* Account for reference dev->ip_ptr (below) */ 2491da177e4SLinus Torvalds in_dev_hold(in_dev); 2501da177e4SLinus Torvalds 25166f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 2521da177e4SLinus Torvalds ip_mc_init_dev(in_dev); 2531da177e4SLinus Torvalds if (dev->flags & IFF_UP) 2541da177e4SLinus Torvalds ip_mc_up(in_dev); 255483479ecSJarek Poplawski 25630c4cf57SDavid L Stevens /* we can receive as soon as ip_ptr is set -- do this last */ 257cf778b00SEric Dumazet rcu_assign_pointer(dev->ip_ptr, in_dev); 258483479ecSJarek Poplawski out: 2591da177e4SLinus Torvalds return in_dev; 2601da177e4SLinus Torvalds out_kfree: 2611da177e4SLinus Torvalds kfree(in_dev); 2621da177e4SLinus Torvalds in_dev = NULL; 2631da177e4SLinus Torvalds goto out; 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 2661da177e4SLinus Torvalds static void in_dev_rcu_put(struct rcu_head *head) 2671da177e4SLinus Torvalds { 2681da177e4SLinus Torvalds struct in_device *idev = container_of(head, struct in_device, rcu_head); 2691da177e4SLinus Torvalds in_dev_put(idev); 2701da177e4SLinus Torvalds } 2711da177e4SLinus Torvalds 2721da177e4SLinus Torvalds static void inetdev_destroy(struct in_device *in_dev) 2731da177e4SLinus Torvalds { 2741da177e4SLinus Torvalds struct in_ifaddr *ifa; 2751da177e4SLinus Torvalds struct net_device *dev; 2761da177e4SLinus Torvalds 2771da177e4SLinus Torvalds ASSERT_RTNL(); 2781da177e4SLinus Torvalds 2791da177e4SLinus Torvalds dev = in_dev->dev; 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds in_dev->dead = 1; 2821da177e4SLinus Torvalds 2831da177e4SLinus Torvalds ip_mc_destroy_dev(in_dev); 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds while ((ifa = in_dev->ifa_list) != NULL) { 2861da177e4SLinus Torvalds inet_del_ifa(in_dev, &in_dev->ifa_list, 0); 2871da177e4SLinus Torvalds inet_free_ifa(ifa); 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds 290a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 2911da177e4SLinus Torvalds 29251602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 2931da177e4SLinus Torvalds neigh_parms_release(&arp_tbl, in_dev->arp_parms); 2941da177e4SLinus Torvalds arp_ifdown(dev); 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds call_rcu(&in_dev->rcu_head, in_dev_rcu_put); 2971da177e4SLinus Torvalds } 2981da177e4SLinus Torvalds 299ff428d72SAl Viro int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b) 3001da177e4SLinus Torvalds { 3011da177e4SLinus Torvalds rcu_read_lock(); 3021da177e4SLinus Torvalds for_primary_ifa(in_dev) { 3031da177e4SLinus Torvalds if (inet_ifa_match(a, ifa)) { 3041da177e4SLinus Torvalds if (!b || inet_ifa_match(b, ifa)) { 3051da177e4SLinus Torvalds rcu_read_unlock(); 3061da177e4SLinus Torvalds return 1; 3071da177e4SLinus Torvalds } 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds } endfor_ifa(in_dev); 3101da177e4SLinus Torvalds rcu_read_unlock(); 3111da177e4SLinus Torvalds return 0; 3121da177e4SLinus Torvalds } 3131da177e4SLinus Torvalds 314d6062cbbSThomas Graf static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 31515e47304SEric W. Biederman int destroy, struct nlmsghdr *nlh, u32 portid) 3161da177e4SLinus Torvalds { 3178f937c60SHarald Welte struct in_ifaddr *promote = NULL; 3180ff60a45SJamal Hadi Salim struct in_ifaddr *ifa, *ifa1 = *ifap; 3190ff60a45SJamal Hadi Salim struct in_ifaddr *last_prim = in_dev->ifa_list; 3200ff60a45SJamal Hadi Salim struct in_ifaddr *prev_prom = NULL; 3210ff60a45SJamal Hadi Salim int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); 3221da177e4SLinus Torvalds 3231da177e4SLinus Torvalds ASSERT_RTNL(); 3241da177e4SLinus Torvalds 3258f937c60SHarald Welte /* 1. Deleting primary ifaddr forces deletion all secondaries 3268f937c60SHarald Welte * unless alias promotion is set 3278f937c60SHarald Welte **/ 3281da177e4SLinus Torvalds 3291da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { 3301da177e4SLinus Torvalds struct in_ifaddr **ifap1 = &ifa1->ifa_next; 3311da177e4SLinus Torvalds 3321da177e4SLinus Torvalds while ((ifa = *ifap1) != NULL) { 3330ff60a45SJamal Hadi Salim if (!(ifa->ifa_flags & IFA_F_SECONDARY) && 3340ff60a45SJamal Hadi Salim ifa1->ifa_scope <= ifa->ifa_scope) 3350ff60a45SJamal Hadi Salim last_prim = ifa; 3360ff60a45SJamal Hadi Salim 3371da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY) || 3381da177e4SLinus Torvalds ifa1->ifa_mask != ifa->ifa_mask || 3391da177e4SLinus Torvalds !inet_ifa_match(ifa1->ifa_address, ifa)) { 3401da177e4SLinus Torvalds ifap1 = &ifa->ifa_next; 3410ff60a45SJamal Hadi Salim prev_prom = ifa; 3421da177e4SLinus Torvalds continue; 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds 3450ff60a45SJamal Hadi Salim if (!do_promote) { 346fd23c3b3SDavid S. Miller inet_hash_remove(ifa); 3471da177e4SLinus Torvalds *ifap1 = ifa->ifa_next; 3481da177e4SLinus Torvalds 34915e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa, nlh, portid); 350e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 351e041c683SAlan Stern NETDEV_DOWN, ifa); 3521da177e4SLinus Torvalds inet_free_ifa(ifa); 3538f937c60SHarald Welte } else { 3548f937c60SHarald Welte promote = ifa; 3558f937c60SHarald Welte break; 3568f937c60SHarald Welte } 3571da177e4SLinus Torvalds } 3581da177e4SLinus Torvalds } 3591da177e4SLinus Torvalds 3602d230e2bSJulian Anastasov /* On promotion all secondaries from subnet are changing 3612d230e2bSJulian Anastasov * the primary IP, we must remove all their routes silently 3622d230e2bSJulian Anastasov * and later to add them back with new prefsrc. Do this 3632d230e2bSJulian Anastasov * while all addresses are on the device list. 3642d230e2bSJulian Anastasov */ 3652d230e2bSJulian Anastasov for (ifa = promote; ifa; ifa = ifa->ifa_next) { 3662d230e2bSJulian Anastasov if (ifa1->ifa_mask == ifa->ifa_mask && 3672d230e2bSJulian Anastasov inet_ifa_match(ifa1->ifa_address, ifa)) 3682d230e2bSJulian Anastasov fib_del_ifaddr(ifa, ifa1); 3692d230e2bSJulian Anastasov } 3702d230e2bSJulian Anastasov 3711da177e4SLinus Torvalds /* 2. Unlink it */ 3721da177e4SLinus Torvalds 3731da177e4SLinus Torvalds *ifap = ifa1->ifa_next; 374fd23c3b3SDavid S. Miller inet_hash_remove(ifa1); 3751da177e4SLinus Torvalds 3761da177e4SLinus Torvalds /* 3. Announce address deletion */ 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds /* Send message first, then call notifier. 3791da177e4SLinus Torvalds At first sight, FIB update triggered by notifier 3801da177e4SLinus Torvalds will refer to already deleted ifaddr, that could confuse 3811da177e4SLinus Torvalds netlink listeners. It is not true: look, gated sees 3821da177e4SLinus Torvalds that route deleted and if it still thinks that ifaddr 3831da177e4SLinus Torvalds is valid, it will try to restore deleted routes... Grr. 3841da177e4SLinus Torvalds So that, this order is correct. 3851da177e4SLinus Torvalds */ 38615e47304SEric W. Biederman rtmsg_ifa(RTM_DELADDR, ifa1, nlh, portid); 387e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); 3880ff60a45SJamal Hadi Salim 3890ff60a45SJamal Hadi Salim if (promote) { 39004024b93SJulian Anastasov struct in_ifaddr *next_sec = promote->ifa_next; 3910ff60a45SJamal Hadi Salim 3920ff60a45SJamal Hadi Salim if (prev_prom) { 3930ff60a45SJamal Hadi Salim prev_prom->ifa_next = promote->ifa_next; 3940ff60a45SJamal Hadi Salim promote->ifa_next = last_prim->ifa_next; 3950ff60a45SJamal Hadi Salim last_prim->ifa_next = promote; 3960ff60a45SJamal Hadi Salim } 3970ff60a45SJamal Hadi Salim 3980ff60a45SJamal Hadi Salim promote->ifa_flags &= ~IFA_F_SECONDARY; 39915e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, promote, nlh, portid); 400e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, 401e041c683SAlan Stern NETDEV_UP, promote); 40204024b93SJulian Anastasov for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { 4030ff60a45SJamal Hadi Salim if (ifa1->ifa_mask != ifa->ifa_mask || 4040ff60a45SJamal Hadi Salim !inet_ifa_match(ifa1->ifa_address, ifa)) 4050ff60a45SJamal Hadi Salim continue; 4060ff60a45SJamal Hadi Salim fib_add_ifaddr(ifa); 4070ff60a45SJamal Hadi Salim } 4080ff60a45SJamal Hadi Salim 4090ff60a45SJamal Hadi Salim } 4106363097cSHerbert Xu if (destroy) 4111da177e4SLinus Torvalds inet_free_ifa(ifa1); 4121da177e4SLinus Torvalds } 4131da177e4SLinus Torvalds 414d6062cbbSThomas Graf static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, 415d6062cbbSThomas Graf int destroy) 416d6062cbbSThomas Graf { 417d6062cbbSThomas Graf __inet_del_ifa(in_dev, ifap, destroy, NULL, 0); 418d6062cbbSThomas Graf } 419d6062cbbSThomas Graf 420d6062cbbSThomas Graf static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, 42115e47304SEric W. Biederman u32 portid) 4221da177e4SLinus Torvalds { 4231da177e4SLinus Torvalds struct in_device *in_dev = ifa->ifa_dev; 4241da177e4SLinus Torvalds struct in_ifaddr *ifa1, **ifap, **last_primary; 4251da177e4SLinus Torvalds 4261da177e4SLinus Torvalds ASSERT_RTNL(); 4271da177e4SLinus Torvalds 4281da177e4SLinus Torvalds if (!ifa->ifa_local) { 4291da177e4SLinus Torvalds inet_free_ifa(ifa); 4301da177e4SLinus Torvalds return 0; 4311da177e4SLinus Torvalds } 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds ifa->ifa_flags &= ~IFA_F_SECONDARY; 4341da177e4SLinus Torvalds last_primary = &in_dev->ifa_list; 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; 4371da177e4SLinus Torvalds ifap = &ifa1->ifa_next) { 4381da177e4SLinus Torvalds if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && 4391da177e4SLinus Torvalds ifa->ifa_scope <= ifa1->ifa_scope) 4401da177e4SLinus Torvalds last_primary = &ifa1->ifa_next; 4411da177e4SLinus Torvalds if (ifa1->ifa_mask == ifa->ifa_mask && 4421da177e4SLinus Torvalds inet_ifa_match(ifa1->ifa_address, ifa)) { 4431da177e4SLinus Torvalds if (ifa1->ifa_local == ifa->ifa_local) { 4441da177e4SLinus Torvalds inet_free_ifa(ifa); 4451da177e4SLinus Torvalds return -EEXIST; 4461da177e4SLinus Torvalds } 4471da177e4SLinus Torvalds if (ifa1->ifa_scope != ifa->ifa_scope) { 4481da177e4SLinus Torvalds inet_free_ifa(ifa); 4491da177e4SLinus Torvalds return -EINVAL; 4501da177e4SLinus Torvalds } 4511da177e4SLinus Torvalds ifa->ifa_flags |= IFA_F_SECONDARY; 4521da177e4SLinus Torvalds } 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds 4551da177e4SLinus Torvalds if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { 4561da177e4SLinus Torvalds net_srandom(ifa->ifa_local); 4571da177e4SLinus Torvalds ifap = last_primary; 4581da177e4SLinus Torvalds } 4591da177e4SLinus Torvalds 4601da177e4SLinus Torvalds ifa->ifa_next = *ifap; 4611da177e4SLinus Torvalds *ifap = ifa; 4621da177e4SLinus Torvalds 463fd23c3b3SDavid S. Miller inet_hash_insert(dev_net(in_dev->dev), ifa); 464fd23c3b3SDavid S. Miller 4651da177e4SLinus Torvalds /* Send message first, then call notifier. 4661da177e4SLinus Torvalds Notifier will trigger FIB update, so that 4671da177e4SLinus Torvalds listeners of netlink will know about new ifaddr */ 46815e47304SEric W. Biederman rtmsg_ifa(RTM_NEWADDR, ifa, nlh, portid); 469e041c683SAlan Stern blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); 4701da177e4SLinus Torvalds 4711da177e4SLinus Torvalds return 0; 4721da177e4SLinus Torvalds } 4731da177e4SLinus Torvalds 474d6062cbbSThomas Graf static int inet_insert_ifa(struct in_ifaddr *ifa) 475d6062cbbSThomas Graf { 476d6062cbbSThomas Graf return __inet_insert_ifa(ifa, NULL, 0); 477d6062cbbSThomas Graf } 478d6062cbbSThomas Graf 4791da177e4SLinus Torvalds static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) 4801da177e4SLinus Torvalds { 481e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 4821da177e4SLinus Torvalds 4831da177e4SLinus Torvalds ASSERT_RTNL(); 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds if (!in_dev) { 4861da177e4SLinus Torvalds inet_free_ifa(ifa); 4871da177e4SLinus Torvalds return -ENOBUFS; 4881da177e4SLinus Torvalds } 48971e27da9SHerbert Xu ipv4_devconf_setall(in_dev); 4901da177e4SLinus Torvalds if (ifa->ifa_dev != in_dev) { 491547b792cSIlpo Järvinen WARN_ON(ifa->ifa_dev); 4921da177e4SLinus Torvalds in_dev_hold(in_dev); 4931da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 4941da177e4SLinus Torvalds } 495f97c1e0cSJoe Perches if (ipv4_is_loopback(ifa->ifa_local)) 4961da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 4971da177e4SLinus Torvalds return inet_insert_ifa(ifa); 4981da177e4SLinus Torvalds } 4991da177e4SLinus Torvalds 5008723e1b4SEric Dumazet /* Caller must hold RCU or RTNL : 5018723e1b4SEric Dumazet * We dont take a reference on found in_device 5028723e1b4SEric Dumazet */ 5037fee0ca2SDenis V. Lunev struct in_device *inetdev_by_index(struct net *net, int ifindex) 5041da177e4SLinus Torvalds { 5051da177e4SLinus Torvalds struct net_device *dev; 5061da177e4SLinus Torvalds struct in_device *in_dev = NULL; 507c148fc2eSEric Dumazet 508c148fc2eSEric Dumazet rcu_read_lock(); 509c148fc2eSEric Dumazet dev = dev_get_by_index_rcu(net, ifindex); 5101da177e4SLinus Torvalds if (dev) 5118723e1b4SEric Dumazet in_dev = rcu_dereference_rtnl(dev->ip_ptr); 512c148fc2eSEric Dumazet rcu_read_unlock(); 5131da177e4SLinus Torvalds return in_dev; 5141da177e4SLinus Torvalds } 5159f9354b9SEric Dumazet EXPORT_SYMBOL(inetdev_by_index); 5161da177e4SLinus Torvalds 5171da177e4SLinus Torvalds /* Called only from RTNL semaphored context. No locks. */ 5181da177e4SLinus Torvalds 51960cad5daSAl Viro struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix, 52060cad5daSAl Viro __be32 mask) 5211da177e4SLinus Torvalds { 5221da177e4SLinus Torvalds ASSERT_RTNL(); 5231da177e4SLinus Torvalds 5241da177e4SLinus Torvalds for_primary_ifa(in_dev) { 5251da177e4SLinus Torvalds if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) 5261da177e4SLinus Torvalds return ifa; 5271da177e4SLinus Torvalds } endfor_ifa(in_dev); 5281da177e4SLinus Torvalds return NULL; 5291da177e4SLinus Torvalds } 5301da177e4SLinus Torvalds 5311da177e4SLinus Torvalds static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 5321da177e4SLinus Torvalds { 5333b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 534dfdd5fd4SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5351da177e4SLinus Torvalds struct in_device *in_dev; 536dfdd5fd4SThomas Graf struct ifaddrmsg *ifm; 5371da177e4SLinus Torvalds struct in_ifaddr *ifa, **ifap; 538dfdd5fd4SThomas Graf int err = -EINVAL; 5391da177e4SLinus Torvalds 5401da177e4SLinus Torvalds ASSERT_RTNL(); 5411da177e4SLinus Torvalds 542dfdd5fd4SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 543dfdd5fd4SThomas Graf if (err < 0) 544dfdd5fd4SThomas Graf goto errout; 545dfdd5fd4SThomas Graf 546dfdd5fd4SThomas Graf ifm = nlmsg_data(nlh); 5477fee0ca2SDenis V. Lunev in_dev = inetdev_by_index(net, ifm->ifa_index); 548dfdd5fd4SThomas Graf if (in_dev == NULL) { 549dfdd5fd4SThomas Graf err = -ENODEV; 550dfdd5fd4SThomas Graf goto errout; 551dfdd5fd4SThomas Graf } 552dfdd5fd4SThomas Graf 5531da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 5541da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 555dfdd5fd4SThomas Graf if (tb[IFA_LOCAL] && 556a7a628c4SAl Viro ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL])) 5571da177e4SLinus Torvalds continue; 558dfdd5fd4SThomas Graf 559dfdd5fd4SThomas Graf if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label)) 560dfdd5fd4SThomas Graf continue; 561dfdd5fd4SThomas Graf 562dfdd5fd4SThomas Graf if (tb[IFA_ADDRESS] && 563dfdd5fd4SThomas Graf (ifm->ifa_prefixlen != ifa->ifa_prefixlen || 564a7a628c4SAl Viro !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa))) 565dfdd5fd4SThomas Graf continue; 566dfdd5fd4SThomas Graf 56715e47304SEric W. Biederman __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).portid); 5681da177e4SLinus Torvalds return 0; 5691da177e4SLinus Torvalds } 570dfdd5fd4SThomas Graf 571dfdd5fd4SThomas Graf err = -EADDRNOTAVAIL; 572dfdd5fd4SThomas Graf errout: 573dfdd5fd4SThomas Graf return err; 5741da177e4SLinus Torvalds } 5751da177e4SLinus Torvalds 5764b8aa9abSDenis V. Lunev static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh) 5771da177e4SLinus Torvalds { 5785c753978SThomas Graf struct nlattr *tb[IFA_MAX+1]; 5795c753978SThomas Graf struct in_ifaddr *ifa; 5805c753978SThomas Graf struct ifaddrmsg *ifm; 5811da177e4SLinus Torvalds struct net_device *dev; 5821da177e4SLinus Torvalds struct in_device *in_dev; 5837b218574SDenis V. Lunev int err; 5841da177e4SLinus Torvalds 5855c753978SThomas Graf err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy); 5865c753978SThomas Graf if (err < 0) 5875c753978SThomas Graf goto errout; 5881da177e4SLinus Torvalds 5895c753978SThomas Graf ifm = nlmsg_data(nlh); 590c4e38f41SEvgeniy Polyakov err = -EINVAL; 5917b218574SDenis V. Lunev if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL) 5925c753978SThomas Graf goto errout; 5931da177e4SLinus Torvalds 5944b8aa9abSDenis V. Lunev dev = __dev_get_by_index(net, ifm->ifa_index); 5955c753978SThomas Graf err = -ENODEV; 5967b218574SDenis V. Lunev if (dev == NULL) 5975c753978SThomas Graf goto errout; 5981da177e4SLinus Torvalds 5995c753978SThomas Graf in_dev = __in_dev_get_rtnl(dev); 6005c753978SThomas Graf err = -ENOBUFS; 6017b218574SDenis V. Lunev if (in_dev == NULL) 6025c753978SThomas Graf goto errout; 60371e27da9SHerbert Xu 6045c753978SThomas Graf ifa = inet_alloc_ifa(); 6057b218574SDenis V. Lunev if (ifa == NULL) 6065c753978SThomas Graf /* 6075c753978SThomas Graf * A potential indev allocation can be left alive, it stays 6085c753978SThomas Graf * assigned to its device and is destroy with it. 6095c753978SThomas Graf */ 6105c753978SThomas Graf goto errout; 6115c753978SThomas Graf 612a4e65d36SPavel Emelyanov ipv4_devconf_setall(in_dev); 6135c753978SThomas Graf in_dev_hold(in_dev); 6145c753978SThomas Graf 6155c753978SThomas Graf if (tb[IFA_ADDRESS] == NULL) 6165c753978SThomas Graf tb[IFA_ADDRESS] = tb[IFA_LOCAL]; 6175c753978SThomas Graf 618fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 6191da177e4SLinus Torvalds ifa->ifa_prefixlen = ifm->ifa_prefixlen; 6201da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); 6211da177e4SLinus Torvalds ifa->ifa_flags = ifm->ifa_flags; 6221da177e4SLinus Torvalds ifa->ifa_scope = ifm->ifa_scope; 6231da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 6245c753978SThomas Graf 625a7a628c4SAl Viro ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]); 626a7a628c4SAl Viro ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]); 6275c753978SThomas Graf 6285c753978SThomas Graf if (tb[IFA_BROADCAST]) 629a7a628c4SAl Viro ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]); 6305c753978SThomas Graf 6315c753978SThomas Graf if (tb[IFA_LABEL]) 6325c753978SThomas Graf nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ); 6331da177e4SLinus Torvalds else 6341da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 6351da177e4SLinus Torvalds 6365c753978SThomas Graf return ifa; 6375c753978SThomas Graf 6385c753978SThomas Graf errout: 6395c753978SThomas Graf return ERR_PTR(err); 6405c753978SThomas Graf } 6415c753978SThomas Graf 6425c753978SThomas Graf static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 6435c753978SThomas Graf { 6443b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 6455c753978SThomas Graf struct in_ifaddr *ifa; 6465c753978SThomas Graf 6475c753978SThomas Graf ASSERT_RTNL(); 6485c753978SThomas Graf 6494b8aa9abSDenis V. Lunev ifa = rtm_to_ifaddr(net, nlh); 6505c753978SThomas Graf if (IS_ERR(ifa)) 6515c753978SThomas Graf return PTR_ERR(ifa); 6525c753978SThomas Graf 65315e47304SEric W. Biederman return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).portid); 6541da177e4SLinus Torvalds } 6551da177e4SLinus Torvalds 6561da177e4SLinus Torvalds /* 6571da177e4SLinus Torvalds * Determine a default network mask, based on the IP address. 6581da177e4SLinus Torvalds */ 6591da177e4SLinus Torvalds 66040384999SEric Dumazet static int inet_abc_len(__be32 addr) 6611da177e4SLinus Torvalds { 6621da177e4SLinus Torvalds int rc = -1; /* Something else, probably a multicast. */ 6631da177e4SLinus Torvalds 664f97c1e0cSJoe Perches if (ipv4_is_zeronet(addr)) 6651da177e4SLinus Torvalds rc = 0; 6661da177e4SLinus Torvalds else { 667714e85beSAl Viro __u32 haddr = ntohl(addr); 6681da177e4SLinus Torvalds 669714e85beSAl Viro if (IN_CLASSA(haddr)) 6701da177e4SLinus Torvalds rc = 8; 671714e85beSAl Viro else if (IN_CLASSB(haddr)) 6721da177e4SLinus Torvalds rc = 16; 673714e85beSAl Viro else if (IN_CLASSC(haddr)) 6741da177e4SLinus Torvalds rc = 24; 6751da177e4SLinus Torvalds } 6761da177e4SLinus Torvalds 6771da177e4SLinus Torvalds return rc; 6781da177e4SLinus Torvalds } 6791da177e4SLinus Torvalds 6801da177e4SLinus Torvalds 681e5b13cb1SDenis V. Lunev int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) 6821da177e4SLinus Torvalds { 6831da177e4SLinus Torvalds struct ifreq ifr; 6841da177e4SLinus Torvalds struct sockaddr_in sin_orig; 6851da177e4SLinus Torvalds struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; 6861da177e4SLinus Torvalds struct in_device *in_dev; 6871da177e4SLinus Torvalds struct in_ifaddr **ifap = NULL; 6881da177e4SLinus Torvalds struct in_ifaddr *ifa = NULL; 6891da177e4SLinus Torvalds struct net_device *dev; 6901da177e4SLinus Torvalds char *colon; 6911da177e4SLinus Torvalds int ret = -EFAULT; 6921da177e4SLinus Torvalds int tryaddrmatch = 0; 6931da177e4SLinus Torvalds 6941da177e4SLinus Torvalds /* 6951da177e4SLinus Torvalds * Fetch the caller's info block into kernel space 6961da177e4SLinus Torvalds */ 6971da177e4SLinus Torvalds 6981da177e4SLinus Torvalds if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) 6991da177e4SLinus Torvalds goto out; 7001da177e4SLinus Torvalds ifr.ifr_name[IFNAMSIZ - 1] = 0; 7011da177e4SLinus Torvalds 7021da177e4SLinus Torvalds /* save original address for comparison */ 7031da177e4SLinus Torvalds memcpy(&sin_orig, sin, sizeof(*sin)); 7041da177e4SLinus Torvalds 7051da177e4SLinus Torvalds colon = strchr(ifr.ifr_name, ':'); 7061da177e4SLinus Torvalds if (colon) 7071da177e4SLinus Torvalds *colon = 0; 7081da177e4SLinus Torvalds 709e5b13cb1SDenis V. Lunev dev_load(net, ifr.ifr_name); 7101da177e4SLinus Torvalds 7111da177e4SLinus Torvalds switch (cmd) { 7121da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 7131da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 7141da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 7151da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 7161da177e4SLinus Torvalds /* Note that these ioctls will not sleep, 7171da177e4SLinus Torvalds so that we do not impose a lock. 7181da177e4SLinus Torvalds One day we will be forced to put shlock here (I mean SMP) 7191da177e4SLinus Torvalds */ 7201da177e4SLinus Torvalds tryaddrmatch = (sin_orig.sin_family == AF_INET); 7211da177e4SLinus Torvalds memset(sin, 0, sizeof(*sin)); 7221da177e4SLinus Torvalds sin->sin_family = AF_INET; 7231da177e4SLinus Torvalds break; 7241da177e4SLinus Torvalds 7251da177e4SLinus Torvalds case SIOCSIFFLAGS: 726bf5b30b8SZhao Hongjiang ret = -EPERM; 72752e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 7281da177e4SLinus Torvalds goto out; 7291da177e4SLinus Torvalds break; 7301da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 7311da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 7321da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 7331da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 734bf5b30b8SZhao Hongjiang ret = -EPERM; 73552e804c6SEric W. Biederman if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) 7361da177e4SLinus Torvalds goto out; 7371da177e4SLinus Torvalds ret = -EINVAL; 7381da177e4SLinus Torvalds if (sin->sin_family != AF_INET) 7391da177e4SLinus Torvalds goto out; 7401da177e4SLinus Torvalds break; 7411da177e4SLinus Torvalds default: 7421da177e4SLinus Torvalds ret = -EINVAL; 7431da177e4SLinus Torvalds goto out; 7441da177e4SLinus Torvalds } 7451da177e4SLinus Torvalds 7461da177e4SLinus Torvalds rtnl_lock(); 7471da177e4SLinus Torvalds 7481da177e4SLinus Torvalds ret = -ENODEV; 7499f9354b9SEric Dumazet dev = __dev_get_by_name(net, ifr.ifr_name); 7509f9354b9SEric Dumazet if (!dev) 7511da177e4SLinus Torvalds goto done; 7521da177e4SLinus Torvalds 7531da177e4SLinus Torvalds if (colon) 7541da177e4SLinus Torvalds *colon = ':'; 7551da177e4SLinus Torvalds 7569f9354b9SEric Dumazet in_dev = __in_dev_get_rtnl(dev); 7579f9354b9SEric Dumazet if (in_dev) { 7581da177e4SLinus Torvalds if (tryaddrmatch) { 7591da177e4SLinus Torvalds /* Matthias Andree */ 7601da177e4SLinus Torvalds /* compare label and address (4.4BSD style) */ 7611da177e4SLinus Torvalds /* note: we only do this for a limited set of ioctls 7621da177e4SLinus Torvalds and only if the original address family was AF_INET. 7631da177e4SLinus Torvalds This is checked above. */ 7641da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 7651da177e4SLinus Torvalds ifap = &ifa->ifa_next) { 7661da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label) && 7671da177e4SLinus Torvalds sin_orig.sin_addr.s_addr == 7686c91afe1SDavid S. Miller ifa->ifa_local) { 7691da177e4SLinus Torvalds break; /* found */ 7701da177e4SLinus Torvalds } 7711da177e4SLinus Torvalds } 7721da177e4SLinus Torvalds } 7731da177e4SLinus Torvalds /* we didn't get a match, maybe the application is 7741da177e4SLinus Torvalds 4.3BSD-style and passed in junk so we fall back to 7751da177e4SLinus Torvalds comparing just the label */ 7761da177e4SLinus Torvalds if (!ifa) { 7771da177e4SLinus Torvalds for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; 7781da177e4SLinus Torvalds ifap = &ifa->ifa_next) 7791da177e4SLinus Torvalds if (!strcmp(ifr.ifr_name, ifa->ifa_label)) 7801da177e4SLinus Torvalds break; 7811da177e4SLinus Torvalds } 7821da177e4SLinus Torvalds } 7831da177e4SLinus Torvalds 7841da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 7851da177e4SLinus Torvalds if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) 7861da177e4SLinus Torvalds goto done; 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds switch (cmd) { 7891da177e4SLinus Torvalds case SIOCGIFADDR: /* Get interface address */ 7901da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_local; 7911da177e4SLinus Torvalds goto rarok; 7921da177e4SLinus Torvalds 7931da177e4SLinus Torvalds case SIOCGIFBRDADDR: /* Get the broadcast address */ 7941da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_broadcast; 7951da177e4SLinus Torvalds goto rarok; 7961da177e4SLinus Torvalds 7971da177e4SLinus Torvalds case SIOCGIFDSTADDR: /* Get the destination address */ 7981da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_address; 7991da177e4SLinus Torvalds goto rarok; 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds case SIOCGIFNETMASK: /* Get the netmask for the interface */ 8021da177e4SLinus Torvalds sin->sin_addr.s_addr = ifa->ifa_mask; 8031da177e4SLinus Torvalds goto rarok; 8041da177e4SLinus Torvalds 8051da177e4SLinus Torvalds case SIOCSIFFLAGS: 8061da177e4SLinus Torvalds if (colon) { 8071da177e4SLinus Torvalds ret = -EADDRNOTAVAIL; 8081da177e4SLinus Torvalds if (!ifa) 8091da177e4SLinus Torvalds break; 8101da177e4SLinus Torvalds ret = 0; 8111da177e4SLinus Torvalds if (!(ifr.ifr_flags & IFF_UP)) 8121da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 1); 8131da177e4SLinus Torvalds break; 8141da177e4SLinus Torvalds } 8151da177e4SLinus Torvalds ret = dev_change_flags(dev, ifr.ifr_flags); 8161da177e4SLinus Torvalds break; 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds case SIOCSIFADDR: /* Set interface address (and family) */ 8191da177e4SLinus Torvalds ret = -EINVAL; 8201da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 8211da177e4SLinus Torvalds break; 8221da177e4SLinus Torvalds 8231da177e4SLinus Torvalds if (!ifa) { 8241da177e4SLinus Torvalds ret = -ENOBUFS; 8259f9354b9SEric Dumazet ifa = inet_alloc_ifa(); 826fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 8279f9354b9SEric Dumazet if (!ifa) 8281da177e4SLinus Torvalds break; 8291da177e4SLinus Torvalds if (colon) 8301da177e4SLinus Torvalds memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); 8311da177e4SLinus Torvalds else 8321da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 8331da177e4SLinus Torvalds } else { 8341da177e4SLinus Torvalds ret = 0; 8351da177e4SLinus Torvalds if (ifa->ifa_local == sin->sin_addr.s_addr) 8361da177e4SLinus Torvalds break; 8371da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8381da177e4SLinus Torvalds ifa->ifa_broadcast = 0; 839148f9729SBjorn Mork ifa->ifa_scope = 0; 8401da177e4SLinus Torvalds } 8411da177e4SLinus Torvalds 8421da177e4SLinus Torvalds ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; 8431da177e4SLinus Torvalds 8441da177e4SLinus Torvalds if (!(dev->flags & IFF_POINTOPOINT)) { 8451da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); 8461da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); 8471da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 8481da177e4SLinus Torvalds ifa->ifa_prefixlen < 31) 8491da177e4SLinus Torvalds ifa->ifa_broadcast = ifa->ifa_address | 8501da177e4SLinus Torvalds ~ifa->ifa_mask; 8511da177e4SLinus Torvalds } else { 8521da177e4SLinus Torvalds ifa->ifa_prefixlen = 32; 8531da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(32); 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds ret = inet_set_ifa(dev, ifa); 8561da177e4SLinus Torvalds break; 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds case SIOCSIFBRDADDR: /* Set the broadcast address */ 8591da177e4SLinus Torvalds ret = 0; 8601da177e4SLinus Torvalds if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { 8611da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8621da177e4SLinus Torvalds ifa->ifa_broadcast = sin->sin_addr.s_addr; 8631da177e4SLinus Torvalds inet_insert_ifa(ifa); 8641da177e4SLinus Torvalds } 8651da177e4SLinus Torvalds break; 8661da177e4SLinus Torvalds 8671da177e4SLinus Torvalds case SIOCSIFDSTADDR: /* Set the destination address */ 8681da177e4SLinus Torvalds ret = 0; 8691da177e4SLinus Torvalds if (ifa->ifa_address == sin->sin_addr.s_addr) 8701da177e4SLinus Torvalds break; 8711da177e4SLinus Torvalds ret = -EINVAL; 8721da177e4SLinus Torvalds if (inet_abc_len(sin->sin_addr.s_addr) < 0) 8731da177e4SLinus Torvalds break; 8741da177e4SLinus Torvalds ret = 0; 8751da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8761da177e4SLinus Torvalds ifa->ifa_address = sin->sin_addr.s_addr; 8771da177e4SLinus Torvalds inet_insert_ifa(ifa); 8781da177e4SLinus Torvalds break; 8791da177e4SLinus Torvalds 8801da177e4SLinus Torvalds case SIOCSIFNETMASK: /* Set the netmask for the interface */ 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds /* 8831da177e4SLinus Torvalds * The mask we set must be legal. 8841da177e4SLinus Torvalds */ 8851da177e4SLinus Torvalds ret = -EINVAL; 8861da177e4SLinus Torvalds if (bad_mask(sin->sin_addr.s_addr, 0)) 8871da177e4SLinus Torvalds break; 8881da177e4SLinus Torvalds ret = 0; 8891da177e4SLinus Torvalds if (ifa->ifa_mask != sin->sin_addr.s_addr) { 890a144ea4bSAl Viro __be32 old_mask = ifa->ifa_mask; 8911da177e4SLinus Torvalds inet_del_ifa(in_dev, ifap, 0); 8921da177e4SLinus Torvalds ifa->ifa_mask = sin->sin_addr.s_addr; 8931da177e4SLinus Torvalds ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); 8941da177e4SLinus Torvalds 8951da177e4SLinus Torvalds /* See if current broadcast address matches 8961da177e4SLinus Torvalds * with current netmask, then recalculate 8971da177e4SLinus Torvalds * the broadcast address. Otherwise it's a 8981da177e4SLinus Torvalds * funny address, so don't touch it since 8991da177e4SLinus Torvalds * the user seems to know what (s)he's doing... 9001da177e4SLinus Torvalds */ 9011da177e4SLinus Torvalds if ((dev->flags & IFF_BROADCAST) && 9021da177e4SLinus Torvalds (ifa->ifa_prefixlen < 31) && 9031da177e4SLinus Torvalds (ifa->ifa_broadcast == 904dcab5e1eSDavid Engel (ifa->ifa_local|~old_mask))) { 9051da177e4SLinus Torvalds ifa->ifa_broadcast = (ifa->ifa_local | 9061da177e4SLinus Torvalds ~sin->sin_addr.s_addr); 9071da177e4SLinus Torvalds } 9081da177e4SLinus Torvalds inet_insert_ifa(ifa); 9091da177e4SLinus Torvalds } 9101da177e4SLinus Torvalds break; 9111da177e4SLinus Torvalds } 9121da177e4SLinus Torvalds done: 9131da177e4SLinus Torvalds rtnl_unlock(); 9141da177e4SLinus Torvalds out: 9151da177e4SLinus Torvalds return ret; 9161da177e4SLinus Torvalds rarok: 9171da177e4SLinus Torvalds rtnl_unlock(); 9181da177e4SLinus Torvalds ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; 9191da177e4SLinus Torvalds goto out; 9201da177e4SLinus Torvalds } 9211da177e4SLinus Torvalds 9221da177e4SLinus Torvalds static int inet_gifconf(struct net_device *dev, char __user *buf, int len) 9231da177e4SLinus Torvalds { 924e5ed6399SHerbert Xu struct in_device *in_dev = __in_dev_get_rtnl(dev); 9251da177e4SLinus Torvalds struct in_ifaddr *ifa; 9261da177e4SLinus Torvalds struct ifreq ifr; 9271da177e4SLinus Torvalds int done = 0; 9281da177e4SLinus Torvalds 9299f9354b9SEric Dumazet if (!in_dev) 9301da177e4SLinus Torvalds goto out; 9311da177e4SLinus Torvalds 9329f9354b9SEric Dumazet for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 9331da177e4SLinus Torvalds if (!buf) { 9341da177e4SLinus Torvalds done += sizeof(ifr); 9351da177e4SLinus Torvalds continue; 9361da177e4SLinus Torvalds } 9371da177e4SLinus Torvalds if (len < (int) sizeof(ifr)) 9381da177e4SLinus Torvalds break; 9391da177e4SLinus Torvalds memset(&ifr, 0, sizeof(struct ifreq)); 9401da177e4SLinus Torvalds if (ifa->ifa_label) 9411da177e4SLinus Torvalds strcpy(ifr.ifr_name, ifa->ifa_label); 9421da177e4SLinus Torvalds else 9431da177e4SLinus Torvalds strcpy(ifr.ifr_name, dev->name); 9441da177e4SLinus Torvalds 9451da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; 9461da177e4SLinus Torvalds (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = 9471da177e4SLinus Torvalds ifa->ifa_local; 9481da177e4SLinus Torvalds 9491da177e4SLinus Torvalds if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { 9501da177e4SLinus Torvalds done = -EFAULT; 9511da177e4SLinus Torvalds break; 9521da177e4SLinus Torvalds } 9531da177e4SLinus Torvalds buf += sizeof(struct ifreq); 9541da177e4SLinus Torvalds len -= sizeof(struct ifreq); 9551da177e4SLinus Torvalds done += sizeof(struct ifreq); 9561da177e4SLinus Torvalds } 9571da177e4SLinus Torvalds out: 9581da177e4SLinus Torvalds return done; 9591da177e4SLinus Torvalds } 9601da177e4SLinus Torvalds 961a61ced5dSAl Viro __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) 9621da177e4SLinus Torvalds { 963a61ced5dSAl Viro __be32 addr = 0; 9641da177e4SLinus Torvalds struct in_device *in_dev; 965c346dca1SYOSHIFUJI Hideaki struct net *net = dev_net(dev); 9661da177e4SLinus Torvalds 9671da177e4SLinus Torvalds rcu_read_lock(); 968e5ed6399SHerbert Xu in_dev = __in_dev_get_rcu(dev); 9691da177e4SLinus Torvalds if (!in_dev) 9701da177e4SLinus Torvalds goto no_in_dev; 9711da177e4SLinus Torvalds 9721da177e4SLinus Torvalds for_primary_ifa(in_dev) { 9731da177e4SLinus Torvalds if (ifa->ifa_scope > scope) 9741da177e4SLinus Torvalds continue; 9751da177e4SLinus Torvalds if (!dst || inet_ifa_match(dst, ifa)) { 9761da177e4SLinus Torvalds addr = ifa->ifa_local; 9771da177e4SLinus Torvalds break; 9781da177e4SLinus Torvalds } 9791da177e4SLinus Torvalds if (!addr) 9801da177e4SLinus Torvalds addr = ifa->ifa_local; 9811da177e4SLinus Torvalds } endfor_ifa(in_dev); 9821da177e4SLinus Torvalds 9831da177e4SLinus Torvalds if (addr) 984c6d14c84SEric Dumazet goto out_unlock; 9859f9354b9SEric Dumazet no_in_dev: 9861da177e4SLinus Torvalds 9871da177e4SLinus Torvalds /* Not loopback addresses on loopback should be preferred 9881da177e4SLinus Torvalds in this case. It is importnat that lo is the first interface 9891da177e4SLinus Torvalds in dev_base list. 9901da177e4SLinus Torvalds */ 991c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 9929f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 9939f9354b9SEric Dumazet if (!in_dev) 9941da177e4SLinus Torvalds continue; 9951da177e4SLinus Torvalds 9961da177e4SLinus Torvalds for_primary_ifa(in_dev) { 9971da177e4SLinus Torvalds if (ifa->ifa_scope != RT_SCOPE_LINK && 9981da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 9991da177e4SLinus Torvalds addr = ifa->ifa_local; 1000c6d14c84SEric Dumazet goto out_unlock; 10011da177e4SLinus Torvalds } 10021da177e4SLinus Torvalds } endfor_ifa(in_dev); 10031da177e4SLinus Torvalds } 1004c6d14c84SEric Dumazet out_unlock: 10051da177e4SLinus Torvalds rcu_read_unlock(); 10061da177e4SLinus Torvalds return addr; 10071da177e4SLinus Torvalds } 10089f9354b9SEric Dumazet EXPORT_SYMBOL(inet_select_addr); 10091da177e4SLinus Torvalds 101060cad5daSAl Viro static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, 101160cad5daSAl Viro __be32 local, int scope) 10121da177e4SLinus Torvalds { 10131da177e4SLinus Torvalds int same = 0; 1014a144ea4bSAl Viro __be32 addr = 0; 10151da177e4SLinus Torvalds 10161da177e4SLinus Torvalds for_ifa(in_dev) { 10171da177e4SLinus Torvalds if (!addr && 10181da177e4SLinus Torvalds (local == ifa->ifa_local || !local) && 10191da177e4SLinus Torvalds ifa->ifa_scope <= scope) { 10201da177e4SLinus Torvalds addr = ifa->ifa_local; 10211da177e4SLinus Torvalds if (same) 10221da177e4SLinus Torvalds break; 10231da177e4SLinus Torvalds } 10241da177e4SLinus Torvalds if (!same) { 10251da177e4SLinus Torvalds same = (!local || inet_ifa_match(local, ifa)) && 10261da177e4SLinus Torvalds (!dst || inet_ifa_match(dst, ifa)); 10271da177e4SLinus Torvalds if (same && addr) { 10281da177e4SLinus Torvalds if (local || !dst) 10291da177e4SLinus Torvalds break; 10301da177e4SLinus Torvalds /* Is the selected addr into dst subnet? */ 10311da177e4SLinus Torvalds if (inet_ifa_match(addr, ifa)) 10321da177e4SLinus Torvalds break; 10331da177e4SLinus Torvalds /* No, then can we use new local src? */ 10341da177e4SLinus Torvalds if (ifa->ifa_scope <= scope) { 10351da177e4SLinus Torvalds addr = ifa->ifa_local; 10361da177e4SLinus Torvalds break; 10371da177e4SLinus Torvalds } 10381da177e4SLinus Torvalds /* search for large dst subnet for addr */ 10391da177e4SLinus Torvalds same = 0; 10401da177e4SLinus Torvalds } 10411da177e4SLinus Torvalds } 10421da177e4SLinus Torvalds } endfor_ifa(in_dev); 10431da177e4SLinus Torvalds 10441da177e4SLinus Torvalds return same ? addr : 0; 10451da177e4SLinus Torvalds } 10461da177e4SLinus Torvalds 10471da177e4SLinus Torvalds /* 10481da177e4SLinus Torvalds * Confirm that local IP address exists using wildcards: 10499bd85e32SDenis V. Lunev * - in_dev: only on this interface, 0=any interface 10501da177e4SLinus Torvalds * - dst: only in the same subnet as dst, 0=any dst 10511da177e4SLinus Torvalds * - local: address, 0=autoselect the local address 10521da177e4SLinus Torvalds * - scope: maximum allowed scope value for the local address 10531da177e4SLinus Torvalds */ 10549bd85e32SDenis V. Lunev __be32 inet_confirm_addr(struct in_device *in_dev, 10559bd85e32SDenis V. Lunev __be32 dst, __be32 local, int scope) 10561da177e4SLinus Torvalds { 105760cad5daSAl Viro __be32 addr = 0; 10589bd85e32SDenis V. Lunev struct net_device *dev; 105939a6d063SDenis V. Lunev struct net *net; 10601da177e4SLinus Torvalds 106139a6d063SDenis V. Lunev if (scope != RT_SCOPE_LINK) 10629bd85e32SDenis V. Lunev return confirm_addr_indev(in_dev, dst, local, scope); 10631da177e4SLinus Torvalds 1064c346dca1SYOSHIFUJI Hideaki net = dev_net(in_dev->dev); 10651da177e4SLinus Torvalds rcu_read_lock(); 1066c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 10679f9354b9SEric Dumazet in_dev = __in_dev_get_rcu(dev); 10689f9354b9SEric Dumazet if (in_dev) { 10691da177e4SLinus Torvalds addr = confirm_addr_indev(in_dev, dst, local, scope); 10701da177e4SLinus Torvalds if (addr) 10711da177e4SLinus Torvalds break; 10721da177e4SLinus Torvalds } 10731da177e4SLinus Torvalds } 10741da177e4SLinus Torvalds rcu_read_unlock(); 10751da177e4SLinus Torvalds 10761da177e4SLinus Torvalds return addr; 10771da177e4SLinus Torvalds } 1078eaddcd76SAndy Gospodarek EXPORT_SYMBOL(inet_confirm_addr); 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds /* 10811da177e4SLinus Torvalds * Device notifier 10821da177e4SLinus Torvalds */ 10831da177e4SLinus Torvalds 10841da177e4SLinus Torvalds int register_inetaddr_notifier(struct notifier_block *nb) 10851da177e4SLinus Torvalds { 1086e041c683SAlan Stern return blocking_notifier_chain_register(&inetaddr_chain, nb); 10871da177e4SLinus Torvalds } 10889f9354b9SEric Dumazet EXPORT_SYMBOL(register_inetaddr_notifier); 10891da177e4SLinus Torvalds 10901da177e4SLinus Torvalds int unregister_inetaddr_notifier(struct notifier_block *nb) 10911da177e4SLinus Torvalds { 1092e041c683SAlan Stern return blocking_notifier_chain_unregister(&inetaddr_chain, nb); 10931da177e4SLinus Torvalds } 10949f9354b9SEric Dumazet EXPORT_SYMBOL(unregister_inetaddr_notifier); 10951da177e4SLinus Torvalds 10969f9354b9SEric Dumazet /* Rename ifa_labels for a device name change. Make some effort to preserve 10979f9354b9SEric Dumazet * existing alias numbering and to create unique labels if possible. 10981da177e4SLinus Torvalds */ 10991da177e4SLinus Torvalds static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) 11001da177e4SLinus Torvalds { 11011da177e4SLinus Torvalds struct in_ifaddr *ifa; 11021da177e4SLinus Torvalds int named = 0; 11031da177e4SLinus Torvalds 11041da177e4SLinus Torvalds for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { 11051da177e4SLinus Torvalds char old[IFNAMSIZ], *dot; 11061da177e4SLinus Torvalds 11071da177e4SLinus Torvalds memcpy(old, ifa->ifa_label, IFNAMSIZ); 11081da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11091da177e4SLinus Torvalds if (named++ == 0) 1110573bf470SThomas Graf goto skip; 111144344b2aSMark McLoughlin dot = strchr(old, ':'); 11121da177e4SLinus Torvalds if (dot == NULL) { 11131da177e4SLinus Torvalds sprintf(old, ":%d", named); 11141da177e4SLinus Torvalds dot = old; 11151da177e4SLinus Torvalds } 11169f9354b9SEric Dumazet if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) 11171da177e4SLinus Torvalds strcat(ifa->ifa_label, dot); 11189f9354b9SEric Dumazet else 11191da177e4SLinus Torvalds strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); 1120573bf470SThomas Graf skip: 1121573bf470SThomas Graf rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); 11221da177e4SLinus Torvalds } 11231da177e4SLinus Torvalds } 11241da177e4SLinus Torvalds 112540384999SEric Dumazet static bool inetdev_valid_mtu(unsigned int mtu) 112606770843SBreno Leitao { 112706770843SBreno Leitao return mtu >= 68; 112806770843SBreno Leitao } 112906770843SBreno Leitao 1130d11327adSIan Campbell static void inetdev_send_gratuitous_arp(struct net_device *dev, 1131d11327adSIan Campbell struct in_device *in_dev) 1132d11327adSIan Campbell 1133d11327adSIan Campbell { 1134b76d0789SZoltan Kiss struct in_ifaddr *ifa; 1135d11327adSIan Campbell 1136b76d0789SZoltan Kiss for (ifa = in_dev->ifa_list; ifa; 1137b76d0789SZoltan Kiss ifa = ifa->ifa_next) { 1138d11327adSIan Campbell arp_send(ARPOP_REQUEST, ETH_P_ARP, 11396c91afe1SDavid S. Miller ifa->ifa_local, dev, 11406c91afe1SDavid S. Miller ifa->ifa_local, NULL, 1141d11327adSIan Campbell dev->dev_addr, NULL); 1142d11327adSIan Campbell } 1143b76d0789SZoltan Kiss } 1144d11327adSIan Campbell 11451da177e4SLinus Torvalds /* Called only under RTNL semaphore */ 11461da177e4SLinus Torvalds 11471da177e4SLinus Torvalds static int inetdev_event(struct notifier_block *this, unsigned long event, 11481da177e4SLinus Torvalds void *ptr) 11491da177e4SLinus Torvalds { 11501da177e4SLinus Torvalds struct net_device *dev = ptr; 1151748e2d93SEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 11521da177e4SLinus Torvalds 11531da177e4SLinus Torvalds ASSERT_RTNL(); 11541da177e4SLinus Torvalds 11551da177e4SLinus Torvalds if (!in_dev) { 11568030f544SHerbert Xu if (event == NETDEV_REGISTER) { 11571da177e4SLinus Torvalds in_dev = inetdev_init(dev); 11588d76527eSHerbert Xu if (!in_dev) 1159b217d616SHerbert Xu return notifier_from_errno(-ENOMEM); 11600cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 116142f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOXFRM, 1); 116242f811b8SHerbert Xu IN_DEV_CONF_SET(in_dev, NOPOLICY, 1); 11631da177e4SLinus Torvalds } 116406770843SBreno Leitao } else if (event == NETDEV_CHANGEMTU) { 116506770843SBreno Leitao /* Re-enabling IP */ 116606770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 116706770843SBreno Leitao in_dev = inetdev_init(dev); 11688030f544SHerbert Xu } 11691da177e4SLinus Torvalds goto out; 11701da177e4SLinus Torvalds } 11711da177e4SLinus Torvalds 11721da177e4SLinus Torvalds switch (event) { 11731da177e4SLinus Torvalds case NETDEV_REGISTER: 117491df42beSJoe Perches pr_debug("%s: bug\n", __func__); 1175a9b3cd7fSStephen Hemminger RCU_INIT_POINTER(dev->ip_ptr, NULL); 11761da177e4SLinus Torvalds break; 11771da177e4SLinus Torvalds case NETDEV_UP: 117806770843SBreno Leitao if (!inetdev_valid_mtu(dev->mtu)) 11791da177e4SLinus Torvalds break; 11800cc217e1SEric W. Biederman if (dev->flags & IFF_LOOPBACK) { 11819f9354b9SEric Dumazet struct in_ifaddr *ifa = inet_alloc_ifa(); 11829f9354b9SEric Dumazet 11839f9354b9SEric Dumazet if (ifa) { 1184fd23c3b3SDavid S. Miller INIT_HLIST_NODE(&ifa->hash); 11851da177e4SLinus Torvalds ifa->ifa_local = 11861da177e4SLinus Torvalds ifa->ifa_address = htonl(INADDR_LOOPBACK); 11871da177e4SLinus Torvalds ifa->ifa_prefixlen = 8; 11881da177e4SLinus Torvalds ifa->ifa_mask = inet_make_mask(8); 11891da177e4SLinus Torvalds in_dev_hold(in_dev); 11901da177e4SLinus Torvalds ifa->ifa_dev = in_dev; 11911da177e4SLinus Torvalds ifa->ifa_scope = RT_SCOPE_HOST; 11921da177e4SLinus Torvalds memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); 11931da177e4SLinus Torvalds inet_insert_ifa(ifa); 11941da177e4SLinus Torvalds } 11951da177e4SLinus Torvalds } 11961da177e4SLinus Torvalds ip_mc_up(in_dev); 1197eefef1cfSStephen Hemminger /* fall through */ 1198eefef1cfSStephen Hemminger case NETDEV_CHANGEADDR: 1199d11327adSIan Campbell if (!IN_DEV_ARP_NOTIFY(in_dev)) 1200d11327adSIan Campbell break; 1201d11327adSIan Campbell /* fall through */ 1202d11327adSIan Campbell case NETDEV_NOTIFY_PEERS: 1203a21090cfSStephen Hemminger /* Send gratuitous ARP to notify of link change */ 1204d11327adSIan Campbell inetdev_send_gratuitous_arp(dev, in_dev); 12051da177e4SLinus Torvalds break; 12061da177e4SLinus Torvalds case NETDEV_DOWN: 12071da177e4SLinus Torvalds ip_mc_down(in_dev); 12081da177e4SLinus Torvalds break; 120993d9b7d7SJiri Pirko case NETDEV_PRE_TYPE_CHANGE: 121075c78500SMoni Shoua ip_mc_unmap(in_dev); 121175c78500SMoni Shoua break; 121293d9b7d7SJiri Pirko case NETDEV_POST_TYPE_CHANGE: 121375c78500SMoni Shoua ip_mc_remap(in_dev); 121475c78500SMoni Shoua break; 12151da177e4SLinus Torvalds case NETDEV_CHANGEMTU: 121606770843SBreno Leitao if (inetdev_valid_mtu(dev->mtu)) 12171da177e4SLinus Torvalds break; 121806770843SBreno Leitao /* disable IP when MTU is not enough */ 12191da177e4SLinus Torvalds case NETDEV_UNREGISTER: 12201da177e4SLinus Torvalds inetdev_destroy(in_dev); 12211da177e4SLinus Torvalds break; 12221da177e4SLinus Torvalds case NETDEV_CHANGENAME: 12231da177e4SLinus Torvalds /* Do not notify about label change, this event is 12241da177e4SLinus Torvalds * not interesting to applications using netlink. 12251da177e4SLinus Torvalds */ 12261da177e4SLinus Torvalds inetdev_changename(dev, in_dev); 12271da177e4SLinus Torvalds 122851602b2aSPavel Emelyanov devinet_sysctl_unregister(in_dev); 122966f27a52SPavel Emelyanov devinet_sysctl_register(in_dev); 12301da177e4SLinus Torvalds break; 12311da177e4SLinus Torvalds } 12321da177e4SLinus Torvalds out: 12331da177e4SLinus Torvalds return NOTIFY_DONE; 12341da177e4SLinus Torvalds } 12351da177e4SLinus Torvalds 12361da177e4SLinus Torvalds static struct notifier_block ip_netdev_notifier = { 12371da177e4SLinus Torvalds .notifier_call = inetdev_event, 12381da177e4SLinus Torvalds }; 12391da177e4SLinus Torvalds 124040384999SEric Dumazet static size_t inet_nlmsg_size(void) 1241339bf98fSThomas Graf { 1242339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifaddrmsg)) 1243339bf98fSThomas Graf + nla_total_size(4) /* IFA_ADDRESS */ 1244339bf98fSThomas Graf + nla_total_size(4) /* IFA_LOCAL */ 1245339bf98fSThomas Graf + nla_total_size(4) /* IFA_BROADCAST */ 1246339bf98fSThomas Graf + nla_total_size(IFNAMSIZ); /* IFA_LABEL */ 1247339bf98fSThomas Graf } 1248339bf98fSThomas Graf 12491da177e4SLinus Torvalds static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, 125015e47304SEric W. Biederman u32 portid, u32 seq, int event, unsigned int flags) 12511da177e4SLinus Torvalds { 12521da177e4SLinus Torvalds struct ifaddrmsg *ifm; 12531da177e4SLinus Torvalds struct nlmsghdr *nlh; 12541da177e4SLinus Torvalds 125515e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), flags); 125647f68512SThomas Graf if (nlh == NULL) 125726932566SPatrick McHardy return -EMSGSIZE; 125847f68512SThomas Graf 125947f68512SThomas Graf ifm = nlmsg_data(nlh); 12601da177e4SLinus Torvalds ifm->ifa_family = AF_INET; 12611da177e4SLinus Torvalds ifm->ifa_prefixlen = ifa->ifa_prefixlen; 12621da177e4SLinus Torvalds ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT; 12631da177e4SLinus Torvalds ifm->ifa_scope = ifa->ifa_scope; 12641da177e4SLinus Torvalds ifm->ifa_index = ifa->ifa_dev->dev->ifindex; 12651da177e4SLinus Torvalds 1266f3756b79SDavid S. Miller if ((ifa->ifa_address && 1267f3756b79SDavid S. Miller nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) || 1268f3756b79SDavid S. Miller (ifa->ifa_local && 1269f3756b79SDavid S. Miller nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) || 1270f3756b79SDavid S. Miller (ifa->ifa_broadcast && 1271f3756b79SDavid S. Miller nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) || 1272f3756b79SDavid S. Miller (ifa->ifa_label[0] && 1273f3756b79SDavid S. Miller nla_put_string(skb, IFA_LABEL, ifa->ifa_label))) 1274f3756b79SDavid S. Miller goto nla_put_failure; 127547f68512SThomas Graf 127647f68512SThomas Graf return nlmsg_end(skb, nlh); 127747f68512SThomas Graf 127847f68512SThomas Graf nla_put_failure: 127926932566SPatrick McHardy nlmsg_cancel(skb, nlh); 128026932566SPatrick McHardy return -EMSGSIZE; 12811da177e4SLinus Torvalds } 12821da177e4SLinus Torvalds 12831da177e4SLinus Torvalds static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) 12841da177e4SLinus Torvalds { 12853b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk); 1286eec4df98SEric Dumazet int h, s_h; 1287eec4df98SEric Dumazet int idx, s_idx; 1288eec4df98SEric Dumazet int ip_idx, s_ip_idx; 12891da177e4SLinus Torvalds struct net_device *dev; 12901da177e4SLinus Torvalds struct in_device *in_dev; 12911da177e4SLinus Torvalds struct in_ifaddr *ifa; 1292eec4df98SEric Dumazet struct hlist_head *head; 1293eec4df98SEric Dumazet struct hlist_node *node; 12941da177e4SLinus Torvalds 1295eec4df98SEric Dumazet s_h = cb->args[0]; 1296eec4df98SEric Dumazet s_idx = idx = cb->args[1]; 1297eec4df98SEric Dumazet s_ip_idx = ip_idx = cb->args[2]; 1298eec4df98SEric Dumazet 1299eec4df98SEric Dumazet for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { 13007562f876SPavel Emelianov idx = 0; 1301eec4df98SEric Dumazet head = &net->dev_index_head[h]; 1302eec4df98SEric Dumazet rcu_read_lock(); 1303eec4df98SEric Dumazet hlist_for_each_entry_rcu(dev, node, head, index_hlist) { 13041da177e4SLinus Torvalds if (idx < s_idx) 13057562f876SPavel Emelianov goto cont; 13064b97efdfSPatrick McHardy if (h > s_h || idx > s_idx) 13071da177e4SLinus Torvalds s_ip_idx = 0; 1308eec4df98SEric Dumazet in_dev = __in_dev_get_rcu(dev); 13099f9354b9SEric Dumazet if (!in_dev) 13107562f876SPavel Emelianov goto cont; 13111da177e4SLinus Torvalds 13121da177e4SLinus Torvalds for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; 13131da177e4SLinus Torvalds ifa = ifa->ifa_next, ip_idx++) { 13141da177e4SLinus Torvalds if (ip_idx < s_ip_idx) 1315596e4150SStephen Hemminger continue; 1316eec4df98SEric Dumazet if (inet_fill_ifaddr(skb, ifa, 131715e47304SEric W. Biederman NETLINK_CB(cb->skb).portid, 13181da177e4SLinus Torvalds cb->nlh->nlmsg_seq, 1319eec4df98SEric Dumazet RTM_NEWADDR, NLM_F_MULTI) <= 0) { 1320eec4df98SEric Dumazet rcu_read_unlock(); 13211da177e4SLinus Torvalds goto done; 13221da177e4SLinus Torvalds } 1323eec4df98SEric Dumazet } 13247562f876SPavel Emelianov cont: 13257562f876SPavel Emelianov idx++; 13261da177e4SLinus Torvalds } 1327eec4df98SEric Dumazet rcu_read_unlock(); 1328eec4df98SEric Dumazet } 13291da177e4SLinus Torvalds 13301da177e4SLinus Torvalds done: 1331eec4df98SEric Dumazet cb->args[0] = h; 1332eec4df98SEric Dumazet cb->args[1] = idx; 1333eec4df98SEric Dumazet cb->args[2] = ip_idx; 13341da177e4SLinus Torvalds 13351da177e4SLinus Torvalds return skb->len; 13361da177e4SLinus Torvalds } 13371da177e4SLinus Torvalds 1338d6062cbbSThomas Graf static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh, 133915e47304SEric W. Biederman u32 portid) 13401da177e4SLinus Torvalds { 134147f68512SThomas Graf struct sk_buff *skb; 1342d6062cbbSThomas Graf u32 seq = nlh ? nlh->nlmsg_seq : 0; 1343d6062cbbSThomas Graf int err = -ENOBUFS; 13444b8aa9abSDenis V. Lunev struct net *net; 13451da177e4SLinus Torvalds 1346c346dca1SYOSHIFUJI Hideaki net = dev_net(ifa->ifa_dev->dev); 1347339bf98fSThomas Graf skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL); 134847f68512SThomas Graf if (skb == NULL) 1349d6062cbbSThomas Graf goto errout; 1350d6062cbbSThomas Graf 135115e47304SEric W. Biederman err = inet_fill_ifaddr(skb, ifa, portid, seq, event, 0); 135226932566SPatrick McHardy if (err < 0) { 135326932566SPatrick McHardy /* -EMSGSIZE implies BUG in inet_nlmsg_size() */ 135426932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 135526932566SPatrick McHardy kfree_skb(skb); 135626932566SPatrick McHardy goto errout; 135726932566SPatrick McHardy } 135815e47304SEric W. Biederman rtnl_notify(skb, net, portid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL); 13591ce85fe4SPablo Neira Ayuso return; 1360d6062cbbSThomas Graf errout: 1361d6062cbbSThomas Graf if (err < 0) 13624b8aa9abSDenis V. Lunev rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); 13631da177e4SLinus Torvalds } 13641da177e4SLinus Torvalds 13659f0f7272SThomas Graf static size_t inet_get_link_af_size(const struct net_device *dev) 13669f0f7272SThomas Graf { 13671fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 13689f0f7272SThomas Graf 13699f0f7272SThomas Graf if (!in_dev) 13709f0f7272SThomas Graf return 0; 13719f0f7272SThomas Graf 13729f0f7272SThomas Graf return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ 13739f0f7272SThomas Graf } 13749f0f7272SThomas Graf 13759f0f7272SThomas Graf static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) 13769f0f7272SThomas Graf { 13771fc19affSEric Dumazet struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); 13789f0f7272SThomas Graf struct nlattr *nla; 13799f0f7272SThomas Graf int i; 13809f0f7272SThomas Graf 13819f0f7272SThomas Graf if (!in_dev) 13829f0f7272SThomas Graf return -ENODATA; 13839f0f7272SThomas Graf 13849f0f7272SThomas Graf nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); 13859f0f7272SThomas Graf if (nla == NULL) 13869f0f7272SThomas Graf return -EMSGSIZE; 13879f0f7272SThomas Graf 13889f0f7272SThomas Graf for (i = 0; i < IPV4_DEVCONF_MAX; i++) 13899f0f7272SThomas Graf ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; 13909f0f7272SThomas Graf 13919f0f7272SThomas Graf return 0; 13929f0f7272SThomas Graf } 13939f0f7272SThomas Graf 13949f0f7272SThomas Graf static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { 13959f0f7272SThomas Graf [IFLA_INET_CONF] = { .type = NLA_NESTED }, 13969f0f7272SThomas Graf }; 13979f0f7272SThomas Graf 1398cf7afbfeSThomas Graf static int inet_validate_link_af(const struct net_device *dev, 1399cf7afbfeSThomas Graf const struct nlattr *nla) 14009f0f7272SThomas Graf { 14019f0f7272SThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 14029f0f7272SThomas Graf int err, rem; 14039f0f7272SThomas Graf 1404f7fce74eSEric Dumazet if (dev && !__in_dev_get_rtnl(dev)) 1405cf7afbfeSThomas Graf return -EAFNOSUPPORT; 14069f0f7272SThomas Graf 14079f0f7272SThomas Graf err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); 14089f0f7272SThomas Graf if (err < 0) 14099f0f7272SThomas Graf return err; 14109f0f7272SThomas Graf 14119f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 14129f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { 14139f0f7272SThomas Graf int cfgid = nla_type(a); 14149f0f7272SThomas Graf 14159f0f7272SThomas Graf if (nla_len(a) < 4) 14169f0f7272SThomas Graf return -EINVAL; 14179f0f7272SThomas Graf 14189f0f7272SThomas Graf if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) 14199f0f7272SThomas Graf return -EINVAL; 14209f0f7272SThomas Graf } 14219f0f7272SThomas Graf } 14229f0f7272SThomas Graf 1423cf7afbfeSThomas Graf return 0; 1424cf7afbfeSThomas Graf } 1425cf7afbfeSThomas Graf 1426cf7afbfeSThomas Graf static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) 1427cf7afbfeSThomas Graf { 1428f7fce74eSEric Dumazet struct in_device *in_dev = __in_dev_get_rtnl(dev); 1429cf7afbfeSThomas Graf struct nlattr *a, *tb[IFLA_INET_MAX+1]; 1430cf7afbfeSThomas Graf int rem; 1431cf7afbfeSThomas Graf 1432cf7afbfeSThomas Graf if (!in_dev) 1433cf7afbfeSThomas Graf return -EAFNOSUPPORT; 1434cf7afbfeSThomas Graf 1435cf7afbfeSThomas Graf if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) 1436cf7afbfeSThomas Graf BUG(); 1437cf7afbfeSThomas Graf 14389f0f7272SThomas Graf if (tb[IFLA_INET_CONF]) { 14399f0f7272SThomas Graf nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) 14409f0f7272SThomas Graf ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); 14419f0f7272SThomas Graf } 14429f0f7272SThomas Graf 14439f0f7272SThomas Graf return 0; 14449f0f7272SThomas Graf } 14459f0f7272SThomas Graf 1446edc9e748SNicolas Dichtel static int inet_netconf_msgsize_devconf(int type) 1447edc9e748SNicolas Dichtel { 1448edc9e748SNicolas Dichtel int size = NLMSG_ALIGN(sizeof(struct netconfmsg)) 1449edc9e748SNicolas Dichtel + nla_total_size(4); /* NETCONFA_IFINDEX */ 1450edc9e748SNicolas Dichtel 14519e551110SNicolas Dichtel /* type -1 is used for ALL */ 14529e551110SNicolas Dichtel if (type == -1 || type == NETCONFA_FORWARDING) 1453edc9e748SNicolas Dichtel size += nla_total_size(4); 1454cc535dfbSNicolas Dichtel if (type == -1 || type == NETCONFA_RP_FILTER) 1455cc535dfbSNicolas Dichtel size += nla_total_size(4); 1456*d67b8c61SNicolas Dichtel if (type == -1 || type == NETCONFA_MC_FORWARDING) 1457*d67b8c61SNicolas Dichtel size += nla_total_size(4); 1458edc9e748SNicolas Dichtel 1459edc9e748SNicolas Dichtel return size; 1460edc9e748SNicolas Dichtel } 1461edc9e748SNicolas Dichtel 1462edc9e748SNicolas Dichtel static int inet_netconf_fill_devconf(struct sk_buff *skb, int ifindex, 1463edc9e748SNicolas Dichtel struct ipv4_devconf *devconf, u32 portid, 1464edc9e748SNicolas Dichtel u32 seq, int event, unsigned int flags, 1465edc9e748SNicolas Dichtel int type) 1466edc9e748SNicolas Dichtel { 1467edc9e748SNicolas Dichtel struct nlmsghdr *nlh; 1468edc9e748SNicolas Dichtel struct netconfmsg *ncm; 1469edc9e748SNicolas Dichtel 1470edc9e748SNicolas Dichtel nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct netconfmsg), 1471edc9e748SNicolas Dichtel flags); 1472edc9e748SNicolas Dichtel if (nlh == NULL) 1473edc9e748SNicolas Dichtel return -EMSGSIZE; 1474edc9e748SNicolas Dichtel 1475edc9e748SNicolas Dichtel ncm = nlmsg_data(nlh); 1476edc9e748SNicolas Dichtel ncm->ncm_family = AF_INET; 1477edc9e748SNicolas Dichtel 1478edc9e748SNicolas Dichtel if (nla_put_s32(skb, NETCONFA_IFINDEX, ifindex) < 0) 1479edc9e748SNicolas Dichtel goto nla_put_failure; 1480edc9e748SNicolas Dichtel 14819e551110SNicolas Dichtel /* type -1 is used for ALL */ 14829e551110SNicolas Dichtel if ((type == -1 || type == NETCONFA_FORWARDING) && 1483edc9e748SNicolas Dichtel nla_put_s32(skb, NETCONFA_FORWARDING, 1484edc9e748SNicolas Dichtel IPV4_DEVCONF(*devconf, FORWARDING)) < 0) 1485edc9e748SNicolas Dichtel goto nla_put_failure; 1486cc535dfbSNicolas Dichtel if ((type == -1 || type == NETCONFA_RP_FILTER) && 1487cc535dfbSNicolas Dichtel nla_put_s32(skb, NETCONFA_RP_FILTER, 1488cc535dfbSNicolas Dichtel IPV4_DEVCONF(*devconf, RP_FILTER)) < 0) 1489cc535dfbSNicolas Dichtel goto nla_put_failure; 1490*d67b8c61SNicolas Dichtel if ((type == -1 || type == NETCONFA_MC_FORWARDING) && 1491*d67b8c61SNicolas Dichtel nla_put_s32(skb, NETCONFA_MC_FORWARDING, 1492*d67b8c61SNicolas Dichtel IPV4_DEVCONF(*devconf, MC_FORWARDING)) < 0) 1493*d67b8c61SNicolas Dichtel goto nla_put_failure; 1494edc9e748SNicolas Dichtel 1495edc9e748SNicolas Dichtel return nlmsg_end(skb, nlh); 1496edc9e748SNicolas Dichtel 1497edc9e748SNicolas Dichtel nla_put_failure: 1498edc9e748SNicolas Dichtel nlmsg_cancel(skb, nlh); 1499edc9e748SNicolas Dichtel return -EMSGSIZE; 1500edc9e748SNicolas Dichtel } 1501edc9e748SNicolas Dichtel 1502*d67b8c61SNicolas Dichtel void inet_netconf_notify_devconf(struct net *net, int type, int ifindex, 1503edc9e748SNicolas Dichtel struct ipv4_devconf *devconf) 1504edc9e748SNicolas Dichtel { 1505edc9e748SNicolas Dichtel struct sk_buff *skb; 1506edc9e748SNicolas Dichtel int err = -ENOBUFS; 1507edc9e748SNicolas Dichtel 1508edc9e748SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(type), GFP_ATOMIC); 1509edc9e748SNicolas Dichtel if (skb == NULL) 1510edc9e748SNicolas Dichtel goto errout; 1511edc9e748SNicolas Dichtel 1512edc9e748SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 0, 0, 1513edc9e748SNicolas Dichtel RTM_NEWNETCONF, 0, type); 1514edc9e748SNicolas Dichtel if (err < 0) { 1515edc9e748SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 1516edc9e748SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 1517edc9e748SNicolas Dichtel kfree_skb(skb); 1518edc9e748SNicolas Dichtel goto errout; 1519edc9e748SNicolas Dichtel } 1520edc9e748SNicolas Dichtel rtnl_notify(skb, net, 0, RTNLGRP_IPV4_NETCONF, NULL, GFP_ATOMIC); 1521edc9e748SNicolas Dichtel return; 1522edc9e748SNicolas Dichtel errout: 1523edc9e748SNicolas Dichtel if (err < 0) 1524edc9e748SNicolas Dichtel rtnl_set_sk_err(net, RTNLGRP_IPV4_NETCONF, err); 1525edc9e748SNicolas Dichtel } 1526edc9e748SNicolas Dichtel 15279e551110SNicolas Dichtel static const struct nla_policy devconf_ipv4_policy[NETCONFA_MAX+1] = { 15289e551110SNicolas Dichtel [NETCONFA_IFINDEX] = { .len = sizeof(int) }, 15299e551110SNicolas Dichtel [NETCONFA_FORWARDING] = { .len = sizeof(int) }, 1530cc535dfbSNicolas Dichtel [NETCONFA_RP_FILTER] = { .len = sizeof(int) }, 15319e551110SNicolas Dichtel }; 15329e551110SNicolas Dichtel 15339e551110SNicolas Dichtel static int inet_netconf_get_devconf(struct sk_buff *in_skb, 15349e551110SNicolas Dichtel struct nlmsghdr *nlh, 15359e551110SNicolas Dichtel void *arg) 15369e551110SNicolas Dichtel { 15379e551110SNicolas Dichtel struct net *net = sock_net(in_skb->sk); 15389e551110SNicolas Dichtel struct nlattr *tb[NETCONFA_MAX+1]; 15399e551110SNicolas Dichtel struct netconfmsg *ncm; 15409e551110SNicolas Dichtel struct sk_buff *skb; 15419e551110SNicolas Dichtel struct ipv4_devconf *devconf; 15429e551110SNicolas Dichtel struct in_device *in_dev; 15439e551110SNicolas Dichtel struct net_device *dev; 15449e551110SNicolas Dichtel int ifindex; 15459e551110SNicolas Dichtel int err; 15469e551110SNicolas Dichtel 15479e551110SNicolas Dichtel err = nlmsg_parse(nlh, sizeof(*ncm), tb, NETCONFA_MAX, 15489e551110SNicolas Dichtel devconf_ipv4_policy); 15499e551110SNicolas Dichtel if (err < 0) 15509e551110SNicolas Dichtel goto errout; 15519e551110SNicolas Dichtel 15529e551110SNicolas Dichtel err = EINVAL; 15539e551110SNicolas Dichtel if (!tb[NETCONFA_IFINDEX]) 15549e551110SNicolas Dichtel goto errout; 15559e551110SNicolas Dichtel 15569e551110SNicolas Dichtel ifindex = nla_get_s32(tb[NETCONFA_IFINDEX]); 15579e551110SNicolas Dichtel switch (ifindex) { 15589e551110SNicolas Dichtel case NETCONFA_IFINDEX_ALL: 15599e551110SNicolas Dichtel devconf = net->ipv4.devconf_all; 15609e551110SNicolas Dichtel break; 15619e551110SNicolas Dichtel case NETCONFA_IFINDEX_DEFAULT: 15629e551110SNicolas Dichtel devconf = net->ipv4.devconf_dflt; 15639e551110SNicolas Dichtel break; 15649e551110SNicolas Dichtel default: 15659e551110SNicolas Dichtel dev = __dev_get_by_index(net, ifindex); 15669e551110SNicolas Dichtel if (dev == NULL) 15679e551110SNicolas Dichtel goto errout; 15689e551110SNicolas Dichtel in_dev = __in_dev_get_rtnl(dev); 15699e551110SNicolas Dichtel if (in_dev == NULL) 15709e551110SNicolas Dichtel goto errout; 15719e551110SNicolas Dichtel devconf = &in_dev->cnf; 15729e551110SNicolas Dichtel break; 15739e551110SNicolas Dichtel } 15749e551110SNicolas Dichtel 15759e551110SNicolas Dichtel err = -ENOBUFS; 15769e551110SNicolas Dichtel skb = nlmsg_new(inet_netconf_msgsize_devconf(-1), GFP_ATOMIC); 15779e551110SNicolas Dichtel if (skb == NULL) 15789e551110SNicolas Dichtel goto errout; 15799e551110SNicolas Dichtel 15809e551110SNicolas Dichtel err = inet_netconf_fill_devconf(skb, ifindex, devconf, 15819e551110SNicolas Dichtel NETLINK_CB(in_skb).portid, 15829e551110SNicolas Dichtel nlh->nlmsg_seq, RTM_NEWNETCONF, 0, 15839e551110SNicolas Dichtel -1); 15849e551110SNicolas Dichtel if (err < 0) { 15859e551110SNicolas Dichtel /* -EMSGSIZE implies BUG in inet_netconf_msgsize_devconf() */ 15869e551110SNicolas Dichtel WARN_ON(err == -EMSGSIZE); 15879e551110SNicolas Dichtel kfree_skb(skb); 15889e551110SNicolas Dichtel goto errout; 15899e551110SNicolas Dichtel } 15909e551110SNicolas Dichtel err = rtnl_unicast(skb, net, NETLINK_CB(in_skb).portid); 15919e551110SNicolas Dichtel errout: 15929e551110SNicolas Dichtel return err; 15939e551110SNicolas Dichtel } 15949e551110SNicolas Dichtel 15951da177e4SLinus Torvalds #ifdef CONFIG_SYSCTL 15961da177e4SLinus Torvalds 1597c0ce9fb3SPavel Emelyanov static void devinet_copy_dflt_conf(struct net *net, int i) 159831be3085SHerbert Xu { 159931be3085SHerbert Xu struct net_device *dev; 160031be3085SHerbert Xu 160131be3085SHerbert Xu rcu_read_lock(); 1602c6d14c84SEric Dumazet for_each_netdev_rcu(net, dev) { 1603c6d14c84SEric Dumazet struct in_device *in_dev; 1604c6d14c84SEric Dumazet 160531be3085SHerbert Xu in_dev = __in_dev_get_rcu(dev); 160631be3085SHerbert Xu if (in_dev && !test_bit(i, in_dev->cnf.state)) 16079355bbd6SPavel Emelyanov in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; 1608c6d14c84SEric Dumazet } 160931be3085SHerbert Xu rcu_read_unlock(); 161031be3085SHerbert Xu } 161131be3085SHerbert Xu 1612c6d14c84SEric Dumazet /* called with RTNL locked */ 1613c0ce9fb3SPavel Emelyanov static void inet_forward_change(struct net *net) 161468dd299bSPavel Emelyanov { 161568dd299bSPavel Emelyanov struct net_device *dev; 1616586f1211SPavel Emelyanov int on = IPV4_DEVCONF_ALL(net, FORWARDING); 161768dd299bSPavel Emelyanov 1618586f1211SPavel Emelyanov IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; 16199355bbd6SPavel Emelyanov IPV4_DEVCONF_DFLT(net, FORWARDING) = on; 1620edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1621edc9e748SNicolas Dichtel NETCONFA_IFINDEX_ALL, 1622edc9e748SNicolas Dichtel net->ipv4.devconf_all); 1623edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1624edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 1625edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 162668dd299bSPavel Emelyanov 1627c0ce9fb3SPavel Emelyanov for_each_netdev(net, dev) { 162868dd299bSPavel Emelyanov struct in_device *in_dev; 16290187bdfbSBen Hutchings if (on) 16300187bdfbSBen Hutchings dev_disable_lro(dev); 163168dd299bSPavel Emelyanov rcu_read_lock(); 163268dd299bSPavel Emelyanov in_dev = __in_dev_get_rcu(dev); 1633edc9e748SNicolas Dichtel if (in_dev) { 163468dd299bSPavel Emelyanov IN_DEV_CONF_SET(in_dev, FORWARDING, on); 1635edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1636edc9e748SNicolas Dichtel dev->ifindex, &in_dev->cnf); 1637edc9e748SNicolas Dichtel } 163868dd299bSPavel Emelyanov rcu_read_unlock(); 163968dd299bSPavel Emelyanov } 164068dd299bSPavel Emelyanov } 164168dd299bSPavel Emelyanov 164231be3085SHerbert Xu static int devinet_conf_proc(ctl_table *ctl, int write, 16438d65af78SAlexey Dobriyan void __user *buffer, 164431be3085SHerbert Xu size_t *lenp, loff_t *ppos) 164531be3085SHerbert Xu { 1646d01ff0a0SPeter Pan(潘卫平) int old_value = *(int *)ctl->data; 16478d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 1648d01ff0a0SPeter Pan(潘卫平) int new_value = *(int *)ctl->data; 164931be3085SHerbert Xu 165031be3085SHerbert Xu if (write) { 165131be3085SHerbert Xu struct ipv4_devconf *cnf = ctl->extra1; 1652c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 165331be3085SHerbert Xu int i = (int *)ctl->data - cnf->data; 165431be3085SHerbert Xu 165531be3085SHerbert Xu set_bit(i, cnf->state); 165631be3085SHerbert Xu 16579355bbd6SPavel Emelyanov if (cnf == net->ipv4.devconf_dflt) 1658c0ce9fb3SPavel Emelyanov devinet_copy_dflt_conf(net, i); 1659d0daebc3SThomas Graf if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 || 1660d0daebc3SThomas Graf i == IPV4_DEVCONF_ROUTE_LOCALNET - 1) 1661d01ff0a0SPeter Pan(潘卫平) if ((new_value == 0) && (old_value != 0)) 16624ccfe6d4SNicolas Dichtel rt_cache_flush(net); 1663cc535dfbSNicolas Dichtel if (i == IPV4_DEVCONF_RP_FILTER - 1 && 1664cc535dfbSNicolas Dichtel new_value != old_value) { 1665cc535dfbSNicolas Dichtel int ifindex; 1666cc535dfbSNicolas Dichtel 1667cc535dfbSNicolas Dichtel if (cnf == net->ipv4.devconf_dflt) 1668cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_DEFAULT; 1669cc535dfbSNicolas Dichtel else if (cnf == net->ipv4.devconf_all) 1670cc535dfbSNicolas Dichtel ifindex = NETCONFA_IFINDEX_ALL; 1671cc535dfbSNicolas Dichtel else { 1672cc535dfbSNicolas Dichtel struct in_device *idev = 1673cc535dfbSNicolas Dichtel container_of(cnf, struct in_device, 1674cc535dfbSNicolas Dichtel cnf); 1675cc535dfbSNicolas Dichtel ifindex = idev->dev->ifindex; 1676cc535dfbSNicolas Dichtel } 1677cc535dfbSNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_RP_FILTER, 1678cc535dfbSNicolas Dichtel ifindex, cnf); 1679cc535dfbSNicolas Dichtel } 168031be3085SHerbert Xu } 168131be3085SHerbert Xu 168231be3085SHerbert Xu return ret; 168331be3085SHerbert Xu } 168431be3085SHerbert Xu 16851da177e4SLinus Torvalds static int devinet_sysctl_forward(ctl_table *ctl, int write, 16868d65af78SAlexey Dobriyan void __user *buffer, 16871da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 16881da177e4SLinus Torvalds { 16891da177e4SLinus Torvalds int *valp = ctl->data; 16901da177e4SLinus Torvalds int val = *valp; 169188af182eSEric W. Biederman loff_t pos = *ppos; 16928d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 16931da177e4SLinus Torvalds 16941da177e4SLinus Torvalds if (write && *valp != val) { 1695c0ce9fb3SPavel Emelyanov struct net *net = ctl->extra2; 1696c0ce9fb3SPavel Emelyanov 16970187bdfbSBen Hutchings if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) { 169888af182eSEric W. Biederman if (!rtnl_trylock()) { 169988af182eSEric W. Biederman /* Restore the original values before restarting */ 170088af182eSEric W. Biederman *valp = val; 170188af182eSEric W. Biederman *ppos = pos; 17029b8adb5eSEric W. Biederman return restart_syscall(); 170388af182eSEric W. Biederman } 17040187bdfbSBen Hutchings if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) { 1705c0ce9fb3SPavel Emelyanov inet_forward_change(net); 1706edc9e748SNicolas Dichtel } else { 17070187bdfbSBen Hutchings struct ipv4_devconf *cnf = ctl->extra1; 17080187bdfbSBen Hutchings struct in_device *idev = 17090187bdfbSBen Hutchings container_of(cnf, struct in_device, cnf); 1710edc9e748SNicolas Dichtel if (*valp) 17110187bdfbSBen Hutchings dev_disable_lro(idev->dev); 1712edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, 1713edc9e748SNicolas Dichtel NETCONFA_FORWARDING, 1714edc9e748SNicolas Dichtel idev->dev->ifindex, 1715edc9e748SNicolas Dichtel cnf); 17160187bdfbSBen Hutchings } 17170187bdfbSBen Hutchings rtnl_unlock(); 17184ccfe6d4SNicolas Dichtel rt_cache_flush(net); 1719edc9e748SNicolas Dichtel } else 1720edc9e748SNicolas Dichtel inet_netconf_notify_devconf(net, NETCONFA_FORWARDING, 1721edc9e748SNicolas Dichtel NETCONFA_IFINDEX_DEFAULT, 1722edc9e748SNicolas Dichtel net->ipv4.devconf_dflt); 17230187bdfbSBen Hutchings } 17241da177e4SLinus Torvalds 17251da177e4SLinus Torvalds return ret; 17261da177e4SLinus Torvalds } 17271da177e4SLinus Torvalds 1728323e126fSDavid S. Miller static int ipv4_doint_and_flush(ctl_table *ctl, int write, 17298d65af78SAlexey Dobriyan void __user *buffer, 17301da177e4SLinus Torvalds size_t *lenp, loff_t *ppos) 17311da177e4SLinus Torvalds { 17321da177e4SLinus Torvalds int *valp = ctl->data; 17331da177e4SLinus Torvalds int val = *valp; 17348d65af78SAlexey Dobriyan int ret = proc_dointvec(ctl, write, buffer, lenp, ppos); 173576e6ebfbSDenis V. Lunev struct net *net = ctl->extra2; 17361da177e4SLinus Torvalds 17371da177e4SLinus Torvalds if (write && *valp != val) 17384ccfe6d4SNicolas Dichtel rt_cache_flush(net); 17391da177e4SLinus Torvalds 17401da177e4SLinus Torvalds return ret; 17411da177e4SLinus Torvalds } 17421da177e4SLinus Torvalds 1743f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \ 174442f811b8SHerbert Xu { \ 174542f811b8SHerbert Xu .procname = name, \ 174642f811b8SHerbert Xu .data = ipv4_devconf.data + \ 174702291680SEric W. Biederman IPV4_DEVCONF_ ## attr - 1, \ 174842f811b8SHerbert Xu .maxlen = sizeof(int), \ 174942f811b8SHerbert Xu .mode = mval, \ 175042f811b8SHerbert Xu .proc_handler = proc, \ 175131be3085SHerbert Xu .extra1 = &ipv4_devconf, \ 175242f811b8SHerbert Xu } 175342f811b8SHerbert Xu 175442f811b8SHerbert Xu #define DEVINET_SYSCTL_RW_ENTRY(attr, name) \ 1755f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc) 175642f811b8SHerbert Xu 175742f811b8SHerbert Xu #define DEVINET_SYSCTL_RO_ENTRY(attr, name) \ 1758f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc) 175942f811b8SHerbert Xu 1760f8572d8fSEric W. Biederman #define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \ 1761f8572d8fSEric W. Biederman DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc) 176242f811b8SHerbert Xu 176342f811b8SHerbert Xu #define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \ 1764f8572d8fSEric W. Biederman DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush) 176542f811b8SHerbert Xu 17661da177e4SLinus Torvalds static struct devinet_sysctl_table { 17671da177e4SLinus Torvalds struct ctl_table_header *sysctl_header; 176802291680SEric W. Biederman struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX]; 17691da177e4SLinus Torvalds } devinet_sysctl = { 17701da177e4SLinus Torvalds .devinet_vars = { 177142f811b8SHerbert Xu DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding", 1772f8572d8fSEric W. Biederman devinet_sysctl_forward), 177342f811b8SHerbert Xu DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"), 177442f811b8SHerbert Xu 177542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"), 177642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"), 177742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"), 177842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"), 177942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), 178042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, 178142f811b8SHerbert Xu "accept_source_route"), 17828153a10cSPatrick McHardy DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), 178328f6aeeaSJamal Hadi Salim DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"), 178442f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), 178542f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), 178642f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), 178742f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"), 178842f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"), 178942f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"), 179042f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"), 179142f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"), 179242f811b8SHerbert Xu DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"), 1793eefef1cfSStephen Hemminger DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"), 179465324144SJesper Dangaard Brouer DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"), 179542f811b8SHerbert Xu 179642f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"), 179742f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"), 179842f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION, 179942f811b8SHerbert Xu "force_igmp_version"), 180042f811b8SHerbert Xu DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES, 180142f811b8SHerbert Xu "promote_secondaries"), 1802d0daebc3SThomas Graf DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET, 1803d0daebc3SThomas Graf "route_localnet"), 18041da177e4SLinus Torvalds }, 18051da177e4SLinus Torvalds }; 18061da177e4SLinus Torvalds 1807ea40b324SPavel Emelyanov static int __devinet_sysctl_register(struct net *net, char *dev_name, 1808f8572d8fSEric W. Biederman struct ipv4_devconf *p) 18091da177e4SLinus Torvalds { 18101da177e4SLinus Torvalds int i; 18119fa89642SPavel Emelyanov struct devinet_sysctl_table *t; 18128607ddb8SEric W. Biederman char path[sizeof("net/ipv4/conf/") + IFNAMSIZ]; 1813bfada697SPavel Emelyanov 18149fa89642SPavel Emelyanov t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL); 18151da177e4SLinus Torvalds if (!t) 18169fa89642SPavel Emelyanov goto out; 18179fa89642SPavel Emelyanov 18181da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { 18191da177e4SLinus Torvalds t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; 182031be3085SHerbert Xu t->devinet_vars[i].extra1 = p; 1821c0ce9fb3SPavel Emelyanov t->devinet_vars[i].extra2 = net; 18221da177e4SLinus Torvalds } 18231da177e4SLinus Torvalds 18248607ddb8SEric W. Biederman snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name); 18251da177e4SLinus Torvalds 18268607ddb8SEric W. Biederman t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars); 18271da177e4SLinus Torvalds if (!t->sysctl_header) 18288607ddb8SEric W. Biederman goto free; 18291da177e4SLinus Torvalds 18301da177e4SLinus Torvalds p->sysctl = t; 1831ea40b324SPavel Emelyanov return 0; 18321da177e4SLinus Torvalds 18331da177e4SLinus Torvalds free: 18341da177e4SLinus Torvalds kfree(t); 18359fa89642SPavel Emelyanov out: 1836ea40b324SPavel Emelyanov return -ENOBUFS; 18371da177e4SLinus Torvalds } 18381da177e4SLinus Torvalds 183951602b2aSPavel Emelyanov static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) 184066f27a52SPavel Emelyanov { 184151602b2aSPavel Emelyanov struct devinet_sysctl_table *t = cnf->sysctl; 184266f27a52SPavel Emelyanov 184351602b2aSPavel Emelyanov if (t == NULL) 184451602b2aSPavel Emelyanov return; 184551602b2aSPavel Emelyanov 184651602b2aSPavel Emelyanov cnf->sysctl = NULL; 1847ff538818SLucian Adrian Grijincu unregister_net_sysctl_table(t->sysctl_header); 18481da177e4SLinus Torvalds kfree(t); 18491da177e4SLinus Torvalds } 185051602b2aSPavel Emelyanov 185151602b2aSPavel Emelyanov static void devinet_sysctl_register(struct in_device *idev) 185251602b2aSPavel Emelyanov { 185354716e3bSEric W. Biederman neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL); 1854c346dca1SYOSHIFUJI Hideaki __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name, 1855f8572d8fSEric W. Biederman &idev->cnf); 185651602b2aSPavel Emelyanov } 185751602b2aSPavel Emelyanov 185851602b2aSPavel Emelyanov static void devinet_sysctl_unregister(struct in_device *idev) 185951602b2aSPavel Emelyanov { 186051602b2aSPavel Emelyanov __devinet_sysctl_unregister(&idev->cnf); 186151602b2aSPavel Emelyanov neigh_sysctl_unregister(idev->arp_parms); 18621da177e4SLinus Torvalds } 18631da177e4SLinus Torvalds 186468dd299bSPavel Emelyanov static struct ctl_table ctl_forward_entry[] = { 186568dd299bSPavel Emelyanov { 186668dd299bSPavel Emelyanov .procname = "ip_forward", 186768dd299bSPavel Emelyanov .data = &ipv4_devconf.data[ 186802291680SEric W. Biederman IPV4_DEVCONF_FORWARDING - 1], 186968dd299bSPavel Emelyanov .maxlen = sizeof(int), 187068dd299bSPavel Emelyanov .mode = 0644, 187168dd299bSPavel Emelyanov .proc_handler = devinet_sysctl_forward, 187268dd299bSPavel Emelyanov .extra1 = &ipv4_devconf, 1873c0ce9fb3SPavel Emelyanov .extra2 = &init_net, 187468dd299bSPavel Emelyanov }, 187568dd299bSPavel Emelyanov { }, 187668dd299bSPavel Emelyanov }; 18772a75de0cSEric Dumazet #endif 187868dd299bSPavel Emelyanov 1879752d14dcSPavel Emelyanov static __net_init int devinet_init_net(struct net *net) 1880752d14dcSPavel Emelyanov { 1881752d14dcSPavel Emelyanov int err; 1882752d14dcSPavel Emelyanov struct ipv4_devconf *all, *dflt; 18832a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 18842a75de0cSEric Dumazet struct ctl_table *tbl = ctl_forward_entry; 1885752d14dcSPavel Emelyanov struct ctl_table_header *forw_hdr; 18862a75de0cSEric Dumazet #endif 1887752d14dcSPavel Emelyanov 1888752d14dcSPavel Emelyanov err = -ENOMEM; 1889752d14dcSPavel Emelyanov all = &ipv4_devconf; 1890752d14dcSPavel Emelyanov dflt = &ipv4_devconf_dflt; 1891752d14dcSPavel Emelyanov 189209ad9bc7SOctavian Purdila if (!net_eq(net, &init_net)) { 1893752d14dcSPavel Emelyanov all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); 1894752d14dcSPavel Emelyanov if (all == NULL) 1895752d14dcSPavel Emelyanov goto err_alloc_all; 1896752d14dcSPavel Emelyanov 1897752d14dcSPavel Emelyanov dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL); 1898752d14dcSPavel Emelyanov if (dflt == NULL) 1899752d14dcSPavel Emelyanov goto err_alloc_dflt; 1900752d14dcSPavel Emelyanov 19012a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 1902752d14dcSPavel Emelyanov tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL); 1903752d14dcSPavel Emelyanov if (tbl == NULL) 1904752d14dcSPavel Emelyanov goto err_alloc_ctl; 1905752d14dcSPavel Emelyanov 190602291680SEric W. Biederman tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1]; 1907752d14dcSPavel Emelyanov tbl[0].extra1 = all; 1908752d14dcSPavel Emelyanov tbl[0].extra2 = net; 19092a75de0cSEric Dumazet #endif 1910752d14dcSPavel Emelyanov } 1911752d14dcSPavel Emelyanov 1912752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 1913f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "all", all); 1914752d14dcSPavel Emelyanov if (err < 0) 1915752d14dcSPavel Emelyanov goto err_reg_all; 1916752d14dcSPavel Emelyanov 1917f8572d8fSEric W. Biederman err = __devinet_sysctl_register(net, "default", dflt); 1918752d14dcSPavel Emelyanov if (err < 0) 1919752d14dcSPavel Emelyanov goto err_reg_dflt; 1920752d14dcSPavel Emelyanov 1921752d14dcSPavel Emelyanov err = -ENOMEM; 19228607ddb8SEric W. Biederman forw_hdr = register_net_sysctl(net, "net/ipv4", tbl); 1923752d14dcSPavel Emelyanov if (forw_hdr == NULL) 1924752d14dcSPavel Emelyanov goto err_reg_ctl; 19252a75de0cSEric Dumazet net->ipv4.forw_hdr = forw_hdr; 1926752d14dcSPavel Emelyanov #endif 1927752d14dcSPavel Emelyanov 1928752d14dcSPavel Emelyanov net->ipv4.devconf_all = all; 1929752d14dcSPavel Emelyanov net->ipv4.devconf_dflt = dflt; 1930752d14dcSPavel Emelyanov return 0; 1931752d14dcSPavel Emelyanov 1932752d14dcSPavel Emelyanov #ifdef CONFIG_SYSCTL 1933752d14dcSPavel Emelyanov err_reg_ctl: 1934752d14dcSPavel Emelyanov __devinet_sysctl_unregister(dflt); 1935752d14dcSPavel Emelyanov err_reg_dflt: 1936752d14dcSPavel Emelyanov __devinet_sysctl_unregister(all); 1937752d14dcSPavel Emelyanov err_reg_all: 1938752d14dcSPavel Emelyanov if (tbl != ctl_forward_entry) 1939752d14dcSPavel Emelyanov kfree(tbl); 1940752d14dcSPavel Emelyanov err_alloc_ctl: 19412a75de0cSEric Dumazet #endif 1942752d14dcSPavel Emelyanov if (dflt != &ipv4_devconf_dflt) 1943752d14dcSPavel Emelyanov kfree(dflt); 1944752d14dcSPavel Emelyanov err_alloc_dflt: 1945752d14dcSPavel Emelyanov if (all != &ipv4_devconf) 1946752d14dcSPavel Emelyanov kfree(all); 1947752d14dcSPavel Emelyanov err_alloc_all: 1948752d14dcSPavel Emelyanov return err; 1949752d14dcSPavel Emelyanov } 1950752d14dcSPavel Emelyanov 1951752d14dcSPavel Emelyanov static __net_exit void devinet_exit_net(struct net *net) 1952752d14dcSPavel Emelyanov { 19532a75de0cSEric Dumazet #ifdef CONFIG_SYSCTL 1954752d14dcSPavel Emelyanov struct ctl_table *tbl; 1955752d14dcSPavel Emelyanov 1956752d14dcSPavel Emelyanov tbl = net->ipv4.forw_hdr->ctl_table_arg; 1957752d14dcSPavel Emelyanov unregister_net_sysctl_table(net->ipv4.forw_hdr); 1958752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_dflt); 1959752d14dcSPavel Emelyanov __devinet_sysctl_unregister(net->ipv4.devconf_all); 1960752d14dcSPavel Emelyanov kfree(tbl); 19612a75de0cSEric Dumazet #endif 1962752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_dflt); 1963752d14dcSPavel Emelyanov kfree(net->ipv4.devconf_all); 1964752d14dcSPavel Emelyanov } 1965752d14dcSPavel Emelyanov 1966752d14dcSPavel Emelyanov static __net_initdata struct pernet_operations devinet_ops = { 1967752d14dcSPavel Emelyanov .init = devinet_init_net, 1968752d14dcSPavel Emelyanov .exit = devinet_exit_net, 1969752d14dcSPavel Emelyanov }; 1970752d14dcSPavel Emelyanov 19719f0f7272SThomas Graf static struct rtnl_af_ops inet_af_ops = { 19729f0f7272SThomas Graf .family = AF_INET, 19739f0f7272SThomas Graf .fill_link_af = inet_fill_link_af, 19749f0f7272SThomas Graf .get_link_af_size = inet_get_link_af_size, 1975cf7afbfeSThomas Graf .validate_link_af = inet_validate_link_af, 1976cf7afbfeSThomas Graf .set_link_af = inet_set_link_af, 19779f0f7272SThomas Graf }; 19789f0f7272SThomas Graf 19791da177e4SLinus Torvalds void __init devinet_init(void) 19801da177e4SLinus Torvalds { 1981fd23c3b3SDavid S. Miller int i; 1982fd23c3b3SDavid S. Miller 1983fd23c3b3SDavid S. Miller for (i = 0; i < IN4_ADDR_HSIZE; i++) 1984fd23c3b3SDavid S. Miller INIT_HLIST_HEAD(&inet_addr_lst[i]); 1985fd23c3b3SDavid S. Miller 1986752d14dcSPavel Emelyanov register_pernet_subsys(&devinet_ops); 1987752d14dcSPavel Emelyanov 19881da177e4SLinus Torvalds register_gifconf(PF_INET, inet_gifconf); 19891da177e4SLinus Torvalds register_netdevice_notifier(&ip_netdev_notifier); 199063f3444fSThomas Graf 19919f0f7272SThomas Graf rtnl_af_register(&inet_af_ops); 19929f0f7272SThomas Graf 1993c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); 1994c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); 1995c7ac8679SGreg Rose rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); 19969e551110SNicolas Dichtel rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, 19979e551110SNicolas Dichtel NULL, NULL); 19981da177e4SLinus Torvalds } 19991da177e4SLinus Torvalds 2000