diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /net/ipv4/devinet.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r-- | net/ipv4/devinet.c | 1508 |
1 files changed, 1508 insertions, 0 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c new file mode 100644 index 000000000000..eea7ef010776 --- /dev/null +++ b/net/ipv4/devinet.c | |||
@@ -0,0 +1,1508 @@ | |||
1 | /* | ||
2 | * NET3 IP device support routines. | ||
3 | * | ||
4 | * Version: $Id: devinet.c,v 1.44 2001/10/31 21:55:54 davem Exp $ | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | * | ||
11 | * Derived from the IP parts of dev.c 1.0.19 | ||
12 | * Authors: Ross Biro, <bir7@leland.Stanford.Edu> | ||
13 | * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG> | ||
14 | * Mark Evans, <evansmp@uhura.aston.ac.uk> | ||
15 | * | ||
16 | * Additional Authors: | ||
17 | * Alan Cox, <gw4pts@gw4pts.ampr.org> | ||
18 | * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | ||
19 | * | ||
20 | * Changes: | ||
21 | * Alexey Kuznetsov: pa_* fields are replaced with ifaddr | ||
22 | * lists. | ||
23 | * Cyrus Durgin: updated for kmod | ||
24 | * Matthias Andree: in devinet_ioctl, compare label and | ||
25 | * address (4.4BSD alias style support), | ||
26 | * fall back to comparing just the label | ||
27 | * if no match found. | ||
28 | */ | ||
29 | |||
30 | #include <linux/config.h> | ||
31 | |||
32 | #include <asm/uaccess.h> | ||
33 | #include <asm/system.h> | ||
34 | #include <linux/bitops.h> | ||
35 | #include <linux/module.h> | ||
36 | #include <linux/types.h> | ||
37 | #include <linux/kernel.h> | ||
38 | #include <linux/sched.h> | ||
39 | #include <linux/string.h> | ||
40 | #include <linux/mm.h> | ||
41 | #include <linux/socket.h> | ||
42 | #include <linux/sockios.h> | ||
43 | #include <linux/in.h> | ||
44 | #include <linux/errno.h> | ||
45 | #include <linux/interrupt.h> | ||
46 | #include <linux/if_ether.h> | ||
47 | #include <linux/inet.h> | ||
48 | #include <linux/netdevice.h> | ||
49 | #include <linux/etherdevice.h> | ||
50 | #include <linux/skbuff.h> | ||
51 | #include <linux/rtnetlink.h> | ||
52 | #include <linux/init.h> | ||
53 | #include <linux/notifier.h> | ||
54 | #include <linux/inetdevice.h> | ||
55 | #include <linux/igmp.h> | ||
56 | #ifdef CONFIG_SYSCTL | ||
57 | #include <linux/sysctl.h> | ||
58 | #endif | ||
59 | #include <linux/kmod.h> | ||
60 | |||
61 | #include <net/ip.h> | ||
62 | #include <net/route.h> | ||
63 | #include <net/ip_fib.h> | ||
64 | |||
65 | struct ipv4_devconf ipv4_devconf = { | ||
66 | .accept_redirects = 1, | ||
67 | .send_redirects = 1, | ||
68 | .secure_redirects = 1, | ||
69 | .shared_media = 1, | ||
70 | }; | ||
71 | |||
72 | static struct ipv4_devconf ipv4_devconf_dflt = { | ||
73 | .accept_redirects = 1, | ||
74 | .send_redirects = 1, | ||
75 | .secure_redirects = 1, | ||
76 | .shared_media = 1, | ||
77 | .accept_source_route = 1, | ||
78 | }; | ||
79 | |||
80 | static void rtmsg_ifa(int event, struct in_ifaddr *); | ||
81 | |||
82 | static struct notifier_block *inetaddr_chain; | ||
83 | static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | ||
84 | int destroy); | ||
85 | #ifdef CONFIG_SYSCTL | ||
86 | static void devinet_sysctl_register(struct in_device *in_dev, | ||
87 | struct ipv4_devconf *p); | ||
88 | static void devinet_sysctl_unregister(struct ipv4_devconf *p); | ||
89 | #endif | ||
90 | |||
91 | /* Locks all the inet devices. */ | ||
92 | |||
93 | static struct in_ifaddr *inet_alloc_ifa(void) | ||
94 | { | ||
95 | struct in_ifaddr *ifa = kmalloc(sizeof(*ifa), GFP_KERNEL); | ||
96 | |||
97 | if (ifa) { | ||
98 | memset(ifa, 0, sizeof(*ifa)); | ||
99 | INIT_RCU_HEAD(&ifa->rcu_head); | ||
100 | } | ||
101 | |||
102 | return ifa; | ||
103 | } | ||
104 | |||
105 | static void inet_rcu_free_ifa(struct rcu_head *head) | ||
106 | { | ||
107 | struct in_ifaddr *ifa = container_of(head, struct in_ifaddr, rcu_head); | ||
108 | if (ifa->ifa_dev) | ||
109 | in_dev_put(ifa->ifa_dev); | ||
110 | kfree(ifa); | ||
111 | } | ||
112 | |||
113 | static inline void inet_free_ifa(struct in_ifaddr *ifa) | ||
114 | { | ||
115 | call_rcu(&ifa->rcu_head, inet_rcu_free_ifa); | ||
116 | } | ||
117 | |||
118 | void in_dev_finish_destroy(struct in_device *idev) | ||
119 | { | ||
120 | struct net_device *dev = idev->dev; | ||
121 | |||
122 | BUG_TRAP(!idev->ifa_list); | ||
123 | BUG_TRAP(!idev->mc_list); | ||
124 | #ifdef NET_REFCNT_DEBUG | ||
125 | printk(KERN_DEBUG "in_dev_finish_destroy: %p=%s\n", | ||
126 | idev, dev ? dev->name : "NIL"); | ||
127 | #endif | ||
128 | dev_put(dev); | ||
129 | if (!idev->dead) | ||
130 | printk("Freeing alive in_device %p\n", idev); | ||
131 | else { | ||
132 | kfree(idev); | ||
133 | } | ||
134 | } | ||
135 | |||
136 | struct in_device *inetdev_init(struct net_device *dev) | ||
137 | { | ||
138 | struct in_device *in_dev; | ||
139 | |||
140 | ASSERT_RTNL(); | ||
141 | |||
142 | in_dev = kmalloc(sizeof(*in_dev), GFP_KERNEL); | ||
143 | if (!in_dev) | ||
144 | goto out; | ||
145 | memset(in_dev, 0, sizeof(*in_dev)); | ||
146 | INIT_RCU_HEAD(&in_dev->rcu_head); | ||
147 | memcpy(&in_dev->cnf, &ipv4_devconf_dflt, sizeof(in_dev->cnf)); | ||
148 | in_dev->cnf.sysctl = NULL; | ||
149 | in_dev->dev = dev; | ||
150 | if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) | ||
151 | goto out_kfree; | ||
152 | /* Reference in_dev->dev */ | ||
153 | dev_hold(dev); | ||
154 | #ifdef CONFIG_SYSCTL | ||
155 | neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, | ||
156 | NET_IPV4_NEIGH, "ipv4", NULL, NULL); | ||
157 | #endif | ||
158 | |||
159 | /* Account for reference dev->ip_ptr */ | ||
160 | in_dev_hold(in_dev); | ||
161 | rcu_assign_pointer(dev->ip_ptr, in_dev); | ||
162 | |||
163 | #ifdef CONFIG_SYSCTL | ||
164 | devinet_sysctl_register(in_dev, &in_dev->cnf); | ||
165 | #endif | ||
166 | ip_mc_init_dev(in_dev); | ||
167 | if (dev->flags & IFF_UP) | ||
168 | ip_mc_up(in_dev); | ||
169 | out: | ||
170 | return in_dev; | ||
171 | out_kfree: | ||
172 | kfree(in_dev); | ||
173 | in_dev = NULL; | ||
174 | goto out; | ||
175 | } | ||
176 | |||
177 | static void in_dev_rcu_put(struct rcu_head *head) | ||
178 | { | ||
179 | struct in_device *idev = container_of(head, struct in_device, rcu_head); | ||
180 | in_dev_put(idev); | ||
181 | } | ||
182 | |||
183 | static void inetdev_destroy(struct in_device *in_dev) | ||
184 | { | ||
185 | struct in_ifaddr *ifa; | ||
186 | struct net_device *dev; | ||
187 | |||
188 | ASSERT_RTNL(); | ||
189 | |||
190 | dev = in_dev->dev; | ||
191 | if (dev == &loopback_dev) | ||
192 | return; | ||
193 | |||
194 | in_dev->dead = 1; | ||
195 | |||
196 | ip_mc_destroy_dev(in_dev); | ||
197 | |||
198 | while ((ifa = in_dev->ifa_list) != NULL) { | ||
199 | inet_del_ifa(in_dev, &in_dev->ifa_list, 0); | ||
200 | inet_free_ifa(ifa); | ||
201 | } | ||
202 | |||
203 | #ifdef CONFIG_SYSCTL | ||
204 | devinet_sysctl_unregister(&in_dev->cnf); | ||
205 | #endif | ||
206 | |||
207 | dev->ip_ptr = NULL; | ||
208 | |||
209 | #ifdef CONFIG_SYSCTL | ||
210 | neigh_sysctl_unregister(in_dev->arp_parms); | ||
211 | #endif | ||
212 | neigh_parms_release(&arp_tbl, in_dev->arp_parms); | ||
213 | arp_ifdown(dev); | ||
214 | |||
215 | call_rcu(&in_dev->rcu_head, in_dev_rcu_put); | ||
216 | } | ||
217 | |||
218 | int inet_addr_onlink(struct in_device *in_dev, u32 a, u32 b) | ||
219 | { | ||
220 | rcu_read_lock(); | ||
221 | for_primary_ifa(in_dev) { | ||
222 | if (inet_ifa_match(a, ifa)) { | ||
223 | if (!b || inet_ifa_match(b, ifa)) { | ||
224 | rcu_read_unlock(); | ||
225 | return 1; | ||
226 | } | ||
227 | } | ||
228 | } endfor_ifa(in_dev); | ||
229 | rcu_read_unlock(); | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | ||
234 | int destroy) | ||
235 | { | ||
236 | struct in_ifaddr *ifa1 = *ifap; | ||
237 | |||
238 | ASSERT_RTNL(); | ||
239 | |||
240 | /* 1. Deleting primary ifaddr forces deletion all secondaries */ | ||
241 | |||
242 | if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { | ||
243 | struct in_ifaddr *ifa; | ||
244 | struct in_ifaddr **ifap1 = &ifa1->ifa_next; | ||
245 | |||
246 | while ((ifa = *ifap1) != NULL) { | ||
247 | if (!(ifa->ifa_flags & IFA_F_SECONDARY) || | ||
248 | ifa1->ifa_mask != ifa->ifa_mask || | ||
249 | !inet_ifa_match(ifa1->ifa_address, ifa)) { | ||
250 | ifap1 = &ifa->ifa_next; | ||
251 | continue; | ||
252 | } | ||
253 | |||
254 | *ifap1 = ifa->ifa_next; | ||
255 | |||
256 | rtmsg_ifa(RTM_DELADDR, ifa); | ||
257 | notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa); | ||
258 | inet_free_ifa(ifa); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | /* 2. Unlink it */ | ||
263 | |||
264 | *ifap = ifa1->ifa_next; | ||
265 | |||
266 | /* 3. Announce address deletion */ | ||
267 | |||
268 | /* Send message first, then call notifier. | ||
269 | At first sight, FIB update triggered by notifier | ||
270 | will refer to already deleted ifaddr, that could confuse | ||
271 | netlink listeners. It is not true: look, gated sees | ||
272 | that route deleted and if it still thinks that ifaddr | ||
273 | is valid, it will try to restore deleted routes... Grr. | ||
274 | So that, this order is correct. | ||
275 | */ | ||
276 | rtmsg_ifa(RTM_DELADDR, ifa1); | ||
277 | notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); | ||
278 | if (destroy) { | ||
279 | inet_free_ifa(ifa1); | ||
280 | |||
281 | if (!in_dev->ifa_list) | ||
282 | inetdev_destroy(in_dev); | ||
283 | } | ||
284 | } | ||
285 | |||
286 | static int inet_insert_ifa(struct in_ifaddr *ifa) | ||
287 | { | ||
288 | struct in_device *in_dev = ifa->ifa_dev; | ||
289 | struct in_ifaddr *ifa1, **ifap, **last_primary; | ||
290 | |||
291 | ASSERT_RTNL(); | ||
292 | |||
293 | if (!ifa->ifa_local) { | ||
294 | inet_free_ifa(ifa); | ||
295 | return 0; | ||
296 | } | ||
297 | |||
298 | ifa->ifa_flags &= ~IFA_F_SECONDARY; | ||
299 | last_primary = &in_dev->ifa_list; | ||
300 | |||
301 | for (ifap = &in_dev->ifa_list; (ifa1 = *ifap) != NULL; | ||
302 | ifap = &ifa1->ifa_next) { | ||
303 | if (!(ifa1->ifa_flags & IFA_F_SECONDARY) && | ||
304 | ifa->ifa_scope <= ifa1->ifa_scope) | ||
305 | last_primary = &ifa1->ifa_next; | ||
306 | if (ifa1->ifa_mask == ifa->ifa_mask && | ||
307 | inet_ifa_match(ifa1->ifa_address, ifa)) { | ||
308 | if (ifa1->ifa_local == ifa->ifa_local) { | ||
309 | inet_free_ifa(ifa); | ||
310 | return -EEXIST; | ||
311 | } | ||
312 | if (ifa1->ifa_scope != ifa->ifa_scope) { | ||
313 | inet_free_ifa(ifa); | ||
314 | return -EINVAL; | ||
315 | } | ||
316 | ifa->ifa_flags |= IFA_F_SECONDARY; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | if (!(ifa->ifa_flags & IFA_F_SECONDARY)) { | ||
321 | net_srandom(ifa->ifa_local); | ||
322 | ifap = last_primary; | ||
323 | } | ||
324 | |||
325 | ifa->ifa_next = *ifap; | ||
326 | *ifap = ifa; | ||
327 | |||
328 | /* Send message first, then call notifier. | ||
329 | Notifier will trigger FIB update, so that | ||
330 | listeners of netlink will know about new ifaddr */ | ||
331 | rtmsg_ifa(RTM_NEWADDR, ifa); | ||
332 | notifier_call_chain(&inetaddr_chain, NETDEV_UP, ifa); | ||
333 | |||
334 | return 0; | ||
335 | } | ||
336 | |||
337 | static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) | ||
338 | { | ||
339 | struct in_device *in_dev = __in_dev_get(dev); | ||
340 | |||
341 | ASSERT_RTNL(); | ||
342 | |||
343 | if (!in_dev) { | ||
344 | in_dev = inetdev_init(dev); | ||
345 | if (!in_dev) { | ||
346 | inet_free_ifa(ifa); | ||
347 | return -ENOBUFS; | ||
348 | } | ||
349 | } | ||
350 | if (ifa->ifa_dev != in_dev) { | ||
351 | BUG_TRAP(!ifa->ifa_dev); | ||
352 | in_dev_hold(in_dev); | ||
353 | ifa->ifa_dev = in_dev; | ||
354 | } | ||
355 | if (LOOPBACK(ifa->ifa_local)) | ||
356 | ifa->ifa_scope = RT_SCOPE_HOST; | ||
357 | return inet_insert_ifa(ifa); | ||
358 | } | ||
359 | |||
360 | struct in_device *inetdev_by_index(int ifindex) | ||
361 | { | ||
362 | struct net_device *dev; | ||
363 | struct in_device *in_dev = NULL; | ||
364 | read_lock(&dev_base_lock); | ||
365 | dev = __dev_get_by_index(ifindex); | ||
366 | if (dev) | ||
367 | in_dev = in_dev_get(dev); | ||
368 | read_unlock(&dev_base_lock); | ||
369 | return in_dev; | ||
370 | } | ||
371 | |||
372 | /* Called only from RTNL semaphored context. No locks. */ | ||
373 | |||
374 | struct in_ifaddr *inet_ifa_byprefix(struct in_device *in_dev, u32 prefix, | ||
375 | u32 mask) | ||
376 | { | ||
377 | ASSERT_RTNL(); | ||
378 | |||
379 | for_primary_ifa(in_dev) { | ||
380 | if (ifa->ifa_mask == mask && inet_ifa_match(prefix, ifa)) | ||
381 | return ifa; | ||
382 | } endfor_ifa(in_dev); | ||
383 | return NULL; | ||
384 | } | ||
385 | |||
386 | static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
387 | { | ||
388 | struct rtattr **rta = arg; | ||
389 | struct in_device *in_dev; | ||
390 | struct ifaddrmsg *ifm = NLMSG_DATA(nlh); | ||
391 | struct in_ifaddr *ifa, **ifap; | ||
392 | |||
393 | ASSERT_RTNL(); | ||
394 | |||
395 | if ((in_dev = inetdev_by_index(ifm->ifa_index)) == NULL) | ||
396 | goto out; | ||
397 | __in_dev_put(in_dev); | ||
398 | |||
399 | for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; | ||
400 | ifap = &ifa->ifa_next) { | ||
401 | if ((rta[IFA_LOCAL - 1] && | ||
402 | memcmp(RTA_DATA(rta[IFA_LOCAL - 1]), | ||
403 | &ifa->ifa_local, 4)) || | ||
404 | (rta[IFA_LABEL - 1] && | ||
405 | rtattr_strcmp(rta[IFA_LABEL - 1], ifa->ifa_label)) || | ||
406 | (rta[IFA_ADDRESS - 1] && | ||
407 | (ifm->ifa_prefixlen != ifa->ifa_prefixlen || | ||
408 | !inet_ifa_match(*(u32*)RTA_DATA(rta[IFA_ADDRESS - 1]), | ||
409 | ifa)))) | ||
410 | continue; | ||
411 | inet_del_ifa(in_dev, ifap, 1); | ||
412 | return 0; | ||
413 | } | ||
414 | out: | ||
415 | return -EADDRNOTAVAIL; | ||
416 | } | ||
417 | |||
418 | static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
419 | { | ||
420 | struct rtattr **rta = arg; | ||
421 | struct net_device *dev; | ||
422 | struct in_device *in_dev; | ||
423 | struct ifaddrmsg *ifm = NLMSG_DATA(nlh); | ||
424 | struct in_ifaddr *ifa; | ||
425 | int rc = -EINVAL; | ||
426 | |||
427 | ASSERT_RTNL(); | ||
428 | |||
429 | if (ifm->ifa_prefixlen > 32 || !rta[IFA_LOCAL - 1]) | ||
430 | goto out; | ||
431 | |||
432 | rc = -ENODEV; | ||
433 | if ((dev = __dev_get_by_index(ifm->ifa_index)) == NULL) | ||
434 | goto out; | ||
435 | |||
436 | rc = -ENOBUFS; | ||
437 | if ((in_dev = __in_dev_get(dev)) == NULL) { | ||
438 | in_dev = inetdev_init(dev); | ||
439 | if (!in_dev) | ||
440 | goto out; | ||
441 | } | ||
442 | |||
443 | if ((ifa = inet_alloc_ifa()) == NULL) | ||
444 | goto out; | ||
445 | |||
446 | if (!rta[IFA_ADDRESS - 1]) | ||
447 | rta[IFA_ADDRESS - 1] = rta[IFA_LOCAL - 1]; | ||
448 | memcpy(&ifa->ifa_local, RTA_DATA(rta[IFA_LOCAL - 1]), 4); | ||
449 | memcpy(&ifa->ifa_address, RTA_DATA(rta[IFA_ADDRESS - 1]), 4); | ||
450 | ifa->ifa_prefixlen = ifm->ifa_prefixlen; | ||
451 | ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); | ||
452 | if (rta[IFA_BROADCAST - 1]) | ||
453 | memcpy(&ifa->ifa_broadcast, | ||
454 | RTA_DATA(rta[IFA_BROADCAST - 1]), 4); | ||
455 | if (rta[IFA_ANYCAST - 1]) | ||
456 | memcpy(&ifa->ifa_anycast, RTA_DATA(rta[IFA_ANYCAST - 1]), 4); | ||
457 | ifa->ifa_flags = ifm->ifa_flags; | ||
458 | ifa->ifa_scope = ifm->ifa_scope; | ||
459 | in_dev_hold(in_dev); | ||
460 | ifa->ifa_dev = in_dev; | ||
461 | if (rta[IFA_LABEL - 1]) | ||
462 | rtattr_strlcpy(ifa->ifa_label, rta[IFA_LABEL - 1], IFNAMSIZ); | ||
463 | else | ||
464 | memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); | ||
465 | |||
466 | rc = inet_insert_ifa(ifa); | ||
467 | out: | ||
468 | return rc; | ||
469 | } | ||
470 | |||
471 | /* | ||
472 | * Determine a default network mask, based on the IP address. | ||
473 | */ | ||
474 | |||
475 | static __inline__ int inet_abc_len(u32 addr) | ||
476 | { | ||
477 | int rc = -1; /* Something else, probably a multicast. */ | ||
478 | |||
479 | if (ZERONET(addr)) | ||
480 | rc = 0; | ||
481 | else { | ||
482 | addr = ntohl(addr); | ||
483 | |||
484 | if (IN_CLASSA(addr)) | ||
485 | rc = 8; | ||
486 | else if (IN_CLASSB(addr)) | ||
487 | rc = 16; | ||
488 | else if (IN_CLASSC(addr)) | ||
489 | rc = 24; | ||
490 | } | ||
491 | |||
492 | return rc; | ||
493 | } | ||
494 | |||
495 | |||
496 | int devinet_ioctl(unsigned int cmd, void __user *arg) | ||
497 | { | ||
498 | struct ifreq ifr; | ||
499 | struct sockaddr_in sin_orig; | ||
500 | struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr; | ||
501 | struct in_device *in_dev; | ||
502 | struct in_ifaddr **ifap = NULL; | ||
503 | struct in_ifaddr *ifa = NULL; | ||
504 | struct net_device *dev; | ||
505 | char *colon; | ||
506 | int ret = -EFAULT; | ||
507 | int tryaddrmatch = 0; | ||
508 | |||
509 | /* | ||
510 | * Fetch the caller's info block into kernel space | ||
511 | */ | ||
512 | |||
513 | if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) | ||
514 | goto out; | ||
515 | ifr.ifr_name[IFNAMSIZ - 1] = 0; | ||
516 | |||
517 | /* save original address for comparison */ | ||
518 | memcpy(&sin_orig, sin, sizeof(*sin)); | ||
519 | |||
520 | colon = strchr(ifr.ifr_name, ':'); | ||
521 | if (colon) | ||
522 | *colon = 0; | ||
523 | |||
524 | #ifdef CONFIG_KMOD | ||
525 | dev_load(ifr.ifr_name); | ||
526 | #endif | ||
527 | |||
528 | switch(cmd) { | ||
529 | case SIOCGIFADDR: /* Get interface address */ | ||
530 | case SIOCGIFBRDADDR: /* Get the broadcast address */ | ||
531 | case SIOCGIFDSTADDR: /* Get the destination address */ | ||
532 | case SIOCGIFNETMASK: /* Get the netmask for the interface */ | ||
533 | /* Note that these ioctls will not sleep, | ||
534 | so that we do not impose a lock. | ||
535 | One day we will be forced to put shlock here (I mean SMP) | ||
536 | */ | ||
537 | tryaddrmatch = (sin_orig.sin_family == AF_INET); | ||
538 | memset(sin, 0, sizeof(*sin)); | ||
539 | sin->sin_family = AF_INET; | ||
540 | break; | ||
541 | |||
542 | case SIOCSIFFLAGS: | ||
543 | ret = -EACCES; | ||
544 | if (!capable(CAP_NET_ADMIN)) | ||
545 | goto out; | ||
546 | break; | ||
547 | case SIOCSIFADDR: /* Set interface address (and family) */ | ||
548 | case SIOCSIFBRDADDR: /* Set the broadcast address */ | ||
549 | case SIOCSIFDSTADDR: /* Set the destination address */ | ||
550 | case SIOCSIFNETMASK: /* Set the netmask for the interface */ | ||
551 | ret = -EACCES; | ||
552 | if (!capable(CAP_NET_ADMIN)) | ||
553 | goto out; | ||
554 | ret = -EINVAL; | ||
555 | if (sin->sin_family != AF_INET) | ||
556 | goto out; | ||
557 | break; | ||
558 | default: | ||
559 | ret = -EINVAL; | ||
560 | goto out; | ||
561 | } | ||
562 | |||
563 | rtnl_lock(); | ||
564 | |||
565 | ret = -ENODEV; | ||
566 | if ((dev = __dev_get_by_name(ifr.ifr_name)) == NULL) | ||
567 | goto done; | ||
568 | |||
569 | if (colon) | ||
570 | *colon = ':'; | ||
571 | |||
572 | if ((in_dev = __in_dev_get(dev)) != NULL) { | ||
573 | if (tryaddrmatch) { | ||
574 | /* Matthias Andree */ | ||
575 | /* compare label and address (4.4BSD style) */ | ||
576 | /* note: we only do this for a limited set of ioctls | ||
577 | and only if the original address family was AF_INET. | ||
578 | This is checked above. */ | ||
579 | for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; | ||
580 | ifap = &ifa->ifa_next) { | ||
581 | if (!strcmp(ifr.ifr_name, ifa->ifa_label) && | ||
582 | sin_orig.sin_addr.s_addr == | ||
583 | ifa->ifa_address) { | ||
584 | break; /* found */ | ||
585 | } | ||
586 | } | ||
587 | } | ||
588 | /* we didn't get a match, maybe the application is | ||
589 | 4.3BSD-style and passed in junk so we fall back to | ||
590 | comparing just the label */ | ||
591 | if (!ifa) { | ||
592 | for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; | ||
593 | ifap = &ifa->ifa_next) | ||
594 | if (!strcmp(ifr.ifr_name, ifa->ifa_label)) | ||
595 | break; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | ret = -EADDRNOTAVAIL; | ||
600 | if (!ifa && cmd != SIOCSIFADDR && cmd != SIOCSIFFLAGS) | ||
601 | goto done; | ||
602 | |||
603 | switch(cmd) { | ||
604 | case SIOCGIFADDR: /* Get interface address */ | ||
605 | sin->sin_addr.s_addr = ifa->ifa_local; | ||
606 | goto rarok; | ||
607 | |||
608 | case SIOCGIFBRDADDR: /* Get the broadcast address */ | ||
609 | sin->sin_addr.s_addr = ifa->ifa_broadcast; | ||
610 | goto rarok; | ||
611 | |||
612 | case SIOCGIFDSTADDR: /* Get the destination address */ | ||
613 | sin->sin_addr.s_addr = ifa->ifa_address; | ||
614 | goto rarok; | ||
615 | |||
616 | case SIOCGIFNETMASK: /* Get the netmask for the interface */ | ||
617 | sin->sin_addr.s_addr = ifa->ifa_mask; | ||
618 | goto rarok; | ||
619 | |||
620 | case SIOCSIFFLAGS: | ||
621 | if (colon) { | ||
622 | ret = -EADDRNOTAVAIL; | ||
623 | if (!ifa) | ||
624 | break; | ||
625 | ret = 0; | ||
626 | if (!(ifr.ifr_flags & IFF_UP)) | ||
627 | inet_del_ifa(in_dev, ifap, 1); | ||
628 | break; | ||
629 | } | ||
630 | ret = dev_change_flags(dev, ifr.ifr_flags); | ||
631 | break; | ||
632 | |||
633 | case SIOCSIFADDR: /* Set interface address (and family) */ | ||
634 | ret = -EINVAL; | ||
635 | if (inet_abc_len(sin->sin_addr.s_addr) < 0) | ||
636 | break; | ||
637 | |||
638 | if (!ifa) { | ||
639 | ret = -ENOBUFS; | ||
640 | if ((ifa = inet_alloc_ifa()) == NULL) | ||
641 | break; | ||
642 | if (colon) | ||
643 | memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); | ||
644 | else | ||
645 | memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); | ||
646 | } else { | ||
647 | ret = 0; | ||
648 | if (ifa->ifa_local == sin->sin_addr.s_addr) | ||
649 | break; | ||
650 | inet_del_ifa(in_dev, ifap, 0); | ||
651 | ifa->ifa_broadcast = 0; | ||
652 | ifa->ifa_anycast = 0; | ||
653 | } | ||
654 | |||
655 | ifa->ifa_address = ifa->ifa_local = sin->sin_addr.s_addr; | ||
656 | |||
657 | if (!(dev->flags & IFF_POINTOPOINT)) { | ||
658 | ifa->ifa_prefixlen = inet_abc_len(ifa->ifa_address); | ||
659 | ifa->ifa_mask = inet_make_mask(ifa->ifa_prefixlen); | ||
660 | if ((dev->flags & IFF_BROADCAST) && | ||
661 | ifa->ifa_prefixlen < 31) | ||
662 | ifa->ifa_broadcast = ifa->ifa_address | | ||
663 | ~ifa->ifa_mask; | ||
664 | } else { | ||
665 | ifa->ifa_prefixlen = 32; | ||
666 | ifa->ifa_mask = inet_make_mask(32); | ||
667 | } | ||
668 | ret = inet_set_ifa(dev, ifa); | ||
669 | break; | ||
670 | |||
671 | case SIOCSIFBRDADDR: /* Set the broadcast address */ | ||
672 | ret = 0; | ||
673 | if (ifa->ifa_broadcast != sin->sin_addr.s_addr) { | ||
674 | inet_del_ifa(in_dev, ifap, 0); | ||
675 | ifa->ifa_broadcast = sin->sin_addr.s_addr; | ||
676 | inet_insert_ifa(ifa); | ||
677 | } | ||
678 | break; | ||
679 | |||
680 | case SIOCSIFDSTADDR: /* Set the destination address */ | ||
681 | ret = 0; | ||
682 | if (ifa->ifa_address == sin->sin_addr.s_addr) | ||
683 | break; | ||
684 | ret = -EINVAL; | ||
685 | if (inet_abc_len(sin->sin_addr.s_addr) < 0) | ||
686 | break; | ||
687 | ret = 0; | ||
688 | inet_del_ifa(in_dev, ifap, 0); | ||
689 | ifa->ifa_address = sin->sin_addr.s_addr; | ||
690 | inet_insert_ifa(ifa); | ||
691 | break; | ||
692 | |||
693 | case SIOCSIFNETMASK: /* Set the netmask for the interface */ | ||
694 | |||
695 | /* | ||
696 | * The mask we set must be legal. | ||
697 | */ | ||
698 | ret = -EINVAL; | ||
699 | if (bad_mask(sin->sin_addr.s_addr, 0)) | ||
700 | break; | ||
701 | ret = 0; | ||
702 | if (ifa->ifa_mask != sin->sin_addr.s_addr) { | ||
703 | inet_del_ifa(in_dev, ifap, 0); | ||
704 | ifa->ifa_mask = sin->sin_addr.s_addr; | ||
705 | ifa->ifa_prefixlen = inet_mask_len(ifa->ifa_mask); | ||
706 | |||
707 | /* See if current broadcast address matches | ||
708 | * with current netmask, then recalculate | ||
709 | * the broadcast address. Otherwise it's a | ||
710 | * funny address, so don't touch it since | ||
711 | * the user seems to know what (s)he's doing... | ||
712 | */ | ||
713 | if ((dev->flags & IFF_BROADCAST) && | ||
714 | (ifa->ifa_prefixlen < 31) && | ||
715 | (ifa->ifa_broadcast == | ||
716 | (ifa->ifa_local|~ifa->ifa_mask))) { | ||
717 | ifa->ifa_broadcast = (ifa->ifa_local | | ||
718 | ~sin->sin_addr.s_addr); | ||
719 | } | ||
720 | inet_insert_ifa(ifa); | ||
721 | } | ||
722 | break; | ||
723 | } | ||
724 | done: | ||
725 | rtnl_unlock(); | ||
726 | out: | ||
727 | return ret; | ||
728 | rarok: | ||
729 | rtnl_unlock(); | ||
730 | ret = copy_to_user(arg, &ifr, sizeof(struct ifreq)) ? -EFAULT : 0; | ||
731 | goto out; | ||
732 | } | ||
733 | |||
734 | static int inet_gifconf(struct net_device *dev, char __user *buf, int len) | ||
735 | { | ||
736 | struct in_device *in_dev = __in_dev_get(dev); | ||
737 | struct in_ifaddr *ifa; | ||
738 | struct ifreq ifr; | ||
739 | int done = 0; | ||
740 | |||
741 | if (!in_dev || (ifa = in_dev->ifa_list) == NULL) | ||
742 | goto out; | ||
743 | |||
744 | for (; ifa; ifa = ifa->ifa_next) { | ||
745 | if (!buf) { | ||
746 | done += sizeof(ifr); | ||
747 | continue; | ||
748 | } | ||
749 | if (len < (int) sizeof(ifr)) | ||
750 | break; | ||
751 | memset(&ifr, 0, sizeof(struct ifreq)); | ||
752 | if (ifa->ifa_label) | ||
753 | strcpy(ifr.ifr_name, ifa->ifa_label); | ||
754 | else | ||
755 | strcpy(ifr.ifr_name, dev->name); | ||
756 | |||
757 | (*(struct sockaddr_in *)&ifr.ifr_addr).sin_family = AF_INET; | ||
758 | (*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr = | ||
759 | ifa->ifa_local; | ||
760 | |||
761 | if (copy_to_user(buf, &ifr, sizeof(struct ifreq))) { | ||
762 | done = -EFAULT; | ||
763 | break; | ||
764 | } | ||
765 | buf += sizeof(struct ifreq); | ||
766 | len -= sizeof(struct ifreq); | ||
767 | done += sizeof(struct ifreq); | ||
768 | } | ||
769 | out: | ||
770 | return done; | ||
771 | } | ||
772 | |||
773 | u32 inet_select_addr(const struct net_device *dev, u32 dst, int scope) | ||
774 | { | ||
775 | u32 addr = 0; | ||
776 | struct in_device *in_dev; | ||
777 | |||
778 | rcu_read_lock(); | ||
779 | in_dev = __in_dev_get(dev); | ||
780 | if (!in_dev) | ||
781 | goto no_in_dev; | ||
782 | |||
783 | for_primary_ifa(in_dev) { | ||
784 | if (ifa->ifa_scope > scope) | ||
785 | continue; | ||
786 | if (!dst || inet_ifa_match(dst, ifa)) { | ||
787 | addr = ifa->ifa_local; | ||
788 | break; | ||
789 | } | ||
790 | if (!addr) | ||
791 | addr = ifa->ifa_local; | ||
792 | } endfor_ifa(in_dev); | ||
793 | no_in_dev: | ||
794 | rcu_read_unlock(); | ||
795 | |||
796 | if (addr) | ||
797 | goto out; | ||
798 | |||
799 | /* Not loopback addresses on loopback should be preferred | ||
800 | in this case. It is importnat that lo is the first interface | ||
801 | in dev_base list. | ||
802 | */ | ||
803 | read_lock(&dev_base_lock); | ||
804 | rcu_read_lock(); | ||
805 | for (dev = dev_base; dev; dev = dev->next) { | ||
806 | if ((in_dev = __in_dev_get(dev)) == NULL) | ||
807 | continue; | ||
808 | |||
809 | for_primary_ifa(in_dev) { | ||
810 | if (ifa->ifa_scope != RT_SCOPE_LINK && | ||
811 | ifa->ifa_scope <= scope) { | ||
812 | addr = ifa->ifa_local; | ||
813 | goto out_unlock_both; | ||
814 | } | ||
815 | } endfor_ifa(in_dev); | ||
816 | } | ||
817 | out_unlock_both: | ||
818 | read_unlock(&dev_base_lock); | ||
819 | rcu_read_unlock(); | ||
820 | out: | ||
821 | return addr; | ||
822 | } | ||
823 | |||
824 | static u32 confirm_addr_indev(struct in_device *in_dev, u32 dst, | ||
825 | u32 local, int scope) | ||
826 | { | ||
827 | int same = 0; | ||
828 | u32 addr = 0; | ||
829 | |||
830 | for_ifa(in_dev) { | ||
831 | if (!addr && | ||
832 | (local == ifa->ifa_local || !local) && | ||
833 | ifa->ifa_scope <= scope) { | ||
834 | addr = ifa->ifa_local; | ||
835 | if (same) | ||
836 | break; | ||
837 | } | ||
838 | if (!same) { | ||
839 | same = (!local || inet_ifa_match(local, ifa)) && | ||
840 | (!dst || inet_ifa_match(dst, ifa)); | ||
841 | if (same && addr) { | ||
842 | if (local || !dst) | ||
843 | break; | ||
844 | /* Is the selected addr into dst subnet? */ | ||
845 | if (inet_ifa_match(addr, ifa)) | ||
846 | break; | ||
847 | /* No, then can we use new local src? */ | ||
848 | if (ifa->ifa_scope <= scope) { | ||
849 | addr = ifa->ifa_local; | ||
850 | break; | ||
851 | } | ||
852 | /* search for large dst subnet for addr */ | ||
853 | same = 0; | ||
854 | } | ||
855 | } | ||
856 | } endfor_ifa(in_dev); | ||
857 | |||
858 | return same? addr : 0; | ||
859 | } | ||
860 | |||
861 | /* | ||
862 | * Confirm that local IP address exists using wildcards: | ||
863 | * - dev: only on this interface, 0=any interface | ||
864 | * - dst: only in the same subnet as dst, 0=any dst | ||
865 | * - local: address, 0=autoselect the local address | ||
866 | * - scope: maximum allowed scope value for the local address | ||
867 | */ | ||
868 | u32 inet_confirm_addr(const struct net_device *dev, u32 dst, u32 local, int scope) | ||
869 | { | ||
870 | u32 addr = 0; | ||
871 | struct in_device *in_dev; | ||
872 | |||
873 | if (dev) { | ||
874 | rcu_read_lock(); | ||
875 | if ((in_dev = __in_dev_get(dev))) | ||
876 | addr = confirm_addr_indev(in_dev, dst, local, scope); | ||
877 | rcu_read_unlock(); | ||
878 | |||
879 | return addr; | ||
880 | } | ||
881 | |||
882 | read_lock(&dev_base_lock); | ||
883 | rcu_read_lock(); | ||
884 | for (dev = dev_base; dev; dev = dev->next) { | ||
885 | if ((in_dev = __in_dev_get(dev))) { | ||
886 | addr = confirm_addr_indev(in_dev, dst, local, scope); | ||
887 | if (addr) | ||
888 | break; | ||
889 | } | ||
890 | } | ||
891 | rcu_read_unlock(); | ||
892 | read_unlock(&dev_base_lock); | ||
893 | |||
894 | return addr; | ||
895 | } | ||
896 | |||
897 | /* | ||
898 | * Device notifier | ||
899 | */ | ||
900 | |||
901 | int register_inetaddr_notifier(struct notifier_block *nb) | ||
902 | { | ||
903 | return notifier_chain_register(&inetaddr_chain, nb); | ||
904 | } | ||
905 | |||
906 | int unregister_inetaddr_notifier(struct notifier_block *nb) | ||
907 | { | ||
908 | return notifier_chain_unregister(&inetaddr_chain, nb); | ||
909 | } | ||
910 | |||
911 | /* Rename ifa_labels for a device name change. Make some effort to preserve existing | ||
912 | * alias numbering and to create unique labels if possible. | ||
913 | */ | ||
914 | static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) | ||
915 | { | ||
916 | struct in_ifaddr *ifa; | ||
917 | int named = 0; | ||
918 | |||
919 | for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { | ||
920 | char old[IFNAMSIZ], *dot; | ||
921 | |||
922 | memcpy(old, ifa->ifa_label, IFNAMSIZ); | ||
923 | memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); | ||
924 | if (named++ == 0) | ||
925 | continue; | ||
926 | dot = strchr(ifa->ifa_label, ':'); | ||
927 | if (dot == NULL) { | ||
928 | sprintf(old, ":%d", named); | ||
929 | dot = old; | ||
930 | } | ||
931 | if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) { | ||
932 | strcat(ifa->ifa_label, dot); | ||
933 | } else { | ||
934 | strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); | ||
935 | } | ||
936 | } | ||
937 | } | ||
938 | |||
939 | /* Called only under RTNL semaphore */ | ||
940 | |||
941 | static int inetdev_event(struct notifier_block *this, unsigned long event, | ||
942 | void *ptr) | ||
943 | { | ||
944 | struct net_device *dev = ptr; | ||
945 | struct in_device *in_dev = __in_dev_get(dev); | ||
946 | |||
947 | ASSERT_RTNL(); | ||
948 | |||
949 | if (!in_dev) { | ||
950 | if (event == NETDEV_REGISTER && dev == &loopback_dev) { | ||
951 | in_dev = inetdev_init(dev); | ||
952 | if (!in_dev) | ||
953 | panic("devinet: Failed to create loopback\n"); | ||
954 | in_dev->cnf.no_xfrm = 1; | ||
955 | in_dev->cnf.no_policy = 1; | ||
956 | } | ||
957 | goto out; | ||
958 | } | ||
959 | |||
960 | switch (event) { | ||
961 | case NETDEV_REGISTER: | ||
962 | printk(KERN_DEBUG "inetdev_event: bug\n"); | ||
963 | dev->ip_ptr = NULL; | ||
964 | break; | ||
965 | case NETDEV_UP: | ||
966 | if (dev->mtu < 68) | ||
967 | break; | ||
968 | if (dev == &loopback_dev) { | ||
969 | struct in_ifaddr *ifa; | ||
970 | if ((ifa = inet_alloc_ifa()) != NULL) { | ||
971 | ifa->ifa_local = | ||
972 | ifa->ifa_address = htonl(INADDR_LOOPBACK); | ||
973 | ifa->ifa_prefixlen = 8; | ||
974 | ifa->ifa_mask = inet_make_mask(8); | ||
975 | in_dev_hold(in_dev); | ||
976 | ifa->ifa_dev = in_dev; | ||
977 | ifa->ifa_scope = RT_SCOPE_HOST; | ||
978 | memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); | ||
979 | inet_insert_ifa(ifa); | ||
980 | } | ||
981 | } | ||
982 | ip_mc_up(in_dev); | ||
983 | break; | ||
984 | case NETDEV_DOWN: | ||
985 | ip_mc_down(in_dev); | ||
986 | break; | ||
987 | case NETDEV_CHANGEMTU: | ||
988 | if (dev->mtu >= 68) | ||
989 | break; | ||
990 | /* MTU falled under 68, disable IP */ | ||
991 | case NETDEV_UNREGISTER: | ||
992 | inetdev_destroy(in_dev); | ||
993 | break; | ||
994 | case NETDEV_CHANGENAME: | ||
995 | /* Do not notify about label change, this event is | ||
996 | * not interesting to applications using netlink. | ||
997 | */ | ||
998 | inetdev_changename(dev, in_dev); | ||
999 | |||
1000 | #ifdef CONFIG_SYSCTL | ||
1001 | devinet_sysctl_unregister(&in_dev->cnf); | ||
1002 | neigh_sysctl_unregister(in_dev->arp_parms); | ||
1003 | neigh_sysctl_register(dev, in_dev->arp_parms, NET_IPV4, | ||
1004 | NET_IPV4_NEIGH, "ipv4", NULL, NULL); | ||
1005 | devinet_sysctl_register(in_dev, &in_dev->cnf); | ||
1006 | #endif | ||
1007 | break; | ||
1008 | } | ||
1009 | out: | ||
1010 | return NOTIFY_DONE; | ||
1011 | } | ||
1012 | |||
1013 | static struct notifier_block ip_netdev_notifier = { | ||
1014 | .notifier_call =inetdev_event, | ||
1015 | }; | ||
1016 | |||
1017 | static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, | ||
1018 | u32 pid, u32 seq, int event) | ||
1019 | { | ||
1020 | struct ifaddrmsg *ifm; | ||
1021 | struct nlmsghdr *nlh; | ||
1022 | unsigned char *b = skb->tail; | ||
1023 | |||
1024 | nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm)); | ||
1025 | if (pid) nlh->nlmsg_flags |= NLM_F_MULTI; | ||
1026 | ifm = NLMSG_DATA(nlh); | ||
1027 | ifm->ifa_family = AF_INET; | ||
1028 | ifm->ifa_prefixlen = ifa->ifa_prefixlen; | ||
1029 | ifm->ifa_flags = ifa->ifa_flags|IFA_F_PERMANENT; | ||
1030 | ifm->ifa_scope = ifa->ifa_scope; | ||
1031 | ifm->ifa_index = ifa->ifa_dev->dev->ifindex; | ||
1032 | if (ifa->ifa_address) | ||
1033 | RTA_PUT(skb, IFA_ADDRESS, 4, &ifa->ifa_address); | ||
1034 | if (ifa->ifa_local) | ||
1035 | RTA_PUT(skb, IFA_LOCAL, 4, &ifa->ifa_local); | ||
1036 | if (ifa->ifa_broadcast) | ||
1037 | RTA_PUT(skb, IFA_BROADCAST, 4, &ifa->ifa_broadcast); | ||
1038 | if (ifa->ifa_anycast) | ||
1039 | RTA_PUT(skb, IFA_ANYCAST, 4, &ifa->ifa_anycast); | ||
1040 | if (ifa->ifa_label[0]) | ||
1041 | RTA_PUT(skb, IFA_LABEL, IFNAMSIZ, &ifa->ifa_label); | ||
1042 | nlh->nlmsg_len = skb->tail - b; | ||
1043 | return skb->len; | ||
1044 | |||
1045 | nlmsg_failure: | ||
1046 | rtattr_failure: | ||
1047 | skb_trim(skb, b - skb->data); | ||
1048 | return -1; | ||
1049 | } | ||
1050 | |||
1051 | static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) | ||
1052 | { | ||
1053 | int idx, ip_idx; | ||
1054 | struct net_device *dev; | ||
1055 | struct in_device *in_dev; | ||
1056 | struct in_ifaddr *ifa; | ||
1057 | int s_ip_idx, s_idx = cb->args[0]; | ||
1058 | |||
1059 | s_ip_idx = ip_idx = cb->args[1]; | ||
1060 | read_lock(&dev_base_lock); | ||
1061 | for (dev = dev_base, idx = 0; dev; dev = dev->next, idx++) { | ||
1062 | if (idx < s_idx) | ||
1063 | continue; | ||
1064 | if (idx > s_idx) | ||
1065 | s_ip_idx = 0; | ||
1066 | rcu_read_lock(); | ||
1067 | if ((in_dev = __in_dev_get(dev)) == NULL) { | ||
1068 | rcu_read_unlock(); | ||
1069 | continue; | ||
1070 | } | ||
1071 | |||
1072 | for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; | ||
1073 | ifa = ifa->ifa_next, ip_idx++) { | ||
1074 | if (ip_idx < s_ip_idx) | ||
1075 | continue; | ||
1076 | if (inet_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, | ||
1077 | cb->nlh->nlmsg_seq, | ||
1078 | RTM_NEWADDR) <= 0) { | ||
1079 | rcu_read_unlock(); | ||
1080 | goto done; | ||
1081 | } | ||
1082 | } | ||
1083 | rcu_read_unlock(); | ||
1084 | } | ||
1085 | |||
1086 | done: | ||
1087 | read_unlock(&dev_base_lock); | ||
1088 | cb->args[0] = idx; | ||
1089 | cb->args[1] = ip_idx; | ||
1090 | |||
1091 | return skb->len; | ||
1092 | } | ||
1093 | |||
1094 | static void rtmsg_ifa(int event, struct in_ifaddr* ifa) | ||
1095 | { | ||
1096 | int size = NLMSG_SPACE(sizeof(struct ifaddrmsg) + 128); | ||
1097 | struct sk_buff *skb = alloc_skb(size, GFP_KERNEL); | ||
1098 | |||
1099 | if (!skb) | ||
1100 | netlink_set_err(rtnl, 0, RTMGRP_IPV4_IFADDR, ENOBUFS); | ||
1101 | else if (inet_fill_ifaddr(skb, ifa, 0, 0, event) < 0) { | ||
1102 | kfree_skb(skb); | ||
1103 | netlink_set_err(rtnl, 0, RTMGRP_IPV4_IFADDR, EINVAL); | ||
1104 | } else { | ||
1105 | NETLINK_CB(skb).dst_groups = RTMGRP_IPV4_IFADDR; | ||
1106 | netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV4_IFADDR, GFP_KERNEL); | ||
1107 | } | ||
1108 | } | ||
1109 | |||
1110 | static struct rtnetlink_link inet_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = { | ||
1111 | [4] = { .doit = inet_rtm_newaddr, }, | ||
1112 | [5] = { .doit = inet_rtm_deladdr, }, | ||
1113 | [6] = { .dumpit = inet_dump_ifaddr, }, | ||
1114 | [8] = { .doit = inet_rtm_newroute, }, | ||
1115 | [9] = { .doit = inet_rtm_delroute, }, | ||
1116 | [10] = { .doit = inet_rtm_getroute, .dumpit = inet_dump_fib, }, | ||
1117 | #ifdef CONFIG_IP_MULTIPLE_TABLES | ||
1118 | [16] = { .doit = inet_rtm_newrule, }, | ||
1119 | [17] = { .doit = inet_rtm_delrule, }, | ||
1120 | [18] = { .dumpit = inet_dump_rules, }, | ||
1121 | #endif | ||
1122 | }; | ||
1123 | |||
1124 | #ifdef CONFIG_SYSCTL | ||
1125 | |||
1126 | void inet_forward_change(void) | ||
1127 | { | ||
1128 | struct net_device *dev; | ||
1129 | int on = ipv4_devconf.forwarding; | ||
1130 | |||
1131 | ipv4_devconf.accept_redirects = !on; | ||
1132 | ipv4_devconf_dflt.forwarding = on; | ||
1133 | |||
1134 | read_lock(&dev_base_lock); | ||
1135 | for (dev = dev_base; dev; dev = dev->next) { | ||
1136 | struct in_device *in_dev; | ||
1137 | rcu_read_lock(); | ||
1138 | in_dev = __in_dev_get(dev); | ||
1139 | if (in_dev) | ||
1140 | in_dev->cnf.forwarding = on; | ||
1141 | rcu_read_unlock(); | ||
1142 | } | ||
1143 | read_unlock(&dev_base_lock); | ||
1144 | |||
1145 | rt_cache_flush(0); | ||
1146 | } | ||
1147 | |||
1148 | static int devinet_sysctl_forward(ctl_table *ctl, int write, | ||
1149 | struct file* filp, void __user *buffer, | ||
1150 | size_t *lenp, loff_t *ppos) | ||
1151 | { | ||
1152 | int *valp = ctl->data; | ||
1153 | int val = *valp; | ||
1154 | int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); | ||
1155 | |||
1156 | if (write && *valp != val) { | ||
1157 | if (valp == &ipv4_devconf.forwarding) | ||
1158 | inet_forward_change(); | ||
1159 | else if (valp != &ipv4_devconf_dflt.forwarding) | ||
1160 | rt_cache_flush(0); | ||
1161 | } | ||
1162 | |||
1163 | return ret; | ||
1164 | } | ||
1165 | |||
1166 | int ipv4_doint_and_flush(ctl_table *ctl, int write, | ||
1167 | struct file* filp, void __user *buffer, | ||
1168 | size_t *lenp, loff_t *ppos) | ||
1169 | { | ||
1170 | int *valp = ctl->data; | ||
1171 | int val = *valp; | ||
1172 | int ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos); | ||
1173 | |||
1174 | if (write && *valp != val) | ||
1175 | rt_cache_flush(0); | ||
1176 | |||
1177 | return ret; | ||
1178 | } | ||
1179 | |||
1180 | int ipv4_doint_and_flush_strategy(ctl_table *table, int __user *name, int nlen, | ||
1181 | void __user *oldval, size_t __user *oldlenp, | ||
1182 | void __user *newval, size_t newlen, | ||
1183 | void **context) | ||
1184 | { | ||
1185 | int *valp = table->data; | ||
1186 | int new; | ||
1187 | |||
1188 | if (!newval || !newlen) | ||
1189 | return 0; | ||
1190 | |||
1191 | if (newlen != sizeof(int)) | ||
1192 | return -EINVAL; | ||
1193 | |||
1194 | if (get_user(new, (int __user *)newval)) | ||
1195 | return -EFAULT; | ||
1196 | |||
1197 | if (new == *valp) | ||
1198 | return 0; | ||
1199 | |||
1200 | if (oldval && oldlenp) { | ||
1201 | size_t len; | ||
1202 | |||
1203 | if (get_user(len, oldlenp)) | ||
1204 | return -EFAULT; | ||
1205 | |||
1206 | if (len) { | ||
1207 | if (len > table->maxlen) | ||
1208 | len = table->maxlen; | ||
1209 | if (copy_to_user(oldval, valp, len)) | ||
1210 | return -EFAULT; | ||
1211 | if (put_user(len, oldlenp)) | ||
1212 | return -EFAULT; | ||
1213 | } | ||
1214 | } | ||
1215 | |||
1216 | *valp = new; | ||
1217 | rt_cache_flush(0); | ||
1218 | return 1; | ||
1219 | } | ||
1220 | |||
1221 | |||
1222 | static struct devinet_sysctl_table { | ||
1223 | struct ctl_table_header *sysctl_header; | ||
1224 | ctl_table devinet_vars[__NET_IPV4_CONF_MAX]; | ||
1225 | ctl_table devinet_dev[2]; | ||
1226 | ctl_table devinet_conf_dir[2]; | ||
1227 | ctl_table devinet_proto_dir[2]; | ||
1228 | ctl_table devinet_root_dir[2]; | ||
1229 | } devinet_sysctl = { | ||
1230 | .devinet_vars = { | ||
1231 | { | ||
1232 | .ctl_name = NET_IPV4_CONF_FORWARDING, | ||
1233 | .procname = "forwarding", | ||
1234 | .data = &ipv4_devconf.forwarding, | ||
1235 | .maxlen = sizeof(int), | ||
1236 | .mode = 0644, | ||
1237 | .proc_handler = &devinet_sysctl_forward, | ||
1238 | }, | ||
1239 | { | ||
1240 | .ctl_name = NET_IPV4_CONF_MC_FORWARDING, | ||
1241 | .procname = "mc_forwarding", | ||
1242 | .data = &ipv4_devconf.mc_forwarding, | ||
1243 | .maxlen = sizeof(int), | ||
1244 | .mode = 0444, | ||
1245 | .proc_handler = &proc_dointvec, | ||
1246 | }, | ||
1247 | { | ||
1248 | .ctl_name = NET_IPV4_CONF_ACCEPT_REDIRECTS, | ||
1249 | .procname = "accept_redirects", | ||
1250 | .data = &ipv4_devconf.accept_redirects, | ||
1251 | .maxlen = sizeof(int), | ||
1252 | .mode = 0644, | ||
1253 | .proc_handler = &proc_dointvec, | ||
1254 | }, | ||
1255 | { | ||
1256 | .ctl_name = NET_IPV4_CONF_SECURE_REDIRECTS, | ||
1257 | .procname = "secure_redirects", | ||
1258 | .data = &ipv4_devconf.secure_redirects, | ||
1259 | .maxlen = sizeof(int), | ||
1260 | .mode = 0644, | ||
1261 | .proc_handler = &proc_dointvec, | ||
1262 | }, | ||
1263 | { | ||
1264 | .ctl_name = NET_IPV4_CONF_SHARED_MEDIA, | ||
1265 | .procname = "shared_media", | ||
1266 | .data = &ipv4_devconf.shared_media, | ||
1267 | .maxlen = sizeof(int), | ||
1268 | .mode = 0644, | ||
1269 | .proc_handler = &proc_dointvec, | ||
1270 | }, | ||
1271 | { | ||
1272 | .ctl_name = NET_IPV4_CONF_RP_FILTER, | ||
1273 | .procname = "rp_filter", | ||
1274 | .data = &ipv4_devconf.rp_filter, | ||
1275 | .maxlen = sizeof(int), | ||
1276 | .mode = 0644, | ||
1277 | .proc_handler = &proc_dointvec, | ||
1278 | }, | ||
1279 | { | ||
1280 | .ctl_name = NET_IPV4_CONF_SEND_REDIRECTS, | ||
1281 | .procname = "send_redirects", | ||
1282 | .data = &ipv4_devconf.send_redirects, | ||
1283 | .maxlen = sizeof(int), | ||
1284 | .mode = 0644, | ||
1285 | .proc_handler = &proc_dointvec, | ||
1286 | }, | ||
1287 | { | ||
1288 | .ctl_name = NET_IPV4_CONF_ACCEPT_SOURCE_ROUTE, | ||
1289 | .procname = "accept_source_route", | ||
1290 | .data = &ipv4_devconf.accept_source_route, | ||
1291 | .maxlen = sizeof(int), | ||
1292 | .mode = 0644, | ||
1293 | .proc_handler = &proc_dointvec, | ||
1294 | }, | ||
1295 | { | ||
1296 | .ctl_name = NET_IPV4_CONF_PROXY_ARP, | ||
1297 | .procname = "proxy_arp", | ||
1298 | .data = &ipv4_devconf.proxy_arp, | ||
1299 | .maxlen = sizeof(int), | ||
1300 | .mode = 0644, | ||
1301 | .proc_handler = &proc_dointvec, | ||
1302 | }, | ||
1303 | { | ||
1304 | .ctl_name = NET_IPV4_CONF_MEDIUM_ID, | ||
1305 | .procname = "medium_id", | ||
1306 | .data = &ipv4_devconf.medium_id, | ||
1307 | .maxlen = sizeof(int), | ||
1308 | .mode = 0644, | ||
1309 | .proc_handler = &proc_dointvec, | ||
1310 | }, | ||
1311 | { | ||
1312 | .ctl_name = NET_IPV4_CONF_BOOTP_RELAY, | ||
1313 | .procname = "bootp_relay", | ||
1314 | .data = &ipv4_devconf.bootp_relay, | ||
1315 | .maxlen = sizeof(int), | ||
1316 | .mode = 0644, | ||
1317 | .proc_handler = &proc_dointvec, | ||
1318 | }, | ||
1319 | { | ||
1320 | .ctl_name = NET_IPV4_CONF_LOG_MARTIANS, | ||
1321 | .procname = "log_martians", | ||
1322 | .data = &ipv4_devconf.log_martians, | ||
1323 | .maxlen = sizeof(int), | ||
1324 | .mode = 0644, | ||
1325 | .proc_handler = &proc_dointvec, | ||
1326 | }, | ||
1327 | { | ||
1328 | .ctl_name = NET_IPV4_CONF_TAG, | ||
1329 | .procname = "tag", | ||
1330 | .data = &ipv4_devconf.tag, | ||
1331 | .maxlen = sizeof(int), | ||
1332 | .mode = 0644, | ||
1333 | .proc_handler = &proc_dointvec, | ||
1334 | }, | ||
1335 | { | ||
1336 | .ctl_name = NET_IPV4_CONF_ARPFILTER, | ||
1337 | .procname = "arp_filter", | ||
1338 | .data = &ipv4_devconf.arp_filter, | ||
1339 | .maxlen = sizeof(int), | ||
1340 | .mode = 0644, | ||
1341 | .proc_handler = &proc_dointvec, | ||
1342 | }, | ||
1343 | { | ||
1344 | .ctl_name = NET_IPV4_CONF_ARP_ANNOUNCE, | ||
1345 | .procname = "arp_announce", | ||
1346 | .data = &ipv4_devconf.arp_announce, | ||
1347 | .maxlen = sizeof(int), | ||
1348 | .mode = 0644, | ||
1349 | .proc_handler = &proc_dointvec, | ||
1350 | }, | ||
1351 | { | ||
1352 | .ctl_name = NET_IPV4_CONF_ARP_IGNORE, | ||
1353 | .procname = "arp_ignore", | ||
1354 | .data = &ipv4_devconf.arp_ignore, | ||
1355 | .maxlen = sizeof(int), | ||
1356 | .mode = 0644, | ||
1357 | .proc_handler = &proc_dointvec, | ||
1358 | }, | ||
1359 | { | ||
1360 | .ctl_name = NET_IPV4_CONF_NOXFRM, | ||
1361 | .procname = "disable_xfrm", | ||
1362 | .data = &ipv4_devconf.no_xfrm, | ||
1363 | .maxlen = sizeof(int), | ||
1364 | .mode = 0644, | ||
1365 | .proc_handler = &ipv4_doint_and_flush, | ||
1366 | .strategy = &ipv4_doint_and_flush_strategy, | ||
1367 | }, | ||
1368 | { | ||
1369 | .ctl_name = NET_IPV4_CONF_NOPOLICY, | ||
1370 | .procname = "disable_policy", | ||
1371 | .data = &ipv4_devconf.no_policy, | ||
1372 | .maxlen = sizeof(int), | ||
1373 | .mode = 0644, | ||
1374 | .proc_handler = &ipv4_doint_and_flush, | ||
1375 | .strategy = &ipv4_doint_and_flush_strategy, | ||
1376 | }, | ||
1377 | { | ||
1378 | .ctl_name = NET_IPV4_CONF_FORCE_IGMP_VERSION, | ||
1379 | .procname = "force_igmp_version", | ||
1380 | .data = &ipv4_devconf.force_igmp_version, | ||
1381 | .maxlen = sizeof(int), | ||
1382 | .mode = 0644, | ||
1383 | .proc_handler = &ipv4_doint_and_flush, | ||
1384 | .strategy = &ipv4_doint_and_flush_strategy, | ||
1385 | }, | ||
1386 | }, | ||
1387 | .devinet_dev = { | ||
1388 | { | ||
1389 | .ctl_name = NET_PROTO_CONF_ALL, | ||
1390 | .procname = "all", | ||
1391 | .mode = 0555, | ||
1392 | .child = devinet_sysctl.devinet_vars, | ||
1393 | }, | ||
1394 | }, | ||
1395 | .devinet_conf_dir = { | ||
1396 | { | ||
1397 | .ctl_name = NET_IPV4_CONF, | ||
1398 | .procname = "conf", | ||
1399 | .mode = 0555, | ||
1400 | .child = devinet_sysctl.devinet_dev, | ||
1401 | }, | ||
1402 | }, | ||
1403 | .devinet_proto_dir = { | ||
1404 | { | ||
1405 | .ctl_name = NET_IPV4, | ||
1406 | .procname = "ipv4", | ||
1407 | .mode = 0555, | ||
1408 | .child = devinet_sysctl.devinet_conf_dir, | ||
1409 | }, | ||
1410 | }, | ||
1411 | .devinet_root_dir = { | ||
1412 | { | ||
1413 | .ctl_name = CTL_NET, | ||
1414 | .procname = "net", | ||
1415 | .mode = 0555, | ||
1416 | .child = devinet_sysctl.devinet_proto_dir, | ||
1417 | }, | ||
1418 | }, | ||
1419 | }; | ||
1420 | |||
1421 | static void devinet_sysctl_register(struct in_device *in_dev, | ||
1422 | struct ipv4_devconf *p) | ||
1423 | { | ||
1424 | int i; | ||
1425 | struct net_device *dev = in_dev ? in_dev->dev : NULL; | ||
1426 | struct devinet_sysctl_table *t = kmalloc(sizeof(*t), GFP_KERNEL); | ||
1427 | char *dev_name = NULL; | ||
1428 | |||
1429 | if (!t) | ||
1430 | return; | ||
1431 | memcpy(t, &devinet_sysctl, sizeof(*t)); | ||
1432 | for (i = 0; i < ARRAY_SIZE(t->devinet_vars) - 1; i++) { | ||
1433 | t->devinet_vars[i].data += (char *)p - (char *)&ipv4_devconf; | ||
1434 | t->devinet_vars[i].de = NULL; | ||
1435 | } | ||
1436 | |||
1437 | if (dev) { | ||
1438 | dev_name = dev->name; | ||
1439 | t->devinet_dev[0].ctl_name = dev->ifindex; | ||
1440 | } else { | ||
1441 | dev_name = "default"; | ||
1442 | t->devinet_dev[0].ctl_name = NET_PROTO_CONF_DEFAULT; | ||
1443 | } | ||
1444 | |||
1445 | /* | ||
1446 | * Make a copy of dev_name, because '.procname' is regarded as const | ||
1447 | * by sysctl and we wouldn't want anyone to change it under our feet | ||
1448 | * (see SIOCSIFNAME). | ||
1449 | */ | ||
1450 | dev_name = net_sysctl_strdup(dev_name); | ||
1451 | if (!dev_name) | ||
1452 | goto free; | ||
1453 | |||
1454 | t->devinet_dev[0].procname = dev_name; | ||
1455 | t->devinet_dev[0].child = t->devinet_vars; | ||
1456 | t->devinet_dev[0].de = NULL; | ||
1457 | t->devinet_conf_dir[0].child = t->devinet_dev; | ||
1458 | t->devinet_conf_dir[0].de = NULL; | ||
1459 | t->devinet_proto_dir[0].child = t->devinet_conf_dir; | ||
1460 | t->devinet_proto_dir[0].de = NULL; | ||
1461 | t->devinet_root_dir[0].child = t->devinet_proto_dir; | ||
1462 | t->devinet_root_dir[0].de = NULL; | ||
1463 | |||
1464 | t->sysctl_header = register_sysctl_table(t->devinet_root_dir, 0); | ||
1465 | if (!t->sysctl_header) | ||
1466 | goto free_procname; | ||
1467 | |||
1468 | p->sysctl = t; | ||
1469 | return; | ||
1470 | |||
1471 | /* error path */ | ||
1472 | free_procname: | ||
1473 | kfree(dev_name); | ||
1474 | free: | ||
1475 | kfree(t); | ||
1476 | return; | ||
1477 | } | ||
1478 | |||
1479 | static void devinet_sysctl_unregister(struct ipv4_devconf *p) | ||
1480 | { | ||
1481 | if (p->sysctl) { | ||
1482 | struct devinet_sysctl_table *t = p->sysctl; | ||
1483 | p->sysctl = NULL; | ||
1484 | unregister_sysctl_table(t->sysctl_header); | ||
1485 | kfree(t->devinet_dev[0].procname); | ||
1486 | kfree(t); | ||
1487 | } | ||
1488 | } | ||
1489 | #endif | ||
1490 | |||
1491 | void __init devinet_init(void) | ||
1492 | { | ||
1493 | register_gifconf(PF_INET, inet_gifconf); | ||
1494 | register_netdevice_notifier(&ip_netdev_notifier); | ||
1495 | rtnetlink_links[PF_INET] = inet_rtnetlink_table; | ||
1496 | #ifdef CONFIG_SYSCTL | ||
1497 | devinet_sysctl.sysctl_header = | ||
1498 | register_sysctl_table(devinet_sysctl.devinet_root_dir, 0); | ||
1499 | devinet_sysctl_register(NULL, &ipv4_devconf_dflt); | ||
1500 | #endif | ||
1501 | } | ||
1502 | |||
1503 | EXPORT_SYMBOL(devinet_ioctl); | ||
1504 | EXPORT_SYMBOL(in_dev_finish_destroy); | ||
1505 | EXPORT_SYMBOL(inet_select_addr); | ||
1506 | EXPORT_SYMBOL(inetdev_by_index); | ||
1507 | EXPORT_SYMBOL(register_inetaddr_notifier); | ||
1508 | EXPORT_SYMBOL(unregister_inetaddr_notifier); | ||