blob: b5aad2bd0445b309c46976d6e974c8f8eb507a5e [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 */
919 ret = 0;
920 tcp_v4_nuke_addr(sin->sin_addr.s_addr);
921 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 }
923done:
924 rtnl_unlock();
925out:
926 return ret;
927rarok:
928 rtnl_unlock();
929 ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
930 goto out;
931}
932
933static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
934{
Herbert Xue5ed6392005-10-03 14:35:55 -0700935 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 struct in_ifaddr *ifa;
937 struct ifreq ifr;
938 int done = 0;
939
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800940 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941 goto out;
942
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800943 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 if (!buf) {
945 done += sizeof(ifr);
946 continue;
947 }
948 if (len < (int) sizeof(ifr))
949 break;
950 memset(&ifr, 0, sizeof(struct ifreq));
951 if (ifa->ifa_label)
952 strcpy(ifr.ifr_name, ifa->ifa_label);
953 else
954 strcpy(ifr.ifr_name, dev->name);
955
956 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
957 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
958 ifa->ifa_local;
959
960 if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
961 done = -EFAULT;
962 break;
963 }
964 buf += sizeof(struct ifreq);
965 len -= sizeof(struct ifreq);
966 done += sizeof(struct ifreq);
967 }
968out:
969 return done;
970}
971
Al Viroa61ced52006-09-26 21:27:54 -0700972__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973{
Al Viroa61ced52006-09-26 21:27:54 -0700974 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975 struct in_device *in_dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900976 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
978 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -0700979 in_dev = __in_dev_get_rcu(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980 if (!in_dev)
981 goto no_in_dev;
982
983 for_primary_ifa(in_dev) {
984 if (ifa->ifa_scope > scope)
985 continue;
986 if (!dst || inet_ifa_match(dst, ifa)) {
987 addr = ifa->ifa_local;
988 break;
989 }
990 if (!addr)
991 addr = ifa->ifa_local;
992 } endfor_ifa(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993
994 if (addr)
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800995 goto out_unlock;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800996no_in_dev:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
998 /* Not loopback addresses on loopback should be preferred
999 in this case. It is importnat that lo is the first interface
1000 in dev_base list.
1001 */
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001002 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001003 in_dev = __in_dev_get_rcu(dev);
1004 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 continue;
1006
1007 for_primary_ifa(in_dev) {
1008 if (ifa->ifa_scope != RT_SCOPE_LINK &&
1009 ifa->ifa_scope <= scope) {
1010 addr = ifa->ifa_local;
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001011 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 }
1013 } endfor_ifa(in_dev);
1014 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001015out_unlock:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 return addr;
1018}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001019EXPORT_SYMBOL(inet_select_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
Al Viro60cad5d2006-09-26 22:17:09 -07001021static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
1022 __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023{
1024 int same = 0;
Al Viroa144ea42006-09-28 18:00:55 -07001025 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026
1027 for_ifa(in_dev) {
1028 if (!addr &&
1029 (local == ifa->ifa_local || !local) &&
1030 ifa->ifa_scope <= scope) {
1031 addr = ifa->ifa_local;
1032 if (same)
1033 break;
1034 }
1035 if (!same) {
1036 same = (!local || inet_ifa_match(local, ifa)) &&
1037 (!dst || inet_ifa_match(dst, ifa));
1038 if (same && addr) {
1039 if (local || !dst)
1040 break;
1041 /* Is the selected addr into dst subnet? */
1042 if (inet_ifa_match(addr, ifa))
1043 break;
1044 /* No, then can we use new local src? */
1045 if (ifa->ifa_scope <= scope) {
1046 addr = ifa->ifa_local;
1047 break;
1048 }
1049 /* search for large dst subnet for addr */
1050 same = 0;
1051 }
1052 }
1053 } endfor_ifa(in_dev);
1054
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001055 return same ? addr : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056}
1057
1058/*
1059 * Confirm that local IP address exists using wildcards:
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001060 * - in_dev: only on this interface, 0=any interface
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 * - dst: only in the same subnet as dst, 0=any dst
1062 * - local: address, 0=autoselect the local address
1063 * - scope: maximum allowed scope value for the local address
1064 */
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001065__be32 inet_confirm_addr(struct in_device *in_dev,
1066 __be32 dst, __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067{
Al Viro60cad5d2006-09-26 22:17:09 -07001068 __be32 addr = 0;
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001069 struct net_device *dev;
Denis V. Lunev39a6d062008-01-14 23:06:19 -08001070 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
Denis V. Lunev39a6d062008-01-14 23:06:19 -08001072 if (scope != RT_SCOPE_LINK)
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001073 return confirm_addr_indev(in_dev, dst, local, scope);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001075 net = dev_net(in_dev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 rcu_read_lock();
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001077 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001078 in_dev = __in_dev_get_rcu(dev);
1079 if (in_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 addr = confirm_addr_indev(in_dev, dst, local, scope);
1081 if (addr)
1082 break;
1083 }
1084 }
1085 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086
1087 return addr;
1088}
1089
1090/*
1091 * Device notifier
1092 */
1093
1094int register_inetaddr_notifier(struct notifier_block *nb)
1095{
Alan Sterne041c682006-03-27 01:16:30 -08001096 return blocking_notifier_chain_register(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001098EXPORT_SYMBOL(register_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099
1100int unregister_inetaddr_notifier(struct notifier_block *nb)
1101{
Alan Sterne041c682006-03-27 01:16:30 -08001102 return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001104EXPORT_SYMBOL(unregister_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001106/* Rename ifa_labels for a device name change. Make some effort to preserve
1107 * existing alias numbering and to create unique labels if possible.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108*/
1109static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001110{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 struct in_ifaddr *ifa;
1112 int named = 0;
1113
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001114 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
1115 char old[IFNAMSIZ], *dot;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116
1117 memcpy(old, ifa->ifa_label, IFNAMSIZ);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001118 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 if (named++ == 0)
Thomas Graf573bf472008-06-10 15:40:04 -07001120 goto skip;
Mark McLoughlin44344b22008-01-04 00:56:25 -08001121 dot = strchr(old, ':');
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001122 if (dot == NULL) {
1123 sprintf(old, ":%d", named);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001124 dot = old;
1125 }
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001126 if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001127 strcat(ifa->ifa_label, dot);
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001128 else
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001129 strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
Thomas Graf573bf472008-06-10 15:40:04 -07001130skip:
1131 rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001132 }
1133}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134
Breno Leitao06770842008-09-02 17:28:58 -07001135static inline bool inetdev_valid_mtu(unsigned mtu)
1136{
1137 return mtu >= 68;
1138}
1139
Ian Campbelld11327a2011-02-11 07:44:16 +00001140static void inetdev_send_gratuitous_arp(struct net_device *dev,
1141 struct in_device *in_dev)
1142
1143{
1144 struct in_ifaddr *ifa = in_dev->ifa_list;
1145
1146 if (!ifa)
1147 return;
1148
1149 arp_send(ARPOP_REQUEST, ETH_P_ARP,
David S. Miller6c91afe2011-03-09 13:27:16 -08001150 ifa->ifa_local, dev,
1151 ifa->ifa_local, NULL,
Ian Campbelld11327a2011-02-11 07:44:16 +00001152 dev->dev_addr, NULL);
1153}
1154
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155/* Called only under RTNL semaphore */
1156
1157static int inetdev_event(struct notifier_block *this, unsigned long event,
1158 void *ptr)
1159{
1160 struct net_device *dev = ptr;
Herbert Xue5ed6392005-10-03 14:35:55 -07001161 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162
1163 ASSERT_RTNL();
1164
1165 if (!in_dev) {
Herbert Xu8030f542007-02-22 01:53:47 +09001166 if (event == NETDEV_REGISTER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 in_dev = inetdev_init(dev);
Herbert Xub217d612007-07-30 17:04:52 -07001168 if (!in_dev)
1169 return notifier_from_errno(-ENOMEM);
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001170 if (dev->flags & IFF_LOOPBACK) {
Herbert Xu42f811b2007-06-04 23:34:44 -07001171 IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
1172 IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
Herbert Xu8030f542007-02-22 01:53:47 +09001173 }
Breno Leitao06770842008-09-02 17:28:58 -07001174 } else if (event == NETDEV_CHANGEMTU) {
1175 /* Re-enabling IP */
1176 if (inetdev_valid_mtu(dev->mtu))
1177 in_dev = inetdev_init(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 }
1179 goto out;
1180 }
1181
1182 switch (event) {
1183 case NETDEV_REGISTER:
1184 printk(KERN_DEBUG "inetdev_event: bug\n");
Eric Dumazet95ae6b22010-09-15 04:04:31 +00001185 rcu_assign_pointer(dev->ip_ptr, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 break;
1187 case NETDEV_UP:
Breno Leitao06770842008-09-02 17:28:58 -07001188 if (!inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 break;
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001190 if (dev->flags & IFF_LOOPBACK) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001191 struct in_ifaddr *ifa = inet_alloc_ifa();
1192
1193 if (ifa) {
David S. Millerfd23c3b2011-02-18 12:42:28 -08001194 INIT_HLIST_NODE(&ifa->hash);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 ifa->ifa_local =
1196 ifa->ifa_address = htonl(INADDR_LOOPBACK);
1197 ifa->ifa_prefixlen = 8;
1198 ifa->ifa_mask = inet_make_mask(8);
1199 in_dev_hold(in_dev);
1200 ifa->ifa_dev = in_dev;
1201 ifa->ifa_scope = RT_SCOPE_HOST;
1202 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
1203 inet_insert_ifa(ifa);
1204 }
1205 }
1206 ip_mc_up(in_dev);
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001207 /* fall through */
1208 case NETDEV_CHANGEADDR:
Ian Campbelld11327a2011-02-11 07:44:16 +00001209 if (!IN_DEV_ARP_NOTIFY(in_dev))
1210 break;
1211 /* fall through */
1212 case NETDEV_NOTIFY_PEERS:
Stephen Hemmingera21090c2009-10-07 03:18:17 -07001213 /* Send gratuitous ARP to notify of link change */
Ian Campbelld11327a2011-02-11 07:44:16 +00001214 inetdev_send_gratuitous_arp(dev, in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 break;
1216 case NETDEV_DOWN:
1217 ip_mc_down(in_dev);
1218 break;
Jiri Pirko93d9b7d2010-03-10 10:28:56 +00001219 case NETDEV_PRE_TYPE_CHANGE:
Moni Shoua75c78502009-09-15 02:37:40 -07001220 ip_mc_unmap(in_dev);
1221 break;
Jiri Pirko93d9b7d2010-03-10 10:28:56 +00001222 case NETDEV_POST_TYPE_CHANGE:
Moni Shoua75c78502009-09-15 02:37:40 -07001223 ip_mc_remap(in_dev);
1224 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 case NETDEV_CHANGEMTU:
Breno Leitao06770842008-09-02 17:28:58 -07001226 if (inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 break;
Breno Leitao06770842008-09-02 17:28:58 -07001228 /* disable IP when MTU is not enough */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001229 case NETDEV_UNREGISTER:
1230 inetdev_destroy(in_dev);
1231 break;
1232 case NETDEV_CHANGENAME:
1233 /* Do not notify about label change, this event is
1234 * not interesting to applications using netlink.
1235 */
1236 inetdev_changename(dev, in_dev);
1237
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001238 devinet_sysctl_unregister(in_dev);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001239 devinet_sysctl_register(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 break;
1241 }
1242out:
1243 return NOTIFY_DONE;
1244}
1245
1246static struct notifier_block ip_netdev_notifier = {
Jianjun Kong539afed2008-11-03 02:48:48 -08001247 .notifier_call = inetdev_event,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248};
1249
Thomas Graf339bf982006-11-10 14:10:15 -08001250static inline size_t inet_nlmsg_size(void)
1251{
1252 return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1253 + nla_total_size(4) /* IFA_ADDRESS */
1254 + nla_total_size(4) /* IFA_LOCAL */
1255 + nla_total_size(4) /* IFA_BROADCAST */
Thomas Graf339bf982006-11-10 14:10:15 -08001256 + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1257}
1258
Linus Torvalds1da177e2005-04-16 15:20:36 -07001259static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
Jamal Hadi Salimb6544c02005-06-18 22:54:12 -07001260 u32 pid, u32 seq, int event, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261{
1262 struct ifaddrmsg *ifm;
1263 struct nlmsghdr *nlh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264
Thomas Graf47f68512006-08-04 23:04:36 -07001265 nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
1266 if (nlh == NULL)
Patrick McHardy26932562007-01-31 23:16:40 -08001267 return -EMSGSIZE;
Thomas Graf47f68512006-08-04 23:04:36 -07001268
1269 ifm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270 ifm->ifa_family = AF_INET;
1271 ifm->ifa_prefixlen = ifa->ifa_prefixlen;
1272 ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
1273 ifm->ifa_scope = ifa->ifa_scope;
1274 ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275
Thomas Graf47f68512006-08-04 23:04:36 -07001276 if (ifa->ifa_address)
Al Viroa7a628c2006-09-26 22:16:43 -07001277 NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address);
Thomas Graf47f68512006-08-04 23:04:36 -07001278
1279 if (ifa->ifa_local)
Al Viroa7a628c2006-09-26 22:16:43 -07001280 NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local);
Thomas Graf47f68512006-08-04 23:04:36 -07001281
1282 if (ifa->ifa_broadcast)
Al Viroa7a628c2006-09-26 22:16:43 -07001283 NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast);
Thomas Graf47f68512006-08-04 23:04:36 -07001284
Thomas Graf47f68512006-08-04 23:04:36 -07001285 if (ifa->ifa_label[0])
1286 NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label);
1287
1288 return nlmsg_end(skb, nlh);
1289
1290nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08001291 nlmsg_cancel(skb, nlh);
1292 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293}
1294
1295static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
1296{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001297 struct net *net = sock_net(skb->sk);
Eric Dumazeteec4df92009-11-12 07:44:25 +00001298 int h, s_h;
1299 int idx, s_idx;
1300 int ip_idx, s_ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 struct net_device *dev;
1302 struct in_device *in_dev;
1303 struct in_ifaddr *ifa;
Eric Dumazeteec4df92009-11-12 07:44:25 +00001304 struct hlist_head *head;
1305 struct hlist_node *node;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306
Eric Dumazeteec4df92009-11-12 07:44:25 +00001307 s_h = cb->args[0];
1308 s_idx = idx = cb->args[1];
1309 s_ip_idx = ip_idx = cb->args[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
Eric Dumazeteec4df92009-11-12 07:44:25 +00001311 for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
1312 idx = 0;
1313 head = &net->dev_index_head[h];
1314 rcu_read_lock();
1315 hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
1316 if (idx < s_idx)
1317 goto cont;
Patrick McHardy4b97efd2010-03-26 20:27:49 -07001318 if (h > s_h || idx > s_idx)
Eric Dumazeteec4df92009-11-12 07:44:25 +00001319 s_ip_idx = 0;
1320 in_dev = __in_dev_get_rcu(dev);
1321 if (!in_dev)
1322 goto cont;
1323
1324 for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
1325 ifa = ifa->ifa_next, ip_idx++) {
1326 if (ip_idx < s_ip_idx)
1327 continue;
1328 if (inet_fill_ifaddr(skb, ifa,
1329 NETLINK_CB(cb->skb).pid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330 cb->nlh->nlmsg_seq,
Eric Dumazeteec4df92009-11-12 07:44:25 +00001331 RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1332 rcu_read_unlock();
1333 goto done;
1334 }
1335 }
Pavel Emelianov7562f872007-05-03 15:13:45 -07001336cont:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001337 idx++;
1338 }
1339 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 }
1341
1342done:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001343 cb->args[0] = h;
1344 cb->args[1] = idx;
1345 cb->args[2] = ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
1347 return skb->len;
1348}
1349
Jianjun Kong539afed2008-11-03 02:48:48 -08001350static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
Thomas Grafd6062cb2006-08-15 00:33:59 -07001351 u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352{
Thomas Graf47f68512006-08-04 23:04:36 -07001353 struct sk_buff *skb;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001354 u32 seq = nlh ? nlh->nlmsg_seq : 0;
1355 int err = -ENOBUFS;
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001356 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001358 net = dev_net(ifa->ifa_dev->dev);
Thomas Graf339bf982006-11-10 14:10:15 -08001359 skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
Thomas Graf47f68512006-08-04 23:04:36 -07001360 if (skb == NULL)
Thomas Grafd6062cb2006-08-15 00:33:59 -07001361 goto errout;
1362
1363 err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08001364 if (err < 0) {
1365 /* -EMSGSIZE implies BUG in inet_nlmsg_size() */
1366 WARN_ON(err == -EMSGSIZE);
1367 kfree_skb(skb);
1368 goto errout;
1369 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001370 rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
1371 return;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001372errout:
1373 if (err < 0)
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001374 rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375}
1376
Thomas Graf9f0f7272010-11-16 04:32:48 +00001377static size_t inet_get_link_af_size(const struct net_device *dev)
1378{
Eric Dumazet1fc19af2011-05-09 20:55:03 -07001379 struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
Thomas Graf9f0f7272010-11-16 04:32:48 +00001380
1381 if (!in_dev)
1382 return 0;
1383
1384 return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
1385}
1386
1387static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
1388{
Eric Dumazet1fc19af2011-05-09 20:55:03 -07001389 struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
Thomas Graf9f0f7272010-11-16 04:32:48 +00001390 struct nlattr *nla;
1391 int i;
1392
1393 if (!in_dev)
1394 return -ENODATA;
1395
1396 nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
1397 if (nla == NULL)
1398 return -EMSGSIZE;
1399
1400 for (i = 0; i < IPV4_DEVCONF_MAX; i++)
1401 ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
1402
1403 return 0;
1404}
1405
1406static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
1407 [IFLA_INET_CONF] = { .type = NLA_NESTED },
1408};
1409
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001410static int inet_validate_link_af(const struct net_device *dev,
1411 const struct nlattr *nla)
Thomas Graf9f0f7272010-11-16 04:32:48 +00001412{
Thomas Graf9f0f7272010-11-16 04:32:48 +00001413 struct nlattr *a, *tb[IFLA_INET_MAX+1];
1414 int err, rem;
1415
Eric Dumazetf7fce742010-12-01 06:03:06 +00001416 if (dev && !__in_dev_get_rtnl(dev))
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001417 return -EAFNOSUPPORT;
Thomas Graf9f0f7272010-11-16 04:32:48 +00001418
1419 err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
1420 if (err < 0)
1421 return err;
1422
1423 if (tb[IFLA_INET_CONF]) {
1424 nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
1425 int cfgid = nla_type(a);
1426
1427 if (nla_len(a) < 4)
1428 return -EINVAL;
1429
1430 if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
1431 return -EINVAL;
1432 }
1433 }
1434
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001435 return 0;
1436}
1437
1438static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1439{
Eric Dumazetf7fce742010-12-01 06:03:06 +00001440 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001441 struct nlattr *a, *tb[IFLA_INET_MAX+1];
1442 int rem;
1443
1444 if (!in_dev)
1445 return -EAFNOSUPPORT;
1446
1447 if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1448 BUG();
1449
Thomas Graf9f0f7272010-11-16 04:32:48 +00001450 if (tb[IFLA_INET_CONF]) {
1451 nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
1452 ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
1453 }
1454
1455 return 0;
1456}
1457
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458#ifdef CONFIG_SYSCTL
1459
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001460static void devinet_copy_dflt_conf(struct net *net, int i)
Herbert Xu31be3082007-06-04 23:35:37 -07001461{
1462 struct net_device *dev;
1463
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001464 rcu_read_lock();
1465 for_each_netdev_rcu(net, dev) {
Herbert Xu31be3082007-06-04 23:35:37 -07001466 struct in_device *in_dev;
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001467
Herbert Xu31be3082007-06-04 23:35:37 -07001468 in_dev = __in_dev_get_rcu(dev);
1469 if (in_dev && !test_bit(i, in_dev->cnf.state))
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001470 in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
Herbert Xu31be3082007-06-04 23:35:37 -07001471 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001472 rcu_read_unlock();
Herbert Xu31be3082007-06-04 23:35:37 -07001473}
1474
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001475/* called with RTNL locked */
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001476static void inet_forward_change(struct net *net)
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001477{
1478 struct net_device *dev;
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001479 int on = IPV4_DEVCONF_ALL(net, FORWARDING);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001480
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001481 IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001482 IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001483
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001484 for_each_netdev(net, dev) {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001485 struct in_device *in_dev;
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001486 if (on)
1487 dev_disable_lro(dev);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001488 rcu_read_lock();
1489 in_dev = __in_dev_get_rcu(dev);
1490 if (in_dev)
1491 IN_DEV_CONF_SET(in_dev, FORWARDING, on);
1492 rcu_read_unlock();
1493 }
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001494}
1495
Herbert Xu31be3082007-06-04 23:35:37 -07001496static int devinet_conf_proc(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001497 void __user *buffer,
Herbert Xu31be3082007-06-04 23:35:37 -07001498 size_t *lenp, loff_t *ppos)
1499{
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001500 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Herbert Xu31be3082007-06-04 23:35:37 -07001501
1502 if (write) {
1503 struct ipv4_devconf *cnf = ctl->extra1;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001504 struct net *net = ctl->extra2;
Herbert Xu31be3082007-06-04 23:35:37 -07001505 int i = (int *)ctl->data - cnf->data;
1506
1507 set_bit(i, cnf->state);
1508
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001509 if (cnf == net->ipv4.devconf_dflt)
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001510 devinet_copy_dflt_conf(net, i);
Herbert Xu31be3082007-06-04 23:35:37 -07001511 }
1512
1513 return ret;
1514}
1515
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516static int devinet_sysctl_forward(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001517 void __user *buffer,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 size_t *lenp, loff_t *ppos)
1519{
1520 int *valp = ctl->data;
1521 int val = *valp;
Eric W. Biederman88af1822010-02-19 13:22:59 +00001522 loff_t pos = *ppos;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001523 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524
1525 if (write && *valp != val) {
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001526 struct net *net = ctl->extra2;
1527
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001528 if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
Eric W. Biederman88af1822010-02-19 13:22:59 +00001529 if (!rtnl_trylock()) {
1530 /* Restore the original values before restarting */
1531 *valp = val;
1532 *ppos = pos;
Eric W. Biederman9b8adb52009-05-13 16:59:21 +00001533 return restart_syscall();
Eric W. Biederman88af1822010-02-19 13:22:59 +00001534 }
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001535 if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1536 inet_forward_change(net);
1537 } else if (*valp) {
1538 struct ipv4_devconf *cnf = ctl->extra1;
1539 struct in_device *idev =
1540 container_of(cnf, struct in_device, cnf);
1541 dev_disable_lro(idev->dev);
1542 }
1543 rtnl_unlock();
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001544 rt_cache_flush(net, 0);
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001545 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 }
1547
1548 return ret;
1549}
1550
David S. Miller323e1262010-12-12 21:55:08 -08001551static int ipv4_doint_and_flush(ctl_table *ctl, int write,
1552 void __user *buffer,
1553 size_t *lenp, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554{
1555 int *valp = ctl->data;
1556 int val = *valp;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001557 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001558 struct net *net = ctl->extra2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
1560 if (write && *valp != val)
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001561 rt_cache_flush(net, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001562
1563 return ret;
1564}
1565
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001566#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
Herbert Xu42f811b2007-06-04 23:34:44 -07001567 { \
Herbert Xu42f811b2007-06-04 23:34:44 -07001568 .procname = name, \
1569 .data = ipv4_devconf.data + \
Eric W. Biederman02291682010-02-14 03:25:51 +00001570 IPV4_DEVCONF_ ## attr - 1, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001571 .maxlen = sizeof(int), \
1572 .mode = mval, \
1573 .proc_handler = proc, \
Herbert Xu31be3082007-06-04 23:35:37 -07001574 .extra1 = &ipv4_devconf, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001575 }
1576
1577#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001578 DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001579
1580#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001581 DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001582
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001583#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1584 DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001585
1586#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001587 DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
Herbert Xu42f811b2007-06-04 23:34:44 -07001588
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589static struct devinet_sysctl_table {
1590 struct ctl_table_header *sysctl_header;
Eric W. Biederman02291682010-02-14 03:25:51 +00001591 struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001592 char *dev_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593} devinet_sysctl = {
1594 .devinet_vars = {
Herbert Xu42f811b2007-06-04 23:34:44 -07001595 DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001596 devinet_sysctl_forward),
Herbert Xu42f811b2007-06-04 23:34:44 -07001597 DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
1598
1599 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
1600 DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
1601 DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
1602 DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
1603 DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
1604 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
1605 "accept_source_route"),
Patrick McHardy8153a102009-12-03 01:25:58 +00001606 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
Jamal Hadi Salim28f6aee2009-12-25 17:30:22 -08001607 DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001608 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
1609 DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
1610 DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
1611 DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
1612 DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
1613 DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
1614 DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
1615 DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
1616 DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001617 DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
Jesper Dangaard Brouer65324142010-01-05 05:50:47 +00001618 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001619
1620 DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
1621 DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
1622 DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
1623 "force_igmp_version"),
1624 DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
1625 "promote_secondaries"),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627};
1628
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001629static int __devinet_sysctl_register(struct net *net, char *dev_name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001630 struct ipv4_devconf *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631{
1632 int i;
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001633 struct devinet_sysctl_table *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001635#define DEVINET_CTL_PATH_DEV 3
1636
1637 struct ctl_path devinet_ctl_path[] = {
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001638 { .procname = "net", },
1639 { .procname = "ipv4", },
1640 { .procname = "conf", },
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001641 { /* to be set */ },
1642 { },
1643 };
1644
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001645 t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646 if (!t)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001647 goto out;
1648
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649 for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
1650 t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
Herbert Xu31be3082007-06-04 23:35:37 -07001651 t->devinet_vars[i].extra1 = p;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001652 t->devinet_vars[i].extra2 = net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653 }
1654
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001655 /*
1656 * Make a copy of dev_name, because '.procname' is regarded as const
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 * by sysctl and we wouldn't want anyone to change it under our feet
1658 * (see SIOCSIFNAME).
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001659 */
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001660 t->dev_name = kstrdup(dev_name, GFP_KERNEL);
1661 if (!t->dev_name)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001662 goto free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001664 devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001666 t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path,
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001667 t->devinet_vars);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 if (!t->sysctl_header)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001669 goto free_procname;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670
1671 p->sysctl = t;
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001672 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001674free_procname:
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001675 kfree(t->dev_name);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001676free:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 kfree(t);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001678out:
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001679 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680}
1681
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001682static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
1683{
1684 struct devinet_sysctl_table *t = cnf->sysctl;
1685
1686 if (t == NULL)
1687 return;
1688
1689 cnf->sysctl = NULL;
Lucian Adrian Grijincuff538812011-05-01 01:44:01 +00001690 unregister_net_sysctl_table(t->sysctl_header);
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001691 kfree(t->dev_name);
1692 kfree(t);
1693}
1694
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001695static void devinet_sysctl_register(struct in_device *idev)
1696{
Eric W. Biederman54716e32010-02-14 03:27:03 +00001697 neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001698 __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001699 &idev->cnf);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001700}
1701
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001702static void devinet_sysctl_unregister(struct in_device *idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001703{
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001704 __devinet_sysctl_unregister(&idev->cnf);
1705 neigh_sysctl_unregister(idev->arp_parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001708static struct ctl_table ctl_forward_entry[] = {
1709 {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001710 .procname = "ip_forward",
1711 .data = &ipv4_devconf.data[
Eric W. Biederman02291682010-02-14 03:25:51 +00001712 IPV4_DEVCONF_FORWARDING - 1],
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001713 .maxlen = sizeof(int),
1714 .mode = 0644,
1715 .proc_handler = devinet_sysctl_forward,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001716 .extra1 = &ipv4_devconf,
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001717 .extra2 = &init_net,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001718 },
1719 { },
1720};
1721
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001722static __net_initdata struct ctl_path net_ipv4_path[] = {
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001723 { .procname = "net", },
1724 { .procname = "ipv4", },
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001725 { },
1726};
Eric Dumazet2a75de02008-01-05 23:08:49 -08001727#endif
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001728
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001729static __net_init int devinet_init_net(struct net *net)
1730{
1731 int err;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001732 struct ipv4_devconf *all, *dflt;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001733#ifdef CONFIG_SYSCTL
1734 struct ctl_table *tbl = ctl_forward_entry;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001735 struct ctl_table_header *forw_hdr;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001736#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001737
1738 err = -ENOMEM;
1739 all = &ipv4_devconf;
1740 dflt = &ipv4_devconf_dflt;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001741
Octavian Purdila09ad9bc2009-11-25 15:14:13 -08001742 if (!net_eq(net, &init_net)) {
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001743 all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1744 if (all == NULL)
1745 goto err_alloc_all;
1746
1747 dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1748 if (dflt == NULL)
1749 goto err_alloc_dflt;
1750
Eric Dumazet2a75de02008-01-05 23:08:49 -08001751#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001752 tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1753 if (tbl == NULL)
1754 goto err_alloc_ctl;
1755
Eric W. Biederman02291682010-02-14 03:25:51 +00001756 tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001757 tbl[0].extra1 = all;
1758 tbl[0].extra2 = net;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001759#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001760 }
1761
1762#ifdef CONFIG_SYSCTL
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001763 err = __devinet_sysctl_register(net, "all", all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001764 if (err < 0)
1765 goto err_reg_all;
1766
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001767 err = __devinet_sysctl_register(net, "default", dflt);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001768 if (err < 0)
1769 goto err_reg_dflt;
1770
1771 err = -ENOMEM;
1772 forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
1773 if (forw_hdr == NULL)
1774 goto err_reg_ctl;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001775 net->ipv4.forw_hdr = forw_hdr;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001776#endif
1777
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001778 net->ipv4.devconf_all = all;
1779 net->ipv4.devconf_dflt = dflt;
1780 return 0;
1781
1782#ifdef CONFIG_SYSCTL
1783err_reg_ctl:
1784 __devinet_sysctl_unregister(dflt);
1785err_reg_dflt:
1786 __devinet_sysctl_unregister(all);
1787err_reg_all:
1788 if (tbl != ctl_forward_entry)
1789 kfree(tbl);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001790err_alloc_ctl:
Eric Dumazet2a75de02008-01-05 23:08:49 -08001791#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001792 if (dflt != &ipv4_devconf_dflt)
1793 kfree(dflt);
1794err_alloc_dflt:
1795 if (all != &ipv4_devconf)
1796 kfree(all);
1797err_alloc_all:
1798 return err;
1799}
1800
1801static __net_exit void devinet_exit_net(struct net *net)
1802{
Eric Dumazet2a75de02008-01-05 23:08:49 -08001803#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001804 struct ctl_table *tbl;
1805
1806 tbl = net->ipv4.forw_hdr->ctl_table_arg;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001807 unregister_net_sysctl_table(net->ipv4.forw_hdr);
1808 __devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1809 __devinet_sysctl_unregister(net->ipv4.devconf_all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001810 kfree(tbl);
Eric Dumazet2a75de02008-01-05 23:08:49 -08001811#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001812 kfree(net->ipv4.devconf_dflt);
1813 kfree(net->ipv4.devconf_all);
1814}
1815
1816static __net_initdata struct pernet_operations devinet_ops = {
1817 .init = devinet_init_net,
1818 .exit = devinet_exit_net,
1819};
1820
Thomas Graf9f0f7272010-11-16 04:32:48 +00001821static struct rtnl_af_ops inet_af_ops = {
1822 .family = AF_INET,
1823 .fill_link_af = inet_fill_link_af,
1824 .get_link_af_size = inet_get_link_af_size,
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001825 .validate_link_af = inet_validate_link_af,
1826 .set_link_af = inet_set_link_af,
Thomas Graf9f0f7272010-11-16 04:32:48 +00001827};
1828
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829void __init devinet_init(void)
1830{
David S. Millerfd23c3b2011-02-18 12:42:28 -08001831 int i;
1832
1833 for (i = 0; i < IN4_ADDR_HSIZE; i++)
1834 INIT_HLIST_HEAD(&inet_addr_lst[i]);
1835
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001836 register_pernet_subsys(&devinet_ops);
1837
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838 register_gifconf(PF_INET, inet_gifconf);
1839 register_netdevice_notifier(&ip_netdev_notifier);
Thomas Graf63f34442007-03-22 11:55:17 -07001840
Thomas Graf9f0f7272010-11-16 04:32:48 +00001841 rtnl_af_register(&inet_af_ops);
1842
Thomas Graf63f34442007-03-22 11:55:17 -07001843 rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
1844 rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
1845 rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846}
1847