blob: d5a4553bebc3f731cec83067c7f2195ffc75c517 [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>
62#include <net/route.h>
63#include <net/ip_fib.h>
Thomas Graf63f34442007-03-22 11:55:17 -070064#include <net/rtnetlink.h>
Pavel Emelyanov752d14d2007-12-16 13:31:47 -080065#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Adrian Bunk0027ba82008-01-31 17:17:31 -080067static struct ipv4_devconf ipv4_devconf = {
Herbert Xu42f811b2007-06-04 23:34:44 -070068 .data = {
Eric W. Biederman02291682010-02-14 03:25:51 +000069 [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
70 [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
71 [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
72 [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
Herbert Xu42f811b2007-06-04 23:34:44 -070073 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070074};
75
76static struct ipv4_devconf ipv4_devconf_dflt = {
Herbert Xu42f811b2007-06-04 23:34:44 -070077 .data = {
Eric W. Biederman02291682010-02-14 03:25:51 +000078 [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
79 [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
80 [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
81 [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
82 [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
Herbert Xu42f811b2007-06-04 23:34:44 -070083 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070084};
85
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -080086#define IPV4_DEVCONF_DFLT(net, attr) \
87 IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
Herbert Xu42f811b2007-06-04 23:34:44 -070088
Patrick McHardyef7c79e2007-06-05 12:38:30 -070089static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
Thomas Graf5c753972006-08-04 23:03:53 -070090 [IFA_LOCAL] = { .type = NLA_U32 },
91 [IFA_ADDRESS] = { .type = NLA_U32 },
92 [IFA_BROADCAST] = { .type = NLA_U32 },
Thomas Graf5176f912006-08-26 20:13:18 -070093 [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
Thomas Graf5c753972006-08-04 23:03:53 -070094};
95
David S. Millerfd23c3b2011-02-18 12:42:28 -080096/* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE
97 * value. So if you change this define, make appropriate changes to
98 * inet_addr_hash as well.
99 */
100#define IN4_ADDR_HSIZE 256
101static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
102static DEFINE_SPINLOCK(inet_addr_hash_lock);
103
104static inline unsigned int inet_addr_hash(struct net *net, __be32 addr)
105{
106 u32 val = (__force u32) addr ^ hash_ptr(net, 8);
107
108 return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) &
109 (IN4_ADDR_HSIZE - 1));
110}
111
112static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
113{
David S. Millere0660082011-03-03 11:24:19 -0800114 unsigned int hash = inet_addr_hash(net, ifa->ifa_local);
David S. Millerfd23c3b2011-02-18 12:42:28 -0800115
116 spin_lock(&inet_addr_hash_lock);
117 hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
118 spin_unlock(&inet_addr_hash_lock);
119}
120
121static void inet_hash_remove(struct in_ifaddr *ifa)
122{
123 spin_lock(&inet_addr_hash_lock);
124 hlist_del_init_rcu(&ifa->hash);
125 spin_unlock(&inet_addr_hash_lock);
126}
127
David S. Miller9435eb12011-02-18 12:43:09 -0800128/**
129 * __ip_dev_find - find the first device with a given source address.
130 * @net: the net namespace
131 * @addr: the source address
132 * @devref: if true, take a reference on the found device
133 *
134 * If a caller uses devref=false, it should be protected by RCU, or RTNL
135 */
136struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
137{
138 unsigned int hash = inet_addr_hash(net, addr);
139 struct net_device *result = NULL;
140 struct in_ifaddr *ifa;
141 struct hlist_node *node;
142
143 rcu_read_lock();
144 hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) {
145 struct net_device *dev = ifa->ifa_dev->dev;
146
147 if (!net_eq(dev_net(dev), net))
148 continue;
David S. Millere0660082011-03-03 11:24:19 -0800149 if (ifa->ifa_local == addr) {
David S. Miller9435eb12011-02-18 12:43:09 -0800150 result = dev;
151 break;
152 }
153 }
154 if (result && devref)
155 dev_hold(result);
156 rcu_read_unlock();
157 return result;
158}
159EXPORT_SYMBOL(__ip_dev_find);
160
Thomas Grafd6062cb2006-08-15 00:33:59 -0700161static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162
Alan Sterne041c682006-03-27 01:16:30 -0800163static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
165 int destroy);
166#ifdef CONFIG_SYSCTL
Pavel Emelyanov66f27a52007-12-02 00:55:54 +1100167static void devinet_sysctl_register(struct in_device *idev);
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800168static void devinet_sysctl_unregister(struct in_device *idev);
169#else
170static inline void devinet_sysctl_register(struct in_device *idev)
171{
172}
173static inline void devinet_sysctl_unregister(struct in_device *idev)
174{
175}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176#endif
177
178/* Locks all the inet devices. */
179
180static struct in_ifaddr *inet_alloc_ifa(void)
181{
Alexey Dobriyan93adcc82008-10-28 13:25:09 -0700182 return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183}
184
185static void inet_rcu_free_ifa(struct rcu_head *head)
186{
187 struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
188 if (ifa->ifa_dev)
189 in_dev_put(ifa->ifa_dev);
190 kfree(ifa);
191}
192
193static inline void inet_free_ifa(struct in_ifaddr *ifa)
194{
195 call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
196}
197
198void in_dev_finish_destroy(struct in_device *idev)
199{
200 struct net_device *dev = idev->dev;
201
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700202 WARN_ON(idev->ifa_list);
203 WARN_ON(idev->mc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204#ifdef NET_REFCNT_DEBUG
205 printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
206 idev, dev ? dev->name : "NIL");
207#endif
208 dev_put(dev);
209 if (!idev->dead)
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800210 pr_err("Freeing alive in_device %p\n", idev);
211 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 kfree(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800214EXPORT_SYMBOL(in_dev_finish_destroy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215
Herbert Xu71e27da2007-06-04 23:36:06 -0700216static struct in_device *inetdev_init(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217{
218 struct in_device *in_dev;
219
220 ASSERT_RTNL();
221
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700222 in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 if (!in_dev)
224 goto out;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900225 memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -0800226 sizeof(in_dev->cnf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 in_dev->cnf.sysctl = NULL;
228 in_dev->dev = dev;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800229 in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
230 if (!in_dev->arp_parms)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 goto out_kfree;
Ben Hutchings0187bdf2008-06-19 16:15:47 -0700232 if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
233 dev_disable_lro(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 /* Reference in_dev->dev */
235 dev_hold(dev);
David L Stevens30c4cf52007-01-04 12:31:14 -0800236 /* Account for reference dev->ip_ptr (below) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 in_dev_hold(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
Pavel Emelyanov66f27a52007-12-02 00:55:54 +1100239 devinet_sysctl_register(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 ip_mc_init_dev(in_dev);
241 if (dev->flags & IFF_UP)
242 ip_mc_up(in_dev);
Jarek Poplawski483479e2007-01-09 14:38:31 -0800243
David L Stevens30c4cf52007-01-04 12:31:14 -0800244 /* we can receive as soon as ip_ptr is set -- do this last */
245 rcu_assign_pointer(dev->ip_ptr, in_dev);
Jarek Poplawski483479e2007-01-09 14:38:31 -0800246out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 return in_dev;
248out_kfree:
249 kfree(in_dev);
250 in_dev = NULL;
251 goto out;
252}
253
254static void in_dev_rcu_put(struct rcu_head *head)
255{
256 struct in_device *idev = container_of(head, struct in_device, rcu_head);
257 in_dev_put(idev);
258}
259
260static void inetdev_destroy(struct in_device *in_dev)
261{
262 struct in_ifaddr *ifa;
263 struct net_device *dev;
264
265 ASSERT_RTNL();
266
267 dev = in_dev->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
269 in_dev->dead = 1;
270
271 ip_mc_destroy_dev(in_dev);
272
273 while ((ifa = in_dev->ifa_list) != NULL) {
274 inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
275 inet_free_ifa(ifa);
276 }
277
Eric Dumazet95ae6b22010-09-15 04:04:31 +0000278 rcu_assign_pointer(dev->ip_ptr, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800280 devinet_sysctl_unregister(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 neigh_parms_release(&arp_tbl, in_dev->arp_parms);
282 arp_ifdown(dev);
283
284 call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
285}
286
Al Viroff428d72006-09-26 22:13:35 -0700287int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288{
289 rcu_read_lock();
290 for_primary_ifa(in_dev) {
291 if (inet_ifa_match(a, ifa)) {
292 if (!b || inet_ifa_match(b, ifa)) {
293 rcu_read_unlock();
294 return 1;
295 }
296 }
297 } endfor_ifa(in_dev);
298 rcu_read_unlock();
299 return 0;
300}
301
Thomas Grafd6062cb2006-08-15 00:33:59 -0700302static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
303 int destroy, struct nlmsghdr *nlh, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
Harald Welte8f937c62005-05-29 20:23:46 -0700305 struct in_ifaddr *promote = NULL;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800306 struct in_ifaddr *ifa, *ifa1 = *ifap;
307 struct in_ifaddr *last_prim = in_dev->ifa_list;
308 struct in_ifaddr *prev_prom = NULL;
309 int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
311 ASSERT_RTNL();
312
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900313 /* 1. Deleting primary ifaddr forces deletion all secondaries
Harald Welte8f937c62005-05-29 20:23:46 -0700314 * unless alias promotion is set
315 **/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317 if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 struct in_ifaddr **ifap1 = &ifa1->ifa_next;
319
320 while ((ifa = *ifap1) != NULL) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900321 if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800322 ifa1->ifa_scope <= ifa->ifa_scope)
323 last_prim = ifa;
324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
326 ifa1->ifa_mask != ifa->ifa_mask ||
327 !inet_ifa_match(ifa1->ifa_address, ifa)) {
328 ifap1 = &ifa->ifa_next;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800329 prev_prom = ifa;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 continue;
331 }
332
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800333 if (!do_promote) {
David S. Millerfd23c3b2011-02-18 12:42:28 -0800334 inet_hash_remove(ifa);
Harald Welte8f937c62005-05-29 20:23:46 -0700335 *ifap1 = ifa->ifa_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
Thomas Grafd6062cb2006-08-15 00:33:59 -0700337 rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800338 blocking_notifier_call_chain(&inetaddr_chain,
339 NETDEV_DOWN, ifa);
Harald Welte8f937c62005-05-29 20:23:46 -0700340 inet_free_ifa(ifa);
341 } else {
342 promote = ifa;
343 break;
344 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 }
346 }
347
Julian Anastasov2d230e22011-03-19 12:13:52 +0000348 /* On promotion all secondaries from subnet are changing
349 * the primary IP, we must remove all their routes silently
350 * and later to add them back with new prefsrc. Do this
351 * while all addresses are on the device list.
352 */
353 for (ifa = promote; ifa; ifa = ifa->ifa_next) {
354 if (ifa1->ifa_mask == ifa->ifa_mask &&
355 inet_ifa_match(ifa1->ifa_address, ifa))
356 fib_del_ifaddr(ifa, ifa1);
357 }
358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 /* 2. Unlink it */
360
361 *ifap = ifa1->ifa_next;
David S. Millerfd23c3b2011-02-18 12:42:28 -0800362 inet_hash_remove(ifa1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363
364 /* 3. Announce address deletion */
365
366 /* Send message first, then call notifier.
367 At first sight, FIB update triggered by notifier
368 will refer to already deleted ifaddr, that could confuse
369 netlink listeners. It is not true: look, gated sees
370 that route deleted and if it still thinks that ifaddr
371 is valid, it will try to restore deleted routes... Grr.
372 So that, this order is correct.
373 */
Thomas Grafd6062cb2006-08-15 00:33:59 -0700374 rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800375 blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800376
377 if (promote) {
Julian Anastasov04024b92011-03-19 12:13:54 +0000378 struct in_ifaddr *next_sec = promote->ifa_next;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800379
380 if (prev_prom) {
381 prev_prom->ifa_next = promote->ifa_next;
382 promote->ifa_next = last_prim->ifa_next;
383 last_prim->ifa_next = promote;
384 }
385
386 promote->ifa_flags &= ~IFA_F_SECONDARY;
Thomas Grafd6062cb2006-08-15 00:33:59 -0700387 rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800388 blocking_notifier_call_chain(&inetaddr_chain,
389 NETDEV_UP, promote);
Julian Anastasov04024b92011-03-19 12:13:54 +0000390 for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800391 if (ifa1->ifa_mask != ifa->ifa_mask ||
392 !inet_ifa_match(ifa1->ifa_address, ifa))
393 continue;
394 fib_add_ifaddr(ifa);
395 }
396
397 }
Herbert Xu63630972007-06-07 18:35:38 -0700398 if (destroy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399 inet_free_ifa(ifa1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400}
401
Thomas Grafd6062cb2006-08-15 00:33:59 -0700402static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
403 int destroy)
404{
405 __inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
406}
407
408static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
409 u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410{
411 struct in_device *in_dev = ifa->ifa_dev;
412 struct in_ifaddr *ifa1, **ifap, **last_primary;
413
414 ASSERT_RTNL();
415
416 if (!ifa->ifa_local) {
417 inet_free_ifa(ifa);
418 return 0;
419 }
420
421 ifa->ifa_flags &= ~IFA_F_SECONDARY;
422 last_primary = &in_dev->ifa_list;
423
424 for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
425 ifap = &ifa1->ifa_next) {
426 if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
427 ifa->ifa_scope <= ifa1->ifa_scope)
428 last_primary = &ifa1->ifa_next;
429 if (ifa1->ifa_mask == ifa->ifa_mask &&
430 inet_ifa_match(ifa1->ifa_address, ifa)) {
431 if (ifa1->ifa_local == ifa->ifa_local) {
432 inet_free_ifa(ifa);
433 return -EEXIST;
434 }
435 if (ifa1->ifa_scope != ifa->ifa_scope) {
436 inet_free_ifa(ifa);
437 return -EINVAL;
438 }
439 ifa->ifa_flags |= IFA_F_SECONDARY;
440 }
441 }
442
443 if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
444 net_srandom(ifa->ifa_local);
445 ifap = last_primary;
446 }
447
448 ifa->ifa_next = *ifap;
449 *ifap = ifa;
450
David S. Millerfd23c3b2011-02-18 12:42:28 -0800451 inet_hash_insert(dev_net(in_dev->dev), ifa);
452
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 /* Send message first, then call notifier.
454 Notifier will trigger FIB update, so that
455 listeners of netlink will know about new ifaddr */
Thomas Grafd6062cb2006-08-15 00:33:59 -0700456 rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800457 blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
459 return 0;
460}
461
Thomas Grafd6062cb2006-08-15 00:33:59 -0700462static int inet_insert_ifa(struct in_ifaddr *ifa)
463{
464 return __inet_insert_ifa(ifa, NULL, 0);
465}
466
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
468{
Herbert Xue5ed6392005-10-03 14:35:55 -0700469 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
471 ASSERT_RTNL();
472
473 if (!in_dev) {
Herbert Xu71e27da2007-06-04 23:36:06 -0700474 inet_free_ifa(ifa);
475 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476 }
Herbert Xu71e27da2007-06-04 23:36:06 -0700477 ipv4_devconf_setall(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 if (ifa->ifa_dev != in_dev) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700479 WARN_ON(ifa->ifa_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 in_dev_hold(in_dev);
481 ifa->ifa_dev = in_dev;
482 }
Joe Perchesf97c1e02007-12-16 13:45:43 -0800483 if (ipv4_is_loopback(ifa->ifa_local))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 ifa->ifa_scope = RT_SCOPE_HOST;
485 return inet_insert_ifa(ifa);
486}
487
Eric Dumazet8723e1b2010-10-19 00:39:26 +0000488/* Caller must hold RCU or RTNL :
489 * We dont take a reference on found in_device
490 */
Denis V. Lunev7fee0ca2008-01-21 17:32:38 -0800491struct in_device *inetdev_by_index(struct net *net, int ifindex)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
493 struct net_device *dev;
494 struct in_device *in_dev = NULL;
Eric Dumazetc148fc22009-11-01 19:23:04 +0000495
496 rcu_read_lock();
497 dev = dev_get_by_index_rcu(net, ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498 if (dev)
Eric Dumazet8723e1b2010-10-19 00:39:26 +0000499 in_dev = rcu_dereference_rtnl(dev->ip_ptr);
Eric Dumazetc148fc22009-11-01 19:23:04 +0000500 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 return in_dev;
502}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800503EXPORT_SYMBOL(inetdev_by_index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
505/* Called only from RTNL semaphored context. No locks. */
506
Al Viro60cad5d2006-09-26 22:17:09 -0700507struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
508 __be32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
510 ASSERT_RTNL();
511
512 for_primary_ifa(in_dev) {
513 if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
514 return ifa;
515 } endfor_ifa(in_dev);
516 return NULL;
517}
518
519static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
520{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900521 struct net *net = sock_net(skb->sk);
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700522 struct nlattr *tb[IFA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 struct in_device *in_dev;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700524 struct ifaddrmsg *ifm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 struct in_ifaddr *ifa, **ifap;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700526 int err = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527
528 ASSERT_RTNL();
529
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700530 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
531 if (err < 0)
532 goto errout;
533
534 ifm = nlmsg_data(nlh);
Denis V. Lunev7fee0ca2008-01-21 17:32:38 -0800535 in_dev = inetdev_by_index(net, ifm->ifa_index);
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700536 if (in_dev == NULL) {
537 err = -ENODEV;
538 goto errout;
539 }
540
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
542 ifap = &ifa->ifa_next) {
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700543 if (tb[IFA_LOCAL] &&
Al Viroa7a628c2006-09-26 22:16:43 -0700544 ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 continue;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700546
547 if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
548 continue;
549
550 if (tb[IFA_ADDRESS] &&
551 (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
Al Viroa7a628c2006-09-26 22:16:43 -0700552 !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700553 continue;
554
Thomas Grafd6062cb2006-08-15 00:33:59 -0700555 __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 return 0;
557 }
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700558
559 err = -EADDRNOTAVAIL;
560errout:
561 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562}
563
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800564static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565{
Thomas Graf5c753972006-08-04 23:03:53 -0700566 struct nlattr *tb[IFA_MAX+1];
567 struct in_ifaddr *ifa;
568 struct ifaddrmsg *ifm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 struct net_device *dev;
570 struct in_device *in_dev;
Denis V. Lunev7b218572008-01-31 18:47:00 -0800571 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Thomas Graf5c753972006-08-04 23:03:53 -0700573 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
574 if (err < 0)
575 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Thomas Graf5c753972006-08-04 23:03:53 -0700577 ifm = nlmsg_data(nlh);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800578 err = -EINVAL;
579 if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700580 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800582 dev = __dev_get_by_index(net, ifm->ifa_index);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800583 err = -ENODEV;
584 if (dev == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700585 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
Thomas Graf5c753972006-08-04 23:03:53 -0700587 in_dev = __in_dev_get_rtnl(dev);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800588 err = -ENOBUFS;
589 if (in_dev == NULL)
Herbert Xu71e27da2007-06-04 23:36:06 -0700590 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Thomas Graf5c753972006-08-04 23:03:53 -0700592 ifa = inet_alloc_ifa();
Denis V. Lunev7b218572008-01-31 18:47:00 -0800593 if (ifa == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700594 /*
595 * A potential indev allocation can be left alive, it stays
596 * assigned to its device and is destroy with it.
597 */
Thomas Graf5c753972006-08-04 23:03:53 -0700598 goto errout;
Thomas Graf5c753972006-08-04 23:03:53 -0700599
Pavel Emelyanova4e65d32007-12-07 23:55:43 -0800600 ipv4_devconf_setall(in_dev);
Thomas Graf5c753972006-08-04 23:03:53 -0700601 in_dev_hold(in_dev);
602
603 if (tb[IFA_ADDRESS] == NULL)
604 tb[IFA_ADDRESS] = tb[IFA_LOCAL];
605
David S. Millerfd23c3b2011-02-18 12:42:28 -0800606 INIT_HLIST_NODE(&ifa->hash);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 ifa->ifa_prefixlen = ifm->ifa_prefixlen;
608 ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 ifa->ifa_flags = ifm->ifa_flags;
610 ifa->ifa_scope = ifm->ifa_scope;
Thomas Graf5c753972006-08-04 23:03:53 -0700611 ifa->ifa_dev = in_dev;
612
Al Viroa7a628c2006-09-26 22:16:43 -0700613 ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
614 ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
Thomas Graf5c753972006-08-04 23:03:53 -0700615
616 if (tb[IFA_BROADCAST])
Al Viroa7a628c2006-09-26 22:16:43 -0700617 ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
Thomas Graf5c753972006-08-04 23:03:53 -0700618
Thomas Graf5c753972006-08-04 23:03:53 -0700619 if (tb[IFA_LABEL])
620 nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 else
622 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
623
Thomas Graf5c753972006-08-04 23:03:53 -0700624 return ifa;
625
626errout:
627 return ERR_PTR(err);
628}
629
630static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
631{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900632 struct net *net = sock_net(skb->sk);
Thomas Graf5c753972006-08-04 23:03:53 -0700633 struct in_ifaddr *ifa;
634
635 ASSERT_RTNL();
636
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800637 ifa = rtm_to_ifaddr(net, nlh);
Thomas Graf5c753972006-08-04 23:03:53 -0700638 if (IS_ERR(ifa))
639 return PTR_ERR(ifa);
640
Thomas Grafd6062cb2006-08-15 00:33:59 -0700641 return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642}
643
644/*
645 * Determine a default network mask, based on the IP address.
646 */
647
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800648static inline int inet_abc_len(__be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649{
650 int rc = -1; /* Something else, probably a multicast. */
651
Joe Perchesf97c1e02007-12-16 13:45:43 -0800652 if (ipv4_is_zeronet(addr))
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900653 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 else {
Al Viro714e85b2006-11-14 20:51:49 -0800655 __u32 haddr = ntohl(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
Al Viro714e85b2006-11-14 20:51:49 -0800657 if (IN_CLASSA(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 rc = 8;
Al Viro714e85b2006-11-14 20:51:49 -0800659 else if (IN_CLASSB(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 rc = 16;
Al Viro714e85b2006-11-14 20:51:49 -0800661 else if (IN_CLASSC(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 rc = 24;
663 }
664
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900665 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666}
667
668
Denis V. Luneve5b13cb2008-02-28 20:51:43 -0800669int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670{
671 struct ifreq ifr;
672 struct sockaddr_in sin_orig;
673 struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
674 struct in_device *in_dev;
675 struct in_ifaddr **ifap = NULL;
676 struct in_ifaddr *ifa = NULL;
677 struct net_device *dev;
678 char *colon;
679 int ret = -EFAULT;
680 int tryaddrmatch = 0;
681
682 /*
683 * Fetch the caller's info block into kernel space
684 */
685
686 if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
687 goto out;
688 ifr.ifr_name[IFNAMSIZ - 1] = 0;
689
690 /* save original address for comparison */
691 memcpy(&sin_orig, sin, sizeof(*sin));
692
693 colon = strchr(ifr.ifr_name, ':');
694 if (colon)
695 *colon = 0;
696
Denis V. Luneve5b13cb2008-02-28 20:51:43 -0800697 dev_load(net, ifr.ifr_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Stephen Hemminger132adf52007-03-08 20:44:43 -0800699 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 case SIOCGIFADDR: /* Get interface address */
701 case SIOCGIFBRDADDR: /* Get the broadcast address */
702 case SIOCGIFDSTADDR: /* Get the destination address */
703 case SIOCGIFNETMASK: /* Get the netmask for the interface */
704 /* Note that these ioctls will not sleep,
705 so that we do not impose a lock.
706 One day we will be forced to put shlock here (I mean SMP)
707 */
708 tryaddrmatch = (sin_orig.sin_family == AF_INET);
709 memset(sin, 0, sizeof(*sin));
710 sin->sin_family = AF_INET;
711 break;
712
713 case SIOCSIFFLAGS:
714 ret = -EACCES;
715 if (!capable(CAP_NET_ADMIN))
716 goto out;
717 break;
718 case SIOCSIFADDR: /* Set interface address (and family) */
719 case SIOCSIFBRDADDR: /* Set the broadcast address */
720 case SIOCSIFDSTADDR: /* Set the destination address */
721 case SIOCSIFNETMASK: /* Set the netmask for the interface */
722 ret = -EACCES;
723 if (!capable(CAP_NET_ADMIN))
724 goto out;
725 ret = -EINVAL;
726 if (sin->sin_family != AF_INET)
727 goto out;
728 break;
729 default:
730 ret = -EINVAL;
731 goto out;
732 }
733
734 rtnl_lock();
735
736 ret = -ENODEV;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800737 dev = __dev_get_by_name(net, ifr.ifr_name);
738 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739 goto done;
740
741 if (colon)
742 *colon = ':';
743
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800744 in_dev = __in_dev_get_rtnl(dev);
745 if (in_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 if (tryaddrmatch) {
747 /* Matthias Andree */
748 /* compare label and address (4.4BSD style) */
749 /* note: we only do this for a limited set of ioctls
750 and only if the original address family was AF_INET.
751 This is checked above. */
752 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
753 ifap = &ifa->ifa_next) {
754 if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
755 sin_orig.sin_addr.s_addr ==
David S. Miller6c91afe2011-03-09 13:27:16 -0800756 ifa->ifa_local) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 break; /* found */
758 }
759 }
760 }
761 /* we didn't get a match, maybe the application is
762 4.3BSD-style and passed in junk so we fall back to
763 comparing just the label */
764 if (!ifa) {
765 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
766 ifap = &ifa->ifa_next)
767 if (!strcmp(ifr.ifr_name, ifa->ifa_label))
768 break;
769 }
770 }
771
772 ret = -EADDRNOTAVAIL;
773 if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
774 goto done;
775
Stephen Hemminger132adf52007-03-08 20:44:43 -0800776 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 case SIOCGIFADDR: /* Get interface address */
778 sin->sin_addr.s_addr = ifa->ifa_local;
779 goto rarok;
780
781 case SIOCGIFBRDADDR: /* Get the broadcast address */
782 sin->sin_addr.s_addr = ifa->ifa_broadcast;
783 goto rarok;
784
785 case SIOCGIFDSTADDR: /* Get the destination address */
786 sin->sin_addr.s_addr = ifa->ifa_address;
787 goto rarok;
788
789 case SIOCGIFNETMASK: /* Get the netmask for the interface */
790 sin->sin_addr.s_addr = ifa->ifa_mask;
791 goto rarok;
792
793 case SIOCSIFFLAGS:
794 if (colon) {
795 ret = -EADDRNOTAVAIL;
796 if (!ifa)
797 break;
798 ret = 0;
799 if (!(ifr.ifr_flags & IFF_UP))
800 inet_del_ifa(in_dev, ifap, 1);
801 break;
802 }
803 ret = dev_change_flags(dev, ifr.ifr_flags);
804 break;
805
806 case SIOCSIFADDR: /* Set interface address (and family) */
807 ret = -EINVAL;
808 if (inet_abc_len(sin->sin_addr.s_addr) < 0)
809 break;
810
811 if (!ifa) {
812 ret = -ENOBUFS;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800813 ifa = inet_alloc_ifa();
David S. Millerfd23c3b2011-02-18 12:42:28 -0800814 INIT_HLIST_NODE(&ifa->hash);
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800815 if (!ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 break;
817 if (colon)
818 memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
819 else
820 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
821 } else {
822 ret = 0;
823 if (ifa->ifa_local == sin->sin_addr.s_addr)
824 break;
825 inet_del_ifa(in_dev, ifap, 0);
826 ifa->ifa_broadcast = 0;
Bjorn Mork148f9722008-02-26 18:17:53 -0800827 ifa->ifa_scope = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 }
829
830 ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
831
832 if (!(dev->flags & IFF_POINTOPOINT)) {
833 ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
834 ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
835 if ((dev->flags & IFF_BROADCAST) &&
836 ifa->ifa_prefixlen < 31)
837 ifa->ifa_broadcast = ifa->ifa_address |
838 ~ifa->ifa_mask;
839 } else {
840 ifa->ifa_prefixlen = 32;
841 ifa->ifa_mask = inet_make_mask(32);
842 }
843 ret = inet_set_ifa(dev, ifa);
844 break;
845
846 case SIOCSIFBRDADDR: /* Set the broadcast address */
847 ret = 0;
848 if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
849 inet_del_ifa(in_dev, ifap, 0);
850 ifa->ifa_broadcast = sin->sin_addr.s_addr;
851 inet_insert_ifa(ifa);
852 }
853 break;
854
855 case SIOCSIFDSTADDR: /* Set the destination address */
856 ret = 0;
857 if (ifa->ifa_address == sin->sin_addr.s_addr)
858 break;
859 ret = -EINVAL;
860 if (inet_abc_len(sin->sin_addr.s_addr) < 0)
861 break;
862 ret = 0;
863 inet_del_ifa(in_dev, ifap, 0);
864 ifa->ifa_address = sin->sin_addr.s_addr;
865 inet_insert_ifa(ifa);
866 break;
867
868 case SIOCSIFNETMASK: /* Set the netmask for the interface */
869
870 /*
871 * The mask we set must be legal.
872 */
873 ret = -EINVAL;
874 if (bad_mask(sin->sin_addr.s_addr, 0))
875 break;
876 ret = 0;
877 if (ifa->ifa_mask != sin->sin_addr.s_addr) {
Al Viroa144ea42006-09-28 18:00:55 -0700878 __be32 old_mask = ifa->ifa_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 inet_del_ifa(in_dev, ifap, 0);
880 ifa->ifa_mask = sin->sin_addr.s_addr;
881 ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
882
883 /* See if current broadcast address matches
884 * with current netmask, then recalculate
885 * the broadcast address. Otherwise it's a
886 * funny address, so don't touch it since
887 * the user seems to know what (s)he's doing...
888 */
889 if ((dev->flags & IFF_BROADCAST) &&
890 (ifa->ifa_prefixlen < 31) &&
891 (ifa->ifa_broadcast ==
David Engeldcab5e12005-10-21 22:09:16 -0500892 (ifa->ifa_local|~old_mask))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 ifa->ifa_broadcast = (ifa->ifa_local |
894 ~sin->sin_addr.s_addr);
895 }
896 inet_insert_ifa(ifa);
897 }
898 break;
899 }
900done:
901 rtnl_unlock();
902out:
903 return ret;
904rarok:
905 rtnl_unlock();
906 ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
907 goto out;
908}
909
910static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
911{
Herbert Xue5ed6392005-10-03 14:35:55 -0700912 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 struct in_ifaddr *ifa;
914 struct ifreq ifr;
915 int done = 0;
916
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800917 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918 goto out;
919
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800920 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921 if (!buf) {
922 done += sizeof(ifr);
923 continue;
924 }
925 if (len < (int) sizeof(ifr))
926 break;
927 memset(&ifr, 0, sizeof(struct ifreq));
928 if (ifa->ifa_label)
929 strcpy(ifr.ifr_name, ifa->ifa_label);
930 else
931 strcpy(ifr.ifr_name, dev->name);
932
933 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
934 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
935 ifa->ifa_local;
936
937 if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
938 done = -EFAULT;
939 break;
940 }
941 buf += sizeof(struct ifreq);
942 len -= sizeof(struct ifreq);
943 done += sizeof(struct ifreq);
944 }
945out:
946 return done;
947}
948
Al Viroa61ced52006-09-26 21:27:54 -0700949__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950{
Al Viroa61ced52006-09-26 21:27:54 -0700951 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 struct in_device *in_dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900953 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
955 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -0700956 in_dev = __in_dev_get_rcu(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 if (!in_dev)
958 goto no_in_dev;
959
960 for_primary_ifa(in_dev) {
961 if (ifa->ifa_scope > scope)
962 continue;
963 if (!dst || inet_ifa_match(dst, ifa)) {
964 addr = ifa->ifa_local;
965 break;
966 }
967 if (!addr)
968 addr = ifa->ifa_local;
969 } endfor_ifa(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970
971 if (addr)
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800972 goto out_unlock;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800973no_in_dev:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974
975 /* Not loopback addresses on loopback should be preferred
976 in this case. It is importnat that lo is the first interface
977 in dev_base list.
978 */
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800979 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800980 in_dev = __in_dev_get_rcu(dev);
981 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 continue;
983
984 for_primary_ifa(in_dev) {
985 if (ifa->ifa_scope != RT_SCOPE_LINK &&
986 ifa->ifa_scope <= scope) {
987 addr = ifa->ifa_local;
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800988 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 }
990 } endfor_ifa(in_dev);
991 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800992out_unlock:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994 return addr;
995}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800996EXPORT_SYMBOL(inet_select_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
Al Viro60cad5d2006-09-26 22:17:09 -0700998static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
999 __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000{
1001 int same = 0;
Al Viroa144ea42006-09-28 18:00:55 -07001002 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003
1004 for_ifa(in_dev) {
1005 if (!addr &&
1006 (local == ifa->ifa_local || !local) &&
1007 ifa->ifa_scope <= scope) {
1008 addr = ifa->ifa_local;
1009 if (same)
1010 break;
1011 }
1012 if (!same) {
1013 same = (!local || inet_ifa_match(local, ifa)) &&
1014 (!dst || inet_ifa_match(dst, ifa));
1015 if (same && addr) {
1016 if (local || !dst)
1017 break;
1018 /* Is the selected addr into dst subnet? */
1019 if (inet_ifa_match(addr, ifa))
1020 break;
1021 /* No, then can we use new local src? */
1022 if (ifa->ifa_scope <= scope) {
1023 addr = ifa->ifa_local;
1024 break;
1025 }
1026 /* search for large dst subnet for addr */
1027 same = 0;
1028 }
1029 }
1030 } endfor_ifa(in_dev);
1031
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001032 return same ? addr : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033}
1034
1035/*
1036 * Confirm that local IP address exists using wildcards:
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001037 * - in_dev: only on this interface, 0=any interface
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 * - dst: only in the same subnet as dst, 0=any dst
1039 * - local: address, 0=autoselect the local address
1040 * - scope: maximum allowed scope value for the local address
1041 */
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001042__be32 inet_confirm_addr(struct in_device *in_dev,
1043 __be32 dst, __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044{
Al Viro60cad5d2006-09-26 22:17:09 -07001045 __be32 addr = 0;
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001046 struct net_device *dev;
Denis V. Lunev39a6d062008-01-14 23:06:19 -08001047 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048
Denis V. Lunev39a6d062008-01-14 23:06:19 -08001049 if (scope != RT_SCOPE_LINK)
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001050 return confirm_addr_indev(in_dev, dst, local, scope);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001052 net = dev_net(in_dev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 rcu_read_lock();
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001054 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001055 in_dev = __in_dev_get_rcu(dev);
1056 if (in_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 addr = confirm_addr_indev(in_dev, dst, local, scope);
1058 if (addr)
1059 break;
1060 }
1061 }
1062 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001063
1064 return addr;
1065}
1066
1067/*
1068 * Device notifier
1069 */
1070
1071int register_inetaddr_notifier(struct notifier_block *nb)
1072{
Alan Sterne041c682006-03-27 01:16:30 -08001073 return blocking_notifier_chain_register(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001075EXPORT_SYMBOL(register_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
1077int unregister_inetaddr_notifier(struct notifier_block *nb)
1078{
Alan Sterne041c682006-03-27 01:16:30 -08001079 return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001081EXPORT_SYMBOL(unregister_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001083/* Rename ifa_labels for a device name change. Make some effort to preserve
1084 * existing alias numbering and to create unique labels if possible.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085*/
1086static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001087{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 struct in_ifaddr *ifa;
1089 int named = 0;
1090
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001091 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
1092 char old[IFNAMSIZ], *dot;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093
1094 memcpy(old, ifa->ifa_label, IFNAMSIZ);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001095 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001096 if (named++ == 0)
Thomas Graf573bf472008-06-10 15:40:04 -07001097 goto skip;
Mark McLoughlin44344b22008-01-04 00:56:25 -08001098 dot = strchr(old, ':');
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001099 if (dot == NULL) {
1100 sprintf(old, ":%d", named);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 dot = old;
1102 }
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001103 if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001104 strcat(ifa->ifa_label, dot);
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001105 else
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001106 strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
Thomas Graf573bf472008-06-10 15:40:04 -07001107skip:
1108 rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001109 }
1110}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111
Breno Leitao067708432008-09-02 17:28:58 -07001112static inline bool inetdev_valid_mtu(unsigned mtu)
1113{
1114 return mtu >= 68;
1115}
1116
Ian Campbelld11327ad2011-02-11 07:44:16 +00001117static void inetdev_send_gratuitous_arp(struct net_device *dev,
1118 struct in_device *in_dev)
1119
1120{
1121 struct in_ifaddr *ifa = in_dev->ifa_list;
1122
1123 if (!ifa)
1124 return;
1125
1126 arp_send(ARPOP_REQUEST, ETH_P_ARP,
David S. Miller6c91afe2011-03-09 13:27:16 -08001127 ifa->ifa_local, dev,
1128 ifa->ifa_local, NULL,
Ian Campbelld11327ad2011-02-11 07:44:16 +00001129 dev->dev_addr, NULL);
1130}
1131
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132/* Called only under RTNL semaphore */
1133
1134static int inetdev_event(struct notifier_block *this, unsigned long event,
1135 void *ptr)
1136{
1137 struct net_device *dev = ptr;
Herbert Xue5ed6392005-10-03 14:35:55 -07001138 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139
1140 ASSERT_RTNL();
1141
1142 if (!in_dev) {
Herbert Xu8030f542007-02-22 01:53:47 +09001143 if (event == NETDEV_REGISTER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 in_dev = inetdev_init(dev);
Herbert Xub217d612007-07-30 17:04:52 -07001145 if (!in_dev)
1146 return notifier_from_errno(-ENOMEM);
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001147 if (dev->flags & IFF_LOOPBACK) {
Herbert Xu42f811b2007-06-04 23:34:44 -07001148 IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
1149 IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
Herbert Xu8030f542007-02-22 01:53:47 +09001150 }
Breno Leitao067708432008-09-02 17:28:58 -07001151 } else if (event == NETDEV_CHANGEMTU) {
1152 /* Re-enabling IP */
1153 if (inetdev_valid_mtu(dev->mtu))
1154 in_dev = inetdev_init(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 }
1156 goto out;
1157 }
1158
1159 switch (event) {
1160 case NETDEV_REGISTER:
1161 printk(KERN_DEBUG "inetdev_event: bug\n");
Eric Dumazet95ae6b22010-09-15 04:04:31 +00001162 rcu_assign_pointer(dev->ip_ptr, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 break;
1164 case NETDEV_UP:
Breno Leitao067708432008-09-02 17:28:58 -07001165 if (!inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 break;
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001167 if (dev->flags & IFF_LOOPBACK) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001168 struct in_ifaddr *ifa = inet_alloc_ifa();
1169
1170 if (ifa) {
David S. Millerfd23c3b2011-02-18 12:42:28 -08001171 INIT_HLIST_NODE(&ifa->hash);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 ifa->ifa_local =
1173 ifa->ifa_address = htonl(INADDR_LOOPBACK);
1174 ifa->ifa_prefixlen = 8;
1175 ifa->ifa_mask = inet_make_mask(8);
1176 in_dev_hold(in_dev);
1177 ifa->ifa_dev = in_dev;
1178 ifa->ifa_scope = RT_SCOPE_HOST;
1179 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
1180 inet_insert_ifa(ifa);
1181 }
1182 }
1183 ip_mc_up(in_dev);
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001184 /* fall through */
1185 case NETDEV_CHANGEADDR:
Ian Campbelld11327ad2011-02-11 07:44:16 +00001186 if (!IN_DEV_ARP_NOTIFY(in_dev))
1187 break;
1188 /* fall through */
1189 case NETDEV_NOTIFY_PEERS:
Stephen Hemmingera21090c2009-10-07 03:18:17 -07001190 /* Send gratuitous ARP to notify of link change */
Ian Campbelld11327ad2011-02-11 07:44:16 +00001191 inetdev_send_gratuitous_arp(dev, in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192 break;
1193 case NETDEV_DOWN:
1194 ip_mc_down(in_dev);
1195 break;
Jiri Pirko93d9b7d2010-03-10 10:28:56 +00001196 case NETDEV_PRE_TYPE_CHANGE:
Moni Shoua75c78502009-09-15 02:37:40 -07001197 ip_mc_unmap(in_dev);
1198 break;
Jiri Pirko93d9b7d2010-03-10 10:28:56 +00001199 case NETDEV_POST_TYPE_CHANGE:
Moni Shoua75c78502009-09-15 02:37:40 -07001200 ip_mc_remap(in_dev);
1201 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 case NETDEV_CHANGEMTU:
Breno Leitao067708432008-09-02 17:28:58 -07001203 if (inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 break;
Breno Leitao067708432008-09-02 17:28:58 -07001205 /* disable IP when MTU is not enough */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206 case NETDEV_UNREGISTER:
1207 inetdev_destroy(in_dev);
1208 break;
1209 case NETDEV_CHANGENAME:
1210 /* Do not notify about label change, this event is
1211 * not interesting to applications using netlink.
1212 */
1213 inetdev_changename(dev, in_dev);
1214
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001215 devinet_sysctl_unregister(in_dev);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001216 devinet_sysctl_register(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217 break;
1218 }
1219out:
1220 return NOTIFY_DONE;
1221}
1222
1223static struct notifier_block ip_netdev_notifier = {
Jianjun Kong539afed2008-11-03 02:48:48 -08001224 .notifier_call = inetdev_event,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225};
1226
Thomas Graf339bf982006-11-10 14:10:15 -08001227static inline size_t inet_nlmsg_size(void)
1228{
1229 return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1230 + nla_total_size(4) /* IFA_ADDRESS */
1231 + nla_total_size(4) /* IFA_LOCAL */
1232 + nla_total_size(4) /* IFA_BROADCAST */
Thomas Graf339bf982006-11-10 14:10:15 -08001233 + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1234}
1235
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
Jamal Hadi Salimb6544c02005-06-18 22:54:12 -07001237 u32 pid, u32 seq, int event, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238{
1239 struct ifaddrmsg *ifm;
1240 struct nlmsghdr *nlh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
Thomas Graf47f68512006-08-04 23:04:36 -07001242 nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
1243 if (nlh == NULL)
Patrick McHardy26932562007-01-31 23:16:40 -08001244 return -EMSGSIZE;
Thomas Graf47f68512006-08-04 23:04:36 -07001245
1246 ifm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247 ifm->ifa_family = AF_INET;
1248 ifm->ifa_prefixlen = ifa->ifa_prefixlen;
1249 ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
1250 ifm->ifa_scope = ifa->ifa_scope;
1251 ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252
Thomas Graf47f68512006-08-04 23:04:36 -07001253 if (ifa->ifa_address)
Al Viroa7a628c2006-09-26 22:16:43 -07001254 NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address);
Thomas Graf47f68512006-08-04 23:04:36 -07001255
1256 if (ifa->ifa_local)
Al Viroa7a628c2006-09-26 22:16:43 -07001257 NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local);
Thomas Graf47f68512006-08-04 23:04:36 -07001258
1259 if (ifa->ifa_broadcast)
Al Viroa7a628c2006-09-26 22:16:43 -07001260 NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast);
Thomas Graf47f68512006-08-04 23:04:36 -07001261
Thomas Graf47f68512006-08-04 23:04:36 -07001262 if (ifa->ifa_label[0])
1263 NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label);
1264
1265 return nlmsg_end(skb, nlh);
1266
1267nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08001268 nlmsg_cancel(skb, nlh);
1269 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001270}
1271
1272static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
1273{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001274 struct net *net = sock_net(skb->sk);
Eric Dumazeteec4df92009-11-12 07:44:25 +00001275 int h, s_h;
1276 int idx, s_idx;
1277 int ip_idx, s_ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 struct net_device *dev;
1279 struct in_device *in_dev;
1280 struct in_ifaddr *ifa;
Eric Dumazeteec4df92009-11-12 07:44:25 +00001281 struct hlist_head *head;
1282 struct hlist_node *node;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283
Eric Dumazeteec4df92009-11-12 07:44:25 +00001284 s_h = cb->args[0];
1285 s_idx = idx = cb->args[1];
1286 s_ip_idx = ip_idx = cb->args[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
Eric Dumazeteec4df92009-11-12 07:44:25 +00001288 for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
1289 idx = 0;
1290 head = &net->dev_index_head[h];
1291 rcu_read_lock();
1292 hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
1293 if (idx < s_idx)
1294 goto cont;
Patrick McHardy4b97efd2010-03-26 20:27:49 -07001295 if (h > s_h || idx > s_idx)
Eric Dumazeteec4df92009-11-12 07:44:25 +00001296 s_ip_idx = 0;
1297 in_dev = __in_dev_get_rcu(dev);
1298 if (!in_dev)
1299 goto cont;
1300
1301 for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
1302 ifa = ifa->ifa_next, ip_idx++) {
1303 if (ip_idx < s_ip_idx)
1304 continue;
1305 if (inet_fill_ifaddr(skb, ifa,
1306 NETLINK_CB(cb->skb).pid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307 cb->nlh->nlmsg_seq,
Eric Dumazeteec4df92009-11-12 07:44:25 +00001308 RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1309 rcu_read_unlock();
1310 goto done;
1311 }
1312 }
Pavel Emelianov7562f872007-05-03 15:13:45 -07001313cont:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001314 idx++;
1315 }
1316 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 }
1318
1319done:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001320 cb->args[0] = h;
1321 cb->args[1] = idx;
1322 cb->args[2] = ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
1324 return skb->len;
1325}
1326
Jianjun Kong539afed2008-11-03 02:48:48 -08001327static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
Thomas Grafd6062cb2006-08-15 00:33:59 -07001328 u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329{
Thomas Graf47f68512006-08-04 23:04:36 -07001330 struct sk_buff *skb;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001331 u32 seq = nlh ? nlh->nlmsg_seq : 0;
1332 int err = -ENOBUFS;
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001333 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001335 net = dev_net(ifa->ifa_dev->dev);
Thomas Graf339bf982006-11-10 14:10:15 -08001336 skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
Thomas Graf47f68512006-08-04 23:04:36 -07001337 if (skb == NULL)
Thomas Grafd6062cb2006-08-15 00:33:59 -07001338 goto errout;
1339
1340 err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08001341 if (err < 0) {
1342 /* -EMSGSIZE implies BUG in inet_nlmsg_size() */
1343 WARN_ON(err == -EMSGSIZE);
1344 kfree_skb(skb);
1345 goto errout;
1346 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001347 rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
1348 return;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001349errout:
1350 if (err < 0)
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001351 rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352}
1353
Thomas Graf9f0f7272010-11-16 04:32:48 +00001354static size_t inet_get_link_af_size(const struct net_device *dev)
1355{
Eric Dumazetf7fce742010-12-01 06:03:06 +00001356 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Thomas Graf9f0f7272010-11-16 04:32:48 +00001357
1358 if (!in_dev)
1359 return 0;
1360
1361 return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
1362}
1363
1364static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
1365{
Eric Dumazetf7fce742010-12-01 06:03:06 +00001366 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Thomas Graf9f0f7272010-11-16 04:32:48 +00001367 struct nlattr *nla;
1368 int i;
1369
1370 if (!in_dev)
1371 return -ENODATA;
1372
1373 nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
1374 if (nla == NULL)
1375 return -EMSGSIZE;
1376
1377 for (i = 0; i < IPV4_DEVCONF_MAX; i++)
1378 ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
1379
1380 return 0;
1381}
1382
1383static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
1384 [IFLA_INET_CONF] = { .type = NLA_NESTED },
1385};
1386
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001387static int inet_validate_link_af(const struct net_device *dev,
1388 const struct nlattr *nla)
Thomas Graf9f0f7272010-11-16 04:32:48 +00001389{
Thomas Graf9f0f7272010-11-16 04:32:48 +00001390 struct nlattr *a, *tb[IFLA_INET_MAX+1];
1391 int err, rem;
1392
Eric Dumazetf7fce742010-12-01 06:03:06 +00001393 if (dev && !__in_dev_get_rtnl(dev))
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001394 return -EAFNOSUPPORT;
Thomas Graf9f0f7272010-11-16 04:32:48 +00001395
1396 err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
1397 if (err < 0)
1398 return err;
1399
1400 if (tb[IFLA_INET_CONF]) {
1401 nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
1402 int cfgid = nla_type(a);
1403
1404 if (nla_len(a) < 4)
1405 return -EINVAL;
1406
1407 if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
1408 return -EINVAL;
1409 }
1410 }
1411
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001412 return 0;
1413}
1414
1415static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1416{
Eric Dumazetf7fce742010-12-01 06:03:06 +00001417 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001418 struct nlattr *a, *tb[IFLA_INET_MAX+1];
1419 int rem;
1420
1421 if (!in_dev)
1422 return -EAFNOSUPPORT;
1423
1424 if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1425 BUG();
1426
Thomas Graf9f0f7272010-11-16 04:32:48 +00001427 if (tb[IFLA_INET_CONF]) {
1428 nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
1429 ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
1430 }
1431
1432 return 0;
1433}
1434
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435#ifdef CONFIG_SYSCTL
1436
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001437static void devinet_copy_dflt_conf(struct net *net, int i)
Herbert Xu31be3082007-06-04 23:35:37 -07001438{
1439 struct net_device *dev;
1440
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001441 rcu_read_lock();
1442 for_each_netdev_rcu(net, dev) {
Herbert Xu31be3082007-06-04 23:35:37 -07001443 struct in_device *in_dev;
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001444
Herbert Xu31be3082007-06-04 23:35:37 -07001445 in_dev = __in_dev_get_rcu(dev);
1446 if (in_dev && !test_bit(i, in_dev->cnf.state))
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001447 in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
Herbert Xu31be3082007-06-04 23:35:37 -07001448 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001449 rcu_read_unlock();
Herbert Xu31be3082007-06-04 23:35:37 -07001450}
1451
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001452/* called with RTNL locked */
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001453static void inet_forward_change(struct net *net)
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001454{
1455 struct net_device *dev;
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001456 int on = IPV4_DEVCONF_ALL(net, FORWARDING);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001457
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001458 IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001459 IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001460
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001461 for_each_netdev(net, dev) {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001462 struct in_device *in_dev;
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001463 if (on)
1464 dev_disable_lro(dev);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001465 rcu_read_lock();
1466 in_dev = __in_dev_get_rcu(dev);
1467 if (in_dev)
1468 IN_DEV_CONF_SET(in_dev, FORWARDING, on);
1469 rcu_read_unlock();
1470 }
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001471}
1472
Herbert Xu31be3082007-06-04 23:35:37 -07001473static int devinet_conf_proc(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001474 void __user *buffer,
Herbert Xu31be3082007-06-04 23:35:37 -07001475 size_t *lenp, loff_t *ppos)
1476{
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001477 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Herbert Xu31be3082007-06-04 23:35:37 -07001478
1479 if (write) {
1480 struct ipv4_devconf *cnf = ctl->extra1;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001481 struct net *net = ctl->extra2;
Herbert Xu31be3082007-06-04 23:35:37 -07001482 int i = (int *)ctl->data - cnf->data;
1483
1484 set_bit(i, cnf->state);
1485
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001486 if (cnf == net->ipv4.devconf_dflt)
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001487 devinet_copy_dflt_conf(net, i);
Herbert Xu31be3082007-06-04 23:35:37 -07001488 }
1489
1490 return ret;
1491}
1492
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493static int devinet_sysctl_forward(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001494 void __user *buffer,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 size_t *lenp, loff_t *ppos)
1496{
1497 int *valp = ctl->data;
1498 int val = *valp;
Eric W. Biederman88af1822010-02-19 13:22:59 +00001499 loff_t pos = *ppos;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001500 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001501
1502 if (write && *valp != val) {
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001503 struct net *net = ctl->extra2;
1504
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001505 if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
Eric W. Biederman88af1822010-02-19 13:22:59 +00001506 if (!rtnl_trylock()) {
1507 /* Restore the original values before restarting */
1508 *valp = val;
1509 *ppos = pos;
Eric W. Biederman9b8adb52009-05-13 16:59:21 +00001510 return restart_syscall();
Eric W. Biederman88af1822010-02-19 13:22:59 +00001511 }
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001512 if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1513 inet_forward_change(net);
1514 } else if (*valp) {
1515 struct ipv4_devconf *cnf = ctl->extra1;
1516 struct in_device *idev =
1517 container_of(cnf, struct in_device, cnf);
1518 dev_disable_lro(idev->dev);
1519 }
1520 rtnl_unlock();
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001521 rt_cache_flush(net, 0);
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001522 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 }
1524
1525 return ret;
1526}
1527
David S. Miller323e1262010-12-12 21:55:08 -08001528static int ipv4_doint_and_flush(ctl_table *ctl, int write,
1529 void __user *buffer,
1530 size_t *lenp, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531{
1532 int *valp = ctl->data;
1533 int val = *valp;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001534 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001535 struct net *net = ctl->extra2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
1537 if (write && *valp != val)
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001538 rt_cache_flush(net, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539
1540 return ret;
1541}
1542
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001543#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
Herbert Xu42f811b2007-06-04 23:34:44 -07001544 { \
Herbert Xu42f811b2007-06-04 23:34:44 -07001545 .procname = name, \
1546 .data = ipv4_devconf.data + \
Eric W. Biederman02291682010-02-14 03:25:51 +00001547 IPV4_DEVCONF_ ## attr - 1, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001548 .maxlen = sizeof(int), \
1549 .mode = mval, \
1550 .proc_handler = proc, \
Herbert Xu31be3082007-06-04 23:35:37 -07001551 .extra1 = &ipv4_devconf, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001552 }
1553
1554#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001555 DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001556
1557#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001558 DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001559
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001560#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1561 DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001562
1563#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001564 DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
Herbert Xu42f811b2007-06-04 23:34:44 -07001565
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566static struct devinet_sysctl_table {
1567 struct ctl_table_header *sysctl_header;
Eric W. Biederman02291682010-02-14 03:25:51 +00001568 struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001569 char *dev_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570} devinet_sysctl = {
1571 .devinet_vars = {
Herbert Xu42f811b2007-06-04 23:34:44 -07001572 DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001573 devinet_sysctl_forward),
Herbert Xu42f811b2007-06-04 23:34:44 -07001574 DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
1575
1576 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
1577 DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
1578 DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
1579 DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
1580 DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
1581 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
1582 "accept_source_route"),
Patrick McHardy8153a102009-12-03 01:25:58 +00001583 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
Jamal Hadi Salim28f6aee2009-12-25 17:30:22 -08001584 DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001585 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
1586 DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
1587 DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
1588 DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
1589 DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
1590 DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
1591 DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
1592 DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
1593 DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001594 DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
Jesper Dangaard Brouer65324142010-01-05 05:50:47 +00001595 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001596
1597 DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
1598 DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
1599 DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
1600 "force_igmp_version"),
1601 DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
1602 "promote_secondaries"),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604};
1605
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001606static int __devinet_sysctl_register(struct net *net, char *dev_name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001607 struct ipv4_devconf *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001608{
1609 int i;
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001610 struct devinet_sysctl_table *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001612#define DEVINET_CTL_PATH_DEV 3
1613
1614 struct ctl_path devinet_ctl_path[] = {
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001615 { .procname = "net", },
1616 { .procname = "ipv4", },
1617 { .procname = "conf", },
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001618 { /* to be set */ },
1619 { },
1620 };
1621
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001622 t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001623 if (!t)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001624 goto out;
1625
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
1627 t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
Herbert Xu31be3082007-06-04 23:35:37 -07001628 t->devinet_vars[i].extra1 = p;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001629 t->devinet_vars[i].extra2 = net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630 }
1631
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001632 /*
1633 * Make a copy of dev_name, because '.procname' is regarded as const
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634 * by sysctl and we wouldn't want anyone to change it under our feet
1635 * (see SIOCSIFNAME).
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001636 */
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001637 t->dev_name = kstrdup(dev_name, GFP_KERNEL);
1638 if (!t->dev_name)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001639 goto free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001641 devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001643 t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path,
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001644 t->devinet_vars);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 if (!t->sysctl_header)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001646 goto free_procname;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
1648 p->sysctl = t;
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001649 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001651free_procname:
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001652 kfree(t->dev_name);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001653free:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001654 kfree(t);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001655out:
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001656 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657}
1658
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001659static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
1660{
1661 struct devinet_sysctl_table *t = cnf->sysctl;
1662
1663 if (t == NULL)
1664 return;
1665
1666 cnf->sysctl = NULL;
1667 unregister_sysctl_table(t->sysctl_header);
1668 kfree(t->dev_name);
1669 kfree(t);
1670}
1671
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001672static void devinet_sysctl_register(struct in_device *idev)
1673{
Eric W. Biederman54716e32010-02-14 03:27:03 +00001674 neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001675 __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001676 &idev->cnf);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001677}
1678
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001679static void devinet_sysctl_unregister(struct in_device *idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001680{
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001681 __devinet_sysctl_unregister(&idev->cnf);
1682 neigh_sysctl_unregister(idev->arp_parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001685static struct ctl_table ctl_forward_entry[] = {
1686 {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001687 .procname = "ip_forward",
1688 .data = &ipv4_devconf.data[
Eric W. Biederman02291682010-02-14 03:25:51 +00001689 IPV4_DEVCONF_FORWARDING - 1],
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001690 .maxlen = sizeof(int),
1691 .mode = 0644,
1692 .proc_handler = devinet_sysctl_forward,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001693 .extra1 = &ipv4_devconf,
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001694 .extra2 = &init_net,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001695 },
1696 { },
1697};
1698
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001699static __net_initdata struct ctl_path net_ipv4_path[] = {
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001700 { .procname = "net", },
1701 { .procname = "ipv4", },
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001702 { },
1703};
Eric Dumazet2a75de02008-01-05 23:08:49 -08001704#endif
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001705
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001706static __net_init int devinet_init_net(struct net *net)
1707{
1708 int err;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001709 struct ipv4_devconf *all, *dflt;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001710#ifdef CONFIG_SYSCTL
1711 struct ctl_table *tbl = ctl_forward_entry;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001712 struct ctl_table_header *forw_hdr;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001713#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001714
1715 err = -ENOMEM;
1716 all = &ipv4_devconf;
1717 dflt = &ipv4_devconf_dflt;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001718
Octavian Purdila09ad9bc2009-11-25 15:14:13 -08001719 if (!net_eq(net, &init_net)) {
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001720 all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1721 if (all == NULL)
1722 goto err_alloc_all;
1723
1724 dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1725 if (dflt == NULL)
1726 goto err_alloc_dflt;
1727
Eric Dumazet2a75de02008-01-05 23:08:49 -08001728#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001729 tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1730 if (tbl == NULL)
1731 goto err_alloc_ctl;
1732
Eric W. Biederman02291682010-02-14 03:25:51 +00001733 tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001734 tbl[0].extra1 = all;
1735 tbl[0].extra2 = net;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001736#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001737 }
1738
1739#ifdef CONFIG_SYSCTL
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001740 err = __devinet_sysctl_register(net, "all", all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001741 if (err < 0)
1742 goto err_reg_all;
1743
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001744 err = __devinet_sysctl_register(net, "default", dflt);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001745 if (err < 0)
1746 goto err_reg_dflt;
1747
1748 err = -ENOMEM;
1749 forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
1750 if (forw_hdr == NULL)
1751 goto err_reg_ctl;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001752 net->ipv4.forw_hdr = forw_hdr;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001753#endif
1754
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001755 net->ipv4.devconf_all = all;
1756 net->ipv4.devconf_dflt = dflt;
1757 return 0;
1758
1759#ifdef CONFIG_SYSCTL
1760err_reg_ctl:
1761 __devinet_sysctl_unregister(dflt);
1762err_reg_dflt:
1763 __devinet_sysctl_unregister(all);
1764err_reg_all:
1765 if (tbl != ctl_forward_entry)
1766 kfree(tbl);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001767err_alloc_ctl:
Eric Dumazet2a75de02008-01-05 23:08:49 -08001768#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001769 if (dflt != &ipv4_devconf_dflt)
1770 kfree(dflt);
1771err_alloc_dflt:
1772 if (all != &ipv4_devconf)
1773 kfree(all);
1774err_alloc_all:
1775 return err;
1776}
1777
1778static __net_exit void devinet_exit_net(struct net *net)
1779{
Eric Dumazet2a75de02008-01-05 23:08:49 -08001780#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001781 struct ctl_table *tbl;
1782
1783 tbl = net->ipv4.forw_hdr->ctl_table_arg;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001784 unregister_net_sysctl_table(net->ipv4.forw_hdr);
1785 __devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1786 __devinet_sysctl_unregister(net->ipv4.devconf_all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001787 kfree(tbl);
Eric Dumazet2a75de02008-01-05 23:08:49 -08001788#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001789 kfree(net->ipv4.devconf_dflt);
1790 kfree(net->ipv4.devconf_all);
1791}
1792
1793static __net_initdata struct pernet_operations devinet_ops = {
1794 .init = devinet_init_net,
1795 .exit = devinet_exit_net,
1796};
1797
Thomas Graf9f0f7272010-11-16 04:32:48 +00001798static struct rtnl_af_ops inet_af_ops = {
1799 .family = AF_INET,
1800 .fill_link_af = inet_fill_link_af,
1801 .get_link_af_size = inet_get_link_af_size,
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001802 .validate_link_af = inet_validate_link_af,
1803 .set_link_af = inet_set_link_af,
Thomas Graf9f0f7272010-11-16 04:32:48 +00001804};
1805
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806void __init devinet_init(void)
1807{
David S. Millerfd23c3b2011-02-18 12:42:28 -08001808 int i;
1809
1810 for (i = 0; i < IN4_ADDR_HSIZE; i++)
1811 INIT_HLIST_HEAD(&inet_addr_lst[i]);
1812
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001813 register_pernet_subsys(&devinet_ops);
1814
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 register_gifconf(PF_INET, inet_gifconf);
1816 register_netdevice_notifier(&ip_netdev_notifier);
Thomas Graf63f34442007-03-22 11:55:17 -07001817
Thomas Graf9f0f7272010-11-16 04:32:48 +00001818 rtnl_af_register(&inet_af_ops);
1819
Thomas Graf63f34442007-03-22 11:55:17 -07001820 rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
1821 rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
1822 rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823}
1824