blob: a8298246dab9d6d305ef5d47c7ef612c9677350d [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * NET3 IP device support routines.
3 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Derived from the IP parts of dev.c 1.0.19
Jesper Juhl02c30a82005-05-05 16:16:16 -070010 * Authors: Ross Biro
Linus Torvalds1da177e2005-04-16 15:20:36 -070011 * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
12 * Mark Evans, <evansmp@uhura.aston.ac.uk>
13 *
14 * Additional Authors:
15 * Alan Cox, <gw4pts@gw4pts.ampr.org>
16 * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
17 *
18 * Changes:
19 * Alexey Kuznetsov: pa_* fields are replaced with ifaddr
20 * lists.
21 * Cyrus Durgin: updated for kmod
22 * Matthias Andree: in devinet_ioctl, compare label and
23 * address (4.4BSD alias style support),
24 * fall back to comparing just the label
25 * if no match found.
26 */
27
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29#include <asm/uaccess.h>
30#include <asm/system.h>
31#include <linux/bitops.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080032#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/module.h>
34#include <linux/types.h>
35#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <linux/string.h>
37#include <linux/mm.h>
38#include <linux/socket.h>
39#include <linux/sockios.h>
40#include <linux/in.h>
41#include <linux/errno.h>
42#include <linux/interrupt.h>
Thomas Graf18237302006-08-04 23:04:54 -070043#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/if_ether.h>
45#include <linux/inet.h>
46#include <linux/netdevice.h>
47#include <linux/etherdevice.h>
48#include <linux/skbuff.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <linux/init.h>
50#include <linux/notifier.h>
51#include <linux/inetdevice.h>
52#include <linux/igmp.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090053#include <linux/slab.h>
David S. Millerfd23c3b2011-02-18 12:42:28 -080054#include <linux/hash.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#ifdef CONFIG_SYSCTL
56#include <linux/sysctl.h>
57#endif
58#include <linux/kmod.h>
59
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020060#include <net/arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#include <net/ip.h>
Robert Lovea7429e52008-05-12 17:08:29 -040062#include <net/tcp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#include <net/route.h>
64#include <net/ip_fib.h>
Thomas Graf63f34442007-03-22 11:55:17 -070065#include <net/rtnetlink.h>
Pavel Emelyanov752d14d2007-12-16 13:31:47 -080066#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
David S. Miller406b6f92011-03-22 21:56:23 -070068#include "fib_lookup.h"
69
Adrian Bunk0027ba82008-01-31 17:17:31 -080070static struct ipv4_devconf ipv4_devconf = {
Herbert Xu42f811b2007-06-04 23:34:44 -070071 .data = {
Eric W. Biederman02291682010-02-14 03:25:51 +000072 [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
73 [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
74 [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
75 [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
Herbert Xu42f811b2007-06-04 23:34:44 -070076 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070077};
78
79static struct ipv4_devconf ipv4_devconf_dflt = {
Herbert Xu42f811b2007-06-04 23:34:44 -070080 .data = {
Eric W. Biederman02291682010-02-14 03:25:51 +000081 [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
82 [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
83 [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
84 [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
85 [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
Herbert Xu42f811b2007-06-04 23:34:44 -070086 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070087};
88
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -080089#define IPV4_DEVCONF_DFLT(net, attr) \
90 IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
Herbert Xu42f811b2007-06-04 23:34:44 -070091
Patrick McHardyef7c79e2007-06-05 12:38:30 -070092static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
Thomas Graf5c753972006-08-04 23:03:53 -070093 [IFA_LOCAL] = { .type = NLA_U32 },
94 [IFA_ADDRESS] = { .type = NLA_U32 },
95 [IFA_BROADCAST] = { .type = NLA_U32 },
Thomas Graf5176f912006-08-26 20:13:18 -070096 [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
Thomas Graf5c753972006-08-04 23:03:53 -070097};
98
David S. Millerfd23c3b2011-02-18 12:42:28 -080099/* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE
100 * value. So if you change this define, make appropriate changes to
101 * inet_addr_hash as well.
102 */
103#define IN4_ADDR_HSIZE 256
104static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
105static DEFINE_SPINLOCK(inet_addr_hash_lock);
106
107static inline unsigned int inet_addr_hash(struct net *net, __be32 addr)
108{
109 u32 val = (__force u32) addr ^ hash_ptr(net, 8);
110
111 return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) &
112 (IN4_ADDR_HSIZE - 1));
113}
114
115static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
116{
David S. Millere0660082011-03-03 11:24:19 -0800117 unsigned int hash = inet_addr_hash(net, ifa->ifa_local);
David S. Millerfd23c3b2011-02-18 12:42:28 -0800118
119 spin_lock(&inet_addr_hash_lock);
120 hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
121 spin_unlock(&inet_addr_hash_lock);
122}
123
124static void inet_hash_remove(struct in_ifaddr *ifa)
125{
126 spin_lock(&inet_addr_hash_lock);
127 hlist_del_init_rcu(&ifa->hash);
128 spin_unlock(&inet_addr_hash_lock);
129}
130
David S. Miller9435eb12011-02-18 12:43:09 -0800131/**
132 * __ip_dev_find - find the first device with a given source address.
133 * @net: the net namespace
134 * @addr: the source address
135 * @devref: if true, take a reference on the found device
136 *
137 * If a caller uses devref=false, it should be protected by RCU, or RTNL
138 */
139struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
140{
141 unsigned int hash = inet_addr_hash(net, addr);
142 struct net_device *result = NULL;
143 struct in_ifaddr *ifa;
144 struct hlist_node *node;
145
146 rcu_read_lock();
147 hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) {
148 struct net_device *dev = ifa->ifa_dev->dev;
149
150 if (!net_eq(dev_net(dev), net))
151 continue;
David S. Millere0660082011-03-03 11:24:19 -0800152 if (ifa->ifa_local == addr) {
David S. Miller9435eb12011-02-18 12:43:09 -0800153 result = dev;
154 break;
155 }
156 }
David S. Miller406b6f92011-03-22 21:56:23 -0700157 if (!result) {
158 struct flowi4 fl4 = { .daddr = addr };
159 struct fib_result res = { 0 };
160 struct fib_table *local;
161
162 /* Fallback to FIB local table so that communication
163 * over loopback subnets work.
164 */
165 local = fib_get_table(net, RT_TABLE_LOCAL);
166 if (local &&
167 !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
168 res.type == RTN_LOCAL)
169 result = FIB_RES_DEV(res);
170 }
David S. Miller9435eb12011-02-18 12:43:09 -0800171 if (result && devref)
172 dev_hold(result);
173 rcu_read_unlock();
174 return result;
175}
176EXPORT_SYMBOL(__ip_dev_find);
177
Thomas Grafd6062cb2006-08-15 00:33:59 -0700178static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
Alan Sterne041c682006-03-27 01:16:30 -0800180static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
182 int destroy);
183#ifdef CONFIG_SYSCTL
Pavel Emelyanov66f27a52007-12-02 00:55:54 +1100184static void devinet_sysctl_register(struct in_device *idev);
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800185static void devinet_sysctl_unregister(struct in_device *idev);
186#else
187static inline void devinet_sysctl_register(struct in_device *idev)
188{
189}
190static inline void devinet_sysctl_unregister(struct in_device *idev)
191{
192}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193#endif
194
195/* Locks all the inet devices. */
196
197static struct in_ifaddr *inet_alloc_ifa(void)
198{
Alexey Dobriyan93adcc82008-10-28 13:25:09 -0700199 return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200}
201
202static void inet_rcu_free_ifa(struct rcu_head *head)
203{
204 struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
205 if (ifa->ifa_dev)
206 in_dev_put(ifa->ifa_dev);
207 kfree(ifa);
208}
209
210static inline void inet_free_ifa(struct in_ifaddr *ifa)
211{
212 call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
213}
214
215void in_dev_finish_destroy(struct in_device *idev)
216{
217 struct net_device *dev = idev->dev;
218
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700219 WARN_ON(idev->ifa_list);
220 WARN_ON(idev->mc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221#ifdef NET_REFCNT_DEBUG
222 printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
223 idev, dev ? dev->name : "NIL");
224#endif
225 dev_put(dev);
226 if (!idev->dead)
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800227 pr_err("Freeing alive in_device %p\n", idev);
228 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 kfree(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800231EXPORT_SYMBOL(in_dev_finish_destroy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232
Herbert Xu71e27da2007-06-04 23:36:06 -0700233static struct in_device *inetdev_init(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234{
235 struct in_device *in_dev;
236
237 ASSERT_RTNL();
238
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700239 in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 if (!in_dev)
241 goto out;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900242 memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -0800243 sizeof(in_dev->cnf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 in_dev->cnf.sysctl = NULL;
245 in_dev->dev = dev;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800246 in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
247 if (!in_dev->arp_parms)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 goto out_kfree;
Ben Hutchings0187bdf2008-06-19 16:15:47 -0700249 if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
250 dev_disable_lro(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 /* Reference in_dev->dev */
252 dev_hold(dev);
David L Stevens30c4cf52007-01-04 12:31:14 -0800253 /* Account for reference dev->ip_ptr (below) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 in_dev_hold(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Pavel Emelyanov66f27a52007-12-02 00:55:54 +1100256 devinet_sysctl_register(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 ip_mc_init_dev(in_dev);
258 if (dev->flags & IFF_UP)
259 ip_mc_up(in_dev);
Jarek Poplawski483479e2007-01-09 14:38:31 -0800260
David L Stevens30c4cf52007-01-04 12:31:14 -0800261 /* we can receive as soon as ip_ptr is set -- do this last */
262 rcu_assign_pointer(dev->ip_ptr, in_dev);
Jarek Poplawski483479e2007-01-09 14:38:31 -0800263out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 return in_dev;
265out_kfree:
266 kfree(in_dev);
267 in_dev = NULL;
268 goto out;
269}
270
271static void in_dev_rcu_put(struct rcu_head *head)
272{
273 struct in_device *idev = container_of(head, struct in_device, rcu_head);
274 in_dev_put(idev);
275}
276
277static void inetdev_destroy(struct in_device *in_dev)
278{
279 struct in_ifaddr *ifa;
280 struct net_device *dev;
281
282 ASSERT_RTNL();
283
284 dev = in_dev->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
286 in_dev->dead = 1;
287
288 ip_mc_destroy_dev(in_dev);
289
290 while ((ifa = in_dev->ifa_list) != NULL) {
291 inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
292 inet_free_ifa(ifa);
293 }
294
Eric Dumazet95ae6b22010-09-15 04:04:31 +0000295 rcu_assign_pointer(dev->ip_ptr, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800297 devinet_sysctl_unregister(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 neigh_parms_release(&arp_tbl, in_dev->arp_parms);
299 arp_ifdown(dev);
300
301 call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
302}
303
Al Viroff428d72006-09-26 22:13:35 -0700304int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305{
306 rcu_read_lock();
307 for_primary_ifa(in_dev) {
308 if (inet_ifa_match(a, ifa)) {
309 if (!b || inet_ifa_match(b, ifa)) {
310 rcu_read_unlock();
311 return 1;
312 }
313 }
314 } endfor_ifa(in_dev);
315 rcu_read_unlock();
316 return 0;
317}
318
Thomas Grafd6062cb2006-08-15 00:33:59 -0700319static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
320 int destroy, struct nlmsghdr *nlh, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321{
Harald Welte8f937c62005-05-29 20:23:46 -0700322 struct in_ifaddr *promote = NULL;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800323 struct in_ifaddr *ifa, *ifa1 = *ifap;
324 struct in_ifaddr *last_prim = in_dev->ifa_list;
325 struct in_ifaddr *prev_prom = NULL;
326 int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328 ASSERT_RTNL();
329
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900330 /* 1. Deleting primary ifaddr forces deletion all secondaries
Harald Welte8f937c62005-05-29 20:23:46 -0700331 * unless alias promotion is set
332 **/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
334 if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335 struct in_ifaddr **ifap1 = &ifa1->ifa_next;
336
337 while ((ifa = *ifap1) != NULL) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900338 if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800339 ifa1->ifa_scope <= ifa->ifa_scope)
340 last_prim = ifa;
341
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
343 ifa1->ifa_mask != ifa->ifa_mask ||
344 !inet_ifa_match(ifa1->ifa_address, ifa)) {
345 ifap1 = &ifa->ifa_next;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800346 prev_prom = ifa;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 continue;
348 }
349
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800350 if (!do_promote) {
David S. Millerfd23c3b2011-02-18 12:42:28 -0800351 inet_hash_remove(ifa);
Harald Welte8f937c62005-05-29 20:23:46 -0700352 *ifap1 = ifa->ifa_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
Thomas Grafd6062cb2006-08-15 00:33:59 -0700354 rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800355 blocking_notifier_call_chain(&inetaddr_chain,
356 NETDEV_DOWN, ifa);
Harald Welte8f937c62005-05-29 20:23:46 -0700357 inet_free_ifa(ifa);
358 } else {
359 promote = ifa;
360 break;
361 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 }
363 }
364
Julian Anastasov2d230e22011-03-19 12:13:52 +0000365 /* On promotion all secondaries from subnet are changing
366 * the primary IP, we must remove all their routes silently
367 * and later to add them back with new prefsrc. Do this
368 * while all addresses are on the device list.
369 */
370 for (ifa = promote; ifa; ifa = ifa->ifa_next) {
371 if (ifa1->ifa_mask == ifa->ifa_mask &&
372 inet_ifa_match(ifa1->ifa_address, ifa))
373 fib_del_ifaddr(ifa, ifa1);
374 }
375
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 /* 2. Unlink it */
377
378 *ifap = ifa1->ifa_next;
David S. Millerfd23c3b2011-02-18 12:42:28 -0800379 inet_hash_remove(ifa1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380
381 /* 3. Announce address deletion */
382
383 /* Send message first, then call notifier.
384 At first sight, FIB update triggered by notifier
385 will refer to already deleted ifaddr, that could confuse
386 netlink listeners. It is not true: look, gated sees
387 that route deleted and if it still thinks that ifaddr
388 is valid, it will try to restore deleted routes... Grr.
389 So that, this order is correct.
390 */
Thomas Grafd6062cb2006-08-15 00:33:59 -0700391 rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800392 blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800393
394 if (promote) {
Julian Anastasov04024b92011-03-19 12:13:54 +0000395 struct in_ifaddr *next_sec = promote->ifa_next;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800396
397 if (prev_prom) {
398 prev_prom->ifa_next = promote->ifa_next;
399 promote->ifa_next = last_prim->ifa_next;
400 last_prim->ifa_next = promote;
401 }
402
403 promote->ifa_flags &= ~IFA_F_SECONDARY;
Thomas Grafd6062cb2006-08-15 00:33:59 -0700404 rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800405 blocking_notifier_call_chain(&inetaddr_chain,
406 NETDEV_UP, promote);
Julian Anastasov04024b92011-03-19 12:13:54 +0000407 for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800408 if (ifa1->ifa_mask != ifa->ifa_mask ||
409 !inet_ifa_match(ifa1->ifa_address, ifa))
410 continue;
411 fib_add_ifaddr(ifa);
412 }
413
414 }
Herbert Xu63630972007-06-07 18:35:38 -0700415 if (destroy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416 inet_free_ifa(ifa1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417}
418
Thomas Grafd6062cb2006-08-15 00:33:59 -0700419static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
420 int destroy)
421{
422 __inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
423}
424
425static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
426 u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427{
428 struct in_device *in_dev = ifa->ifa_dev;
429 struct in_ifaddr *ifa1, **ifap, **last_primary;
430
431 ASSERT_RTNL();
432
433 if (!ifa->ifa_local) {
434 inet_free_ifa(ifa);
435 return 0;
436 }
437
438 ifa->ifa_flags &= ~IFA_F_SECONDARY;
439 last_primary = &in_dev->ifa_list;
440
441 for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
442 ifap = &ifa1->ifa_next) {
443 if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
444 ifa->ifa_scope <= ifa1->ifa_scope)
445 last_primary = &ifa1->ifa_next;
446 if (ifa1->ifa_mask == ifa->ifa_mask &&
447 inet_ifa_match(ifa1->ifa_address, ifa)) {
448 if (ifa1->ifa_local == ifa->ifa_local) {
449 inet_free_ifa(ifa);
450 return -EEXIST;
451 }
452 if (ifa1->ifa_scope != ifa->ifa_scope) {
453 inet_free_ifa(ifa);
454 return -EINVAL;
455 }
456 ifa->ifa_flags |= IFA_F_SECONDARY;
457 }
458 }
459
460 if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
461 net_srandom(ifa->ifa_local);
462 ifap = last_primary;
463 }
464
465 ifa->ifa_next = *ifap;
466 *ifap = ifa;
467
David S. Millerfd23c3b2011-02-18 12:42:28 -0800468 inet_hash_insert(dev_net(in_dev->dev), ifa);
469
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 /* Send message first, then call notifier.
471 Notifier will trigger FIB update, so that
472 listeners of netlink will know about new ifaddr */
Thomas Grafd6062cb2006-08-15 00:33:59 -0700473 rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800474 blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700475
476 return 0;
477}
478
Thomas Grafd6062cb2006-08-15 00:33:59 -0700479static int inet_insert_ifa(struct in_ifaddr *ifa)
480{
481 return __inet_insert_ifa(ifa, NULL, 0);
482}
483
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
485{
Herbert Xue5ed6392005-10-03 14:35:55 -0700486 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487
488 ASSERT_RTNL();
489
490 if (!in_dev) {
Herbert Xu71e27da2007-06-04 23:36:06 -0700491 inet_free_ifa(ifa);
492 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493 }
Herbert Xu71e27da2007-06-04 23:36:06 -0700494 ipv4_devconf_setall(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 if (ifa->ifa_dev != in_dev) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700496 WARN_ON(ifa->ifa_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 in_dev_hold(in_dev);
498 ifa->ifa_dev = in_dev;
499 }
Joe Perchesf97c1e02007-12-16 13:45:43 -0800500 if (ipv4_is_loopback(ifa->ifa_local))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 ifa->ifa_scope = RT_SCOPE_HOST;
502 return inet_insert_ifa(ifa);
503}
504
Eric Dumazet8723e1b2010-10-19 00:39:26 +0000505/* Caller must hold RCU or RTNL :
506 * We dont take a reference on found in_device
507 */
Denis V. Lunev7fee0ca2008-01-21 17:32:38 -0800508struct in_device *inetdev_by_index(struct net *net, int ifindex)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
510 struct net_device *dev;
511 struct in_device *in_dev = NULL;
Eric Dumazetc148fc22009-11-01 19:23:04 +0000512
513 rcu_read_lock();
514 dev = dev_get_by_index_rcu(net, ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 if (dev)
Eric Dumazet8723e1b2010-10-19 00:39:26 +0000516 in_dev = rcu_dereference_rtnl(dev->ip_ptr);
Eric Dumazetc148fc22009-11-01 19:23:04 +0000517 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 return in_dev;
519}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800520EXPORT_SYMBOL(inetdev_by_index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
522/* Called only from RTNL semaphored context. No locks. */
523
Al Viro60cad5d2006-09-26 22:17:09 -0700524struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
525 __be32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526{
527 ASSERT_RTNL();
528
529 for_primary_ifa(in_dev) {
530 if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
531 return ifa;
532 } endfor_ifa(in_dev);
533 return NULL;
534}
535
536static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
537{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900538 struct net *net = sock_net(skb->sk);
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700539 struct nlattr *tb[IFA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 struct in_device *in_dev;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700541 struct ifaddrmsg *ifm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 struct in_ifaddr *ifa, **ifap;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700543 int err = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544
545 ASSERT_RTNL();
546
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700547 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
548 if (err < 0)
549 goto errout;
550
551 ifm = nlmsg_data(nlh);
Denis V. Lunev7fee0ca2008-01-21 17:32:38 -0800552 in_dev = inetdev_by_index(net, ifm->ifa_index);
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700553 if (in_dev == NULL) {
554 err = -ENODEV;
555 goto errout;
556 }
557
Linus Torvalds1da177e2005-04-16 15:20:36 -0700558 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
559 ifap = &ifa->ifa_next) {
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700560 if (tb[IFA_LOCAL] &&
Al Viroa7a628c2006-09-26 22:16:43 -0700561 ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 continue;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700563
564 if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
565 continue;
566
567 if (tb[IFA_ADDRESS] &&
568 (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
Al Viroa7a628c2006-09-26 22:16:43 -0700569 !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700570 continue;
571
Thomas Grafd6062cb2006-08-15 00:33:59 -0700572 __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 return 0;
574 }
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700575
576 err = -EADDRNOTAVAIL;
577errout:
578 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579}
580
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800581static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582{
Thomas Graf5c753972006-08-04 23:03:53 -0700583 struct nlattr *tb[IFA_MAX+1];
584 struct in_ifaddr *ifa;
585 struct ifaddrmsg *ifm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 struct net_device *dev;
587 struct in_device *in_dev;
Denis V. Lunev7b218572008-01-31 18:47:00 -0800588 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
Thomas Graf5c753972006-08-04 23:03:53 -0700590 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
591 if (err < 0)
592 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Thomas Graf5c753972006-08-04 23:03:53 -0700594 ifm = nlmsg_data(nlh);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800595 err = -EINVAL;
596 if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700597 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800599 dev = __dev_get_by_index(net, ifm->ifa_index);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800600 err = -ENODEV;
601 if (dev == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700602 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Thomas Graf5c753972006-08-04 23:03:53 -0700604 in_dev = __in_dev_get_rtnl(dev);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800605 err = -ENOBUFS;
606 if (in_dev == NULL)
Herbert Xu71e27da2007-06-04 23:36:06 -0700607 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608
Thomas Graf5c753972006-08-04 23:03:53 -0700609 ifa = inet_alloc_ifa();
Denis V. Lunev7b218572008-01-31 18:47:00 -0800610 if (ifa == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700611 /*
612 * A potential indev allocation can be left alive, it stays
613 * assigned to its device and is destroy with it.
614 */
Thomas Graf5c753972006-08-04 23:03:53 -0700615 goto errout;
Thomas Graf5c753972006-08-04 23:03:53 -0700616
Pavel Emelyanova4e65d32007-12-07 23:55:43 -0800617 ipv4_devconf_setall(in_dev);
Thomas Graf5c753972006-08-04 23:03:53 -0700618 in_dev_hold(in_dev);
619
620 if (tb[IFA_ADDRESS] == NULL)
621 tb[IFA_ADDRESS] = tb[IFA_LOCAL];
622
David S. Millerfd23c3b2011-02-18 12:42:28 -0800623 INIT_HLIST_NODE(&ifa->hash);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 ifa->ifa_prefixlen = ifm->ifa_prefixlen;
625 ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 ifa->ifa_flags = ifm->ifa_flags;
627 ifa->ifa_scope = ifm->ifa_scope;
Thomas Graf5c753972006-08-04 23:03:53 -0700628 ifa->ifa_dev = in_dev;
629
Al Viroa7a628c2006-09-26 22:16:43 -0700630 ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
631 ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
Thomas Graf5c753972006-08-04 23:03:53 -0700632
633 if (tb[IFA_BROADCAST])
Al Viroa7a628c2006-09-26 22:16:43 -0700634 ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
Thomas Graf5c753972006-08-04 23:03:53 -0700635
Thomas Graf5c753972006-08-04 23:03:53 -0700636 if (tb[IFA_LABEL])
637 nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 else
639 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
640
Thomas Graf5c753972006-08-04 23:03:53 -0700641 return ifa;
642
643errout:
644 return ERR_PTR(err);
645}
646
647static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
648{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900649 struct net *net = sock_net(skb->sk);
Thomas Graf5c753972006-08-04 23:03:53 -0700650 struct in_ifaddr *ifa;
651
652 ASSERT_RTNL();
653
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800654 ifa = rtm_to_ifaddr(net, nlh);
Thomas Graf5c753972006-08-04 23:03:53 -0700655 if (IS_ERR(ifa))
656 return PTR_ERR(ifa);
657
Thomas Grafd6062cb2006-08-15 00:33:59 -0700658 return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659}
660
661/*
662 * Determine a default network mask, based on the IP address.
663 */
664
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800665static inline int inet_abc_len(__be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666{
667 int rc = -1; /* Something else, probably a multicast. */
668
Joe Perchesf97c1e02007-12-16 13:45:43 -0800669 if (ipv4_is_zeronet(addr))
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900670 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 else {
Al Viro714e85b2006-11-14 20:51:49 -0800672 __u32 haddr = ntohl(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673
Al Viro714e85b2006-11-14 20:51:49 -0800674 if (IN_CLASSA(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 rc = 8;
Al Viro714e85b2006-11-14 20:51:49 -0800676 else if (IN_CLASSB(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 rc = 16;
Al Viro714e85b2006-11-14 20:51:49 -0800678 else if (IN_CLASSC(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 rc = 24;
680 }
681
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900682 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683}
684
685
Denis V. Luneve5b13cb2008-02-28 20:51:43 -0800686int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687{
688 struct ifreq ifr;
689 struct sockaddr_in sin_orig;
690 struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
691 struct in_device *in_dev;
692 struct in_ifaddr **ifap = NULL;
693 struct in_ifaddr *ifa = NULL;
694 struct net_device *dev;
695 char *colon;
696 int ret = -EFAULT;
697 int tryaddrmatch = 0;
698
699 /*
700 * Fetch the caller's info block into kernel space
701 */
702
703 if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
704 goto out;
705 ifr.ifr_name[IFNAMSIZ - 1] = 0;
706
707 /* save original address for comparison */
708 memcpy(&sin_orig, sin, sizeof(*sin));
709
710 colon = strchr(ifr.ifr_name, ':');
711 if (colon)
712 *colon = 0;
713
Denis V. Luneve5b13cb2008-02-28 20:51:43 -0800714 dev_load(net, ifr.ifr_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715
Stephen Hemminger132adf52007-03-08 20:44:43 -0800716 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 case SIOCGIFADDR: /* Get interface address */
718 case SIOCGIFBRDADDR: /* Get the broadcast address */
719 case SIOCGIFDSTADDR: /* Get the destination address */
720 case SIOCGIFNETMASK: /* Get the netmask for the interface */
721 /* Note that these ioctls will not sleep,
722 so that we do not impose a lock.
723 One day we will be forced to put shlock here (I mean SMP)
724 */
725 tryaddrmatch = (sin_orig.sin_family == AF_INET);
726 memset(sin, 0, sizeof(*sin));
727 sin->sin_family = AF_INET;
728 break;
729
730 case SIOCSIFFLAGS:
731 ret = -EACCES;
732 if (!capable(CAP_NET_ADMIN))
733 goto out;
734 break;
735 case SIOCSIFADDR: /* Set interface address (and family) */
736 case SIOCSIFBRDADDR: /* Set the broadcast address */
737 case SIOCSIFDSTADDR: /* Set the destination address */
738 case SIOCSIFNETMASK: /* Set the netmask for the interface */
Robert Lovea7429e52008-05-12 17:08:29 -0400739 case SIOCKILLADDR: /* Nuke all sockets on this address */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 ret = -EACCES;
741 if (!capable(CAP_NET_ADMIN))
742 goto out;
743 ret = -EINVAL;
744 if (sin->sin_family != AF_INET)
745 goto out;
746 break;
747 default:
748 ret = -EINVAL;
749 goto out;
750 }
751
752 rtnl_lock();
753
754 ret = -ENODEV;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800755 dev = __dev_get_by_name(net, ifr.ifr_name);
756 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 goto done;
758
759 if (colon)
760 *colon = ':';
761
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800762 in_dev = __in_dev_get_rtnl(dev);
763 if (in_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 if (tryaddrmatch) {
765 /* Matthias Andree */
766 /* compare label and address (4.4BSD style) */
767 /* note: we only do this for a limited set of ioctls
768 and only if the original address family was AF_INET.
769 This is checked above. */
770 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
771 ifap = &ifa->ifa_next) {
772 if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
773 sin_orig.sin_addr.s_addr ==
David S. Miller6c91afe2011-03-09 13:27:16 -0800774 ifa->ifa_local) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 break; /* found */
776 }
777 }
778 }
779 /* we didn't get a match, maybe the application is
780 4.3BSD-style and passed in junk so we fall back to
781 comparing just the label */
782 if (!ifa) {
783 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
784 ifap = &ifa->ifa_next)
785 if (!strcmp(ifr.ifr_name, ifa->ifa_label))
786 break;
787 }
788 }
789
790 ret = -EADDRNOTAVAIL;
Robert Lovea7429e52008-05-12 17:08:29 -0400791 if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS
792 && cmd != SIOCKILLADDR)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 goto done;
794
Stephen Hemminger132adf52007-03-08 20:44:43 -0800795 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796 case SIOCGIFADDR: /* Get interface address */
797 sin->sin_addr.s_addr = ifa->ifa_local;
798 goto rarok;
799
800 case SIOCGIFBRDADDR: /* Get the broadcast address */
801 sin->sin_addr.s_addr = ifa->ifa_broadcast;
802 goto rarok;
803
804 case SIOCGIFDSTADDR: /* Get the destination address */
805 sin->sin_addr.s_addr = ifa->ifa_address;
806 goto rarok;
807
808 case SIOCGIFNETMASK: /* Get the netmask for the interface */
809 sin->sin_addr.s_addr = ifa->ifa_mask;
810 goto rarok;
811
812 case SIOCSIFFLAGS:
813 if (colon) {
814 ret = -EADDRNOTAVAIL;
815 if (!ifa)
816 break;
817 ret = 0;
818 if (!(ifr.ifr_flags & IFF_UP))
819 inet_del_ifa(in_dev, ifap, 1);
820 break;
821 }
822 ret = dev_change_flags(dev, ifr.ifr_flags);
823 break;
824
825 case SIOCSIFADDR: /* Set interface address (and family) */
826 ret = -EINVAL;
827 if (inet_abc_len(sin->sin_addr.s_addr) < 0)
828 break;
829
830 if (!ifa) {
831 ret = -ENOBUFS;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800832 ifa = inet_alloc_ifa();
David S. Millerfd23c3b2011-02-18 12:42:28 -0800833 INIT_HLIST_NODE(&ifa->hash);
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800834 if (!ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 break;
836 if (colon)
837 memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
838 else
839 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
840 } else {
841 ret = 0;
842 if (ifa->ifa_local == sin->sin_addr.s_addr)
843 break;
844 inet_del_ifa(in_dev, ifap, 0);
845 ifa->ifa_broadcast = 0;
Bjorn Mork148f9722008-02-26 18:17:53 -0800846 ifa->ifa_scope = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847 }
848
849 ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
850
851 if (!(dev->flags & IFF_POINTOPOINT)) {
852 ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
853 ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
854 if ((dev->flags & IFF_BROADCAST) &&
855 ifa->ifa_prefixlen < 31)
856 ifa->ifa_broadcast = ifa->ifa_address |
857 ~ifa->ifa_mask;
858 } else {
859 ifa->ifa_prefixlen = 32;
860 ifa->ifa_mask = inet_make_mask(32);
861 }
862 ret = inet_set_ifa(dev, ifa);
863 break;
864
865 case SIOCSIFBRDADDR: /* Set the broadcast address */
866 ret = 0;
867 if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
868 inet_del_ifa(in_dev, ifap, 0);
869 ifa->ifa_broadcast = sin->sin_addr.s_addr;
870 inet_insert_ifa(ifa);
871 }
872 break;
873
874 case SIOCSIFDSTADDR: /* Set the destination address */
875 ret = 0;
876 if (ifa->ifa_address == sin->sin_addr.s_addr)
877 break;
878 ret = -EINVAL;
879 if (inet_abc_len(sin->sin_addr.s_addr) < 0)
880 break;
881 ret = 0;
882 inet_del_ifa(in_dev, ifap, 0);
883 ifa->ifa_address = sin->sin_addr.s_addr;
884 inet_insert_ifa(ifa);
885 break;
886
887 case SIOCSIFNETMASK: /* Set the netmask for the interface */
888
889 /*
890 * The mask we set must be legal.
891 */
892 ret = -EINVAL;
893 if (bad_mask(sin->sin_addr.s_addr, 0))
894 break;
895 ret = 0;
896 if (ifa->ifa_mask != sin->sin_addr.s_addr) {
Al Viroa144ea42006-09-28 18:00:55 -0700897 __be32 old_mask = ifa->ifa_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898 inet_del_ifa(in_dev, ifap, 0);
899 ifa->ifa_mask = sin->sin_addr.s_addr;
900 ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
901
902 /* See if current broadcast address matches
903 * with current netmask, then recalculate
904 * the broadcast address. Otherwise it's a
905 * funny address, so don't touch it since
906 * the user seems to know what (s)he's doing...
907 */
908 if ((dev->flags & IFF_BROADCAST) &&
909 (ifa->ifa_prefixlen < 31) &&
910 (ifa->ifa_broadcast ==
David Engeldcab5e12005-10-21 22:09:16 -0500911 (ifa->ifa_local|~old_mask))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 ifa->ifa_broadcast = (ifa->ifa_local |
913 ~sin->sin_addr.s_addr);
914 }
915 inet_insert_ifa(ifa);
916 }
917 break;
Robert Lovea7429e52008-05-12 17:08:29 -0400918 case SIOCKILLADDR: /* Nuke all connections on this address */
Lorenzo Colitti810bf5d2011-03-10 20:24:12 -0800919 ret = tcp_nuke_addr(net, (struct sockaddr *) sin);
Robert Lovea7429e52008-05-12 17:08:29 -0400920 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 }
922done:
923 rtnl_unlock();
924out:
925 return ret;
926rarok:
927 rtnl_unlock();
928 ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
929 goto out;
930}
931
932static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
933{
Herbert Xue5ed6392005-10-03 14:35:55 -0700934 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 struct in_ifaddr *ifa;
936 struct ifreq ifr;
937 int done = 0;
938
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800939 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 goto out;
941
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800942 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 if (!buf) {
944 done += sizeof(ifr);
945 continue;
946 }
947 if (len < (int) sizeof(ifr))
948 break;
949 memset(&ifr, 0, sizeof(struct ifreq));
950 if (ifa->ifa_label)
951 strcpy(ifr.ifr_name, ifa->ifa_label);
952 else
953 strcpy(ifr.ifr_name, dev->name);
954
955 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
956 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
957 ifa->ifa_local;
958
959 if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
960 done = -EFAULT;
961 break;
962 }
963 buf += sizeof(struct ifreq);
964 len -= sizeof(struct ifreq);
965 done += sizeof(struct ifreq);
966 }
967out:
968 return done;
969}
970
Al Viroa61ced52006-09-26 21:27:54 -0700971__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972{
Al Viroa61ced52006-09-26 21:27:54 -0700973 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 struct in_device *in_dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900975 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976
977 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -0700978 in_dev = __in_dev_get_rcu(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 if (!in_dev)
980 goto no_in_dev;
981
982 for_primary_ifa(in_dev) {
983 if (ifa->ifa_scope > scope)
984 continue;
985 if (!dst || inet_ifa_match(dst, ifa)) {
986 addr = ifa->ifa_local;
987 break;
988 }
989 if (!addr)
990 addr = ifa->ifa_local;
991 } endfor_ifa(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992
993 if (addr)
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800994 goto out_unlock;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800995no_in_dev:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996
997 /* Not loopback addresses on loopback should be preferred
998 in this case. It is importnat that lo is the first interface
999 in dev_base list.
1000 */
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001001 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001002 in_dev = __in_dev_get_rcu(dev);
1003 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 continue;
1005
1006 for_primary_ifa(in_dev) {
1007 if (ifa->ifa_scope != RT_SCOPE_LINK &&
1008 ifa->ifa_scope <= scope) {
1009 addr = ifa->ifa_local;
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001010 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 }
1012 } endfor_ifa(in_dev);
1013 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001014out_unlock:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 return addr;
1017}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001018EXPORT_SYMBOL(inet_select_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019
Al Viro60cad5d2006-09-26 22:17:09 -07001020static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
1021 __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022{
1023 int same = 0;
Al Viroa144ea42006-09-28 18:00:55 -07001024 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
1026 for_ifa(in_dev) {
1027 if (!addr &&
1028 (local == ifa->ifa_local || !local) &&
1029 ifa->ifa_scope <= scope) {
1030 addr = ifa->ifa_local;
1031 if (same)
1032 break;
1033 }
1034 if (!same) {
1035 same = (!local || inet_ifa_match(local, ifa)) &&
1036 (!dst || inet_ifa_match(dst, ifa));
1037 if (same && addr) {
1038 if (local || !dst)
1039 break;
1040 /* Is the selected addr into dst subnet? */
1041 if (inet_ifa_match(addr, ifa))
1042 break;
1043 /* No, then can we use new local src? */
1044 if (ifa->ifa_scope <= scope) {
1045 addr = ifa->ifa_local;
1046 break;
1047 }
1048 /* search for large dst subnet for addr */
1049 same = 0;
1050 }
1051 }
1052 } endfor_ifa(in_dev);
1053
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001054 return same ? addr : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055}
1056
1057/*
1058 * Confirm that local IP address exists using wildcards:
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001059 * - in_dev: only on this interface, 0=any interface
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 * - dst: only in the same subnet as dst, 0=any dst
1061 * - local: address, 0=autoselect the local address
1062 * - scope: maximum allowed scope value for the local address
1063 */
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001064__be32 inet_confirm_addr(struct in_device *in_dev,
1065 __be32 dst, __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066{
Al Viro60cad5d2006-09-26 22:17:09 -07001067 __be32 addr = 0;
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001068 struct net_device *dev;
Denis V. Lunev39a6d062008-01-14 23:06:19 -08001069 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070
Denis V. Lunev39a6d062008-01-14 23:06:19 -08001071 if (scope != RT_SCOPE_LINK)
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001072 return confirm_addr_indev(in_dev, dst, local, scope);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001074 net = dev_net(in_dev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 rcu_read_lock();
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001076 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001077 in_dev = __in_dev_get_rcu(dev);
1078 if (in_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079 addr = confirm_addr_indev(in_dev, dst, local, scope);
1080 if (addr)
1081 break;
1082 }
1083 }
1084 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
1086 return addr;
1087}
1088
1089/*
1090 * Device notifier
1091 */
1092
1093int register_inetaddr_notifier(struct notifier_block *nb)
1094{
Alan Sterne041c682006-03-27 01:16:30 -08001095 return blocking_notifier_chain_register(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001097EXPORT_SYMBOL(register_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098
1099int unregister_inetaddr_notifier(struct notifier_block *nb)
1100{
Alan Sterne041c682006-03-27 01:16:30 -08001101 return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001102}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001103EXPORT_SYMBOL(unregister_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001105/* Rename ifa_labels for a device name change. Make some effort to preserve
1106 * existing alias numbering and to create unique labels if possible.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107*/
1108static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001109{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 struct in_ifaddr *ifa;
1111 int named = 0;
1112
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001113 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
1114 char old[IFNAMSIZ], *dot;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115
1116 memcpy(old, ifa->ifa_label, IFNAMSIZ);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001117 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 if (named++ == 0)
Thomas Graf573bf472008-06-10 15:40:04 -07001119 goto skip;
Mark McLoughlin44344b22008-01-04 00:56:25 -08001120 dot = strchr(old, ':');
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001121 if (dot == NULL) {
1122 sprintf(old, ":%d", named);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123 dot = old;
1124 }
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001125 if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001126 strcat(ifa->ifa_label, dot);
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001127 else
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001128 strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
Thomas Graf573bf472008-06-10 15:40:04 -07001129skip:
1130 rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001131 }
1132}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133
Breno Leitao06770842008-09-02 17:28:58 -07001134static inline bool inetdev_valid_mtu(unsigned mtu)
1135{
1136 return mtu >= 68;
1137}
1138
Ian Campbelld11327a2011-02-11 07:44:16 +00001139static void inetdev_send_gratuitous_arp(struct net_device *dev,
1140 struct in_device *in_dev)
1141
1142{
1143 struct in_ifaddr *ifa = in_dev->ifa_list;
1144
1145 if (!ifa)
1146 return;
1147
1148 arp_send(ARPOP_REQUEST, ETH_P_ARP,
David S. Miller6c91afe2011-03-09 13:27:16 -08001149 ifa->ifa_local, dev,
1150 ifa->ifa_local, NULL,
Ian Campbelld11327a2011-02-11 07:44:16 +00001151 dev->dev_addr, NULL);
1152}
1153
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154/* Called only under RTNL semaphore */
1155
1156static int inetdev_event(struct notifier_block *this, unsigned long event,
1157 void *ptr)
1158{
1159 struct net_device *dev = ptr;
Herbert Xue5ed6392005-10-03 14:35:55 -07001160 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161
1162 ASSERT_RTNL();
1163
1164 if (!in_dev) {
Herbert Xu8030f542007-02-22 01:53:47 +09001165 if (event == NETDEV_REGISTER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 in_dev = inetdev_init(dev);
Herbert Xub217d612007-07-30 17:04:52 -07001167 if (!in_dev)
1168 return notifier_from_errno(-ENOMEM);
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001169 if (dev->flags & IFF_LOOPBACK) {
Herbert Xu42f811b2007-06-04 23:34:44 -07001170 IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
1171 IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
Herbert Xu8030f542007-02-22 01:53:47 +09001172 }
Breno Leitao06770842008-09-02 17:28:58 -07001173 } else if (event == NETDEV_CHANGEMTU) {
1174 /* Re-enabling IP */
1175 if (inetdev_valid_mtu(dev->mtu))
1176 in_dev = inetdev_init(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 }
1178 goto out;
1179 }
1180
1181 switch (event) {
1182 case NETDEV_REGISTER:
1183 printk(KERN_DEBUG "inetdev_event: bug\n");
Eric Dumazet95ae6b22010-09-15 04:04:31 +00001184 rcu_assign_pointer(dev->ip_ptr, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 break;
1186 case NETDEV_UP:
Breno Leitao06770842008-09-02 17:28:58 -07001187 if (!inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 break;
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001189 if (dev->flags & IFF_LOOPBACK) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001190 struct in_ifaddr *ifa = inet_alloc_ifa();
1191
1192 if (ifa) {
David S. Millerfd23c3b2011-02-18 12:42:28 -08001193 INIT_HLIST_NODE(&ifa->hash);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 ifa->ifa_local =
1195 ifa->ifa_address = htonl(INADDR_LOOPBACK);
1196 ifa->ifa_prefixlen = 8;
1197 ifa->ifa_mask = inet_make_mask(8);
1198 in_dev_hold(in_dev);
1199 ifa->ifa_dev = in_dev;
1200 ifa->ifa_scope = RT_SCOPE_HOST;
1201 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
1202 inet_insert_ifa(ifa);
1203 }
1204 }
1205 ip_mc_up(in_dev);
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001206 /* fall through */
1207 case NETDEV_CHANGEADDR:
Ian Campbelld11327a2011-02-11 07:44:16 +00001208 if (!IN_DEV_ARP_NOTIFY(in_dev))
1209 break;
1210 /* fall through */
1211 case NETDEV_NOTIFY_PEERS:
Stephen Hemmingera21090c2009-10-07 03:18:17 -07001212 /* Send gratuitous ARP to notify of link change */
Ian Campbelld11327a2011-02-11 07:44:16 +00001213 inetdev_send_gratuitous_arp(dev, in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214 break;
1215 case NETDEV_DOWN:
1216 ip_mc_down(in_dev);
1217 break;
Jiri Pirko93d9b7d2010-03-10 10:28:56 +00001218 case NETDEV_PRE_TYPE_CHANGE:
Moni Shoua75c78502009-09-15 02:37:40 -07001219 ip_mc_unmap(in_dev);
1220 break;
Jiri Pirko93d9b7d2010-03-10 10:28:56 +00001221 case NETDEV_POST_TYPE_CHANGE:
Moni Shoua75c78502009-09-15 02:37:40 -07001222 ip_mc_remap(in_dev);
1223 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001224 case NETDEV_CHANGEMTU:
Breno Leitao06770842008-09-02 17:28:58 -07001225 if (inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 break;
Breno Leitao06770842008-09-02 17:28:58 -07001227 /* disable IP when MTU is not enough */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 case NETDEV_UNREGISTER:
1229 inetdev_destroy(in_dev);
1230 break;
1231 case NETDEV_CHANGENAME:
1232 /* Do not notify about label change, this event is
1233 * not interesting to applications using netlink.
1234 */
1235 inetdev_changename(dev, in_dev);
1236
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001237 devinet_sysctl_unregister(in_dev);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001238 devinet_sysctl_register(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001239 break;
1240 }
1241out:
1242 return NOTIFY_DONE;
1243}
1244
1245static struct notifier_block ip_netdev_notifier = {
Jianjun Kong539afed2008-11-03 02:48:48 -08001246 .notifier_call = inetdev_event,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247};
1248
Thomas Graf339bf982006-11-10 14:10:15 -08001249static inline size_t inet_nlmsg_size(void)
1250{
1251 return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1252 + nla_total_size(4) /* IFA_ADDRESS */
1253 + nla_total_size(4) /* IFA_LOCAL */
1254 + nla_total_size(4) /* IFA_BROADCAST */
Thomas Graf339bf982006-11-10 14:10:15 -08001255 + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1256}
1257
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
Jamal Hadi Salimb6544c02005-06-18 22:54:12 -07001259 u32 pid, u32 seq, int event, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260{
1261 struct ifaddrmsg *ifm;
1262 struct nlmsghdr *nlh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
Thomas Graf47f68512006-08-04 23:04:36 -07001264 nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
1265 if (nlh == NULL)
Patrick McHardy26932562007-01-31 23:16:40 -08001266 return -EMSGSIZE;
Thomas Graf47f68512006-08-04 23:04:36 -07001267
1268 ifm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 ifm->ifa_family = AF_INET;
1270 ifm->ifa_prefixlen = ifa->ifa_prefixlen;
1271 ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
1272 ifm->ifa_scope = ifa->ifa_scope;
1273 ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
Thomas Graf47f68512006-08-04 23:04:36 -07001275 if (ifa->ifa_address)
Al Viroa7a628c2006-09-26 22:16:43 -07001276 NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address);
Thomas Graf47f68512006-08-04 23:04:36 -07001277
1278 if (ifa->ifa_local)
Al Viroa7a628c2006-09-26 22:16:43 -07001279 NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local);
Thomas Graf47f68512006-08-04 23:04:36 -07001280
1281 if (ifa->ifa_broadcast)
Al Viroa7a628c2006-09-26 22:16:43 -07001282 NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast);
Thomas Graf47f68512006-08-04 23:04:36 -07001283
Thomas Graf47f68512006-08-04 23:04:36 -07001284 if (ifa->ifa_label[0])
1285 NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label);
1286
1287 return nlmsg_end(skb, nlh);
1288
1289nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08001290 nlmsg_cancel(skb, nlh);
1291 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292}
1293
1294static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
1295{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001296 struct net *net = sock_net(skb->sk);
Eric Dumazeteec4df92009-11-12 07:44:25 +00001297 int h, s_h;
1298 int idx, s_idx;
1299 int ip_idx, s_ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 struct net_device *dev;
1301 struct in_device *in_dev;
1302 struct in_ifaddr *ifa;
Eric Dumazeteec4df92009-11-12 07:44:25 +00001303 struct hlist_head *head;
1304 struct hlist_node *node;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
Eric Dumazeteec4df92009-11-12 07:44:25 +00001306 s_h = cb->args[0];
1307 s_idx = idx = cb->args[1];
1308 s_ip_idx = ip_idx = cb->args[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309
Eric Dumazeteec4df92009-11-12 07:44:25 +00001310 for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
1311 idx = 0;
1312 head = &net->dev_index_head[h];
1313 rcu_read_lock();
1314 hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
1315 if (idx < s_idx)
1316 goto cont;
Patrick McHardy4b97efd2010-03-26 20:27:49 -07001317 if (h > s_h || idx > s_idx)
Eric Dumazeteec4df92009-11-12 07:44:25 +00001318 s_ip_idx = 0;
1319 in_dev = __in_dev_get_rcu(dev);
1320 if (!in_dev)
1321 goto cont;
1322
1323 for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
1324 ifa = ifa->ifa_next, ip_idx++) {
1325 if (ip_idx < s_ip_idx)
1326 continue;
1327 if (inet_fill_ifaddr(skb, ifa,
1328 NETLINK_CB(cb->skb).pid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 cb->nlh->nlmsg_seq,
Eric Dumazeteec4df92009-11-12 07:44:25 +00001330 RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1331 rcu_read_unlock();
1332 goto done;
1333 }
1334 }
Pavel Emelianov7562f872007-05-03 15:13:45 -07001335cont:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001336 idx++;
1337 }
1338 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 }
1340
1341done:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001342 cb->args[0] = h;
1343 cb->args[1] = idx;
1344 cb->args[2] = ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345
1346 return skb->len;
1347}
1348
Jianjun Kong539afed2008-11-03 02:48:48 -08001349static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
Thomas Grafd6062cb2006-08-15 00:33:59 -07001350 u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351{
Thomas Graf47f68512006-08-04 23:04:36 -07001352 struct sk_buff *skb;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001353 u32 seq = nlh ? nlh->nlmsg_seq : 0;
1354 int err = -ENOBUFS;
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001355 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001356
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001357 net = dev_net(ifa->ifa_dev->dev);
Thomas Graf339bf982006-11-10 14:10:15 -08001358 skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
Thomas Graf47f68512006-08-04 23:04:36 -07001359 if (skb == NULL)
Thomas Grafd6062cb2006-08-15 00:33:59 -07001360 goto errout;
1361
1362 err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08001363 if (err < 0) {
1364 /* -EMSGSIZE implies BUG in inet_nlmsg_size() */
1365 WARN_ON(err == -EMSGSIZE);
1366 kfree_skb(skb);
1367 goto errout;
1368 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001369 rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
1370 return;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001371errout:
1372 if (err < 0)
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001373 rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374}
1375
Thomas Graf9f0f7272010-11-16 04:32:48 +00001376static size_t inet_get_link_af_size(const struct net_device *dev)
1377{
Eric Dumazet1fc19af2011-05-09 20:55:03 -07001378 struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
Thomas Graf9f0f7272010-11-16 04:32:48 +00001379
1380 if (!in_dev)
1381 return 0;
1382
1383 return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
1384}
1385
1386static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
1387{
Eric Dumazet1fc19af2011-05-09 20:55:03 -07001388 struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
Thomas Graf9f0f7272010-11-16 04:32:48 +00001389 struct nlattr *nla;
1390 int i;
1391
1392 if (!in_dev)
1393 return -ENODATA;
1394
1395 nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
1396 if (nla == NULL)
1397 return -EMSGSIZE;
1398
1399 for (i = 0; i < IPV4_DEVCONF_MAX; i++)
1400 ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
1401
1402 return 0;
1403}
1404
1405static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
1406 [IFLA_INET_CONF] = { .type = NLA_NESTED },
1407};
1408
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001409static int inet_validate_link_af(const struct net_device *dev,
1410 const struct nlattr *nla)
Thomas Graf9f0f7272010-11-16 04:32:48 +00001411{
Thomas Graf9f0f7272010-11-16 04:32:48 +00001412 struct nlattr *a, *tb[IFLA_INET_MAX+1];
1413 int err, rem;
1414
Eric Dumazetf7fce742010-12-01 06:03:06 +00001415 if (dev && !__in_dev_get_rtnl(dev))
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001416 return -EAFNOSUPPORT;
Thomas Graf9f0f7272010-11-16 04:32:48 +00001417
1418 err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
1419 if (err < 0)
1420 return err;
1421
1422 if (tb[IFLA_INET_CONF]) {
1423 nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
1424 int cfgid = nla_type(a);
1425
1426 if (nla_len(a) < 4)
1427 return -EINVAL;
1428
1429 if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
1430 return -EINVAL;
1431 }
1432 }
1433
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001434 return 0;
1435}
1436
1437static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1438{
Eric Dumazetf7fce742010-12-01 06:03:06 +00001439 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001440 struct nlattr *a, *tb[IFLA_INET_MAX+1];
1441 int rem;
1442
1443 if (!in_dev)
1444 return -EAFNOSUPPORT;
1445
1446 if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1447 BUG();
1448
Thomas Graf9f0f7272010-11-16 04:32:48 +00001449 if (tb[IFLA_INET_CONF]) {
1450 nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
1451 ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
1452 }
1453
1454 return 0;
1455}
1456
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457#ifdef CONFIG_SYSCTL
1458
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001459static void devinet_copy_dflt_conf(struct net *net, int i)
Herbert Xu31be3082007-06-04 23:35:37 -07001460{
1461 struct net_device *dev;
1462
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001463 rcu_read_lock();
1464 for_each_netdev_rcu(net, dev) {
Herbert Xu31be3082007-06-04 23:35:37 -07001465 struct in_device *in_dev;
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001466
Herbert Xu31be3082007-06-04 23:35:37 -07001467 in_dev = __in_dev_get_rcu(dev);
1468 if (in_dev && !test_bit(i, in_dev->cnf.state))
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001469 in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
Herbert Xu31be3082007-06-04 23:35:37 -07001470 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001471 rcu_read_unlock();
Herbert Xu31be3082007-06-04 23:35:37 -07001472}
1473
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001474/* called with RTNL locked */
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001475static void inet_forward_change(struct net *net)
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001476{
1477 struct net_device *dev;
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001478 int on = IPV4_DEVCONF_ALL(net, FORWARDING);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001479
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001480 IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001481 IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001482
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001483 for_each_netdev(net, dev) {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001484 struct in_device *in_dev;
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001485 if (on)
1486 dev_disable_lro(dev);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001487 rcu_read_lock();
1488 in_dev = __in_dev_get_rcu(dev);
1489 if (in_dev)
1490 IN_DEV_CONF_SET(in_dev, FORWARDING, on);
1491 rcu_read_unlock();
1492 }
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001493}
1494
Herbert Xu31be3082007-06-04 23:35:37 -07001495static int devinet_conf_proc(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001496 void __user *buffer,
Herbert Xu31be3082007-06-04 23:35:37 -07001497 size_t *lenp, loff_t *ppos)
1498{
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001499 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Herbert Xu31be3082007-06-04 23:35:37 -07001500
1501 if (write) {
1502 struct ipv4_devconf *cnf = ctl->extra1;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001503 struct net *net = ctl->extra2;
Herbert Xu31be3082007-06-04 23:35:37 -07001504 int i = (int *)ctl->data - cnf->data;
1505
1506 set_bit(i, cnf->state);
1507
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001508 if (cnf == net->ipv4.devconf_dflt)
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001509 devinet_copy_dflt_conf(net, i);
Herbert Xu31be3082007-06-04 23:35:37 -07001510 }
1511
1512 return ret;
1513}
1514
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515static int devinet_sysctl_forward(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001516 void __user *buffer,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517 size_t *lenp, loff_t *ppos)
1518{
1519 int *valp = ctl->data;
1520 int val = *valp;
Eric W. Biederman88af1822010-02-19 13:22:59 +00001521 loff_t pos = *ppos;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001522 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523
1524 if (write && *valp != val) {
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001525 struct net *net = ctl->extra2;
1526
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001527 if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
Eric W. Biederman88af1822010-02-19 13:22:59 +00001528 if (!rtnl_trylock()) {
1529 /* Restore the original values before restarting */
1530 *valp = val;
1531 *ppos = pos;
Eric W. Biederman9b8adb52009-05-13 16:59:21 +00001532 return restart_syscall();
Eric W. Biederman88af1822010-02-19 13:22:59 +00001533 }
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001534 if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1535 inet_forward_change(net);
1536 } else if (*valp) {
1537 struct ipv4_devconf *cnf = ctl->extra1;
1538 struct in_device *idev =
1539 container_of(cnf, struct in_device, cnf);
1540 dev_disable_lro(idev->dev);
1541 }
1542 rtnl_unlock();
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001543 rt_cache_flush(net, 0);
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001544 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545 }
1546
1547 return ret;
1548}
1549
David S. Miller323e1262010-12-12 21:55:08 -08001550static int ipv4_doint_and_flush(ctl_table *ctl, int write,
1551 void __user *buffer,
1552 size_t *lenp, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553{
1554 int *valp = ctl->data;
1555 int val = *valp;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001556 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001557 struct net *net = ctl->extra2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001558
1559 if (write && *valp != val)
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001560 rt_cache_flush(net, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561
1562 return ret;
1563}
1564
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001565#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
Herbert Xu42f811b2007-06-04 23:34:44 -07001566 { \
Herbert Xu42f811b2007-06-04 23:34:44 -07001567 .procname = name, \
1568 .data = ipv4_devconf.data + \
Eric W. Biederman02291682010-02-14 03:25:51 +00001569 IPV4_DEVCONF_ ## attr - 1, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001570 .maxlen = sizeof(int), \
1571 .mode = mval, \
1572 .proc_handler = proc, \
Herbert Xu31be3082007-06-04 23:35:37 -07001573 .extra1 = &ipv4_devconf, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001574 }
1575
1576#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001577 DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001578
1579#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001580 DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001581
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001582#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1583 DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001584
1585#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001586 DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
Herbert Xu42f811b2007-06-04 23:34:44 -07001587
Linus Torvalds1da177e2005-04-16 15:20:36 -07001588static struct devinet_sysctl_table {
1589 struct ctl_table_header *sysctl_header;
Eric W. Biederman02291682010-02-14 03:25:51 +00001590 struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001591 char *dev_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592} devinet_sysctl = {
1593 .devinet_vars = {
Herbert Xu42f811b2007-06-04 23:34:44 -07001594 DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001595 devinet_sysctl_forward),
Herbert Xu42f811b2007-06-04 23:34:44 -07001596 DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
1597
1598 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
1599 DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
1600 DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
1601 DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
1602 DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
1603 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
1604 "accept_source_route"),
Patrick McHardy8153a102009-12-03 01:25:58 +00001605 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
Jamal Hadi Salim28f6aee2009-12-25 17:30:22 -08001606 DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001607 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
1608 DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
1609 DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
1610 DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
1611 DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
1612 DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
1613 DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
1614 DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
1615 DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001616 DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
Jesper Dangaard Brouer65324142010-01-05 05:50:47 +00001617 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001618
1619 DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
1620 DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
1621 DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
1622 "force_igmp_version"),
1623 DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
1624 "promote_secondaries"),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626};
1627
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001628static int __devinet_sysctl_register(struct net *net, char *dev_name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001629 struct ipv4_devconf *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630{
1631 int i;
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001632 struct devinet_sysctl_table *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001634#define DEVINET_CTL_PATH_DEV 3
1635
1636 struct ctl_path devinet_ctl_path[] = {
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001637 { .procname = "net", },
1638 { .procname = "ipv4", },
1639 { .procname = "conf", },
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001640 { /* to be set */ },
1641 { },
1642 };
1643
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001644 t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 if (!t)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001646 goto out;
1647
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648 for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
1649 t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
Herbert Xu31be3082007-06-04 23:35:37 -07001650 t->devinet_vars[i].extra1 = p;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001651 t->devinet_vars[i].extra2 = net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 }
1653
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001654 /*
1655 * Make a copy of dev_name, because '.procname' is regarded as const
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656 * by sysctl and we wouldn't want anyone to change it under our feet
1657 * (see SIOCSIFNAME).
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001658 */
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001659 t->dev_name = kstrdup(dev_name, GFP_KERNEL);
1660 if (!t->dev_name)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001661 goto free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001663 devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001665 t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path,
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001666 t->devinet_vars);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001667 if (!t->sysctl_header)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001668 goto free_procname;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001669
1670 p->sysctl = t;
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001671 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001672
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001673free_procname:
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001674 kfree(t->dev_name);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001675free:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 kfree(t);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001677out:
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001678 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679}
1680
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001681static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
1682{
1683 struct devinet_sysctl_table *t = cnf->sysctl;
1684
1685 if (t == NULL)
1686 return;
1687
1688 cnf->sysctl = NULL;
Lucian Adrian Grijincuff538812011-05-01 01:44:01 +00001689 unregister_net_sysctl_table(t->sysctl_header);
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001690 kfree(t->dev_name);
1691 kfree(t);
1692}
1693
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001694static void devinet_sysctl_register(struct in_device *idev)
1695{
Eric W. Biederman54716e32010-02-14 03:27:03 +00001696 neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001697 __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001698 &idev->cnf);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001699}
1700
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001701static void devinet_sysctl_unregister(struct in_device *idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702{
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001703 __devinet_sysctl_unregister(&idev->cnf);
1704 neigh_sysctl_unregister(idev->arp_parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001707static struct ctl_table ctl_forward_entry[] = {
1708 {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001709 .procname = "ip_forward",
1710 .data = &ipv4_devconf.data[
Eric W. Biederman02291682010-02-14 03:25:51 +00001711 IPV4_DEVCONF_FORWARDING - 1],
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001712 .maxlen = sizeof(int),
1713 .mode = 0644,
1714 .proc_handler = devinet_sysctl_forward,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001715 .extra1 = &ipv4_devconf,
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001716 .extra2 = &init_net,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001717 },
1718 { },
1719};
1720
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001721static __net_initdata struct ctl_path net_ipv4_path[] = {
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001722 { .procname = "net", },
1723 { .procname = "ipv4", },
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001724 { },
1725};
Eric Dumazet2a75de02008-01-05 23:08:49 -08001726#endif
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001727
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001728static __net_init int devinet_init_net(struct net *net)
1729{
1730 int err;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001731 struct ipv4_devconf *all, *dflt;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001732#ifdef CONFIG_SYSCTL
1733 struct ctl_table *tbl = ctl_forward_entry;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001734 struct ctl_table_header *forw_hdr;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001735#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001736
1737 err = -ENOMEM;
1738 all = &ipv4_devconf;
1739 dflt = &ipv4_devconf_dflt;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001740
Octavian Purdila09ad9bc2009-11-25 15:14:13 -08001741 if (!net_eq(net, &init_net)) {
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001742 all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1743 if (all == NULL)
1744 goto err_alloc_all;
1745
1746 dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1747 if (dflt == NULL)
1748 goto err_alloc_dflt;
1749
Eric Dumazet2a75de02008-01-05 23:08:49 -08001750#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001751 tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1752 if (tbl == NULL)
1753 goto err_alloc_ctl;
1754
Eric W. Biederman02291682010-02-14 03:25:51 +00001755 tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001756 tbl[0].extra1 = all;
1757 tbl[0].extra2 = net;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001758#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001759 }
1760
1761#ifdef CONFIG_SYSCTL
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001762 err = __devinet_sysctl_register(net, "all", all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001763 if (err < 0)
1764 goto err_reg_all;
1765
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001766 err = __devinet_sysctl_register(net, "default", dflt);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001767 if (err < 0)
1768 goto err_reg_dflt;
1769
1770 err = -ENOMEM;
1771 forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
1772 if (forw_hdr == NULL)
1773 goto err_reg_ctl;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001774 net->ipv4.forw_hdr = forw_hdr;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001775#endif
1776
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001777 net->ipv4.devconf_all = all;
1778 net->ipv4.devconf_dflt = dflt;
1779 return 0;
1780
1781#ifdef CONFIG_SYSCTL
1782err_reg_ctl:
1783 __devinet_sysctl_unregister(dflt);
1784err_reg_dflt:
1785 __devinet_sysctl_unregister(all);
1786err_reg_all:
1787 if (tbl != ctl_forward_entry)
1788 kfree(tbl);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001789err_alloc_ctl:
Eric Dumazet2a75de02008-01-05 23:08:49 -08001790#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001791 if (dflt != &ipv4_devconf_dflt)
1792 kfree(dflt);
1793err_alloc_dflt:
1794 if (all != &ipv4_devconf)
1795 kfree(all);
1796err_alloc_all:
1797 return err;
1798}
1799
1800static __net_exit void devinet_exit_net(struct net *net)
1801{
Eric Dumazet2a75de02008-01-05 23:08:49 -08001802#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001803 struct ctl_table *tbl;
1804
1805 tbl = net->ipv4.forw_hdr->ctl_table_arg;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001806 unregister_net_sysctl_table(net->ipv4.forw_hdr);
1807 __devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1808 __devinet_sysctl_unregister(net->ipv4.devconf_all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001809 kfree(tbl);
Eric Dumazet2a75de02008-01-05 23:08:49 -08001810#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001811 kfree(net->ipv4.devconf_dflt);
1812 kfree(net->ipv4.devconf_all);
1813}
1814
1815static __net_initdata struct pernet_operations devinet_ops = {
1816 .init = devinet_init_net,
1817 .exit = devinet_exit_net,
1818};
1819
Thomas Graf9f0f7272010-11-16 04:32:48 +00001820static struct rtnl_af_ops inet_af_ops = {
1821 .family = AF_INET,
1822 .fill_link_af = inet_fill_link_af,
1823 .get_link_af_size = inet_get_link_af_size,
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001824 .validate_link_af = inet_validate_link_af,
1825 .set_link_af = inet_set_link_af,
Thomas Graf9f0f7272010-11-16 04:32:48 +00001826};
1827
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828void __init devinet_init(void)
1829{
David S. Millerfd23c3b2011-02-18 12:42:28 -08001830 int i;
1831
1832 for (i = 0; i < IN4_ADDR_HSIZE; i++)
1833 INIT_HLIST_HEAD(&inet_addr_lst[i]);
1834
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001835 register_pernet_subsys(&devinet_ops);
1836
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 register_gifconf(PF_INET, inet_gifconf);
1838 register_netdevice_notifier(&ip_netdev_notifier);
Thomas Graf63f34442007-03-22 11:55:17 -07001839
Thomas Graf9f0f7272010-11-16 04:32:48 +00001840 rtnl_af_register(&inet_af_ops);
1841
Thomas Graf63f34442007-03-22 11:55:17 -07001842 rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
1843 rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
1844 rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845}
1846