blob: 7620382058a0abce7956fed3e6826e6db7be9270 [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
Alexey Dobriyanf221e722008-10-15 22:04:23 -07001314static int devinet_conf_sysctl(ctl_table *table,
Herbert Xu31be3082007-06-04 23:35:37 -07001315 void __user *oldval, size_t __user *oldlenp,
1316 void __user *newval, size_t newlen)
1317{
1318 struct ipv4_devconf *cnf;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001319 struct net *net;
Herbert Xu31be3082007-06-04 23:35:37 -07001320 int *valp = table->data;
1321 int new;
1322 int i;
1323
1324 if (!newval || !newlen)
1325 return 0;
1326
1327 if (newlen != sizeof(int))
1328 return -EINVAL;
1329
1330 if (get_user(new, (int __user *)newval))
1331 return -EFAULT;
1332
1333 if (new == *valp)
1334 return 0;
1335
1336 if (oldval && oldlenp) {
1337 size_t len;
1338
1339 if (get_user(len, oldlenp))
1340 return -EFAULT;
1341
1342 if (len) {
1343 if (len > table->maxlen)
1344 len = table->maxlen;
1345 if (copy_to_user(oldval, valp, len))
1346 return -EFAULT;
1347 if (put_user(len, oldlenp))
1348 return -EFAULT;
1349 }
1350 }
1351
1352 *valp = new;
1353
1354 cnf = table->extra1;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001355 net = table->extra2;
Herbert Xu31be3082007-06-04 23:35:37 -07001356 i = (int *)table->data - cnf->data;
1357
1358 set_bit(i, cnf->state);
1359
Pavel Emelyanov9355bbd2007-12-16 13:32:16 -08001360 if (cnf == net->ipv4.devconf_dflt)
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001361 devinet_copy_dflt_conf(net, i);
Herbert Xu31be3082007-06-04 23:35:37 -07001362
1363 return 1;
1364}
1365
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366static int devinet_sysctl_forward(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001367 void __user *buffer,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368 size_t *lenp, loff_t *ppos)
1369{
1370 int *valp = ctl->data;
1371 int val = *valp;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001372 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
1374 if (write && *valp != val) {
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001375 struct net *net = ctl->extra2;
1376
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001377 if (valp != &IPV4_DEVCONF_DFLT(net, FORWARDING)) {
Eric W. Biederman9b8adb52009-05-13 16:59:21 +00001378 if (!rtnl_trylock())
1379 return restart_syscall();
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001380 if (valp == &IPV4_DEVCONF_ALL(net, FORWARDING)) {
1381 inet_forward_change(net);
1382 } else if (*valp) {
1383 struct ipv4_devconf *cnf = ctl->extra1;
1384 struct in_device *idev =
1385 container_of(cnf, struct in_device, cnf);
1386 dev_disable_lro(idev->dev);
1387 }
1388 rtnl_unlock();
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001389 rt_cache_flush(net, 0);
Ben Hutchings0187bdf2008-06-19 16:15:47 -07001390 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001391 }
1392
1393 return ret;
1394}
1395
1396int ipv4_doint_and_flush(ctl_table *ctl, int write,
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001397 void __user *buffer,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398 size_t *lenp, loff_t *ppos)
1399{
1400 int *valp = ctl->data;
1401 int val = *valp;
Alexey Dobriyan8d65af72009-09-23 15:57:19 -07001402 int ret = proc_dointvec(ctl, write, buffer, lenp, ppos);
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001403 struct net *net = ctl->extra2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404
1405 if (write && *valp != val)
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001406 rt_cache_flush(net, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001407
1408 return ret;
1409}
1410
Alexey Dobriyanf221e722008-10-15 22:04:23 -07001411int ipv4_doint_and_flush_strategy(ctl_table *table,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412 void __user *oldval, size_t __user *oldlenp,
Alexey Dobriyan1f29bcd2006-12-10 02:19:10 -08001413 void __user *newval, size_t newlen)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414{
Alexey Dobriyanf221e722008-10-15 22:04:23 -07001415 int ret = devinet_conf_sysctl(table, oldval, oldlenp, newval, newlen);
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001416 struct net *net = table->extra2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001417
Herbert Xu31be3082007-06-04 23:35:37 -07001418 if (ret == 1)
Denis V. Lunev76e6ebf2008-07-05 19:00:44 -07001419 rt_cache_flush(net, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
Herbert Xu31be3082007-06-04 23:35:37 -07001421 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001422}
1423
1424
Herbert Xu42f811b2007-06-04 23:34:44 -07001425#define DEVINET_SYSCTL_ENTRY(attr, name, mval, proc, sysctl) \
1426 { \
1427 .ctl_name = NET_IPV4_CONF_ ## attr, \
1428 .procname = name, \
1429 .data = ipv4_devconf.data + \
1430 NET_IPV4_CONF_ ## attr - 1, \
1431 .maxlen = sizeof(int), \
1432 .mode = mval, \
1433 .proc_handler = proc, \
1434 .strategy = sysctl, \
Herbert Xu31be3082007-06-04 23:35:37 -07001435 .extra1 = &ipv4_devconf, \
Herbert Xu42f811b2007-06-04 23:34:44 -07001436 }
1437
1438#define DEVINET_SYSCTL_RW_ENTRY(attr, name) \
Herbert Xu31be3082007-06-04 23:35:37 -07001439 DEVINET_SYSCTL_ENTRY(attr, name, 0644, devinet_conf_proc, \
1440 devinet_conf_sysctl)
Herbert Xu42f811b2007-06-04 23:34:44 -07001441
1442#define DEVINET_SYSCTL_RO_ENTRY(attr, name) \
Herbert Xu31be3082007-06-04 23:35:37 -07001443 DEVINET_SYSCTL_ENTRY(attr, name, 0444, devinet_conf_proc, \
1444 devinet_conf_sysctl)
Herbert Xu42f811b2007-06-04 23:34:44 -07001445
1446#define DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, proc, sysctl) \
1447 DEVINET_SYSCTL_ENTRY(attr, name, 0644, proc, sysctl)
1448
1449#define DEVINET_SYSCTL_FLUSHING_ENTRY(attr, name) \
1450 DEVINET_SYSCTL_COMPLEX_ENTRY(attr, name, ipv4_doint_and_flush, \
1451 ipv4_doint_and_flush_strategy)
1452
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453static struct devinet_sysctl_table {
1454 struct ctl_table_header *sysctl_header;
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001455 struct ctl_table devinet_vars[__NET_IPV4_CONF_MAX];
1456 char *dev_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001457} devinet_sysctl = {
1458 .devinet_vars = {
Herbert Xu42f811b2007-06-04 23:34:44 -07001459 DEVINET_SYSCTL_COMPLEX_ENTRY(FORWARDING, "forwarding",
Herbert Xu31be3082007-06-04 23:35:37 -07001460 devinet_sysctl_forward,
1461 devinet_conf_sysctl),
Herbert Xu42f811b2007-06-04 23:34:44 -07001462 DEVINET_SYSCTL_RO_ENTRY(MC_FORWARDING, "mc_forwarding"),
1463
1464 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_REDIRECTS, "accept_redirects"),
1465 DEVINET_SYSCTL_RW_ENTRY(SECURE_REDIRECTS, "secure_redirects"),
1466 DEVINET_SYSCTL_RW_ENTRY(SHARED_MEDIA, "shared_media"),
1467 DEVINET_SYSCTL_RW_ENTRY(RP_FILTER, "rp_filter"),
1468 DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"),
1469 DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE,
1470 "accept_source_route"),
1471 DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"),
1472 DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"),
1473 DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"),
1474 DEVINET_SYSCTL_RW_ENTRY(LOG_MARTIANS, "log_martians"),
1475 DEVINET_SYSCTL_RW_ENTRY(TAG, "tag"),
1476 DEVINET_SYSCTL_RW_ENTRY(ARPFILTER, "arp_filter"),
1477 DEVINET_SYSCTL_RW_ENTRY(ARP_ANNOUNCE, "arp_announce"),
1478 DEVINET_SYSCTL_RW_ENTRY(ARP_IGNORE, "arp_ignore"),
1479 DEVINET_SYSCTL_RW_ENTRY(ARP_ACCEPT, "arp_accept"),
Stephen Hemmingereefef1c2009-02-01 01:04:33 -08001480 DEVINET_SYSCTL_RW_ENTRY(ARP_NOTIFY, "arp_notify"),
Herbert Xu42f811b2007-06-04 23:34:44 -07001481
1482 DEVINET_SYSCTL_FLUSHING_ENTRY(NOXFRM, "disable_xfrm"),
1483 DEVINET_SYSCTL_FLUSHING_ENTRY(NOPOLICY, "disable_policy"),
1484 DEVINET_SYSCTL_FLUSHING_ENTRY(FORCE_IGMP_VERSION,
1485 "force_igmp_version"),
1486 DEVINET_SYSCTL_FLUSHING_ENTRY(PROMOTE_SECONDARIES,
1487 "promote_secondaries"),
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489};
1490
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001491static int __devinet_sysctl_register(struct net *net, char *dev_name,
1492 int ctl_name, struct ipv4_devconf *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493{
1494 int i;
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001495 struct devinet_sysctl_table *t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001497#define DEVINET_CTL_PATH_DEV 3
1498
1499 struct ctl_path devinet_ctl_path[] = {
1500 { .procname = "net", .ctl_name = CTL_NET, },
1501 { .procname = "ipv4", .ctl_name = NET_IPV4, },
1502 { .procname = "conf", .ctl_name = NET_IPV4_CONF, },
1503 { /* to be set */ },
1504 { },
1505 };
1506
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001507 t = kmemdup(&devinet_sysctl, sizeof(*t), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 if (!t)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001509 goto out;
1510
Linus Torvalds1da177e2005-04-16 15:20:36 -07001511 for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) {
1512 t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf;
Herbert Xu31be3082007-06-04 23:35:37 -07001513 t->devinet_vars[i].extra1 = p;
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001514 t->devinet_vars[i].extra2 = net;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 }
1516
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001517 /*
1518 * Make a copy of dev_name, because '.procname' is regarded as const
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 * by sysctl and we wouldn't want anyone to change it under our feet
1520 * (see SIOCSIFNAME).
YOSHIFUJI Hideakie905a9e2007-02-09 23:24:47 +09001521 */
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001522 t->dev_name = kstrdup(dev_name, GFP_KERNEL);
1523 if (!t->dev_name)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001524 goto free;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001526 devinet_ctl_path[DEVINET_CTL_PATH_DEV].procname = t->dev_name;
1527 devinet_ctl_path[DEVINET_CTL_PATH_DEV].ctl_name = ctl_name;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001528
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001529 t->sysctl_header = register_net_sysctl_table(net, devinet_ctl_path,
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001530 t->devinet_vars);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 if (!t->sysctl_header)
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001532 goto free_procname;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
1534 p->sysctl = t;
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001535 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001537free_procname:
Pavel Emelyanovbfada692007-12-02 00:57:08 +11001538 kfree(t->dev_name);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001539free:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540 kfree(t);
Pavel Emelyanov9fa89642007-12-02 00:17:46 +11001541out:
Pavel Emelyanovea40b322007-12-16 13:30:07 -08001542 return -ENOBUFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543}
1544
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001545static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf)
1546{
1547 struct devinet_sysctl_table *t = cnf->sysctl;
1548
1549 if (t == NULL)
1550 return;
1551
1552 cnf->sysctl = NULL;
1553 unregister_sysctl_table(t->sysctl_header);
1554 kfree(t->dev_name);
1555 kfree(t);
1556}
1557
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001558static void devinet_sysctl_register(struct in_device *idev)
1559{
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001560 neigh_sysctl_register(idev->dev, idev->arp_parms, NET_IPV4,
1561 NET_IPV4_NEIGH, "ipv4", NULL, NULL);
YOSHIFUJI Hideakic346dca2008-03-25 21:47:49 +09001562 __devinet_sysctl_register(dev_net(idev->dev), idev->dev->name,
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001563 idev->dev->ifindex, &idev->cnf);
Pavel Emelyanov66f27a52007-12-02 00:55:54 +11001564}
1565
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001566static void devinet_sysctl_unregister(struct in_device *idev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567{
Pavel Emelyanov51602b22007-12-11 02:17:40 -08001568 __devinet_sysctl_unregister(&idev->cnf);
1569 neigh_sysctl_unregister(idev->arp_parms);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001570}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001572static struct ctl_table ctl_forward_entry[] = {
1573 {
1574 .ctl_name = NET_IPV4_FORWARD,
1575 .procname = "ip_forward",
1576 .data = &ipv4_devconf.data[
1577 NET_IPV4_CONF_FORWARDING - 1],
1578 .maxlen = sizeof(int),
1579 .mode = 0644,
1580 .proc_handler = devinet_sysctl_forward,
1581 .strategy = devinet_conf_sysctl,
1582 .extra1 = &ipv4_devconf,
Pavel Emelyanovc0ce9fb2007-12-16 13:31:14 -08001583 .extra2 = &init_net,
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001584 },
1585 { },
1586};
1587
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001588static __net_initdata struct ctl_path net_ipv4_path[] = {
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001589 { .procname = "net", .ctl_name = CTL_NET, },
1590 { .procname = "ipv4", .ctl_name = NET_IPV4, },
1591 { },
1592};
Eric Dumazet2a75de02008-01-05 23:08:49 -08001593#endif
Pavel Emelyanov68dd2992007-12-05 01:44:58 -08001594
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001595static __net_init int devinet_init_net(struct net *net)
1596{
1597 int err;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001598 struct ipv4_devconf *all, *dflt;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001599#ifdef CONFIG_SYSCTL
1600 struct ctl_table *tbl = ctl_forward_entry;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001601 struct ctl_table_header *forw_hdr;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001602#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001603
1604 err = -ENOMEM;
1605 all = &ipv4_devconf;
1606 dflt = &ipv4_devconf_dflt;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001607
1608 if (net != &init_net) {
1609 all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL);
1610 if (all == NULL)
1611 goto err_alloc_all;
1612
1613 dflt = kmemdup(dflt, sizeof(ipv4_devconf_dflt), GFP_KERNEL);
1614 if (dflt == NULL)
1615 goto err_alloc_dflt;
1616
Eric Dumazet2a75de02008-01-05 23:08:49 -08001617#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001618 tbl = kmemdup(tbl, sizeof(ctl_forward_entry), GFP_KERNEL);
1619 if (tbl == NULL)
1620 goto err_alloc_ctl;
1621
1622 tbl[0].data = &all->data[NET_IPV4_CONF_FORWARDING - 1];
1623 tbl[0].extra1 = all;
1624 tbl[0].extra2 = net;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001625#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001626 }
1627
1628#ifdef CONFIG_SYSCTL
1629 err = __devinet_sysctl_register(net, "all",
1630 NET_PROTO_CONF_ALL, all);
1631 if (err < 0)
1632 goto err_reg_all;
1633
1634 err = __devinet_sysctl_register(net, "default",
1635 NET_PROTO_CONF_DEFAULT, dflt);
1636 if (err < 0)
1637 goto err_reg_dflt;
1638
1639 err = -ENOMEM;
1640 forw_hdr = register_net_sysctl_table(net, net_ipv4_path, tbl);
1641 if (forw_hdr == NULL)
1642 goto err_reg_ctl;
Eric Dumazet2a75de02008-01-05 23:08:49 -08001643 net->ipv4.forw_hdr = forw_hdr;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001644#endif
1645
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001646 net->ipv4.devconf_all = all;
1647 net->ipv4.devconf_dflt = dflt;
1648 return 0;
1649
1650#ifdef CONFIG_SYSCTL
1651err_reg_ctl:
1652 __devinet_sysctl_unregister(dflt);
1653err_reg_dflt:
1654 __devinet_sysctl_unregister(all);
1655err_reg_all:
1656 if (tbl != ctl_forward_entry)
1657 kfree(tbl);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001658err_alloc_ctl:
Eric Dumazet2a75de02008-01-05 23:08:49 -08001659#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001660 if (dflt != &ipv4_devconf_dflt)
1661 kfree(dflt);
1662err_alloc_dflt:
1663 if (all != &ipv4_devconf)
1664 kfree(all);
1665err_alloc_all:
1666 return err;
1667}
1668
1669static __net_exit void devinet_exit_net(struct net *net)
1670{
Eric Dumazet2a75de02008-01-05 23:08:49 -08001671#ifdef CONFIG_SYSCTL
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001672 struct ctl_table *tbl;
1673
1674 tbl = net->ipv4.forw_hdr->ctl_table_arg;
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001675 unregister_net_sysctl_table(net->ipv4.forw_hdr);
1676 __devinet_sysctl_unregister(net->ipv4.devconf_dflt);
1677 __devinet_sysctl_unregister(net->ipv4.devconf_all);
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001678 kfree(tbl);
Eric Dumazet2a75de02008-01-05 23:08:49 -08001679#endif
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001680 kfree(net->ipv4.devconf_dflt);
1681 kfree(net->ipv4.devconf_all);
1682}
1683
1684static __net_initdata struct pernet_operations devinet_ops = {
1685 .init = devinet_init_net,
1686 .exit = devinet_exit_net,
1687};
1688
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689void __init devinet_init(void)
1690{
Pavel Emelyanov752d14d2007-12-16 13:31:47 -08001691 register_pernet_subsys(&devinet_ops);
1692
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 register_gifconf(PF_INET, inet_gifconf);
1694 register_netdevice_notifier(&ip_netdev_notifier);
Thomas Graf63f34442007-03-22 11:55:17 -07001695
1696 rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL);
1697 rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL);
1698 rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699}
1700