blob: 0715f4cac391f22e5167db52d35e9f09899fc932 [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>
53#ifdef CONFIG_SYSCTL
54#include <linux/sysctl.h>
55#endif
56#include <linux/kmod.h>
57
Arnaldo Carvalho de Melo14c85022005-12-27 02:43:12 -020058#include <net/arp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070059#include <net/ip.h>
60#include <net/route.h>
61#include <net/ip_fib.h>
Thomas Graf63f34442007-03-22 11:55:17 -070062#include <net/rtnetlink.h>
Pavel Emelyanov752d14d2007-12-16 13:31:47 -080063#include <net/net_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
Adrian Bunk0027ba82008-01-31 17:17:31 -080065static struct ipv4_devconf ipv4_devconf = {
Herbert Xu42f811b2007-06-04 23:34:44 -070066 .data = {
67 [NET_IPV4_CONF_ACCEPT_REDIRECTS - 1] = 1,
68 [NET_IPV4_CONF_SEND_REDIRECTS - 1] = 1,
69 [NET_IPV4_CONF_SECURE_REDIRECTS - 1] = 1,
70 [NET_IPV4_CONF_SHARED_MEDIA - 1] = 1,
71 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070072};
73
74static struct ipv4_devconf ipv4_devconf_dflt = {
Herbert Xu42f811b2007-06-04 23:34:44 -070075 .data = {
76 [NET_IPV4_CONF_ACCEPT_REDIRECTS - 1] = 1,
77 [NET_IPV4_CONF_SEND_REDIRECTS - 1] = 1,
78 [NET_IPV4_CONF_SECURE_REDIRECTS - 1] = 1,
79 [NET_IPV4_CONF_SHARED_MEDIA - 1] = 1,
80 [NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE - 1] = 1,
81 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070082};
83
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -080084#define IPV4_DEVCONF_DFLT(net, attr) \
85 IPV4_DEVCONF((*net->ipv4.devconf_dflt), attr)
Herbert Xu42f811b2007-06-04 23:34:44 -070086
Patrick McHardyef7c79e2007-06-05 12:38:30 -070087static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = {
Thomas Graf5c753972006-08-04 23:03:53 -070088 [IFA_LOCAL] = { .type = NLA_U32 },
89 [IFA_ADDRESS] = { .type = NLA_U32 },
90 [IFA_BROADCAST] = { .type = NLA_U32 },
Thomas Graf5176f912006-08-26 20:13:18 -070091 [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 },
Thomas Graf5c753972006-08-04 23:03:53 -070092};
93
Thomas Grafd6062cb2006-08-15 00:33:59 -070094static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095
Alan Sterne041c682006-03-27 01:16:30 -080096static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
98 int destroy);
99#ifdef CONFIG_SYSCTL
Pavel Emelyanov66f27a52007-12-02 00:55:54 +1100100static void devinet_sysctl_register(struct in_device *idev);
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800101static void devinet_sysctl_unregister(struct in_device *idev);
102#else
103static inline void devinet_sysctl_register(struct in_device *idev)
104{
105}
106static inline void devinet_sysctl_unregister(struct in_device *idev)
107{
108}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109#endif
110
111/* Locks all the inet devices. */
112
113static struct in_ifaddr *inet_alloc_ifa(void)
114{
Alexey Dobriyan93adcc82008-10-28 13:25:09 -0700115 return kzalloc(sizeof(struct in_ifaddr), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116}
117
118static void inet_rcu_free_ifa(struct rcu_head *head)
119{
120 struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head);
121 if (ifa->ifa_dev)
122 in_dev_put(ifa->ifa_dev);
123 kfree(ifa);
124}
125
126static inline void inet_free_ifa(struct in_ifaddr *ifa)
127{
128 call_rcu(&ifa->rcu_head, inet_rcu_free_ifa);
129}
130
131void in_dev_finish_destroy(struct in_device *idev)
132{
133 struct net_device *dev = idev->dev;
134
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700135 WARN_ON(idev->ifa_list);
136 WARN_ON(idev->mc_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137#ifdef NET_REFCNT_DEBUG
138 printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n",
139 idev, dev ? dev->name : "NIL");
140#endif
141 dev_put(dev);
142 if (!idev->dead)
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800143 pr_err("Freeing alive in_device %p\n", idev);
144 else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 kfree(idev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800147EXPORT_SYMBOL(in_dev_finish_destroy);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
Herbert Xu71e27da2007-06-04 23:36:06 -0700149static struct in_device *inetdev_init(struct net_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150{
151 struct in_device *in_dev;
152
153 ASSERT_RTNL();
154
Panagiotis Issaris0da974f2006-07-21 14:51:30 -0700155 in_dev = kzalloc(sizeof(*in_dev), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 if (!in_dev)
157 goto out;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900158 memcpy(&in_dev->cnf, dev_net(dev)->ipv4.devconf_dflt,
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -0800159 sizeof(in_dev->cnf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 in_dev->cnf.sysctl = NULL;
161 in_dev->dev = dev;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800162 in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl);
163 if (!in_dev->arp_parms)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 goto out_kfree;
Ben Hutchings0187bdf2008-06-19 16:15:47 -0700165 if (IPV4_DEVCONF(in_dev->cnf, FORWARDING))
166 dev_disable_lro(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 /* Reference in_dev->dev */
168 dev_hold(dev);
David L Stevens30c4cf52007-01-04 12:31:14 -0800169 /* Account for reference dev->ip_ptr (below) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170 in_dev_hold(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171
Pavel Emelyanov66f27a52007-12-02 00:55:54 +1100172 devinet_sysctl_register(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 ip_mc_init_dev(in_dev);
174 if (dev->flags & IFF_UP)
175 ip_mc_up(in_dev);
Jarek Poplawski483479e2007-01-09 14:38:31 -0800176
David L Stevens30c4cf52007-01-04 12:31:14 -0800177 /* we can receive as soon as ip_ptr is set -- do this last */
178 rcu_assign_pointer(dev->ip_ptr, in_dev);
Jarek Poplawski483479e2007-01-09 14:38:31 -0800179out:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 return in_dev;
181out_kfree:
182 kfree(in_dev);
183 in_dev = NULL;
184 goto out;
185}
186
187static void in_dev_rcu_put(struct rcu_head *head)
188{
189 struct in_device *idev = container_of(head, struct in_device, rcu_head);
190 in_dev_put(idev);
191}
192
193static void inetdev_destroy(struct in_device *in_dev)
194{
195 struct in_ifaddr *ifa;
196 struct net_device *dev;
197
198 ASSERT_RTNL();
199
200 dev = in_dev->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201
202 in_dev->dead = 1;
203
204 ip_mc_destroy_dev(in_dev);
205
206 while ((ifa = in_dev->ifa_list) != NULL) {
207 inet_del_ifa(in_dev, &in_dev->ifa_list, 0);
208 inet_free_ifa(ifa);
209 }
210
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 dev->ip_ptr = NULL;
212
Pavel Emelyanov51602b22007-12-11 02:17:40 -0800213 devinet_sysctl_unregister(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 neigh_parms_release(&arp_tbl, in_dev->arp_parms);
215 arp_ifdown(dev);
216
217 call_rcu(&in_dev->rcu_head, in_dev_rcu_put);
218}
219
Al Viroff428d72006-09-26 22:13:35 -0700220int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221{
222 rcu_read_lock();
223 for_primary_ifa(in_dev) {
224 if (inet_ifa_match(a, ifa)) {
225 if (!b || inet_ifa_match(b, ifa)) {
226 rcu_read_unlock();
227 return 1;
228 }
229 }
230 } endfor_ifa(in_dev);
231 rcu_read_unlock();
232 return 0;
233}
234
Thomas Grafd6062cb2006-08-15 00:33:59 -0700235static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
236 int destroy, struct nlmsghdr *nlh, u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237{
Harald Welte8f937c62005-05-29 20:23:46 -0700238 struct in_ifaddr *promote = NULL;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800239 struct in_ifaddr *ifa, *ifa1 = *ifap;
240 struct in_ifaddr *last_prim = in_dev->ifa_list;
241 struct in_ifaddr *prev_prom = NULL;
242 int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
244 ASSERT_RTNL();
245
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900246 /* 1. Deleting primary ifaddr forces deletion all secondaries
Harald Welte8f937c62005-05-29 20:23:46 -0700247 * unless alias promotion is set
248 **/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
250 if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 struct in_ifaddr **ifap1 = &ifa1->ifa_next;
252
253 while ((ifa = *ifap1) != NULL) {
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900254 if (!(ifa->ifa_flags & IFA_F_SECONDARY) &&
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800255 ifa1->ifa_scope <= ifa->ifa_scope)
256 last_prim = ifa;
257
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 if (!(ifa->ifa_flags & IFA_F_SECONDARY) ||
259 ifa1->ifa_mask != ifa->ifa_mask ||
260 !inet_ifa_match(ifa1->ifa_address, ifa)) {
261 ifap1 = &ifa->ifa_next;
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800262 prev_prom = ifa;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 continue;
264 }
265
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800266 if (!do_promote) {
Harald Welte8f937c62005-05-29 20:23:46 -0700267 *ifap1 = ifa->ifa_next;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268
Thomas Grafd6062cb2006-08-15 00:33:59 -0700269 rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800270 blocking_notifier_call_chain(&inetaddr_chain,
271 NETDEV_DOWN, ifa);
Harald Welte8f937c62005-05-29 20:23:46 -0700272 inet_free_ifa(ifa);
273 } else {
274 promote = ifa;
275 break;
276 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 }
278 }
279
280 /* 2. Unlink it */
281
282 *ifap = ifa1->ifa_next;
283
284 /* 3. Announce address deletion */
285
286 /* Send message first, then call notifier.
287 At first sight, FIB update triggered by notifier
288 will refer to already deleted ifaddr, that could confuse
289 netlink listeners. It is not true: look, gated sees
290 that route deleted and if it still thinks that ifaddr
291 is valid, it will try to restore deleted routes... Grr.
292 So that, this order is correct.
293 */
Thomas Grafd6062cb2006-08-15 00:33:59 -0700294 rtmsg_ifa(RTM_DELADDR, ifa1, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800295 blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1);
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800296
297 if (promote) {
298
299 if (prev_prom) {
300 prev_prom->ifa_next = promote->ifa_next;
301 promote->ifa_next = last_prim->ifa_next;
302 last_prim->ifa_next = promote;
303 }
304
305 promote->ifa_flags &= ~IFA_F_SECONDARY;
Thomas Grafd6062cb2006-08-15 00:33:59 -0700306 rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800307 blocking_notifier_call_chain(&inetaddr_chain,
308 NETDEV_UP, promote);
Jamal Hadi Salim0ff60a42005-11-22 14:47:37 -0800309 for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) {
310 if (ifa1->ifa_mask != ifa->ifa_mask ||
311 !inet_ifa_match(ifa1->ifa_address, ifa))
312 continue;
313 fib_add_ifaddr(ifa);
314 }
315
316 }
Herbert Xu63630972007-06-07 18:35:38 -0700317 if (destroy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 inet_free_ifa(ifa1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319}
320
Thomas Grafd6062cb2006-08-15 00:33:59 -0700321static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
322 int destroy)
323{
324 __inet_del_ifa(in_dev, ifap, destroy, NULL, 0);
325}
326
327static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
328 u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329{
330 struct in_device *in_dev = ifa->ifa_dev;
331 struct in_ifaddr *ifa1, **ifap, **last_primary;
332
333 ASSERT_RTNL();
334
335 if (!ifa->ifa_local) {
336 inet_free_ifa(ifa);
337 return 0;
338 }
339
340 ifa->ifa_flags &= ~IFA_F_SECONDARY;
341 last_primary = &in_dev->ifa_list;
342
343 for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL;
344 ifap = &ifa1->ifa_next) {
345 if (!(ifa1->ifa_flags & IFA_F_SECONDARY) &&
346 ifa->ifa_scope <= ifa1->ifa_scope)
347 last_primary = &ifa1->ifa_next;
348 if (ifa1->ifa_mask == ifa->ifa_mask &&
349 inet_ifa_match(ifa1->ifa_address, ifa)) {
350 if (ifa1->ifa_local == ifa->ifa_local) {
351 inet_free_ifa(ifa);
352 return -EEXIST;
353 }
354 if (ifa1->ifa_scope != ifa->ifa_scope) {
355 inet_free_ifa(ifa);
356 return -EINVAL;
357 }
358 ifa->ifa_flags |= IFA_F_SECONDARY;
359 }
360 }
361
362 if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
363 net_srandom(ifa->ifa_local);
364 ifap = last_primary;
365 }
366
367 ifa->ifa_next = *ifap;
368 *ifap = ifa;
369
370 /* Send message first, then call notifier.
371 Notifier will trigger FIB update, so that
372 listeners of netlink will know about new ifaddr */
Thomas Grafd6062cb2006-08-15 00:33:59 -0700373 rtmsg_ifa(RTM_NEWADDR, ifa, nlh, pid);
Alan Sterne041c682006-03-27 01:16:30 -0800374 blocking_notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375
376 return 0;
377}
378
Thomas Grafd6062cb2006-08-15 00:33:59 -0700379static int inet_insert_ifa(struct in_ifaddr *ifa)
380{
381 return __inet_insert_ifa(ifa, NULL, 0);
382}
383
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa)
385{
Herbert Xue5ed6392005-10-03 14:35:55 -0700386 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387
388 ASSERT_RTNL();
389
390 if (!in_dev) {
Herbert Xu71e27da2007-06-04 23:36:06 -0700391 inet_free_ifa(ifa);
392 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 }
Herbert Xu71e27da2007-06-04 23:36:06 -0700394 ipv4_devconf_setall(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 if (ifa->ifa_dev != in_dev) {
Ilpo Järvinen547b7922008-07-25 21:43:18 -0700396 WARN_ON(ifa->ifa_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 in_dev_hold(in_dev);
398 ifa->ifa_dev = in_dev;
399 }
Joe Perchesf97c1e02007-12-16 13:45:43 -0800400 if (ipv4_is_loopback(ifa->ifa_local))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 ifa->ifa_scope = RT_SCOPE_HOST;
402 return inet_insert_ifa(ifa);
403}
404
Denis V. Lunev7fee0ca2008-01-21 17:32:38 -0800405struct in_device *inetdev_by_index(struct net *net, int ifindex)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406{
407 struct net_device *dev;
408 struct in_device *in_dev = NULL;
Eric Dumazetc148fc22009-11-01 19:23:04 +0000409
410 rcu_read_lock();
411 dev = dev_get_by_index_rcu(net, ifindex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 if (dev)
413 in_dev = in_dev_get(dev);
Eric Dumazetc148fc22009-11-01 19:23:04 +0000414 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 return in_dev;
416}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800417EXPORT_SYMBOL(inetdev_by_index);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418
419/* Called only from RTNL semaphored context. No locks. */
420
Al Viro60cad5d2006-09-26 22:17:09 -0700421struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, __be32 prefix,
422 __be32 mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423{
424 ASSERT_RTNL();
425
426 for_primary_ifa(in_dev) {
427 if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa))
428 return ifa;
429 } endfor_ifa(in_dev);
430 return NULL;
431}
432
433static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
434{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900435 struct net *net = sock_net(skb->sk);
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700436 struct nlattr *tb[IFA_MAX+1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 struct in_device *in_dev;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700438 struct ifaddrmsg *ifm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 struct in_ifaddr *ifa, **ifap;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700440 int err = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 ASSERT_RTNL();
443
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700444 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
445 if (err < 0)
446 goto errout;
447
448 ifm = nlmsg_data(nlh);
Denis V. Lunev7fee0ca2008-01-21 17:32:38 -0800449 in_dev = inetdev_by_index(net, ifm->ifa_index);
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700450 if (in_dev == NULL) {
451 err = -ENODEV;
452 goto errout;
453 }
454
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 __in_dev_put(in_dev);
456
457 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
458 ifap = &ifa->ifa_next) {
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700459 if (tb[IFA_LOCAL] &&
Al Viroa7a628c2006-09-26 22:16:43 -0700460 ifa->ifa_local != nla_get_be32(tb[IFA_LOCAL]))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 continue;
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700462
463 if (tb[IFA_LABEL] && nla_strcmp(tb[IFA_LABEL], ifa->ifa_label))
464 continue;
465
466 if (tb[IFA_ADDRESS] &&
467 (ifm->ifa_prefixlen != ifa->ifa_prefixlen ||
Al Viroa7a628c2006-09-26 22:16:43 -0700468 !inet_ifa_match(nla_get_be32(tb[IFA_ADDRESS]), ifa)))
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700469 continue;
470
Thomas Grafd6062cb2006-08-15 00:33:59 -0700471 __inet_del_ifa(in_dev, ifap, 1, nlh, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 return 0;
473 }
Thomas Grafdfdd5fd2006-08-04 23:04:17 -0700474
475 err = -EADDRNOTAVAIL;
476errout:
477 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478}
479
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800480static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481{
Thomas Graf5c753972006-08-04 23:03:53 -0700482 struct nlattr *tb[IFA_MAX+1];
483 struct in_ifaddr *ifa;
484 struct ifaddrmsg *ifm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 struct net_device *dev;
486 struct in_device *in_dev;
Denis V. Lunev7b218572008-01-31 18:47:00 -0800487 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
Thomas Graf5c753972006-08-04 23:03:53 -0700489 err = nlmsg_parse(nlh, sizeof(*ifm), tb, IFA_MAX, ifa_ipv4_policy);
490 if (err < 0)
491 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
Thomas Graf5c753972006-08-04 23:03:53 -0700493 ifm = nlmsg_data(nlh);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800494 err = -EINVAL;
495 if (ifm->ifa_prefixlen > 32 || tb[IFA_LOCAL] == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700496 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800498 dev = __dev_get_by_index(net, ifm->ifa_index);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800499 err = -ENODEV;
500 if (dev == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700501 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Thomas Graf5c753972006-08-04 23:03:53 -0700503 in_dev = __in_dev_get_rtnl(dev);
Denis V. Lunev7b218572008-01-31 18:47:00 -0800504 err = -ENOBUFS;
505 if (in_dev == NULL)
Herbert Xu71e27da2007-06-04 23:36:06 -0700506 goto errout;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
Thomas Graf5c753972006-08-04 23:03:53 -0700508 ifa = inet_alloc_ifa();
Denis V. Lunev7b218572008-01-31 18:47:00 -0800509 if (ifa == NULL)
Thomas Graf5c753972006-08-04 23:03:53 -0700510 /*
511 * A potential indev allocation can be left alive, it stays
512 * assigned to its device and is destroy with it.
513 */
Thomas Graf5c753972006-08-04 23:03:53 -0700514 goto errout;
Thomas Graf5c753972006-08-04 23:03:53 -0700515
Pavel Emelyanova4e65d32007-12-07 23:55:43 -0800516 ipv4_devconf_setall(in_dev);
Thomas Graf5c753972006-08-04 23:03:53 -0700517 in_dev_hold(in_dev);
518
519 if (tb[IFA_ADDRESS] == NULL)
520 tb[IFA_ADDRESS] = tb[IFA_LOCAL];
521
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 ifa->ifa_prefixlen = ifm->ifa_prefixlen;
523 ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 ifa->ifa_flags = ifm->ifa_flags;
525 ifa->ifa_scope = ifm->ifa_scope;
Thomas Graf5c753972006-08-04 23:03:53 -0700526 ifa->ifa_dev = in_dev;
527
Al Viroa7a628c2006-09-26 22:16:43 -0700528 ifa->ifa_local = nla_get_be32(tb[IFA_LOCAL]);
529 ifa->ifa_address = nla_get_be32(tb[IFA_ADDRESS]);
Thomas Graf5c753972006-08-04 23:03:53 -0700530
531 if (tb[IFA_BROADCAST])
Al Viroa7a628c2006-09-26 22:16:43 -0700532 ifa->ifa_broadcast = nla_get_be32(tb[IFA_BROADCAST]);
Thomas Graf5c753972006-08-04 23:03:53 -0700533
Thomas Graf5c753972006-08-04 23:03:53 -0700534 if (tb[IFA_LABEL])
535 nla_strlcpy(ifa->ifa_label, tb[IFA_LABEL], IFNAMSIZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 else
537 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
538
Thomas Graf5c753972006-08-04 23:03:53 -0700539 return ifa;
540
541errout:
542 return ERR_PTR(err);
543}
544
545static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
546{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +0900547 struct net *net = sock_net(skb->sk);
Thomas Graf5c753972006-08-04 23:03:53 -0700548 struct in_ifaddr *ifa;
549
550 ASSERT_RTNL();
551
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -0800552 ifa = rtm_to_ifaddr(net, nlh);
Thomas Graf5c753972006-08-04 23:03:53 -0700553 if (IS_ERR(ifa))
554 return PTR_ERR(ifa);
555
Thomas Grafd6062cb2006-08-15 00:33:59 -0700556 return __inet_insert_ifa(ifa, nlh, NETLINK_CB(skb).pid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557}
558
559/*
560 * Determine a default network mask, based on the IP address.
561 */
562
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800563static inline int inet_abc_len(__be32 addr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564{
565 int rc = -1; /* Something else, probably a multicast. */
566
Joe Perchesf97c1e02007-12-16 13:45:43 -0800567 if (ipv4_is_zeronet(addr))
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900568 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 else {
Al Viro714e85b2006-11-14 20:51:49 -0800570 __u32 haddr = ntohl(addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
Al Viro714e85b2006-11-14 20:51:49 -0800572 if (IN_CLASSA(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 rc = 8;
Al Viro714e85b2006-11-14 20:51:49 -0800574 else if (IN_CLASSB(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 rc = 16;
Al Viro714e85b2006-11-14 20:51:49 -0800576 else if (IN_CLASSC(haddr))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 rc = 24;
578 }
579
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +0900580 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581}
582
583
Denis V. Luneve5b13cb2008-02-28 20:51:43 -0800584int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585{
586 struct ifreq ifr;
587 struct sockaddr_in sin_orig;
588 struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
589 struct in_device *in_dev;
590 struct in_ifaddr **ifap = NULL;
591 struct in_ifaddr *ifa = NULL;
592 struct net_device *dev;
593 char *colon;
594 int ret = -EFAULT;
595 int tryaddrmatch = 0;
596
597 /*
598 * Fetch the caller's info block into kernel space
599 */
600
601 if (copy_from_user(&ifr, arg, sizeof(struct ifreq)))
602 goto out;
603 ifr.ifr_name[IFNAMSIZ - 1] = 0;
604
605 /* save original address for comparison */
606 memcpy(&sin_orig, sin, sizeof(*sin));
607
608 colon = strchr(ifr.ifr_name, ':');
609 if (colon)
610 *colon = 0;
611
Denis V. Luneve5b13cb2008-02-28 20:51:43 -0800612 dev_load(net, ifr.ifr_name);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Stephen Hemminger132adf52007-03-08 20:44:43 -0800614 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 case SIOCGIFADDR: /* Get interface address */
616 case SIOCGIFBRDADDR: /* Get the broadcast address */
617 case SIOCGIFDSTADDR: /* Get the destination address */
618 case SIOCGIFNETMASK: /* Get the netmask for the interface */
619 /* Note that these ioctls will not sleep,
620 so that we do not impose a lock.
621 One day we will be forced to put shlock here (I mean SMP)
622 */
623 tryaddrmatch = (sin_orig.sin_family == AF_INET);
624 memset(sin, 0, sizeof(*sin));
625 sin->sin_family = AF_INET;
626 break;
627
628 case SIOCSIFFLAGS:
629 ret = -EACCES;
630 if (!capable(CAP_NET_ADMIN))
631 goto out;
632 break;
633 case SIOCSIFADDR: /* Set interface address (and family) */
634 case SIOCSIFBRDADDR: /* Set the broadcast address */
635 case SIOCSIFDSTADDR: /* Set the destination address */
636 case SIOCSIFNETMASK: /* Set the netmask for the interface */
637 ret = -EACCES;
638 if (!capable(CAP_NET_ADMIN))
639 goto out;
640 ret = -EINVAL;
641 if (sin->sin_family != AF_INET)
642 goto out;
643 break;
644 default:
645 ret = -EINVAL;
646 goto out;
647 }
648
649 rtnl_lock();
650
651 ret = -ENODEV;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800652 dev = __dev_get_by_name(net, ifr.ifr_name);
653 if (!dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654 goto done;
655
656 if (colon)
657 *colon = ':';
658
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800659 in_dev = __in_dev_get_rtnl(dev);
660 if (in_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 if (tryaddrmatch) {
662 /* Matthias Andree */
663 /* compare label and address (4.4BSD style) */
664 /* note: we only do this for a limited set of ioctls
665 and only if the original address family was AF_INET.
666 This is checked above. */
667 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
668 ifap = &ifa->ifa_next) {
669 if (!strcmp(ifr.ifr_name, ifa->ifa_label) &&
670 sin_orig.sin_addr.s_addr ==
671 ifa->ifa_address) {
672 break; /* found */
673 }
674 }
675 }
676 /* we didn't get a match, maybe the application is
677 4.3BSD-style and passed in junk so we fall back to
678 comparing just the label */
679 if (!ifa) {
680 for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL;
681 ifap = &ifa->ifa_next)
682 if (!strcmp(ifr.ifr_name, ifa->ifa_label))
683 break;
684 }
685 }
686
687 ret = -EADDRNOTAVAIL;
688 if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS)
689 goto done;
690
Stephen Hemminger132adf52007-03-08 20:44:43 -0800691 switch (cmd) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692 case SIOCGIFADDR: /* Get interface address */
693 sin->sin_addr.s_addr = ifa->ifa_local;
694 goto rarok;
695
696 case SIOCGIFBRDADDR: /* Get the broadcast address */
697 sin->sin_addr.s_addr = ifa->ifa_broadcast;
698 goto rarok;
699
700 case SIOCGIFDSTADDR: /* Get the destination address */
701 sin->sin_addr.s_addr = ifa->ifa_address;
702 goto rarok;
703
704 case SIOCGIFNETMASK: /* Get the netmask for the interface */
705 sin->sin_addr.s_addr = ifa->ifa_mask;
706 goto rarok;
707
708 case SIOCSIFFLAGS:
709 if (colon) {
710 ret = -EADDRNOTAVAIL;
711 if (!ifa)
712 break;
713 ret = 0;
714 if (!(ifr.ifr_flags & IFF_UP))
715 inet_del_ifa(in_dev, ifap, 1);
716 break;
717 }
718 ret = dev_change_flags(dev, ifr.ifr_flags);
719 break;
720
721 case SIOCSIFADDR: /* Set interface address (and family) */
722 ret = -EINVAL;
723 if (inet_abc_len(sin->sin_addr.s_addr) < 0)
724 break;
725
726 if (!ifa) {
727 ret = -ENOBUFS;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800728 ifa = inet_alloc_ifa();
729 if (!ifa)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730 break;
731 if (colon)
732 memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ);
733 else
734 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
735 } else {
736 ret = 0;
737 if (ifa->ifa_local == sin->sin_addr.s_addr)
738 break;
739 inet_del_ifa(in_dev, ifap, 0);
740 ifa->ifa_broadcast = 0;
Bjorn Mork148f9722008-02-26 18:17:53 -0800741 ifa->ifa_scope = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 }
743
744 ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr;
745
746 if (!(dev->flags & IFF_POINTOPOINT)) {
747 ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address);
748 ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen);
749 if ((dev->flags & IFF_BROADCAST) &&
750 ifa->ifa_prefixlen < 31)
751 ifa->ifa_broadcast = ifa->ifa_address |
752 ~ifa->ifa_mask;
753 } else {
754 ifa->ifa_prefixlen = 32;
755 ifa->ifa_mask = inet_make_mask(32);
756 }
757 ret = inet_set_ifa(dev, ifa);
758 break;
759
760 case SIOCSIFBRDADDR: /* Set the broadcast address */
761 ret = 0;
762 if (ifa->ifa_broadcast != sin->sin_addr.s_addr) {
763 inet_del_ifa(in_dev, ifap, 0);
764 ifa->ifa_broadcast = sin->sin_addr.s_addr;
765 inet_insert_ifa(ifa);
766 }
767 break;
768
769 case SIOCSIFDSTADDR: /* Set the destination address */
770 ret = 0;
771 if (ifa->ifa_address == sin->sin_addr.s_addr)
772 break;
773 ret = -EINVAL;
774 if (inet_abc_len(sin->sin_addr.s_addr) < 0)
775 break;
776 ret = 0;
777 inet_del_ifa(in_dev, ifap, 0);
778 ifa->ifa_address = sin->sin_addr.s_addr;
779 inet_insert_ifa(ifa);
780 break;
781
782 case SIOCSIFNETMASK: /* Set the netmask for the interface */
783
784 /*
785 * The mask we set must be legal.
786 */
787 ret = -EINVAL;
788 if (bad_mask(sin->sin_addr.s_addr, 0))
789 break;
790 ret = 0;
791 if (ifa->ifa_mask != sin->sin_addr.s_addr) {
Al Viroa144ea42006-09-28 18:00:55 -0700792 __be32 old_mask = ifa->ifa_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793 inet_del_ifa(in_dev, ifap, 0);
794 ifa->ifa_mask = sin->sin_addr.s_addr;
795 ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask);
796
797 /* See if current broadcast address matches
798 * with current netmask, then recalculate
799 * the broadcast address. Otherwise it's a
800 * funny address, so don't touch it since
801 * the user seems to know what (s)he's doing...
802 */
803 if ((dev->flags & IFF_BROADCAST) &&
804 (ifa->ifa_prefixlen < 31) &&
805 (ifa->ifa_broadcast ==
David Engeldcab5e12005-10-21 22:09:16 -0500806 (ifa->ifa_local|~old_mask))) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 ifa->ifa_broadcast = (ifa->ifa_local |
808 ~sin->sin_addr.s_addr);
809 }
810 inet_insert_ifa(ifa);
811 }
812 break;
813 }
814done:
815 rtnl_unlock();
816out:
817 return ret;
818rarok:
819 rtnl_unlock();
820 ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0;
821 goto out;
822}
823
824static int inet_gifconf(struct net_device *dev, char __user *buf, int len)
825{
Herbert Xue5ed6392005-10-03 14:35:55 -0700826 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827 struct in_ifaddr *ifa;
828 struct ifreq ifr;
829 int done = 0;
830
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800831 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 goto out;
833
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800834 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 if (!buf) {
836 done += sizeof(ifr);
837 continue;
838 }
839 if (len < (int) sizeof(ifr))
840 break;
841 memset(&ifr, 0, sizeof(struct ifreq));
842 if (ifa->ifa_label)
843 strcpy(ifr.ifr_name, ifa->ifa_label);
844 else
845 strcpy(ifr.ifr_name, dev->name);
846
847 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET;
848 (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr =
849 ifa->ifa_local;
850
851 if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) {
852 done = -EFAULT;
853 break;
854 }
855 buf += sizeof(struct ifreq);
856 len -= sizeof(struct ifreq);
857 done += sizeof(struct ifreq);
858 }
859out:
860 return done;
861}
862
Al Viroa61ced52006-09-26 21:27:54 -0700863__be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864{
Al Viroa61ced52006-09-26 21:27:54 -0700865 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 struct in_device *in_dev;
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900867 struct net *net = dev_net(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
869 rcu_read_lock();
Herbert Xue5ed6392005-10-03 14:35:55 -0700870 in_dev = __in_dev_get_rcu(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 if (!in_dev)
872 goto no_in_dev;
873
874 for_primary_ifa(in_dev) {
875 if (ifa->ifa_scope > scope)
876 continue;
877 if (!dst || inet_ifa_match(dst, ifa)) {
878 addr = ifa->ifa_local;
879 break;
880 }
881 if (!addr)
882 addr = ifa->ifa_local;
883 } endfor_ifa(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
885 if (addr)
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800886 goto out_unlock;
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800887no_in_dev:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
889 /* Not loopback addresses on loopback should be preferred
890 in this case. It is importnat that lo is the first interface
891 in dev_base list.
892 */
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800893 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800894 in_dev = __in_dev_get_rcu(dev);
895 if (!in_dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 continue;
897
898 for_primary_ifa(in_dev) {
899 if (ifa->ifa_scope != RT_SCOPE_LINK &&
900 ifa->ifa_scope <= scope) {
901 addr = ifa->ifa_local;
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800902 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903 }
904 } endfor_ifa(in_dev);
905 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800906out_unlock:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 return addr;
909}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800910EXPORT_SYMBOL(inet_select_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
Al Viro60cad5d2006-09-26 22:17:09 -0700912static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst,
913 __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914{
915 int same = 0;
Al Viroa144ea42006-09-28 18:00:55 -0700916 __be32 addr = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
918 for_ifa(in_dev) {
919 if (!addr &&
920 (local == ifa->ifa_local || !local) &&
921 ifa->ifa_scope <= scope) {
922 addr = ifa->ifa_local;
923 if (same)
924 break;
925 }
926 if (!same) {
927 same = (!local || inet_ifa_match(local, ifa)) &&
928 (!dst || inet_ifa_match(dst, ifa));
929 if (same && addr) {
930 if (local || !dst)
931 break;
932 /* Is the selected addr into dst subnet? */
933 if (inet_ifa_match(addr, ifa))
934 break;
935 /* No, then can we use new local src? */
936 if (ifa->ifa_scope <= scope) {
937 addr = ifa->ifa_local;
938 break;
939 }
940 /* search for large dst subnet for addr */
941 same = 0;
942 }
943 }
944 } endfor_ifa(in_dev);
945
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800946 return same ? addr : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947}
948
949/*
950 * Confirm that local IP address exists using wildcards:
Denis V. Lunev9bd85e32008-01-14 23:05:55 -0800951 * - in_dev: only on this interface, 0=any interface
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 * - dst: only in the same subnet as dst, 0=any dst
953 * - local: address, 0=autoselect the local address
954 * - scope: maximum allowed scope value for the local address
955 */
Denis V. Lunev9bd85e32008-01-14 23:05:55 -0800956__be32 inet_confirm_addr(struct in_device *in_dev,
957 __be32 dst, __be32 local, int scope)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958{
Al Viro60cad5d2006-09-26 22:17:09 -0700959 __be32 addr = 0;
Denis V. Lunev9bd85e32008-01-14 23:05:55 -0800960 struct net_device *dev;
Denis V. Lunev39a6d062008-01-14 23:06:19 -0800961 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962
Denis V. Lunev39a6d062008-01-14 23:06:19 -0800963 if (scope != RT_SCOPE_LINK)
Denis V. Lunev9bd85e32008-01-14 23:05:55 -0800964 return confirm_addr_indev(in_dev, dst, local, scope);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +0900966 net = dev_net(in_dev->dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 rcu_read_lock();
Eric Dumazetc6d14c82009-11-04 05:43:23 -0800968 for_each_netdev_rcu(net, dev) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800969 in_dev = __in_dev_get_rcu(dev);
970 if (in_dev) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 addr = confirm_addr_indev(in_dev, dst, local, scope);
972 if (addr)
973 break;
974 }
975 }
976 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977
978 return addr;
979}
980
981/*
982 * Device notifier
983 */
984
985int register_inetaddr_notifier(struct notifier_block *nb)
986{
Alan Sterne041c682006-03-27 01:16:30 -0800987 return blocking_notifier_chain_register(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800989EXPORT_SYMBOL(register_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990
991int unregister_inetaddr_notifier(struct notifier_block *nb)
992{
Alan Sterne041c682006-03-27 01:16:30 -0800993 return blocking_notifier_chain_unregister(&inetaddr_chain, nb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994}
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800995EXPORT_SYMBOL(unregister_inetaddr_notifier);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996
Eric Dumazet9f9354b2009-11-04 22:05:10 -0800997/* Rename ifa_labels for a device name change. Make some effort to preserve
998 * existing alias numbering and to create unique labels if possible.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999*/
1000static void inetdev_changename(struct net_device *dev, struct in_device *in_dev)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001001{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 struct in_ifaddr *ifa;
1003 int named = 0;
1004
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001005 for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
1006 char old[IFNAMSIZ], *dot;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
1008 memcpy(old, ifa->ifa_label, IFNAMSIZ);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001009 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010 if (named++ == 0)
Thomas Graf573bf472008-06-10 15:40:04 -07001011 goto skip;
Mark McLoughlin44344b22008-01-04 00:56:25 -08001012 dot = strchr(old, ':');
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001013 if (dot == NULL) {
1014 sprintf(old, ":%d", named);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001015 dot = old;
1016 }
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001017 if (strlen(dot) + strlen(dev->name) < IFNAMSIZ)
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001018 strcat(ifa->ifa_label, dot);
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001019 else
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001020 strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot);
Thomas Graf573bf472008-06-10 15:40:04 -07001021skip:
1022 rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0);
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001023 }
1024}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025
Breno Leitao06770842008-09-02 17:28:58 -07001026static inline bool inetdev_valid_mtu(unsigned mtu)
1027{
1028 return mtu >= 68;
1029}
1030
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031/* Called only under RTNL semaphore */
1032
1033static int inetdev_event(struct notifier_block *this, unsigned long event,
1034 void *ptr)
1035{
1036 struct net_device *dev = ptr;
Herbert Xue5ed6392005-10-03 14:35:55 -07001037 struct in_device *in_dev = __in_dev_get_rtnl(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
1039 ASSERT_RTNL();
1040
1041 if (!in_dev) {
Herbert Xu8030f542007-02-22 01:53:47 +09001042 if (event == NETDEV_REGISTER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 in_dev = inetdev_init(dev);
Herbert Xub217d612007-07-30 17:04:52 -07001044 if (!in_dev)
1045 return notifier_from_errno(-ENOMEM);
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001046 if (dev->flags & IFF_LOOPBACK) {
Herbert Xu42f811b2007-06-04 23:34:44 -07001047 IN_DEV_CONF_SET(in_dev, NOXFRM, 1);
1048 IN_DEV_CONF_SET(in_dev, NOPOLICY, 1);
Herbert Xu8030f542007-02-22 01:53:47 +09001049 }
Breno Leitao06770842008-09-02 17:28:58 -07001050 } else if (event == NETDEV_CHANGEMTU) {
1051 /* Re-enabling IP */
1052 if (inetdev_valid_mtu(dev->mtu))
1053 in_dev = inetdev_init(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 }
1055 goto out;
1056 }
1057
1058 switch (event) {
1059 case NETDEV_REGISTER:
1060 printk(KERN_DEBUG "inetdev_event: bug\n");
1061 dev->ip_ptr = NULL;
1062 break;
1063 case NETDEV_UP:
Breno Leitao06770842008-09-02 17:28:58 -07001064 if (!inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 break;
Eric W. Biederman0cc217e2007-09-26 22:10:06 -07001066 if (dev->flags & IFF_LOOPBACK) {
Eric Dumazet9f9354b2009-11-04 22:05:10 -08001067 struct in_ifaddr *ifa = inet_alloc_ifa();
1068
1069 if (ifa) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 ifa->ifa_local =
1071 ifa->ifa_address = htonl(INADDR_LOOPBACK);
1072 ifa->ifa_prefixlen = 8;
1073 ifa->ifa_mask = inet_make_mask(8);
1074 in_dev_hold(in_dev);
1075 ifa->ifa_dev = in_dev;
1076 ifa->ifa_scope = RT_SCOPE_HOST;
1077 memcpy(ifa->ifa_label, dev->name, IFNAMSIZ);
1078 inet_insert_ifa(ifa);
1079 }
1080 }
1081 ip_mc_up(in_dev);
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001082 /* fall through */
1083 case NETDEV_CHANGEADDR:
Stephen Hemmingera21090c2009-10-07 03:18:17 -07001084 /* Send gratuitous ARP to notify of link change */
1085 if (IN_DEV_ARP_NOTIFY(in_dev)) {
1086 struct in_ifaddr *ifa = in_dev->ifa_list;
1087
1088 if (ifa)
1089 arp_send(ARPOP_REQUEST, ETH_P_ARP,
1090 ifa->ifa_address, dev,
1091 ifa->ifa_address, NULL,
1092 dev->dev_addr, NULL);
1093 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001094 break;
1095 case NETDEV_DOWN:
1096 ip_mc_down(in_dev);
1097 break;
Moni Shoua75c78502009-09-15 02:37:40 -07001098 case NETDEV_BONDING_OLDTYPE:
1099 ip_mc_unmap(in_dev);
1100 break;
1101 case NETDEV_BONDING_NEWTYPE:
1102 ip_mc_remap(in_dev);
1103 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104 case NETDEV_CHANGEMTU:
Breno Leitao06770842008-09-02 17:28:58 -07001105 if (inetdev_valid_mtu(dev->mtu))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 break;
Breno Leitao06770842008-09-02 17:28:58 -07001107 /* disable IP when MTU is not enough */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 case NETDEV_UNREGISTER:
1109 inetdev_destroy(in_dev);
1110 break;
1111 case NETDEV_CHANGENAME:
1112 /* Do not notify about label change, this event is
1113 * not interesting to applications using netlink.
1114 */
1115 inetdev_changename(dev, in_dev);
1116
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001117 devinet_sysctl_unregister(in_dev);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001118 devinet_sysctl_register(in_dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 break;
1120 }
1121out:
1122 return NOTIFY_DONE;
1123}
1124
1125static struct notifier_block ip_netdev_notifier = {
Jianjun Kong539afed2008-11-03 02:48:48 -08001126 .notifier_call = inetdev_event,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127};
1128
Thomas Graf339bf982006-11-10 14:10:15 -08001129static inline size_t inet_nlmsg_size(void)
1130{
1131 return NLMSG_ALIGN(sizeof(struct ifaddrmsg))
1132 + nla_total_size(4) /* IFA_ADDRESS */
1133 + nla_total_size(4) /* IFA_LOCAL */
1134 + nla_total_size(4) /* IFA_BROADCAST */
Thomas Graf339bf982006-11-10 14:10:15 -08001135 + nla_total_size(IFNAMSIZ); /* IFA_LABEL */
1136}
1137
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa,
Jamal Hadi Salimb6544c02005-06-18 22:54:12 -07001139 u32 pid, u32 seq, int event, unsigned int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140{
1141 struct ifaddrmsg *ifm;
1142 struct nlmsghdr *nlh;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143
Thomas Graf47f68512006-08-04 23:04:36 -07001144 nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), flags);
1145 if (nlh == NULL)
Patrick McHardy26932562007-01-31 23:16:40 -08001146 return -EMSGSIZE;
Thomas Graf47f68512006-08-04 23:04:36 -07001147
1148 ifm = nlmsg_data(nlh);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 ifm->ifa_family = AF_INET;
1150 ifm->ifa_prefixlen = ifa->ifa_prefixlen;
1151 ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT;
1152 ifm->ifa_scope = ifa->ifa_scope;
1153 ifm->ifa_index = ifa->ifa_dev->dev->ifindex;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154
Thomas Graf47f68512006-08-04 23:04:36 -07001155 if (ifa->ifa_address)
Al Viroa7a628c2006-09-26 22:16:43 -07001156 NLA_PUT_BE32(skb, IFA_ADDRESS, ifa->ifa_address);
Thomas Graf47f68512006-08-04 23:04:36 -07001157
1158 if (ifa->ifa_local)
Al Viroa7a628c2006-09-26 22:16:43 -07001159 NLA_PUT_BE32(skb, IFA_LOCAL, ifa->ifa_local);
Thomas Graf47f68512006-08-04 23:04:36 -07001160
1161 if (ifa->ifa_broadcast)
Al Viroa7a628c2006-09-26 22:16:43 -07001162 NLA_PUT_BE32(skb, IFA_BROADCAST, ifa->ifa_broadcast);
Thomas Graf47f68512006-08-04 23:04:36 -07001163
Thomas Graf47f68512006-08-04 23:04:36 -07001164 if (ifa->ifa_label[0])
1165 NLA_PUT_STRING(skb, IFA_LABEL, ifa->ifa_label);
1166
1167 return nlmsg_end(skb, nlh);
1168
1169nla_put_failure:
Patrick McHardy26932562007-01-31 23:16:40 -08001170 nlmsg_cancel(skb, nlh);
1171 return -EMSGSIZE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172}
1173
1174static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb)
1175{
YOSHIFUJI Hideaki3b1e0a62008-03-26 02:26:21 +09001176 struct net *net = sock_net(skb->sk);
Eric Dumazeteec4df92009-11-12 07:44:25 +00001177 int h, s_h;
1178 int idx, s_idx;
1179 int ip_idx, s_ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 struct net_device *dev;
1181 struct in_device *in_dev;
1182 struct in_ifaddr *ifa;
Eric Dumazeteec4df92009-11-12 07:44:25 +00001183 struct hlist_head *head;
1184 struct hlist_node *node;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185
Eric Dumazeteec4df92009-11-12 07:44:25 +00001186 s_h = cb->args[0];
1187 s_idx = idx = cb->args[1];
1188 s_ip_idx = ip_idx = cb->args[2];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189
Eric Dumazeteec4df92009-11-12 07:44:25 +00001190 for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) {
1191 idx = 0;
1192 head = &net->dev_index_head[h];
1193 rcu_read_lock();
1194 hlist_for_each_entry_rcu(dev, node, head, index_hlist) {
1195 if (idx < s_idx)
1196 goto cont;
1197 if (idx > s_idx)
1198 s_ip_idx = 0;
1199 in_dev = __in_dev_get_rcu(dev);
1200 if (!in_dev)
1201 goto cont;
1202
1203 for (ifa = in_dev->ifa_list, ip_idx = 0; ifa;
1204 ifa = ifa->ifa_next, ip_idx++) {
1205 if (ip_idx < s_ip_idx)
1206 continue;
1207 if (inet_fill_ifaddr(skb, ifa,
1208 NETLINK_CB(cb->skb).pid,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 cb->nlh->nlmsg_seq,
Eric Dumazeteec4df92009-11-12 07:44:25 +00001210 RTM_NEWADDR, NLM_F_MULTI) <= 0) {
1211 rcu_read_unlock();
1212 goto done;
1213 }
1214 }
Pavel Emelianov7562f872007-05-03 15:13:45 -07001215cont:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001216 idx++;
1217 }
1218 rcu_read_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 }
1220
1221done:
Eric Dumazeteec4df92009-11-12 07:44:25 +00001222 cb->args[0] = h;
1223 cb->args[1] = idx;
1224 cb->args[2] = ip_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225
1226 return skb->len;
1227}
1228
Jianjun Kong539afed2008-11-03 02:48:48 -08001229static void rtmsg_ifa(int event, struct in_ifaddr *ifa, struct nlmsghdr *nlh,
Thomas Grafd6062cb2006-08-15 00:33:59 -07001230 u32 pid)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231{
Thomas Graf47f68512006-08-04 23:04:36 -07001232 struct sk_buff *skb;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001233 u32 seq = nlh ? nlh->nlmsg_seq : 0;
1234 int err = -ENOBUFS;
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001235 struct net *net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001237 net = dev_net(ifa->ifa_dev->dev);
Thomas Graf339bf982006-11-10 14:10:15 -08001238 skb = nlmsg_new(inet_nlmsg_size(), GFP_KERNEL);
Thomas Graf47f68512006-08-04 23:04:36 -07001239 if (skb == NULL)
Thomas Grafd6062cb2006-08-15 00:33:59 -07001240 goto errout;
1241
1242 err = inet_fill_ifaddr(skb, ifa, pid, seq, event, 0);
Patrick McHardy26932562007-01-31 23:16:40 -08001243 if (err < 0) {
1244 /* -EMSGSIZE implies BUG in inet_nlmsg_size() */
1245 WARN_ON(err == -EMSGSIZE);
1246 kfree_skb(skb);
1247 goto errout;
1248 }
Pablo Neira Ayuso1ce85fe2009-02-24 23:18:28 -08001249 rtnl_notify(skb, net, pid, RTNLGRP_IPV4_IFADDR, nlh, GFP_KERNEL);
1250 return;
Thomas Grafd6062cb2006-08-15 00:33:59 -07001251errout:
1252 if (err < 0)
Denis V. Lunev4b8aa9a2008-01-31 18:47:40 -08001253 rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001254}
1255
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256#ifdef CONFIG_SYSCTL
1257
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001258static void devinet_copy_dflt_conf(struct net *net, int i)
Herbert Xu31be3082007-06-04 23:35:37 -07001259{
1260 struct net_device *dev;
1261
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001262 rcu_read_lock();
1263 for_each_netdev_rcu(net, dev) {
Herbert Xu31be3082007-06-04 23:35:37 -07001264 struct in_device *in_dev;
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001265
Herbert Xu31be3082007-06-04 23:35:37 -07001266 in_dev = __in_dev_get_rcu(dev);
1267 if (in_dev && !test_bit(i, in_dev->cnf.state))
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001268 in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i];
Herbert Xu31be3082007-06-04 23:35:37 -07001269 }
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001270 rcu_read_unlock();
Herbert Xu31be3082007-06-04 23:35:37 -07001271}
1272
Eric Dumazetc6d14c82009-11-04 05:43:23 -08001273/* called with RTNL locked */
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001274static void inet_forward_change(struct net *net)
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001275{
1276 struct net_device *dev;
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001277 int on = IPV4_DEVCONF_ALL(net, FORWARDING);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001278
Pavel Emelyanov586f1212007-12-16 13:32:48 -08001279 IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on;
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001280 IPV4_DEVCONF_DFLT(net, FORWARDING) = on;
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001281
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001282 for_each_netdev(net, dev) {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001283 struct in_device *in_dev;
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001284 if (on)
1285 dev_disable_lro(dev);
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001286 rcu_read_lock();
1287 in_dev = __in_dev_get_rcu(dev);
1288 if (in_dev)
1289 IN_DEV_CONF_SET(in_dev, FORWARDING, on);
1290 rcu_read_unlock();
1291 }
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001292}
1293
Herbert Xu31be3082007-06-04 23:35:37 -07001294static int devinet_conf_proc(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001295 void __user *buffer,
Herbert Xu31be3082007-06-04 23:35:37 -07001296 size_t *lenp, loff_t *ppos)
1297{
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001298 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Herbert Xu31be3082007-06-04 23:35:37 -07001299
1300 if (write) {
1301 struct ipv4_devconf *cnf = ctl->extra1;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001302 struct net *net = ctl->extra2;
Herbert Xu31be3082007-06-04 23:35:37 -07001303 int i = (int *)ctl->data - cnf->data;
1304
1305 set_bit(i, cnf->state);
1306
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001307 if (cnf == net->ipv4.devconf_dflt)
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001308 devinet_copy_dflt_conf(net, i);
Herbert Xu31be3082007-06-04 23:35:37 -07001309 }
1310
1311 return ret;
1312}
1313
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314static int devinet_sysctl_forward(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001315 void __user *buffer,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 size_t *lenp, loff_t *ppos)
1317{
1318 int *valp = ctl->data;
1319 int val = *valp;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001320 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321
1322 if (write && *valp != val) {
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001323 struct net *net = ctl->extra2;
1324
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001325 if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
Eric W. Biederman9b8adb52009-05-13 16:59:21 +00001326 if (!rtnl_trylock())
1327 return restart_syscall();
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001328 if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1329 inet_forward_change(net);
1330 } else if (*valp) {
1331 struct ipv4_devconf *cnf = ctl->extra1;
1332 struct in_device *idev =
1333 container_of(cnf, struct in_device, cnf);
1334 dev_disable_lro(idev->dev);
1335 }
1336 rtnl_unlock();
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001337 rt_cache_flush(net, 0);
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001338 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 }
1340
1341 return ret;
1342}
1343
1344int ipv4_doint_and_flush(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001345 void __user *buffer,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346 size_t *lenp, loff_t *ppos)
1347{
1348 int *valp = ctl->data;
1349 int val = *valp;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001350 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001351 struct net *net = ctl->extra2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
1353 if (write && *valp != val)
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001354 rt_cache_flush(net, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355
1356 return ret;
1357}
1358
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001359#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc) \
Herbert Xu42f811b2007-06-04 23:34:44 -07001360 { \
Herbert Xu42f811b2007-06-04 23:34:44 -07001361 .procname = name, \
1362 .data = ipv4_devconf.data + \
1363 NET_IPV4_CONF_ ## attr - 1, \
1364 .maxlen = sizeof(int), \
1365 .mode = mval, \
1366 .proc_handler = proc, \
Herbert Xu31be3082007-06-04 23:35:37 -07001367 .extra1 = &ipv4_devconf, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001368 }
1369
1370#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001371 DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001372
1373#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001374 DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001375
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001376#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc) \
1377 DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc)
Herbert Xu42f811b2007-06-04 23:34:44 -07001378
1379#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001380 DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush)
Herbert Xu42f811b2007-06-04 23:34:44 -07001381
Linus Torvalds1da177e2005-04-16 15:20:36 -07001382static struct devinet_sysctl_table {
1383 struct ctl_table_header *sysctl_header;
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001384 struct ctl_table devinet_vars[__NET_IPV4_CONF_MAX];
1385 char *dev_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386} devinet_sysctl = {
1387 .devinet_vars = {
Herbert Xu42f811b2007-06-04 23:34:44 -07001388 DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001389 devinet_sysctl_forward),
Herbert Xu42f811b2007-06-04 23:34:44 -07001390 DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
1391
1392 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
1393 DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
1394 DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
1395 DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
1396 DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
1397 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
1398 "accept_source_route"),
Patrick McHardy8153a102009-12-03 01:25:58 +00001399 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001400 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
1401 DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
1402 DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
1403 DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
1404 DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
1405 DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
1406 DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
1407 DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
1408 DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001409 DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
Jesper Dangaard Brouer65324142010-01-05 05:50:47 +00001410 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP_PVLAN, "proxy_arp_pvlan"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001411
1412 DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
1413 DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
1414 DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
1415 "force_igmp_version"),
1416 DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
1417 "promote_secondaries"),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419};
1420
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001421static int __devinet_sysctl_register(struct net *net, char *dev_name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001422 struct ipv4_devconf *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423{
1424 int i;
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001425 struct devinet_sysctl_table *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001427#define DEVINET_CTL_PATH_DEV 3
1428
1429 struct ctl_path devinet_ctl_path[] = {
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001430 { .procname = "net", },
1431 { .procname = "ipv4", },
1432 { .procname = "conf", },
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001433 { /* to be set */ },
1434 { },
1435 };
1436
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001437 t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 if (!t)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001439 goto out;
1440
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
1442 t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
Herbert Xu31be3082007-06-04 23:35:37 -07001443 t->devinet_vars[i].extra1 = p;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001444 t->devinet_vars[i].extra2 = net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445 }
1446
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001447 /*
1448 * Make a copy of dev_name, because '.procname' is regarded as const
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 * by sysctl and we wouldn't want anyone to change it under our feet
1450 * (see SIOCSIFNAME).
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001451 */
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001452 t->dev_name = kstrdup(dev_name, GFP_KERNEL);
1453 if (!t->dev_name)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001454 goto free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001456 devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001458 t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path,
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001459 t->devinet_vars);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 if (!t->sysctl_header)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001461 goto free_procname;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
1463 p->sysctl = t;
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001464 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001466free_procname:
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001467 kfree(t->dev_name);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001468free:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 kfree(t);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001470out:
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001471 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472}
1473
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001474static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
1475{
1476 struct devinet_sysctl_table *t = cnf->sysctl;
1477
1478 if (t == NULL)
1479 return;
1480
1481 cnf->sysctl = NULL;
1482 unregister_sysctl_table(t->sysctl_header);
1483 kfree(t->dev_name);
1484 kfree(t);
1485}
1486
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001487static void devinet_sysctl_register(struct in_device *idev)
1488{
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001489 neigh_sysctl_register(idev->dev, idev->arp_parms, NET_IPV4,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001490 NET_IPV4_NEIGH, "ipv4", NULL);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001491 __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001492 &idev->cnf);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001493}
1494
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001495static void devinet_sysctl_unregister(struct in_device *idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496{
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001497 __devinet_sysctl_unregister(&idev->cnf);
1498 neigh_sysctl_unregister(idev->arp_parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001501static struct ctl_table ctl_forward_entry[] = {
1502 {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001503 .procname = "ip_forward",
1504 .data = &ipv4_devconf.data[
1505 NET_IPV4_CONF_FORWARDING - 1],
1506 .maxlen = sizeof(int),
1507 .mode = 0644,
1508 .proc_handler = devinet_sysctl_forward,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001509 .extra1 = &ipv4_devconf,
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001510 .extra2 = &init_net,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001511 },
1512 { },
1513};
1514
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001515static __net_initdata struct ctl_path net_ipv4_path[] = {
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001516 { .procname = "net", },
1517 { .procname = "ipv4", },
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001518 { },
1519};
Eric Dumazet2a75de02008-01-05 23:08:49 -08001520#endif
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001521
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001522static __net_init int devinet_init_net(struct net *net)
1523{
1524 int err;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001525 struct ipv4_devconf *all, *dflt;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001526#ifdef CONFIG_SYSCTL
1527 struct ctl_table *tbl = ctl_forward_entry;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001528 struct ctl_table_header *forw_hdr;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001529#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001530
1531 err = -ENOMEM;
1532 all = &ipv4_devconf;
1533 dflt = &ipv4_devconf_dflt;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001534
Octavian Purdila09ad9bc2009-11-25 15:14:13 -08001535 if (!net_eq(net, &init_net)) {
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001536 all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1537 if (all == NULL)
1538 goto err_alloc_all;
1539
1540 dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1541 if (dflt == NULL)
1542 goto err_alloc_dflt;
1543
Eric Dumazet2a75de02008-01-05 23:08:49 -08001544#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001545 tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1546 if (tbl == NULL)
1547 goto err_alloc_ctl;
1548
1549 tbl[0].data = &all->data[NET_IPV4_CONF_FORWARDING - 1];
1550 tbl[0].extra1 = all;
1551 tbl[0].extra2 = net;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001552#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001553 }
1554
1555#ifdef CONFIG_SYSCTL
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001556 err = __devinet_sysctl_register(net, "all", all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001557 if (err < 0)
1558 goto err_reg_all;
1559
Eric W. Biedermanf8572d82009-11-05 13:32:03 -08001560 err = __devinet_sysctl_register(net, "default", dflt);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001561 if (err < 0)
1562 goto err_reg_dflt;
1563
1564 err = -ENOMEM;
1565 forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
1566 if (forw_hdr == NULL)
1567 goto err_reg_ctl;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001568 net->ipv4.forw_hdr = forw_hdr;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001569#endif
1570
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001571 net->ipv4.devconf_all = all;
1572 net->ipv4.devconf_dflt = dflt;
1573 return 0;
1574
1575#ifdef CONFIG_SYSCTL
1576err_reg_ctl:
1577 __devinet_sysctl_unregister(dflt);
1578err_reg_dflt:
1579 __devinet_sysctl_unregister(all);
1580err_reg_all:
1581 if (tbl != ctl_forward_entry)
1582 kfree(tbl);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001583err_alloc_ctl:
Eric Dumazet2a75de02008-01-05 23:08:49 -08001584#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001585 if (dflt != &ipv4_devconf_dflt)
1586 kfree(dflt);
1587err_alloc_dflt:
1588 if (all != &ipv4_devconf)
1589 kfree(all);
1590err_alloc_all:
1591 return err;
1592}
1593
1594static __net_exit void devinet_exit_net(struct net *net)
1595{
Eric Dumazet2a75de02008-01-05 23:08:49 -08001596#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001597 struct ctl_table *tbl;
1598
1599 tbl = net->ipv4.forw_hdr->ctl_table_arg;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001600 unregister_net_sysctl_table(net->ipv4.forw_hdr);
1601 __devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1602 __devinet_sysctl_unregister(net->ipv4.devconf_all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001603 kfree(tbl);
Eric Dumazet2a75de02008-01-05 23:08:49 -08001604#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001605 kfree(net->ipv4.devconf_dflt);
1606 kfree(net->ipv4.devconf_all);
1607}
1608
1609static __net_initdata struct pernet_operations devinet_ops = {
1610 .init = devinet_init_net,
1611 .exit = devinet_exit_net,
1612};
1613
Linus Torvalds1da177e2005-04-16 15:20:36 -07001614void __init devinet_init(void)
1615{
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001616 register_pernet_subsys(&devinet_ops);
1617
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 register_gifconf(PF_INET, inet_gifconf);
1619 register_netdevice_notifier(&ip_netdev_notifier);
Thomas Graf63f34442007-03-22 11:55:17 -07001620
1621 rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
1622 rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
1623 rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624}
1625