[IPV4]: Fix secondary IP addresses after promotion
[linux-2.6/verdex.git] / net / ipv4 / fib_frontend.c
blob882f88f6d13b8b500f0c3744cb9cc574f5e987ce
1 /*
2 * INET An implementation of the TCP/IP protocol suite for the LINUX
3 * operating system. INET is implemented using the BSD Socket
4 * interface as the means of communication with the user level.
6 * IPv4 Forwarding Information Base: FIB frontend.
8 * Version: $Id: fib_frontend.c,v 1.26 2001/10/31 21:55:54 davem Exp $
10 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
18 #include <linux/config.h>
19 #include <linux/module.h>
20 #include <asm/uaccess.h>
21 #include <asm/system.h>
22 #include <linux/bitops.h>
23 #include <linux/types.h>
24 #include <linux/kernel.h>
25 #include <linux/sched.h>
26 #include <linux/mm.h>
27 #include <linux/string.h>
28 #include <linux/socket.h>
29 #include <linux/sockios.h>
30 #include <linux/errno.h>
31 #include <linux/in.h>
32 #include <linux/inet.h>
33 #include <linux/netdevice.h>
34 #include <linux/if_arp.h>
35 #include <linux/skbuff.h>
36 #include <linux/netlink.h>
37 #include <linux/init.h>
39 #include <net/ip.h>
40 #include <net/protocol.h>
41 #include <net/route.h>
42 #include <net/tcp.h>
43 #include <net/sock.h>
44 #include <net/icmp.h>
45 #include <net/arp.h>
46 #include <net/ip_fib.h>
48 #define FFprint(a...) printk(KERN_DEBUG a)
50 #ifndef CONFIG_IP_MULTIPLE_TABLES
52 #define RT_TABLE_MIN RT_TABLE_MAIN
54 struct fib_table *ip_fib_local_table;
55 struct fib_table *ip_fib_main_table;
57 #else
59 #define RT_TABLE_MIN 1
61 struct fib_table *fib_tables[RT_TABLE_MAX+1];
63 struct fib_table *__fib_new_table(int id)
65 struct fib_table *tb;
67 tb = fib_hash_init(id);
68 if (!tb)
69 return NULL;
70 fib_tables[id] = tb;
71 return tb;
75 #endif /* CONFIG_IP_MULTIPLE_TABLES */
78 static void fib_flush(void)
80 int flushed = 0;
81 #ifdef CONFIG_IP_MULTIPLE_TABLES
82 struct fib_table *tb;
83 int id;
85 for (id = RT_TABLE_MAX; id>0; id--) {
86 if ((tb = fib_get_table(id))==NULL)
87 continue;
88 flushed += tb->tb_flush(tb);
90 #else /* CONFIG_IP_MULTIPLE_TABLES */
91 flushed += ip_fib_main_table->tb_flush(ip_fib_main_table);
92 flushed += ip_fib_local_table->tb_flush(ip_fib_local_table);
93 #endif /* CONFIG_IP_MULTIPLE_TABLES */
95 if (flushed)
96 rt_cache_flush(-1);
100 * Find the first device with a given source address.
103 struct net_device * ip_dev_find(u32 addr)
105 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
106 struct fib_result res;
107 struct net_device *dev = NULL;
109 #ifdef CONFIG_IP_MULTIPLE_TABLES
110 res.r = NULL;
111 #endif
113 if (!ip_fib_local_table ||
114 ip_fib_local_table->tb_lookup(ip_fib_local_table, &fl, &res))
115 return NULL;
116 if (res.type != RTN_LOCAL)
117 goto out;
118 dev = FIB_RES_DEV(res);
120 if (dev)
121 dev_hold(dev);
122 out:
123 fib_res_put(&res);
124 return dev;
127 unsigned inet_addr_type(u32 addr)
129 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = addr } } };
130 struct fib_result res;
131 unsigned ret = RTN_BROADCAST;
133 if (ZERONET(addr) || BADCLASS(addr))
134 return RTN_BROADCAST;
135 if (MULTICAST(addr))
136 return RTN_MULTICAST;
138 #ifdef CONFIG_IP_MULTIPLE_TABLES
139 res.r = NULL;
140 #endif
142 if (ip_fib_local_table) {
143 ret = RTN_UNICAST;
144 if (!ip_fib_local_table->tb_lookup(ip_fib_local_table,
145 &fl, &res)) {
146 ret = res.type;
147 fib_res_put(&res);
150 return ret;
153 /* Given (packet source, input interface) and optional (dst, oif, tos):
154 - (main) check, that source is valid i.e. not broadcast or our local
155 address.
156 - figure out what "logical" interface this packet arrived
157 and calculate "specific destination" address.
158 - check, that packet arrived from expected physical interface.
161 int fib_validate_source(u32 src, u32 dst, u8 tos, int oif,
162 struct net_device *dev, u32 *spec_dst, u32 *itag)
164 struct in_device *in_dev;
165 struct flowi fl = { .nl_u = { .ip4_u =
166 { .daddr = src,
167 .saddr = dst,
168 .tos = tos } },
169 .iif = oif };
170 struct fib_result res;
171 int no_addr, rpf;
172 int ret;
174 no_addr = rpf = 0;
175 rcu_read_lock();
176 in_dev = __in_dev_get_rcu(dev);
177 if (in_dev) {
178 no_addr = in_dev->ifa_list == NULL;
179 rpf = IN_DEV_RPFILTER(in_dev);
181 rcu_read_unlock();
183 if (in_dev == NULL)
184 goto e_inval;
186 if (fib_lookup(&fl, &res))
187 goto last_resort;
188 if (res.type != RTN_UNICAST)
189 goto e_inval_res;
190 *spec_dst = FIB_RES_PREFSRC(res);
191 fib_combine_itag(itag, &res);
192 #ifdef CONFIG_IP_ROUTE_MULTIPATH
193 if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1)
194 #else
195 if (FIB_RES_DEV(res) == dev)
196 #endif
198 ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
199 fib_res_put(&res);
200 return ret;
202 fib_res_put(&res);
203 if (no_addr)
204 goto last_resort;
205 if (rpf)
206 goto e_inval;
207 fl.oif = dev->ifindex;
209 ret = 0;
210 if (fib_lookup(&fl, &res) == 0) {
211 if (res.type == RTN_UNICAST) {
212 *spec_dst = FIB_RES_PREFSRC(res);
213 ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
215 fib_res_put(&res);
217 return ret;
219 last_resort:
220 if (rpf)
221 goto e_inval;
222 *spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE);
223 *itag = 0;
224 return 0;
226 e_inval_res:
227 fib_res_put(&res);
228 e_inval:
229 return -EINVAL;
232 #ifndef CONFIG_IP_NOSIOCRT
235 * Handle IP routing ioctl calls. These are used to manipulate the routing tables
238 int ip_rt_ioctl(unsigned int cmd, void __user *arg)
240 int err;
241 struct kern_rta rta;
242 struct rtentry r;
243 struct {
244 struct nlmsghdr nlh;
245 struct rtmsg rtm;
246 } req;
248 switch (cmd) {
249 case SIOCADDRT: /* Add a route */
250 case SIOCDELRT: /* Delete a route */
251 if (!capable(CAP_NET_ADMIN))
252 return -EPERM;
253 if (copy_from_user(&r, arg, sizeof(struct rtentry)))
254 return -EFAULT;
255 rtnl_lock();
256 err = fib_convert_rtentry(cmd, &req.nlh, &req.rtm, &rta, &r);
257 if (err == 0) {
258 if (cmd == SIOCDELRT) {
259 struct fib_table *tb = fib_get_table(req.rtm.rtm_table);
260 err = -ESRCH;
261 if (tb)
262 err = tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL);
263 } else {
264 struct fib_table *tb = fib_new_table(req.rtm.rtm_table);
265 err = -ENOBUFS;
266 if (tb)
267 err = tb->tb_insert(tb, &req.rtm, &rta, &req.nlh, NULL);
269 kfree(rta.rta_mx);
271 rtnl_unlock();
272 return err;
274 return -EINVAL;
277 #else
279 int ip_rt_ioctl(unsigned int cmd, void *arg)
281 return -EINVAL;
284 #endif
286 static int inet_check_attr(struct rtmsg *r, struct rtattr **rta)
288 int i;
290 for (i=1; i<=RTA_MAX; i++) {
291 struct rtattr *attr = rta[i-1];
292 if (attr) {
293 if (RTA_PAYLOAD(attr) < 4)
294 return -EINVAL;
295 if (i != RTA_MULTIPATH && i != RTA_METRICS)
296 rta[i-1] = (struct rtattr*)RTA_DATA(attr);
299 return 0;
302 int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
304 struct fib_table * tb;
305 struct rtattr **rta = arg;
306 struct rtmsg *r = NLMSG_DATA(nlh);
308 if (inet_check_attr(r, rta))
309 return -EINVAL;
311 tb = fib_get_table(r->rtm_table);
312 if (tb)
313 return tb->tb_delete(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
314 return -ESRCH;
317 int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
319 struct fib_table * tb;
320 struct rtattr **rta = arg;
321 struct rtmsg *r = NLMSG_DATA(nlh);
323 if (inet_check_attr(r, rta))
324 return -EINVAL;
326 tb = fib_new_table(r->rtm_table);
327 if (tb)
328 return tb->tb_insert(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
329 return -ENOBUFS;
332 int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
334 int t;
335 int s_t;
336 struct fib_table *tb;
338 if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
339 ((struct rtmsg*)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)
340 return ip_rt_dump(skb, cb);
342 s_t = cb->args[0];
343 if (s_t == 0)
344 s_t = cb->args[0] = RT_TABLE_MIN;
346 for (t=s_t; t<=RT_TABLE_MAX; t++) {
347 if (t < s_t) continue;
348 if (t > s_t)
349 memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
350 if ((tb = fib_get_table(t))==NULL)
351 continue;
352 if (tb->tb_dump(tb, skb, cb) < 0)
353 break;
356 cb->args[0] = t;
358 return skb->len;
361 /* Prepare and feed intra-kernel routing request.
362 Really, it should be netlink message, but :-( netlink
363 can be not configured, so that we feed it directly
364 to fib engine. It is legal, because all events occur
365 only when netlink is already locked.
368 static void fib_magic(int cmd, int type, u32 dst, int dst_len, struct in_ifaddr *ifa)
370 struct fib_table * tb;
371 struct {
372 struct nlmsghdr nlh;
373 struct rtmsg rtm;
374 } req;
375 struct kern_rta rta;
377 memset(&req.rtm, 0, sizeof(req.rtm));
378 memset(&rta, 0, sizeof(rta));
380 if (type == RTN_UNICAST)
381 tb = fib_new_table(RT_TABLE_MAIN);
382 else
383 tb = fib_new_table(RT_TABLE_LOCAL);
385 if (tb == NULL)
386 return;
388 req.nlh.nlmsg_len = sizeof(req);
389 req.nlh.nlmsg_type = cmd;
390 req.nlh.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_APPEND;
391 req.nlh.nlmsg_pid = 0;
392 req.nlh.nlmsg_seq = 0;
394 req.rtm.rtm_dst_len = dst_len;
395 req.rtm.rtm_table = tb->tb_id;
396 req.rtm.rtm_protocol = RTPROT_KERNEL;
397 req.rtm.rtm_scope = (type != RTN_LOCAL ? RT_SCOPE_LINK : RT_SCOPE_HOST);
398 req.rtm.rtm_type = type;
400 rta.rta_dst = &dst;
401 rta.rta_prefsrc = &ifa->ifa_local;
402 rta.rta_oif = &ifa->ifa_dev->dev->ifindex;
404 if (cmd == RTM_NEWROUTE)
405 tb->tb_insert(tb, &req.rtm, &rta, &req.nlh, NULL);
406 else
407 tb->tb_delete(tb, &req.rtm, &rta, &req.nlh, NULL);
410 void fib_add_ifaddr(struct in_ifaddr *ifa)
412 struct in_device *in_dev = ifa->ifa_dev;
413 struct net_device *dev = in_dev->dev;
414 struct in_ifaddr *prim = ifa;
415 u32 mask = ifa->ifa_mask;
416 u32 addr = ifa->ifa_local;
417 u32 prefix = ifa->ifa_address&mask;
419 if (ifa->ifa_flags&IFA_F_SECONDARY) {
420 prim = inet_ifa_byprefix(in_dev, prefix, mask);
421 if (prim == NULL) {
422 printk(KERN_DEBUG "fib_add_ifaddr: bug: prim == NULL\n");
423 return;
427 fib_magic(RTM_NEWROUTE, RTN_LOCAL, addr, 32, prim);
429 if (!(dev->flags&IFF_UP))
430 return;
432 /* Add broadcast address, if it is explicitly assigned. */
433 if (ifa->ifa_broadcast && ifa->ifa_broadcast != 0xFFFFFFFF)
434 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
436 if (!ZERONET(prefix) && !(ifa->ifa_flags&IFA_F_SECONDARY) &&
437 (prefix != addr || ifa->ifa_prefixlen < 32)) {
438 fib_magic(RTM_NEWROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
439 RTN_UNICAST, prefix, ifa->ifa_prefixlen, prim);
441 /* Add network specific broadcasts, when it takes a sense */
442 if (ifa->ifa_prefixlen < 31) {
443 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix, 32, prim);
444 fib_magic(RTM_NEWROUTE, RTN_BROADCAST, prefix|~mask, 32, prim);
449 static void fib_del_ifaddr(struct in_ifaddr *ifa)
451 struct in_device *in_dev = ifa->ifa_dev;
452 struct net_device *dev = in_dev->dev;
453 struct in_ifaddr *ifa1;
454 struct in_ifaddr *prim = ifa;
455 u32 brd = ifa->ifa_address|~ifa->ifa_mask;
456 u32 any = ifa->ifa_address&ifa->ifa_mask;
457 #define LOCAL_OK 1
458 #define BRD_OK 2
459 #define BRD0_OK 4
460 #define BRD1_OK 8
461 unsigned ok = 0;
463 if (!(ifa->ifa_flags&IFA_F_SECONDARY))
464 fib_magic(RTM_DELROUTE, dev->flags&IFF_LOOPBACK ? RTN_LOCAL :
465 RTN_UNICAST, any, ifa->ifa_prefixlen, prim);
466 else {
467 prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
468 if (prim == NULL) {
469 printk(KERN_DEBUG "fib_del_ifaddr: bug: prim == NULL\n");
470 return;
474 /* Deletion is more complicated than add.
475 We should take care of not to delete too much :-)
477 Scan address list to be sure that addresses are really gone.
480 for (ifa1 = in_dev->ifa_list; ifa1; ifa1 = ifa1->ifa_next) {
481 if (ifa->ifa_local == ifa1->ifa_local)
482 ok |= LOCAL_OK;
483 if (ifa->ifa_broadcast == ifa1->ifa_broadcast)
484 ok |= BRD_OK;
485 if (brd == ifa1->ifa_broadcast)
486 ok |= BRD1_OK;
487 if (any == ifa1->ifa_broadcast)
488 ok |= BRD0_OK;
491 if (!(ok&BRD_OK))
492 fib_magic(RTM_DELROUTE, RTN_BROADCAST, ifa->ifa_broadcast, 32, prim);
493 if (!(ok&BRD1_OK))
494 fib_magic(RTM_DELROUTE, RTN_BROADCAST, brd, 32, prim);
495 if (!(ok&BRD0_OK))
496 fib_magic(RTM_DELROUTE, RTN_BROADCAST, any, 32, prim);
497 if (!(ok&LOCAL_OK)) {
498 fib_magic(RTM_DELROUTE, RTN_LOCAL, ifa->ifa_local, 32, prim);
500 /* Check, that this local address finally disappeared. */
501 if (inet_addr_type(ifa->ifa_local) != RTN_LOCAL) {
502 /* And the last, but not the least thing.
503 We must flush stray FIB entries.
505 First of all, we scan fib_info list searching
506 for stray nexthop entries, then ignite fib_flush.
508 if (fib_sync_down(ifa->ifa_local, NULL, 0))
509 fib_flush();
512 #undef LOCAL_OK
513 #undef BRD_OK
514 #undef BRD0_OK
515 #undef BRD1_OK
518 static void nl_fib_lookup(struct fib_result_nl *frn, struct fib_table *tb )
521 struct fib_result res;
522 struct flowi fl = { .nl_u = { .ip4_u = { .daddr = frn->fl_addr,
523 .fwmark = frn->fl_fwmark,
524 .tos = frn->fl_tos,
525 .scope = frn->fl_scope } } };
526 if (tb) {
527 local_bh_disable();
529 frn->tb_id = tb->tb_id;
530 frn->err = tb->tb_lookup(tb, &fl, &res);
532 if (!frn->err) {
533 frn->prefixlen = res.prefixlen;
534 frn->nh_sel = res.nh_sel;
535 frn->type = res.type;
536 frn->scope = res.scope;
538 local_bh_enable();
542 static void nl_fib_input(struct sock *sk, int len)
544 struct sk_buff *skb = NULL;
545 struct nlmsghdr *nlh = NULL;
546 struct fib_result_nl *frn;
547 int err;
548 u32 pid;
549 struct fib_table *tb;
551 skb = skb_recv_datagram(sk, 0, 0, &err);
552 nlh = (struct nlmsghdr *)skb->data;
554 frn = (struct fib_result_nl *) NLMSG_DATA(nlh);
555 tb = fib_get_table(frn->tb_id_in);
557 nl_fib_lookup(frn, tb);
559 pid = nlh->nlmsg_pid; /*pid of sending process */
560 NETLINK_CB(skb).pid = 0; /* from kernel */
561 NETLINK_CB(skb).dst_pid = pid;
562 NETLINK_CB(skb).dst_group = 0; /* unicast */
563 netlink_unicast(sk, skb, pid, MSG_DONTWAIT);
566 static void nl_fib_lookup_init(void)
568 netlink_kernel_create(NETLINK_FIB_LOOKUP, 0, nl_fib_input, THIS_MODULE);
571 static void fib_disable_ip(struct net_device *dev, int force)
573 if (fib_sync_down(0, dev, force))
574 fib_flush();
575 rt_cache_flush(0);
576 arp_ifdown(dev);
579 static int fib_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
581 struct in_ifaddr *ifa = (struct in_ifaddr*)ptr;
583 switch (event) {
584 case NETDEV_UP:
585 fib_add_ifaddr(ifa);
586 #ifdef CONFIG_IP_ROUTE_MULTIPATH
587 fib_sync_up(ifa->ifa_dev->dev);
588 #endif
589 rt_cache_flush(-1);
590 break;
591 case NETDEV_DOWN:
592 fib_del_ifaddr(ifa);
593 if (ifa->ifa_dev->ifa_list == NULL) {
594 /* Last address was deleted from this interface.
595 Disable IP.
597 fib_disable_ip(ifa->ifa_dev->dev, 1);
598 } else {
599 rt_cache_flush(-1);
601 break;
603 return NOTIFY_DONE;
606 static int fib_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
608 struct net_device *dev = ptr;
609 struct in_device *in_dev = __in_dev_get_rtnl(dev);
611 if (event == NETDEV_UNREGISTER) {
612 fib_disable_ip(dev, 2);
613 return NOTIFY_DONE;
616 if (!in_dev)
617 return NOTIFY_DONE;
619 switch (event) {
620 case NETDEV_UP:
621 for_ifa(in_dev) {
622 fib_add_ifaddr(ifa);
623 } endfor_ifa(in_dev);
624 #ifdef CONFIG_IP_ROUTE_MULTIPATH
625 fib_sync_up(dev);
626 #endif
627 rt_cache_flush(-1);
628 break;
629 case NETDEV_DOWN:
630 fib_disable_ip(dev, 0);
631 break;
632 case NETDEV_CHANGEMTU:
633 case NETDEV_CHANGE:
634 rt_cache_flush(0);
635 break;
637 return NOTIFY_DONE;
640 static struct notifier_block fib_inetaddr_notifier = {
641 .notifier_call =fib_inetaddr_event,
644 static struct notifier_block fib_netdev_notifier = {
645 .notifier_call =fib_netdev_event,
648 void __init ip_fib_init(void)
650 #ifndef CONFIG_IP_MULTIPLE_TABLES
651 ip_fib_local_table = fib_hash_init(RT_TABLE_LOCAL);
652 ip_fib_main_table = fib_hash_init(RT_TABLE_MAIN);
653 #else
654 fib_rules_init();
655 #endif
657 register_netdevice_notifier(&fib_netdev_notifier);
658 register_inetaddr_notifier(&fib_inetaddr_notifier);
659 nl_fib_lookup_init();
662 EXPORT_SYMBOL(inet_addr_type);
663 EXPORT_SYMBOL(ip_rt_ioctl);