blob: 6a5e6e4b142c98393391013911824c8821d98167 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/bitops.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080031#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <linux/module.h>
33#include <linux/types.h>
34#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/string.h>
36#include <linux/mm.h>
37#include <linux/socket.h>
38#include <linux/sockios.h>
39#include <linux/in.h>
40#include <linux/errno.h>
41#include <linux/interrupt.h>
Thomas Graf18237302006-08-04 23:04:54 -070042#include <linux/if_addr.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043#include <linux/if_ether.h>
44#include <linux/inet.h>
45#include <linux/netdevice.h>
46#include <linux/etherdevice.h>
47#include <linux/skbuff.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <linux/init.h>
49#include <linux/notifier.h>
50#include <linux/inetdevice.h>
51#include <linux/igmp.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090052#include <linux/slab.h>
David S. Millerfd23c3b2011-02-18 12:42:28 -080053#include <linux/hash.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#ifdef CONFIG_SYSCTL
55#include <linux/sysctl.h>
56#endif
57#include <linux/kmod.h>
58
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020059#include <net/arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#include <net/ip.h>
61#include <net/route.h>
62#include <net/ip_fib.h>
Thomas Graf63f34442007-03-22 11:55:17 -070063#include <net/rtnetlink.h>
Pavel Emelyanov752d14d2007-12-16 13:31:47 -080064#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
David S. Miller406b6f92011-03-22 21:56:23 -070066#include "fib_lookup.h"
67
Adrian Bunk0027ba82008-01-31 17:17:31 -080068static struct ipv4_devconf ipv4_devconf = {
Herbert Xu42f811b2007-06-04 23:34:44 -070069 .data = {
Eric W. Biederman02291682010-02-14 03:25:51 +000070 [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
71 [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
72 [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
73 [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
Herbert Xu42f811b2007-06-04 23:34:44 -070074 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070075};
76
77static struct ipv4_devconf ipv4_devconf_dflt = {
Herbert Xu42f811b2007-06-04 23:34:44 -070078 .data = {
Eric W. Biederman02291682010-02-14 03:25:51 +000079 [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1,
80 [IPV4_DEVCONF_SEND_REDIRECTS - 1] = 1,
81 [IPV4_DEVCONF_SECURE_REDIRECTS - 1] = 1,
82 [IPV4_DEVCONF_SHARED_MEDIA - 1] = 1,
83 [IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
Herbert Xu42f811b2007-06-04 23:34:44 -070084 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070085};
86
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -080087#define IPV4_DEVCONF_DFLT(net, attr) \
88 IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
Herbert Xu42f811b2007-06-04 23:34:44 -070089
Patrick McHardyef7c79e2007-06-05 12:38:30 -070090static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
Thomas Graf5c753972006-08-04 23:03:53 -070091 [IFA_LOCAL] = { .type = NLA_U32 },
92 [IFA_ADDRESS] = { .type = NLA_U32 },
93 [IFA_BROADCAST] = { .type = NLA_U32 },
Thomas Graf5176f912006-08-26 20:13:18 -070094 [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
Thomas Graf5c753972006-08-04 23:03:53 -070095};
96
Eric Dumazet40384992012-08-03 21:06:50 +000097#define IN4_ADDR_HSIZE_SHIFT 8
98#define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT)
99
David S. Millerfd23c3b2011-02-18 12:42:28 -0800100static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE];
101static DEFINE_SPINLOCK(inet_addr_hash_lock);
102
Eric Dumazet40384992012-08-03 21:06:50 +0000103static u32 inet_addr_hash(struct net *net, __be32 addr)
David S. Millerfd23c3b2011-02-18 12:42:28 -0800104{
Eric Dumazet40384992012-08-03 21:06:50 +0000105 u32 val = (__force u32) addr ^ net_hash_mix(net);
David S. Millerfd23c3b2011-02-18 12:42:28 -0800106
Eric Dumazet40384992012-08-03 21:06:50 +0000107 return hash_32(val, IN4_ADDR_HSIZE_SHIFT);
David S. Millerfd23c3b2011-02-18 12:42:28 -0800108}
109
110static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa)
111{
Eric Dumazet40384992012-08-03 21:06:50 +0000112 u32 hash = inet_addr_hash(net, ifa->ifa_local);
David S. Millerfd23c3b2011-02-18 12:42:28 -0800113
114 spin_lock(&inet_addr_hash_lock);
115 hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]);
116 spin_unlock(&inet_addr_hash_lock);
117}
118
119static void inet_hash_remove(struct in_ifaddr *ifa)
120{
121 spin_lock(&inet_addr_hash_lock);
122 hlist_del_init_rcu(&ifa->hash);
123 spin_unlock(&inet_addr_hash_lock);
124}
125
David S. Miller9435eb12011-02-18 12:43:09 -0800126/**
127 * __ip_dev_find - find the first device with a given source address.
128 * @net: the net namespace
129 * @addr: the source address
130 * @devref: if true, take a reference on the found device
131 *
132 * If a caller uses devref=false, it should be protected by RCU, or RTNL
133 */
134struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref)
135{
Eric Dumazet40384992012-08-03 21:06:50 +0000136 u32 hash = inet_addr_hash(net, addr);
David S. Miller9435eb12011-02-18 12:43:09 -0800137 struct net_device *result = NULL;
138 struct in_ifaddr *ifa;
139 struct hlist_node *node;
140
141 rcu_read_lock();
142 hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) {
David S. Millere0660082011-03-03 11:24:19 -0800143 if (ifa->ifa_local == addr) {
Eric Dumazet40384992012-08-03 21:06:50 +0000144 struct net_device *dev = ifa->ifa_dev->dev;
145
146 if (!net_eq(dev_net(dev), net))
147 continue;
David S. Miller9435eb12011-02-18 12:43:09 -0800148 result = dev;
149 break;
150 }
151 }
David S. Miller406b6f92011-03-22 21:56:23 -0700152 if (!result) {
153 struct flowi4 fl4 = { .daddr = addr };
154 struct fib_result res = { 0 };
155 struct fib_table *local;
156
157 /* Fallback to FIB local table so that communication
158 * over loopback subnets work.
159 */
160 local = fib_get_table(net, RT_TABLE_LOCAL);
161 if (local &&
162 !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) &&
163 res.type == RTN_LOCAL)
164 result = FIB_RES_DEV(res);
165 }
David S. Miller9435eb12011-02-18 12:43:09 -0800166 if (result && devref)
167 dev_hold(result);
168 rcu_read_unlock();
169 return result;
170}
171EXPORT_SYMBOL(__ip_dev_find);
172
Thomas Grafd6062cb2006-08-15 00:33:59 -0700173static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174
Alan Sterne041c682006-03-27 01:16:30 -0800175static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
177 int destroy);
178#ifdef CONFIG_SYSCTL
Pavel Emelyanov66f27a52007-12-02 00:55:54 +1100179static void devinet_sysctl_register(struct in_device *idev);
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800180static void devinet_sysctl_unregister(struct in_device *idev);
181#else
Eric Dumazet40384992012-08-03 21:06:50 +0000182static void devinet_sysctl_register(struct in_device *idev)
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800183{
184}
Eric Dumazet40384992012-08-03 21:06:50 +0000185static void devinet_sysctl_unregister(struct in_device *idev)
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800186{
187}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188#endif
189
190/* Locks all the inet devices. */
191
192static struct in_ifaddr *inet_alloc_ifa(void)
193{
Alexey Dobriyan93adcc82008-10-28 13:25:09 -0700194 return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195}
196
197static void inet_rcu_free_ifa(struct rcu_head *head)
198{
199 struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
200 if (ifa->ifa_dev)
201 in_dev_put(ifa->ifa_dev);
202 kfree(ifa);
203}
204
Eric Dumazet40384992012-08-03 21:06:50 +0000205static void inet_free_ifa(struct in_ifaddr *ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206{
207 call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
208}
209
210void in_dev_finish_destroy(struct in_device *idev)
211{
212 struct net_device *dev = idev->dev;
213
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700214 WARN_ON(idev->ifa_list);
215 WARN_ON(idev->mc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216#ifdef NET_REFCNT_DEBUG
Joe Perches91df42b2012-05-15 14:11:54 +0000217 pr_debug("%s: %p=%s\n", __func__, idev, dev ? dev->name : "NIL");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218#endif
219 dev_put(dev);
220 if (!idev->dead)
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800221 pr_err("Freeing alive in_device %p\n", idev);
222 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 kfree(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800225EXPORT_SYMBOL(in_dev_finish_destroy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
Herbert Xu71e27da2007-06-04 23:36:06 -0700227static struct in_device *inetdev_init(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
229 struct in_device *in_dev;
230
231 ASSERT_RTNL();
232
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700233 in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 if (!in_dev)
235 goto out;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900236 memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -0800237 sizeof(in_dev->cnf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 in_dev->cnf.sysctl = NULL;
239 in_dev->dev = dev;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800240 in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
241 if (!in_dev->arp_parms)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 goto out_kfree;
Ben Hutchings0187bdf2008-06-19 16:15:47 -0700243 if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
244 dev_disable_lro(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 /* Reference in_dev->dev */
246 dev_hold(dev);
David L Stevens30c4cf52007-01-04 12:31:14 -0800247 /* Account for reference dev->ip_ptr (below) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 in_dev_hold(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Pavel Emelyanov66f27a52007-12-02 00:55:54 +1100250 devinet_sysctl_register(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 ip_mc_init_dev(in_dev);
252 if (dev->flags & IFF_UP)
253 ip_mc_up(in_dev);
Jarek Poplawski483479e2007-01-09 14:38:31 -0800254
David L Stevens30c4cf52007-01-04 12:31:14 -0800255 /* we can receive as soon as ip_ptr is set -- do this last */
Eric Dumazetcf778b02012-01-12 04:41:32 +0000256 rcu_assign_pointer(dev->ip_ptr, in_dev);
Jarek Poplawski483479e2007-01-09 14:38:31 -0800257out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 return in_dev;
259out_kfree:
260 kfree(in_dev);
261 in_dev = NULL;
262 goto out;
263}
264
265static void in_dev_rcu_put(struct rcu_head *head)
266{
267 struct in_device *idev = container_of(head, struct in_device, rcu_head);
268 in_dev_put(idev);
269}
270
271static void inetdev_destroy(struct in_device *in_dev)
272{
273 struct in_ifaddr *ifa;
274 struct net_device *dev;
275
276 ASSERT_RTNL();
277
278 dev = in_dev->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279
280 in_dev->dead = 1;
281
282 ip_mc_destroy_dev(in_dev);
283
284 while ((ifa = in_dev->ifa_list) != NULL) {
285 inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
286 inet_free_ifa(ifa);
287 }
288
Stephen Hemmingera9b3cd72011-08-01 16:19:00 +0000289 RCU_INIT_POINTER(dev->ip_ptr, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800291 devinet_sysctl_unregister(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 neigh_parms_release(&arp_tbl, in_dev->arp_parms);
293 arp_ifdown(dev);
294
295 call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
296}
297
Al Viroff428d72006-09-26 22:13:35 -0700298int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{
300 rcu_read_lock();
301 for_primary_ifa(in_dev) {
302 if (inet_ifa_match(a, ifa)) {
303 if (!b || inet_ifa_match(b, ifa)) {
304 rcu_read_unlock();
305 return 1;
306 }
307 }
308 } endfor_ifa(in_dev);
309 rcu_read_unlock();
310 return 0;
311}
312
Thomas Grafd6062cb2006-08-15 00:33:59 -0700313static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
314 int destroy, struct nlmsghdr *nlh, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315{
Harald Welte8f937c62005-05-29 20:23:46 -0700316 struct in_ifaddr *promote = NULL;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800317 struct in_ifaddr *ifa, *ifa1 = *ifap;
318 struct in_ifaddr *last_prim = in_dev->ifa_list;
319 struct in_ifaddr *prev_prom = NULL;
320 int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
322 ASSERT_RTNL();
323
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900324 /* 1. Deleting primary ifaddr forces deletion all secondaries
Harald Welte8f937c62005-05-29 20:23:46 -0700325 * unless alias promotion is set
326 **/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
328 if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 struct in_ifaddr **ifap1 = &ifa1->ifa_next;
330
331 while ((ifa = *ifap1) != NULL) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900332 if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800333 ifa1->ifa_scope <= ifa->ifa_scope)
334 last_prim = ifa;
335
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
337 ifa1->ifa_mask != ifa->ifa_mask ||
338 !inet_ifa_match(ifa1->ifa_address, ifa)) {
339 ifap1 = &ifa->ifa_next;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800340 prev_prom = ifa;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 continue;
342 }
343
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800344 if (!do_promote) {
David S. Millerfd23c3b2011-02-18 12:42:28 -0800345 inet_hash_remove(ifa);
Harald Welte8f937c62005-05-29 20:23:46 -0700346 *ifap1 = ifa->ifa_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347
Thomas Grafd6062cb2006-08-15 00:33:59 -0700348 rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800349 blocking_notifier_call_chain(&inetaddr_chain,
350 NETDEV_DOWN, ifa);
Harald Welte8f937c62005-05-29 20:23:46 -0700351 inet_free_ifa(ifa);
352 } else {
353 promote = ifa;
354 break;
355 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 }
357 }
358
Julian Anastasov2d230e22011-03-19 12:13:52 +0000359 /* On promotion all secondaries from subnet are changing
360 * the primary IP, we must remove all their routes silently
361 * and later to add them back with new prefsrc. Do this
362 * while all addresses are on the device list.
363 */
364 for (ifa = promote; ifa; ifa = ifa->ifa_next) {
365 if (ifa1->ifa_mask == ifa->ifa_mask &&
366 inet_ifa_match(ifa1->ifa_address, ifa))
367 fib_del_ifaddr(ifa, ifa1);
368 }
369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 /* 2. Unlink it */
371
372 *ifap = ifa1->ifa_next;
David S. Millerfd23c3b2011-02-18 12:42:28 -0800373 inet_hash_remove(ifa1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
375 /* 3. Announce address deletion */
376
377 /* Send message first, then call notifier.
378 At first sight, FIB update triggered by notifier
379 will refer to already deleted ifaddr, that could confuse
380 netlink listeners. It is not true: look, gated sees
381 that route deleted and if it still thinks that ifaddr
382 is valid, it will try to restore deleted routes... Grr.
383 So that, this order is correct.
384 */
Thomas Grafd6062cb2006-08-15 00:33:59 -0700385 rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800386 blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800387
388 if (promote) {
Julian Anastasov04024b92011-03-19 12:13:54 +0000389 struct in_ifaddr *next_sec = promote->ifa_next;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800390
391 if (prev_prom) {
392 prev_prom->ifa_next = promote->ifa_next;
393 promote->ifa_next = last_prim->ifa_next;
394 last_prim->ifa_next = promote;
395 }
396
397 promote->ifa_flags &= ~IFA_F_SECONDARY;
Thomas Grafd6062cb2006-08-15 00:33:59 -0700398 rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800399 blocking_notifier_call_chain(&inetaddr_chain,
400 NETDEV_UP, promote);
Julian Anastasov04024b92011-03-19 12:13:54 +0000401 for (ifa = next_sec; ifa; ifa = ifa->ifa_next) {
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800402 if (ifa1->ifa_mask != ifa->ifa_mask ||
403 !inet_ifa_match(ifa1->ifa_address, ifa))
404 continue;
405 fib_add_ifaddr(ifa);
406 }
407
408 }
Herbert Xu63630972007-06-07 18:35:38 -0700409 if (destroy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 inet_free_ifa(ifa1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411}
412
Thomas Grafd6062cb2006-08-15 00:33:59 -0700413static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
414 int destroy)
415{
416 __inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
417}
418
419static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
420 u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421{
422 struct in_device *in_dev = ifa->ifa_dev;
423 struct in_ifaddr *ifa1, **ifap, **last_primary;
424
425 ASSERT_RTNL();
426
427 if (!ifa->ifa_local) {
428 inet_free_ifa(ifa);
429 return 0;
430 }
431
432 ifa->ifa_flags &= ~IFA_F_SECONDARY;
433 last_primary = &in_dev->ifa_list;
434
435 for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
436 ifap = &ifa1->ifa_next) {
437 if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
438 ifa->ifa_scope <= ifa1->ifa_scope)
439 last_primary = &ifa1->ifa_next;
440 if (ifa1->ifa_mask == ifa->ifa_mask &&
441 inet_ifa_match(ifa1->ifa_address, ifa)) {
442 if (ifa1->ifa_local == ifa->ifa_local) {
443 inet_free_ifa(ifa);
444 return -EEXIST;
445 }
446 if (ifa1->ifa_scope != ifa->ifa_scope) {
447 inet_free_ifa(ifa);
448 return -EINVAL;
449 }
450 ifa->ifa_flags |= IFA_F_SECONDARY;
451 }
452 }
453
454 if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
455 net_srandom(ifa->ifa_local);
456 ifap = last_primary;
457 }
458
459 ifa->ifa_next = *ifap;
460 *ifap = ifa;
461
David S. Millerfd23c3b2011-02-18 12:42:28 -0800462 inet_hash_insert(dev_net(in_dev->dev), ifa);
463
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 /* Send message first, then call notifier.
465 Notifier will trigger FIB update, so that
466 listeners of netlink will know about new ifaddr */
Thomas Grafd6062cb2006-08-15 00:33:59 -0700467 rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800468 blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
470 return 0;
471}
472
Thomas Grafd6062cb2006-08-15 00:33:59 -0700473static int inet_insert_ifa(struct in_ifaddr *ifa)
474{
475 return __inet_insert_ifa(ifa, NULL, 0);
476}
477
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
479{
Herbert Xue5ed6392005-10-03 14:35:55 -0700480 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481
482 ASSERT_RTNL();
483
484 if (!in_dev) {
Herbert Xu71e27da2007-06-04 23:36:06 -0700485 inet_free_ifa(ifa);
486 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 }
Herbert Xu71e27da2007-06-04 23:36:06 -0700488 ipv4_devconf_setall(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 if (ifa->ifa_dev != in_dev) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700490 WARN_ON(ifa->ifa_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 in_dev_hold(in_dev);
492 ifa->ifa_dev = in_dev;
493 }
Joe Perchesf97c1e02007-12-16 13:45:43 -0800494 if (ipv4_is_loopback(ifa->ifa_local))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 ifa->ifa_scope = RT_SCOPE_HOST;
496 return inet_insert_ifa(ifa);
497}
498
Eric Dumazet8723e1b2010-10-19 00:39:26 +0000499/* Caller must hold RCU or RTNL :
500 * We dont take a reference on found in_device
501 */
Denis V. Lunev7fee0ca2008-01-21 17:32:38 -0800502struct in_device *inetdev_by_index(struct net *net, int ifindex)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503{
504 struct net_device *dev;
505 struct in_device *in_dev = NULL;
Eric Dumazetc148fc22009-11-01 19:23:04 +0000506
507 rcu_read_lock();
508 dev = dev_get_by_index_rcu(net, ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 if (dev)
Eric Dumazet8723e1b2010-10-19 00:39:26 +0000510 in_dev = rcu_dereference_rtnl(dev->ip_ptr);
Eric Dumazetc148fc22009-11-01 19:23:04 +0000511 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 return in_dev;
513}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800514EXPORT_SYMBOL(inetdev_by_index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
516/* Called only from RTNL semaphored context. No locks. */
517
Al Viro60cad5d2006-09-26 22:17:09 -0700518struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
519 __be32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520{
521 ASSERT_RTNL();
522
523 for_primary_ifa(in_dev) {
524 if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
525 return ifa;
526 } endfor_ifa(in_dev);
527 return NULL;
528}
529
530static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
531{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900532 struct net *net = sock_net(skb->sk);
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700533 struct nlattr *tb[IFA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534 struct in_device *in_dev;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700535 struct ifaddrmsg *ifm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 struct in_ifaddr *ifa, **ifap;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700537 int err = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539 ASSERT_RTNL();
540
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700541 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
542 if (err < 0)
543 goto errout;
544
545 ifm = nlmsg_data(nlh);
Denis V. Lunev7fee0ca2008-01-21 17:32:38 -0800546 in_dev = inetdev_by_index(net, ifm->ifa_index);
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700547 if (in_dev == NULL) {
548 err = -ENODEV;
549 goto errout;
550 }
551
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
553 ifap = &ifa->ifa_next) {
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700554 if (tb[IFA_LOCAL] &&
Al Viroa7a628c2006-09-26 22:16:43 -0700555 ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 continue;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700557
558 if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
559 continue;
560
561 if (tb[IFA_ADDRESS] &&
562 (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
Al Viroa7a628c2006-09-26 22:16:43 -0700563 !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700564 continue;
565
Thomas Grafd6062cb2006-08-15 00:33:59 -0700566 __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 return 0;
568 }
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700569
570 err = -EADDRNOTAVAIL;
571errout:
572 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573}
574
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800575static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576{
Thomas Graf5c753972006-08-04 23:03:53 -0700577 struct nlattr *tb[IFA_MAX+1];
578 struct in_ifaddr *ifa;
579 struct ifaddrmsg *ifm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 struct net_device *dev;
581 struct in_device *in_dev;
Denis V. Lunev7b218572008-01-31 18:47:00 -0800582 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Thomas Graf5c753972006-08-04 23:03:53 -0700584 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
585 if (err < 0)
586 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
Thomas Graf5c753972006-08-04 23:03:53 -0700588 ifm = nlmsg_data(nlh);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800589 err = -EINVAL;
590 if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700591 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800593 dev = __dev_get_by_index(net, ifm->ifa_index);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800594 err = -ENODEV;
595 if (dev == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700596 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597
Thomas Graf5c753972006-08-04 23:03:53 -0700598 in_dev = __in_dev_get_rtnl(dev);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800599 err = -ENOBUFS;
600 if (in_dev == NULL)
Herbert Xu71e27da2007-06-04 23:36:06 -0700601 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602
Thomas Graf5c753972006-08-04 23:03:53 -0700603 ifa = inet_alloc_ifa();
Denis V. Lunev7b218572008-01-31 18:47:00 -0800604 if (ifa == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700605 /*
606 * A potential indev allocation can be left alive, it stays
607 * assigned to its device and is destroy with it.
608 */
Thomas Graf5c753972006-08-04 23:03:53 -0700609 goto errout;
Thomas Graf5c753972006-08-04 23:03:53 -0700610
Pavel Emelyanova4e65d32007-12-07 23:55:43 -0800611 ipv4_devconf_setall(in_dev);
Thomas Graf5c753972006-08-04 23:03:53 -0700612 in_dev_hold(in_dev);
613
614 if (tb[IFA_ADDRESS] == NULL)
615 tb[IFA_ADDRESS] = tb[IFA_LOCAL];
616
David S. Millerfd23c3b2011-02-18 12:42:28 -0800617 INIT_HLIST_NODE(&ifa->hash);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 ifa->ifa_prefixlen = ifm->ifa_prefixlen;
619 ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 ifa->ifa_flags = ifm->ifa_flags;
621 ifa->ifa_scope = ifm->ifa_scope;
Thomas Graf5c753972006-08-04 23:03:53 -0700622 ifa->ifa_dev = in_dev;
623
Al Viroa7a628c2006-09-26 22:16:43 -0700624 ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
625 ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
Thomas Graf5c753972006-08-04 23:03:53 -0700626
627 if (tb[IFA_BROADCAST])
Al Viroa7a628c2006-09-26 22:16:43 -0700628 ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
Thomas Graf5c753972006-08-04 23:03:53 -0700629
Thomas Graf5c753972006-08-04 23:03:53 -0700630 if (tb[IFA_LABEL])
631 nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 else
633 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
634
Thomas Graf5c753972006-08-04 23:03:53 -0700635 return ifa;
636
637errout:
638 return ERR_PTR(err);
639}
640
641static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
642{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900643 struct net *net = sock_net(skb->sk);
Thomas Graf5c753972006-08-04 23:03:53 -0700644 struct in_ifaddr *ifa;
645
646 ASSERT_RTNL();
647
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800648 ifa = rtm_to_ifaddr(net, nlh);
Thomas Graf5c753972006-08-04 23:03:53 -0700649 if (IS_ERR(ifa))
650 return PTR_ERR(ifa);
651
Thomas Grafd6062cb2006-08-15 00:33:59 -0700652 return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653}
654
655/*
656 * Determine a default network mask, based on the IP address.
657 */
658
Eric Dumazet40384992012-08-03 21:06:50 +0000659static int inet_abc_len(__be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
661 int rc = -1; /* Something else, probably a multicast. */
662
Joe Perchesf97c1e02007-12-16 13:45:43 -0800663 if (ipv4_is_zeronet(addr))
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900664 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 else {
Al Viro714e85b2006-11-14 20:51:49 -0800666 __u32 haddr = ntohl(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667
Al Viro714e85b2006-11-14 20:51:49 -0800668 if (IN_CLASSA(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 rc = 8;
Al Viro714e85b2006-11-14 20:51:49 -0800670 else if (IN_CLASSB(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 rc = 16;
Al Viro714e85b2006-11-14 20:51:49 -0800672 else if (IN_CLASSC(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 rc = 24;
674 }
675
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900676 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677}
678
679
Denis V. Luneve5b13cb2008-02-28 20:51:43 -0800680int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681{
682 struct ifreq ifr;
683 struct sockaddr_in sin_orig;
684 struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
685 struct in_device *in_dev;
686 struct in_ifaddr **ifap = NULL;
687 struct in_ifaddr *ifa = NULL;
688 struct net_device *dev;
689 char *colon;
690 int ret = -EFAULT;
691 int tryaddrmatch = 0;
692
693 /*
694 * Fetch the caller's info block into kernel space
695 */
696
697 if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
698 goto out;
699 ifr.ifr_name[IFNAMSIZ - 1] = 0;
700
701 /* save original address for comparison */
702 memcpy(&sin_orig, sin, sizeof(*sin));
703
704 colon = strchr(ifr.ifr_name, ':');
705 if (colon)
706 *colon = 0;
707
Denis V. Luneve5b13cb2008-02-28 20:51:43 -0800708 dev_load(net, ifr.ifr_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709
Stephen Hemminger132adf52007-03-08 20:44:43 -0800710 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 case SIOCGIFADDR: /* Get interface address */
712 case SIOCGIFBRDADDR: /* Get the broadcast address */
713 case SIOCGIFDSTADDR: /* Get the destination address */
714 case SIOCGIFNETMASK: /* Get the netmask for the interface */
715 /* Note that these ioctls will not sleep,
716 so that we do not impose a lock.
717 One day we will be forced to put shlock here (I mean SMP)
718 */
719 tryaddrmatch = (sin_orig.sin_family == AF_INET);
720 memset(sin, 0, sizeof(*sin));
721 sin->sin_family = AF_INET;
722 break;
723
724 case SIOCSIFFLAGS:
725 ret = -EACCES;
726 if (!capable(CAP_NET_ADMIN))
727 goto out;
728 break;
729 case SIOCSIFADDR: /* Set interface address (and family) */
730 case SIOCSIFBRDADDR: /* Set the broadcast address */
731 case SIOCSIFDSTADDR: /* Set the destination address */
732 case SIOCSIFNETMASK: /* Set the netmask for the interface */
733 ret = -EACCES;
734 if (!capable(CAP_NET_ADMIN))
735 goto out;
736 ret = -EINVAL;
737 if (sin->sin_family != AF_INET)
738 goto out;
739 break;
740 default:
741 ret = -EINVAL;
742 goto out;
743 }
744
745 rtnl_lock();
746
747 ret = -ENODEV;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800748 dev = __dev_get_by_name(net, ifr.ifr_name);
749 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 goto done;
751
752 if (colon)
753 *colon = ':';
754
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800755 in_dev = __in_dev_get_rtnl(dev);
756 if (in_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 if (tryaddrmatch) {
758 /* Matthias Andree */
759 /* compare label and address (4.4BSD style) */
760 /* note: we only do this for a limited set of ioctls
761 and only if the original address family was AF_INET.
762 This is checked above. */
763 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
764 ifap = &ifa->ifa_next) {
765 if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
766 sin_orig.sin_addr.s_addr ==
David S. Miller6c91afe2011-03-09 13:27:16 -0800767 ifa->ifa_local) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 break; /* found */
769 }
770 }
771 }
772 /* we didn't get a match, maybe the application is
773 4.3BSD-style and passed in junk so we fall back to
774 comparing just the label */
775 if (!ifa) {
776 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
777 ifap = &ifa->ifa_next)
778 if (!strcmp(ifr.ifr_name, ifa->ifa_label))
779 break;
780 }
781 }
782
783 ret = -EADDRNOTAVAIL;
784 if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
785 goto done;
786
Stephen Hemminger132adf52007-03-08 20:44:43 -0800787 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 case SIOCGIFADDR: /* Get interface address */
789 sin->sin_addr.s_addr = ifa->ifa_local;
790 goto rarok;
791
792 case SIOCGIFBRDADDR: /* Get the broadcast address */
793 sin->sin_addr.s_addr = ifa->ifa_broadcast;
794 goto rarok;
795
796 case SIOCGIFDSTADDR: /* Get the destination address */
797 sin->sin_addr.s_addr = ifa->ifa_address;
798 goto rarok;
799
800 case SIOCGIFNETMASK: /* Get the netmask for the interface */
801 sin->sin_addr.s_addr = ifa->ifa_mask;
802 goto rarok;
803
804 case SIOCSIFFLAGS:
805 if (colon) {
806 ret = -EADDRNOTAVAIL;
807 if (!ifa)
808 break;
809 ret = 0;
810 if (!(ifr.ifr_flags & IFF_UP))
811 inet_del_ifa(in_dev, ifap, 1);
812 break;
813 }
814 ret = dev_change_flags(dev, ifr.ifr_flags);
815 break;
816
817 case SIOCSIFADDR: /* Set interface address (and family) */
818 ret = -EINVAL;
819 if (inet_abc_len(sin->sin_addr.s_addr) < 0)
820 break;
821
822 if (!ifa) {
823 ret = -ENOBUFS;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800824 ifa = inet_alloc_ifa();
David S. Millerfd23c3b2011-02-18 12:42:28 -0800825 INIT_HLIST_NODE(&ifa->hash);
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800826 if (!ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 break;
828 if (colon)
829 memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
830 else
831 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
832 } else {
833 ret = 0;
834 if (ifa->ifa_local == sin->sin_addr.s_addr)
835 break;
836 inet_del_ifa(in_dev, ifap, 0);
837 ifa->ifa_broadcast = 0;
Bjorn Mork148f9722008-02-26 18:17:53 -0800838 ifa->ifa_scope = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 }
840
841 ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
842
843 if (!(dev->flags & IFF_POINTOPOINT)) {
844 ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
845 ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
846 if ((dev->flags & IFF_BROADCAST) &&
847 ifa->ifa_prefixlen < 31)
848 ifa->ifa_broadcast = ifa->ifa_address |
849 ~ifa->ifa_mask;
850 } else {
851 ifa->ifa_prefixlen = 32;
852 ifa->ifa_mask = inet_make_mask(32);
853 }
854 ret = inet_set_ifa(dev, ifa);
855 break;
856
857 case SIOCSIFBRDADDR: /* Set the broadcast address */
858 ret = 0;
859 if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
860 inet_del_ifa(in_dev, ifap, 0);
861 ifa->ifa_broadcast = sin->sin_addr.s_addr;
862 inet_insert_ifa(ifa);
863 }
864 break;
865
866 case SIOCSIFDSTADDR: /* Set the destination address */
867 ret = 0;
868 if (ifa->ifa_address == sin->sin_addr.s_addr)
869 break;
870 ret = -EINVAL;
871 if (inet_abc_len(sin->sin_addr.s_addr) < 0)
872 break;
873 ret = 0;
874 inet_del_ifa(in_dev, ifap, 0);
875 ifa->ifa_address = sin->sin_addr.s_addr;
876 inet_insert_ifa(ifa);
877 break;
878
879 case SIOCSIFNETMASK: /* Set the netmask for the interface */
880
881 /*
882 * The mask we set must be legal.
883 */
884 ret = -EINVAL;
885 if (bad_mask(sin->sin_addr.s_addr, 0))
886 break;
887 ret = 0;
888 if (ifa->ifa_mask != sin->sin_addr.s_addr) {
Al Viroa144ea42006-09-28 18:00:55 -0700889 __be32 old_mask = ifa->ifa_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 inet_del_ifa(in_dev, ifap, 0);
891 ifa->ifa_mask = sin->sin_addr.s_addr;
892 ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
893
894 /* See if current broadcast address matches
895 * with current netmask, then recalculate
896 * the broadcast address. Otherwise it's a
897 * funny address, so don't touch it since
898 * the user seems to know what (s)he's doing...
899 */
900 if ((dev->flags & IFF_BROADCAST) &&
901 (ifa->ifa_prefixlen < 31) &&
902 (ifa->ifa_broadcast ==
David Engeldcab5e12005-10-21 22:09:16 -0500903 (ifa->ifa_local|~old_mask))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904 ifa->ifa_broadcast = (ifa->ifa_local |
905 ~sin->sin_addr.s_addr);
906 }
907 inet_insert_ifa(ifa);
908 }
909 break;
910 }
911done:
912 rtnl_unlock();
913out:
914 return ret;
915rarok:
916 rtnl_unlock();
917 ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
918 goto out;
919}
920
921static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
922{
Herbert Xue5ed6392005-10-03 14:35:55 -0700923 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 struct in_ifaddr *ifa;
925 struct ifreq ifr;
926 int done = 0;
927
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800928 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 goto out;
930
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800931 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 if (!buf) {
933 done += sizeof(ifr);
934 continue;
935 }
936 if (len < (int) sizeof(ifr))
937 break;
938 memset(&ifr, 0, sizeof(struct ifreq));
939 if (ifa->ifa_label)
940 strcpy(ifr.ifr_name, ifa->ifa_label);
941 else
942 strcpy(ifr.ifr_name, dev->name);
943
944 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
945 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
946 ifa->ifa_local;
947
948 if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
949 done = -EFAULT;
950 break;
951 }
952 buf += sizeof(struct ifreq);
953 len -= sizeof(struct ifreq);
954 done += sizeof(struct ifreq);
955 }
956out:
957 return done;
958}
959
Al Viroa61ced52006-09-26 21:27:54 -0700960__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961{
Al Viroa61ced52006-09-26 21:27:54 -0700962 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 struct in_device *in_dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900964 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
966 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -0700967 in_dev = __in_dev_get_rcu(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968 if (!in_dev)
969 goto no_in_dev;
970
971 for_primary_ifa(in_dev) {
972 if (ifa->ifa_scope > scope)
973 continue;
974 if (!dst || inet_ifa_match(dst, ifa)) {
975 addr = ifa->ifa_local;
976 break;
977 }
978 if (!addr)
979 addr = ifa->ifa_local;
980 } endfor_ifa(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981
982 if (addr)
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800983 goto out_unlock;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800984no_in_dev:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985
986 /* Not loopback addresses on loopback should be preferred
987 in this case. It is importnat that lo is the first interface
988 in dev_base list.
989 */
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800990 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800991 in_dev = __in_dev_get_rcu(dev);
992 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 continue;
994
995 for_primary_ifa(in_dev) {
996 if (ifa->ifa_scope != RT_SCOPE_LINK &&
997 ifa->ifa_scope <= scope) {
998 addr = ifa->ifa_local;
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800999 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 }
1001 } endfor_ifa(in_dev);
1002 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001003out_unlock:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 return addr;
1006}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001007EXPORT_SYMBOL(inet_select_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008
Al Viro60cad5d2006-09-26 22:17:09 -07001009static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
1010 __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011{
1012 int same = 0;
Al Viroa144ea42006-09-28 18:00:55 -07001013 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014
1015 for_ifa(in_dev) {
1016 if (!addr &&
1017 (local == ifa->ifa_local || !local) &&
1018 ifa->ifa_scope <= scope) {
1019 addr = ifa->ifa_local;
1020 if (same)
1021 break;
1022 }
1023 if (!same) {
1024 same = (!local || inet_ifa_match(local, ifa)) &&
1025 (!dst || inet_ifa_match(dst, ifa));
1026 if (same && addr) {
1027 if (local || !dst)
1028 break;
1029 /* Is the selected addr into dst subnet? */
1030 if (inet_ifa_match(addr, ifa))
1031 break;
1032 /* No, then can we use new local src? */
1033 if (ifa->ifa_scope <= scope) {
1034 addr = ifa->ifa_local;
1035 break;
1036 }
1037 /* search for large dst subnet for addr */
1038 same = 0;
1039 }
1040 }
1041 } endfor_ifa(in_dev);
1042
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001043 return same ? addr : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044}
1045
1046/*
1047 * Confirm that local IP address exists using wildcards:
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001048 * - in_dev: only on this interface, 0=any interface
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 * - dst: only in the same subnet as dst, 0=any dst
1050 * - local: address, 0=autoselect the local address
1051 * - scope: maximum allowed scope value for the local address
1052 */
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001053__be32 inet_confirm_addr(struct in_device *in_dev,
1054 __be32 dst, __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055{
Al Viro60cad5d2006-09-26 22:17:09 -07001056 __be32 addr = 0;
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001057 struct net_device *dev;
Denis V. Lunev39a6d062008-01-14 23:06:19 -08001058 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
Denis V. Lunev39a6d062008-01-14 23:06:19 -08001060 if (scope != RT_SCOPE_LINK)
Denis V. Lunev9bd85e32008-01-14 23:05:55 -08001061 return confirm_addr_indev(in_dev, dst, local, scope);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001063 net = dev_net(in_dev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 rcu_read_lock();
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001065 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001066 in_dev = __in_dev_get_rcu(dev);
1067 if (in_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001068 addr = confirm_addr_indev(in_dev, dst, local, scope);
1069 if (addr)
1070 break;
1071 }
1072 }
1073 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
1075 return addr;
1076}
Andy Gospodarekeaddcd72012-03-22 16:14:29 +00001077EXPORT_SYMBOL(inet_confirm_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078
1079/*
1080 * Device notifier
1081 */
1082
1083int register_inetaddr_notifier(struct notifier_block *nb)
1084{
Alan Sterne041c682006-03-27 01:16:30 -08001085 return blocking_notifier_chain_register(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001087EXPORT_SYMBOL(register_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088
1089int unregister_inetaddr_notifier(struct notifier_block *nb)
1090{
Alan Sterne041c682006-03-27 01:16:30 -08001091 return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092}
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001093EXPORT_SYMBOL(unregister_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001095/* Rename ifa_labels for a device name change. Make some effort to preserve
1096 * existing alias numbering and to create unique labels if possible.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097*/
1098static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001099{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 struct in_ifaddr *ifa;
1101 int named = 0;
1102
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001103 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
1104 char old[IFNAMSIZ], *dot;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105
1106 memcpy(old, ifa->ifa_label, IFNAMSIZ);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001107 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 if (named++ == 0)
Thomas Graf573bf472008-06-10 15:40:04 -07001109 goto skip;
Mark McLoughlin44344b22008-01-04 00:56:25 -08001110 dot = strchr(old, ':');
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001111 if (dot == NULL) {
1112 sprintf(old, ":%d", named);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 dot = old;
1114 }
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001115 if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001116 strcat(ifa->ifa_label, dot);
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001117 else
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001118 strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
Thomas Graf573bf472008-06-10 15:40:04 -07001119skip:
1120 rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001121 }
1122}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001123
Eric Dumazet40384992012-08-03 21:06:50 +00001124static bool inetdev_valid_mtu(unsigned int mtu)
Breno Leitao06770842008-09-02 17:28:58 -07001125{
1126 return mtu >= 68;
1127}
1128
Ian Campbelld11327ad2011-02-11 07:44:16 +00001129static void inetdev_send_gratuitous_arp(struct net_device *dev,
1130 struct in_device *in_dev)
1131
1132{
Zoltan Kissb76d0782011-07-24 13:09:30 +00001133 struct in_ifaddr *ifa;
Ian Campbelld11327ad2011-02-11 07:44:16 +00001134
Zoltan Kissb76d0782011-07-24 13:09:30 +00001135 for (ifa = in_dev->ifa_list; ifa;
1136 ifa = ifa->ifa_next) {
1137 arp_send(ARPOP_REQUEST, ETH_P_ARP,
1138 ifa->ifa_local, dev,
1139 ifa->ifa_local, NULL,
1140 dev->dev_addr, NULL);
1141 }
Ian Campbelld11327ad2011-02-11 07:44:16 +00001142}
1143
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144/* Called only under RTNL semaphore */
1145
1146static int inetdev_event(struct notifier_block *this, unsigned long event,
1147 void *ptr)
1148{
1149 struct net_device *dev = ptr;
Eric Dumazet0115e8e2012-08-22 17:19:46 +00001150 struct in_device *in_dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151
Eric Dumazet0115e8e2012-08-22 17:19:46 +00001152 if (event == NETDEV_UNREGISTER_FINAL)
1153 goto out;
1154
1155 in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 ASSERT_RTNL();
1157
1158 if (!in_dev) {
Herbert Xu8030f542007-02-22 01:53:47 +09001159 if (event == NETDEV_REGISTER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 in_dev = inetdev_init(dev);
Herbert Xub217d612007-07-30 17:04:52 -07001161 if (!in_dev)
1162 return notifier_from_errno(-ENOMEM);
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001163 if (dev->flags & IFF_LOOPBACK) {
Herbert Xu42f811b2007-06-04 23:34:44 -07001164 IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
1165 IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
Herbert Xu8030f542007-02-22 01:53:47 +09001166 }
Breno Leitao06770842008-09-02 17:28:58 -07001167 } else if (event == NETDEV_CHANGEMTU) {
1168 /* Re-enabling IP */
1169 if (inetdev_valid_mtu(dev->mtu))
1170 in_dev = inetdev_init(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 }
1172 goto out;
1173 }
1174
1175 switch (event) {
1176 case NETDEV_REGISTER:
Joe Perches91df42b2012-05-15 14:11:54 +00001177 pr_debug("%s: bug\n", __func__);
Stephen Hemmingera9b3cd72011-08-01 16:19:00 +00001178 RCU_INIT_POINTER(dev->ip_ptr, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 break;
1180 case NETDEV_UP:
Breno Leitao06770842008-09-02 17:28:58 -07001181 if (!inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 break;
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001183 if (dev->flags & IFF_LOOPBACK) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001184 struct in_ifaddr *ifa = inet_alloc_ifa();
1185
1186 if (ifa) {
David S. Millerfd23c3b2011-02-18 12:42:28 -08001187 INIT_HLIST_NODE(&ifa->hash);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 ifa->ifa_local =
1189 ifa->ifa_address = htonl(INADDR_LOOPBACK);
1190 ifa->ifa_prefixlen = 8;
1191 ifa->ifa_mask = inet_make_mask(8);
1192 in_dev_hold(in_dev);
1193 ifa->ifa_dev = in_dev;
1194 ifa->ifa_scope = RT_SCOPE_HOST;
1195 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
1196 inet_insert_ifa(ifa);
1197 }
1198 }
1199 ip_mc_up(in_dev);
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001200 /* fall through */
1201 case NETDEV_CHANGEADDR:
Ian Campbelld11327ad2011-02-11 07:44:16 +00001202 if (!IN_DEV_ARP_NOTIFY(in_dev))
1203 break;
1204 /* fall through */
1205 case NETDEV_NOTIFY_PEERS:
Stephen Hemmingera21090c2009-10-07 03:18:17 -07001206 /* Send gratuitous ARP to notify of link change */
Ian Campbelld11327ad2011-02-11 07:44:16 +00001207 inetdev_send_gratuitous_arp(dev, in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208 break;
1209 case NETDEV_DOWN:
1210 ip_mc_down(in_dev);
1211 break;
Jiri Pirko93d9b7d2010-03-10 10:28:56 +00001212 case NETDEV_PRE_TYPE_CHANGE:
Moni Shoua75c78502009-09-15 02:37:40 -07001213 ip_mc_unmap(in_dev);
1214 break;
Jiri Pirko93d9b7d2010-03-10 10:28:56 +00001215 case NETDEV_POST_TYPE_CHANGE:
Moni Shoua75c78502009-09-15 02:37:40 -07001216 ip_mc_remap(in_dev);
1217 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218 case NETDEV_CHANGEMTU:
Breno Leitao06770842008-09-02 17:28:58 -07001219 if (inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 break;
Breno Leitao06770842008-09-02 17:28:58 -07001221 /* disable IP when MTU is not enough */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 case NETDEV_UNREGISTER:
1223 inetdev_destroy(in_dev);
1224 break;
1225 case NETDEV_CHANGENAME:
1226 /* Do not notify about label change, this event is
1227 * not interesting to applications using netlink.
1228 */
1229 inetdev_changename(dev, in_dev);
1230
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001231 devinet_sysctl_unregister(in_dev);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001232 devinet_sysctl_register(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 break;
1234 }
1235out:
1236 return NOTIFY_DONE;
1237}
1238
1239static struct notifier_block ip_netdev_notifier = {
Jianjun Kong539afed2008-11-03 02:48:48 -08001240 .notifier_call = inetdev_event,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241};
1242
Eric Dumazet40384992012-08-03 21:06:50 +00001243static size_t inet_nlmsg_size(void)
Thomas Graf339bf982006-11-10 14:10:15 -08001244{
1245 return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1246 + nla_total_size(4) /* IFA_ADDRESS */
1247 + nla_total_size(4) /* IFA_LOCAL */
1248 + nla_total_size(4) /* IFA_BROADCAST */
Thomas Graf339bf982006-11-10 14:10:15 -08001249 + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1250}
1251
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
Jamal Hadi Salimb6544c02005-06-18 22:54:12 -07001253 u32 pid, u32 seq, int event, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254{
1255 struct ifaddrmsg *ifm;
1256 struct nlmsghdr *nlh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257
Thomas Graf47f68512006-08-04 23:04:36 -07001258 nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
1259 if (nlh == NULL)
Patrick McHardy26932562007-01-31 23:16:40 -08001260 return -EMSGSIZE;
Thomas Graf47f68512006-08-04 23:04:36 -07001261
1262 ifm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 ifm->ifa_family = AF_INET;
1264 ifm->ifa_prefixlen = ifa->ifa_prefixlen;
1265 ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
1266 ifm->ifa_scope = ifa->ifa_scope;
1267 ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268
David S. Millerf3756b72012-04-01 20:39:02 -04001269 if ((ifa->ifa_address &&
1270 nla_put_be32(skb, IFA_ADDRESS, ifa->ifa_address)) ||
1271 (ifa->ifa_local &&
1272 nla_put_be32(skb, IFA_LOCAL, ifa->ifa_local)) ||
1273 (ifa->ifa_broadcast &&
1274 nla_put_be32(skb, IFA_BROADCAST, ifa->ifa_broadcast)) ||
1275 (ifa->ifa_label[0] &&
1276 nla_put_string(skb, IFA_LABEL, ifa->ifa_label)))
1277 goto nla_put_failure;
Thomas Graf47f68512006-08-04 23:04:36 -07001278
1279 return nlmsg_end(skb, nlh);
1280
1281nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08001282 nlmsg_cancel(skb, nlh);
1283 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284}
1285
1286static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
1287{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001288 struct net *net = sock_net(skb->sk);
Eric Dumazeteec4df92009-11-12 07:44:25 +00001289 int h, s_h;
1290 int idx, s_idx;
1291 int ip_idx, s_ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 struct net_device *dev;
1293 struct in_device *in_dev;
1294 struct in_ifaddr *ifa;
Eric Dumazeteec4df92009-11-12 07:44:25 +00001295 struct hlist_head *head;
1296 struct hlist_node *node;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001297
Eric Dumazeteec4df92009-11-12 07:44:25 +00001298 s_h = cb->args[0];
1299 s_idx = idx = cb->args[1];
1300 s_ip_idx = ip_idx = cb->args[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301
Eric Dumazeteec4df92009-11-12 07:44:25 +00001302 for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
1303 idx = 0;
1304 head = &net->dev_index_head[h];
1305 rcu_read_lock();
1306 hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
1307 if (idx < s_idx)
1308 goto cont;
Patrick McHardy4b97efd2010-03-26 20:27:49 -07001309 if (h > s_h || idx > s_idx)
Eric Dumazeteec4df92009-11-12 07:44:25 +00001310 s_ip_idx = 0;
1311 in_dev = __in_dev_get_rcu(dev);
1312 if (!in_dev)
1313 goto cont;
1314
1315 for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
1316 ifa = ifa->ifa_next, ip_idx++) {
1317 if (ip_idx < s_ip_idx)
1318 continue;
1319 if (inet_fill_ifaddr(skb, ifa,
1320 NETLINK_CB(cb->skb).pid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321 cb->nlh->nlmsg_seq,
Eric Dumazeteec4df92009-11-12 07:44:25 +00001322 RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1323 rcu_read_unlock();
1324 goto done;
1325 }
1326 }
Pavel Emelianov7562f872007-05-03 15:13:45 -07001327cont:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001328 idx++;
1329 }
1330 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 }
1332
1333done:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001334 cb->args[0] = h;
1335 cb->args[1] = idx;
1336 cb->args[2] = ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
1338 return skb->len;
1339}
1340
Jianjun Kong539afed2008-11-03 02:48:48 -08001341static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
Thomas Grafd6062cb2006-08-15 00:33:59 -07001342 u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343{
Thomas Graf47f68512006-08-04 23:04:36 -07001344 struct sk_buff *skb;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001345 u32 seq = nlh ? nlh->nlmsg_seq : 0;
1346 int err = -ENOBUFS;
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001347 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001349 net = dev_net(ifa->ifa_dev->dev);
Thomas Graf339bf982006-11-10 14:10:15 -08001350 skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
Thomas Graf47f68512006-08-04 23:04:36 -07001351 if (skb == NULL)
Thomas Grafd6062cb2006-08-15 00:33:59 -07001352 goto errout;
1353
1354 err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08001355 if (err < 0) {
1356 /* -EMSGSIZE implies BUG in inet_nlmsg_size() */
1357 WARN_ON(err == -EMSGSIZE);
1358 kfree_skb(skb);
1359 goto errout;
1360 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001361 rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
1362 return;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001363errout:
1364 if (err < 0)
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001365 rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366}
1367
Thomas Graf9f0f7272010-11-16 04:32:48 +00001368static size_t inet_get_link_af_size(const struct net_device *dev)
1369{
Eric Dumazet1fc19af2011-05-09 20:55:03 -07001370 struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
Thomas Graf9f0f7272010-11-16 04:32:48 +00001371
1372 if (!in_dev)
1373 return 0;
1374
1375 return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */
1376}
1377
1378static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev)
1379{
Eric Dumazet1fc19af2011-05-09 20:55:03 -07001380 struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr);
Thomas Graf9f0f7272010-11-16 04:32:48 +00001381 struct nlattr *nla;
1382 int i;
1383
1384 if (!in_dev)
1385 return -ENODATA;
1386
1387 nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4);
1388 if (nla == NULL)
1389 return -EMSGSIZE;
1390
1391 for (i = 0; i < IPV4_DEVCONF_MAX; i++)
1392 ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i];
1393
1394 return 0;
1395}
1396
1397static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = {
1398 [IFLA_INET_CONF] = { .type = NLA_NESTED },
1399};
1400
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001401static int inet_validate_link_af(const struct net_device *dev,
1402 const struct nlattr *nla)
Thomas Graf9f0f7272010-11-16 04:32:48 +00001403{
Thomas Graf9f0f7272010-11-16 04:32:48 +00001404 struct nlattr *a, *tb[IFLA_INET_MAX+1];
1405 int err, rem;
1406
Eric Dumazetf7fce742010-12-01 06:03:06 +00001407 if (dev && !__in_dev_get_rtnl(dev))
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001408 return -EAFNOSUPPORT;
Thomas Graf9f0f7272010-11-16 04:32:48 +00001409
1410 err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy);
1411 if (err < 0)
1412 return err;
1413
1414 if (tb[IFLA_INET_CONF]) {
1415 nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) {
1416 int cfgid = nla_type(a);
1417
1418 if (nla_len(a) < 4)
1419 return -EINVAL;
1420
1421 if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX)
1422 return -EINVAL;
1423 }
1424 }
1425
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001426 return 0;
1427}
1428
1429static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla)
1430{
Eric Dumazetf7fce742010-12-01 06:03:06 +00001431 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001432 struct nlattr *a, *tb[IFLA_INET_MAX+1];
1433 int rem;
1434
1435 if (!in_dev)
1436 return -EAFNOSUPPORT;
1437
1438 if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0)
1439 BUG();
1440
Thomas Graf9f0f7272010-11-16 04:32:48 +00001441 if (tb[IFLA_INET_CONF]) {
1442 nla_for_each_nested(a, tb[IFLA_INET_CONF], rem)
1443 ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a));
1444 }
1445
1446 return 0;
1447}
1448
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449#ifdef CONFIG_SYSCTL
1450
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001451static void devinet_copy_dflt_conf(struct net *net, int i)
Herbert Xu31be3082007-06-04 23:35:37 -07001452{
1453 struct net_device *dev;
1454
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001455 rcu_read_lock();
1456 for_each_netdev_rcu(net, dev) {
Herbert Xu31be3082007-06-04 23:35:37 -07001457 struct in_device *in_dev;
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001458
Herbert Xu31be3082007-06-04 23:35:37 -07001459 in_dev = __in_dev_get_rcu(dev);
1460 if (in_dev && !test_bit(i, in_dev->cnf.state))
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001461 in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
Herbert Xu31be3082007-06-04 23:35:37 -07001462 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001463 rcu_read_unlock();
Herbert Xu31be3082007-06-04 23:35:37 -07001464}
1465
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001466/* called with RTNL locked */
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001467static void inet_forward_change(struct net *net)
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001468{
1469 struct net_device *dev;
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001470 int on = IPV4_DEVCONF_ALL(net, FORWARDING);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001471
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001472 IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001473 IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001474
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001475 for_each_netdev(net, dev) {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001476 struct in_device *in_dev;
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001477 if (on)
1478 dev_disable_lro(dev);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001479 rcu_read_lock();
1480 in_dev = __in_dev_get_rcu(dev);
1481 if (in_dev)
1482 IN_DEV_CONF_SET(in_dev, FORWARDING, on);
1483 rcu_read_unlock();
1484 }
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001485}
1486
Herbert Xu31be3082007-06-04 23:35:37 -07001487static int devinet_conf_proc(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001488 void __user *buffer,
Herbert Xu31be3082007-06-04 23:35:37 -07001489 size_t *lenp, loff_t *ppos)
1490{
Peter Pan(潘卫平)d01ff0a2011-12-01 15:47:06 +00001491 int old_value = *(int *)ctl->data;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001492 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Peter Pan(潘卫平)d01ff0a2011-12-01 15:47:06 +00001493 int new_value = *(int *)ctl->data;
Herbert Xu31be3082007-06-04 23:35:37 -07001494
1495 if (write) {
1496 struct ipv4_devconf *cnf = ctl->extra1;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001497 struct net *net = ctl->extra2;
Herbert Xu31be3082007-06-04 23:35:37 -07001498 int i = (int *)ctl->data - cnf->data;
1499
1500 set_bit(i, cnf->state);
1501
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001502 if (cnf == net->ipv4.devconf_dflt)
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001503 devinet_copy_dflt_conf(net, i);
Thomas Grafd0daebc32012-06-12 00:44:01 +00001504 if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
1505 i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
Peter Pan(潘卫平)d01ff0a2011-12-01 15:47:06 +00001506 if ((new_value == 0) && (old_value != 0))
1507 rt_cache_flush(net, 0);
Herbert Xu31be3082007-06-04 23:35:37 -07001508 }
1509
1510 return ret;
1511}
1512
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513static int devinet_sysctl_forward(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001514 void __user *buffer,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 size_t *lenp, loff_t *ppos)
1516{
1517 int *valp = ctl->data;
1518 int val = *valp;
Eric W. Biederman88af1822010-02-19 13:22:59 +00001519 loff_t pos = *ppos;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001520 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
1522 if (write && *valp != val) {
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001523 struct net *net = ctl->extra2;
1524
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001525 if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
Eric W. Biederman88af1822010-02-19 13:22:59 +00001526 if (!rtnl_trylock()) {
1527 /* Restore the original values before restarting */
1528 *valp = val;
1529 *ppos = pos;
Eric W. Biederman9b8adb52009-05-13 16:59:21 +00001530 return restart_syscall();
Eric W. Biederman88af1822010-02-19 13:22:59 +00001531 }
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001532 if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1533 inet_forward_change(net);
1534 } else if (*valp) {
1535 struct ipv4_devconf *cnf = ctl->extra1;
1536 struct in_device *idev =
1537 container_of(cnf, struct in_device, cnf);
1538 dev_disable_lro(idev->dev);
1539 }
1540 rtnl_unlock();
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001541 rt_cache_flush(net, 0);
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001542 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 }
1544
1545 return ret;
1546}
1547
David S. Miller323e1262010-12-12 21:55:08 -08001548static int ipv4_doint_and_flush(ctl_table *ctl, int write,
1549 void __user *buffer,
1550 size_t *lenp, loff_t *ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551{
1552 int *valp = ctl->data;
1553 int val = *valp;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001554 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001555 struct net *net = ctl->extra2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
1557 if (write && *valp != val)
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001558 rt_cache_flush(net, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
1560 return ret;
1561}
1562
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001563#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
Herbert Xu42f811b2007-06-04 23:34:44 -07001564 { \
Herbert Xu42f811b2007-06-04 23:34:44 -07001565 .procname = name, \
1566 .data = ipv4_devconf.data + \
Eric W. Biederman02291682010-02-14 03:25:51 +00001567 IPV4_DEVCONF_ ## attr - 1, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001568 .maxlen = sizeof(int), \
1569 .mode = mval, \
1570 .proc_handler = proc, \
Herbert Xu31be3082007-06-04 23:35:37 -07001571 .extra1 = &ipv4_devconf, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001572 }
1573
1574#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001575 DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001576
1577#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001578 DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001579
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001580#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1581 DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001582
1583#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001584 DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
Herbert Xu42f811b2007-06-04 23:34:44 -07001585
Linus Torvalds1da177e2005-04-16 15:20:36 -07001586static struct devinet_sysctl_table {
1587 struct ctl_table_header *sysctl_header;
Eric W. Biederman02291682010-02-14 03:25:51 +00001588 struct ctl_table devinet_vars[__IPV4_DEVCONF_MAX];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589} devinet_sysctl = {
1590 .devinet_vars = {
Herbert Xu42f811b2007-06-04 23:34:44 -07001591 DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001592 devinet_sysctl_forward),
Herbert Xu42f811b2007-06-04 23:34:44 -07001593 DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
1594
1595 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
1596 DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
1597 DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
1598 DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
1599 DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
1600 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
1601 "accept_source_route"),
Patrick McHardy8153a102009-12-03 01:25:58 +00001602 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
Jamal Hadi Salim28f6aee2009-12-25 17:30:22 -08001603 DEVINET_SYSCTL_RW_ENTRY(SRC_VMARK, "src_valid_mark"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001604 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
1605 DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
1606 DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
1607 DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
1608 DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
1609 DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
1610 DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
1611 DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
1612 DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001613 DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
Jesper Dangaard Brouer65324142010-01-05 05:50:47 +00001614 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001615
1616 DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
1617 DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
1618 DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
1619 "force_igmp_version"),
1620 DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
1621 "promote_secondaries"),
Thomas Grafd0daebc32012-06-12 00:44:01 +00001622 DEVINET_SYSCTL_FLUSHING_ENTRY(ROUTE_LOCALNET,
1623 "route_localnet"),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001625};
1626
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001627static int __devinet_sysctl_register(struct net *net, char *dev_name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001628 struct ipv4_devconf *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629{
1630 int i;
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001631 struct devinet_sysctl_table *t;
Eric W. Biederman8607ddb2012-04-19 13:42:09 +00001632 char path[sizeof("net/ipv4/conf/") + IFNAMSIZ];
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001633
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001634 t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635 if (!t)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001636 goto out;
1637
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638 for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
1639 t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
Herbert Xu31be3082007-06-04 23:35:37 -07001640 t->devinet_vars[i].extra1 = p;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001641 t->devinet_vars[i].extra2 = net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001642 }
1643
Eric W. Biederman8607ddb2012-04-19 13:42:09 +00001644 snprintf(path, sizeof(path), "net/ipv4/conf/%s", dev_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
Eric W. Biederman8607ddb2012-04-19 13:42:09 +00001646 t->sysctl_header = register_net_sysctl(net, path, t->devinet_vars);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647 if (!t->sysctl_header)
Eric W. Biederman8607ddb2012-04-19 13:42:09 +00001648 goto free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
1650 p->sysctl = t;
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001651 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652
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;
Lucian Adrian Grijincuff538812011-05-01 01:44:01 +00001667 unregister_net_sysctl_table(t->sysctl_header);
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001668 kfree(t);
1669}
1670
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001671static void devinet_sysctl_register(struct in_device *idev)
1672{
Eric W. Biederman54716e32010-02-14 03:27:03 +00001673 neigh_sysctl_register(idev->dev, idev->arp_parms, "ipv4", NULL);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001674 __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001675 &idev->cnf);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001676}
1677
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001678static void devinet_sysctl_unregister(struct in_device *idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001679{
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001680 __devinet_sysctl_unregister(&idev->cnf);
1681 neigh_sysctl_unregister(idev->arp_parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001682}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001684static struct ctl_table ctl_forward_entry[] = {
1685 {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001686 .procname = "ip_forward",
1687 .data = &ipv4_devconf.data[
Eric W. Biederman02291682010-02-14 03:25:51 +00001688 IPV4_DEVCONF_FORWARDING - 1],
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001689 .maxlen = sizeof(int),
1690 .mode = 0644,
1691 .proc_handler = devinet_sysctl_forward,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001692 .extra1 = &ipv4_devconf,
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001693 .extra2 = &init_net,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001694 },
1695 { },
1696};
Eric Dumazet2a75de02008-01-05 23:08:49 -08001697#endif
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001698
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001699static __net_init int devinet_init_net(struct net *net)
1700{
1701 int err;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001702 struct ipv4_devconf *all, *dflt;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001703#ifdef CONFIG_SYSCTL
1704 struct ctl_table *tbl = ctl_forward_entry;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001705 struct ctl_table_header *forw_hdr;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001706#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001707
1708 err = -ENOMEM;
1709 all = &ipv4_devconf;
1710 dflt = &ipv4_devconf_dflt;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001711
Octavian Purdila09ad9bc2009-11-25 15:14:13 -08001712 if (!net_eq(net, &init_net)) {
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001713 all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1714 if (all == NULL)
1715 goto err_alloc_all;
1716
1717 dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1718 if (dflt == NULL)
1719 goto err_alloc_dflt;
1720
Eric Dumazet2a75de02008-01-05 23:08:49 -08001721#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001722 tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1723 if (tbl == NULL)
1724 goto err_alloc_ctl;
1725
Eric W. Biederman02291682010-02-14 03:25:51 +00001726 tbl[0].data = &all->data[IPV4_DEVCONF_FORWARDING - 1];
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001727 tbl[0].extra1 = all;
1728 tbl[0].extra2 = net;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001729#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001730 }
1731
1732#ifdef CONFIG_SYSCTL
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001733 err = __devinet_sysctl_register(net, "all", all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001734 if (err < 0)
1735 goto err_reg_all;
1736
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001737 err = __devinet_sysctl_register(net, "default", dflt);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001738 if (err < 0)
1739 goto err_reg_dflt;
1740
1741 err = -ENOMEM;
Eric W. Biederman8607ddb2012-04-19 13:42:09 +00001742 forw_hdr = register_net_sysctl(net, "net/ipv4", tbl);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001743 if (forw_hdr == NULL)
1744 goto err_reg_ctl;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001745 net->ipv4.forw_hdr = forw_hdr;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001746#endif
1747
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001748 net->ipv4.devconf_all = all;
1749 net->ipv4.devconf_dflt = dflt;
1750 return 0;
1751
1752#ifdef CONFIG_SYSCTL
1753err_reg_ctl:
1754 __devinet_sysctl_unregister(dflt);
1755err_reg_dflt:
1756 __devinet_sysctl_unregister(all);
1757err_reg_all:
1758 if (tbl != ctl_forward_entry)
1759 kfree(tbl);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001760err_alloc_ctl:
Eric Dumazet2a75de02008-01-05 23:08:49 -08001761#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001762 if (dflt != &ipv4_devconf_dflt)
1763 kfree(dflt);
1764err_alloc_dflt:
1765 if (all != &ipv4_devconf)
1766 kfree(all);
1767err_alloc_all:
1768 return err;
1769}
1770
1771static __net_exit void devinet_exit_net(struct net *net)
1772{
Eric Dumazet2a75de02008-01-05 23:08:49 -08001773#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001774 struct ctl_table *tbl;
1775
1776 tbl = net->ipv4.forw_hdr->ctl_table_arg;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001777 unregister_net_sysctl_table(net->ipv4.forw_hdr);
1778 __devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1779 __devinet_sysctl_unregister(net->ipv4.devconf_all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001780 kfree(tbl);
Eric Dumazet2a75de02008-01-05 23:08:49 -08001781#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001782 kfree(net->ipv4.devconf_dflt);
1783 kfree(net->ipv4.devconf_all);
1784}
1785
1786static __net_initdata struct pernet_operations devinet_ops = {
1787 .init = devinet_init_net,
1788 .exit = devinet_exit_net,
1789};
1790
Thomas Graf9f0f7272010-11-16 04:32:48 +00001791static struct rtnl_af_ops inet_af_ops = {
1792 .family = AF_INET,
1793 .fill_link_af = inet_fill_link_af,
1794 .get_link_af_size = inet_get_link_af_size,
Thomas Grafcf7afbf2010-11-22 01:31:54 +00001795 .validate_link_af = inet_validate_link_af,
1796 .set_link_af = inet_set_link_af,
Thomas Graf9f0f7272010-11-16 04:32:48 +00001797};
1798
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799void __init devinet_init(void)
1800{
David S. Millerfd23c3b2011-02-18 12:42:28 -08001801 int i;
1802
1803 for (i = 0; i < IN4_ADDR_HSIZE; i++)
1804 INIT_HLIST_HEAD(&inet_addr_lst[i]);
1805
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001806 register_pernet_subsys(&devinet_ops);
1807
Linus Torvalds1da177e2005-04-16 15:20:36 -07001808 register_gifconf(PF_INET, inet_gifconf);
1809 register_netdevice_notifier(&ip_netdev_notifier);
Thomas Graf63f34442007-03-22 11:55:17 -07001810
Thomas Graf9f0f7272010-11-16 04:32:48 +00001811 rtnl_af_register(&inet_af_ops);
1812
Greg Rosec7ac8672011-06-10 01:27:09 +00001813 rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL);
1814 rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL);
1815 rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816}
1817