2 * Movement Detection Module
5 * Henrik Petander <lpetande@cc.hut.fi>
7 * $Id: s.mdetect.c 1.135 03/10/02 15:26:27+03:00 henkku@mart10.hut.mediapoli.com $
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version
12 * 2 of the License, or (at your option) any later version.
14 * Handles the L3 movement detection of mobile node and also
15 * changing of its routes.
22 * Nanno Langstraat : Locking fixes
23 * Venkata Jagana : Locking fix
26 #include <linux/autoconf.h>
27 #include <linux/errno.h>
28 #include <linux/init.h>
29 #include <linux/if_arp.h>
30 #include <linux/route.h>
32 #include <net/ip6_route.h>
33 #include <net/addrconf.h>
34 #include <net/mipglue.h>
36 #include <linux/sysctl.h>
37 #endif /* CONFIG_SYSCTL */
43 #include "multiaccess_ctl.h"
48 #define DEBUG_MDETECT 7
50 #define DEF_RTR_POLL_IVAL RTR_SOLICITATION_INTERVAL /* In seconds */
53 #define CURR_RTR_PRESENT 0
54 #define CURR_RTR_NOT_PRESENT 1
55 #define STOP_RTR_PROBE 2
61 #define MIPV6_MDF_NONE 0x0
62 #define MIPV6_MDF_HAS_RTR_PREV 0x1
64 #define ROUTER_REACHABLE 1
66 #define NOT_REACHABLE 3
68 /* R_TIME_OUT paramater is used to make the decision when to change the
69 * default router, if the current one is unreachable. 2s is pretty aggressive
70 * and may result in hopping between two routers. OTOH a small value enhances
73 #define R_TIME_OUT 30*HZ
75 /* maximum RA interval for router unreachability detection */
76 #define MAX_RADV_INTERVAL 6000*HZ /* 6000 ms... */
78 /* Threshold for exponential resending of router solicitations */
79 #define RS_RESEND_LINEAR 10*HZ
81 #define EAGER_CELL_SWITCHING 1
82 #define LAZY_CELL_SWITCHING 0
85 #define ROUTER_ADDRESS 0x20
88 #define ND_RA_FLAG_MANAGED 0x80
89 #define ND_RA_FLAG_OTHER 0x40
90 #define ND_RA_FLAG_HA 0x20
92 #define COA_TENTATIVE 0x10
95 struct list_head list;
96 struct in6_addr ll_addr;
97 struct in6_addr raddr; /* Also contains prefix */
98 __u8 link_addr[MAX_ADDR_LEN]; /* link layer address */
103 int pfix_len; /* Length of the network prefix */
104 unsigned long lifetime; /* from ra */
107 __u32 interval; /* ra interval in milliseconds, 0 if not set */
108 int glob_addr; /*Whether raddr contains also routers global address*/
109 __u8 flags; /* RA flags, for example ha */
110 struct in6_addr CoA; /* care-off address used with this router */
111 int extra_addr_route;
114 /* dad could also be RESPECT_DAD for duplicate address detection of
115 new care-of addresses */
118 /* Only one choice, nothing else implemented */
119 int max_rtr_reach_time = DEF_RTR_POLL_IVAL;
120 int mdet_mech = EAGER_CELL_SWITCHING;
122 int eager_cell_switching = 1; /* This can't be set from anywhere for now */
123 static spinlock_t router_lock;
124 static spinlock_t ho_lock;
126 static void coa_timer_handler(unsigned long arg);
127 static void timer_handler(unsigned long foo);
128 static struct router *curr_router = NULL, *next_router = NULL;
129 static struct timer_list r_timer = { function: timer_handler };
130 static struct timer_list coa_timer = { function: coa_timer_handler };
131 #define MAX_ROUTERS 1000
132 static LIST_HEAD(rtr_list);
133 static int num_routers = 0;
134 static struct handoff *_ho = NULL;
136 * Functions for handling the default router list, which movement
137 * detection uses for avoiding loops etc.
140 /* TODO: Send NS to router after MAX interval has passed from last RA */
141 static int mipv6_router_state(struct router *rtr)
144 if (time_before(jiffies, (unsigned long)(rtr->last_ra_rcvd + (rtr->interval * HZ) / 1000)))
145 return ROUTER_REACHABLE;
147 return NOT_REACHABLE;
150 if (time_after(jiffies, (unsigned long)(rtr->last_ra_rcvd + (rtr->lifetime * HZ))))
151 return NOT_REACHABLE;
152 return ROUTER_REACHABLE;
155 /* searches for a specific router or any router that is reachable,
156 * if address is NULL. Also deletes obsolete routers.
158 static void mipv6_router_gc(void)
160 struct router *curr = NULL;
161 struct list_head *lh, *lh_tmp;
165 list_for_each_safe(lh, lh_tmp, &rtr_list) {
166 curr = list_entry(lh, struct router, list);
167 if (mipv6_router_state(curr) == NOT_REACHABLE && !curr->is_current) {
169 list_del_init(&curr->list);
170 DEBUG(DBG_DATADUMP, "Deleting unreachable router %x:%x:%x:%x:%x:%x:%x:%x",
171 NIPV6ADDR(&curr->raddr));
175 DEBUG(DBG_DATADUMP, "NOT Deleting router %x:%x:%x:%x:%x:%x:%x:%x",
176 NIPV6ADDR(&curr->raddr));
181 static struct router *mipv6_rtr_get(struct in6_addr *search_addr)
183 struct router *rtr = NULL;
184 struct list_head *lh;
188 if (search_addr == NULL)
190 list_for_each(lh, &rtr_list) {
191 rtr = list_entry(lh, struct router, list);
192 if(!ipv6_addr_cmp(search_addr, &rtr->raddr)) {
200 * Adds router to list
202 static struct router *mipv6_rtr_add(struct router *nrt)
209 /* check if someone is trying DoS attack, or we just have some
211 if (num_routers > MAX_ROUTERS) {
213 "failed to add new router, MAX_ROUTERS exceeded");
217 rptr = kmalloc(sizeof(struct router), GFP_ATOMIC);
219 memcpy(rptr, nrt, sizeof(struct router));
220 list_add(&rptr->list, &rtr_list);
223 DEBUG(DBG_INFO, "Adding router: %x:%x:%x:%x:%x:%x:%x:%x, "
224 "lifetime : %d sec, adv.interval: %d millisec",
225 NIPV6ADDR(&rptr->raddr), rptr->lifetime, rptr->interval);
227 DEBUG(DBG_INFO, "num_routers after addition: %d", num_routers);
231 /* Cleans up the list */
232 static void list_free(struct router **curr_router_p)
235 struct list_head *lh, *lh_tmp;
239 DEBUG(DBG_INFO, "Freeing the router list");
240 /* set curr_router->prev_router and curr_router NULL */
241 *curr_router_p = NULL;
242 list_for_each_safe(lh, lh_tmp, &rtr_list) {
243 tmp = list_entry(lh, struct router, list);
244 DEBUG(DBG_INFO, "%x:%x:%x:%x:%x:%x:%x:%x",
245 NIPV6ADDR(&tmp->ll_addr));
246 list_del(&tmp->list);
252 int rs_state = START;
254 /* Sends router solicitations to all valid devices
255 * source = link local address (of sending interface)
256 * dstaddr = all routers multicast address
257 * Solicitations are sent at an exponentially decreasing rate
259 * TODO: send solicitation first at a normal rate (from ipv6) and
260 * after that use the exponentially increasing intervals
262 static int rs_send(void)
264 struct net_device *dev;
265 struct in6_addr raddr, lladdr;
266 struct inet6_dev *in6_dev = NULL;
269 if (rs_state == START) {
272 } else if (num_rs++ > MAX_RTR_SOLICITATIONS)
275 ipv6_addr_all_routers(&raddr);
276 read_lock(&dev_base_lock);
278 /* Send router solicitations to all interfaces */
279 for (dev = dev_base; dev; dev = dev->next) {
280 if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER) {
281 DEBUG(DBG_DATADUMP, "Sending RS to device %s",
283 if (!ipv6_get_lladdr(dev, &lladdr)) {
284 ndisc_send_rs(dev, &lladdr, &raddr);
285 in6_dev = in6_dev_get(dev);
286 in6_dev->if_flags |= IF_RS_SENT;
287 in6_dev_put(in6_dev);
289 DEBUG(DBG_DATADUMP, "%s: device doesn't have link-local address!\n", dev->name);
295 read_unlock(&dev_base_lock);
296 return RTR_SOLICITATION_INTERVAL;
299 /* Create a new CoA for MN and also add a route to it if it is still tentative
300 to allow MN to get packets to the address immediately
302 static int form_coa(struct in6_addr *coa, struct in6_addr *pfix,
303 int plen, int ifindex)
305 struct net_device *dev;
308 if ((dev = dev_get_by_index(ifindex)) == NULL) {
309 DEBUG(DBG_WARNING, "Device is not present");
312 if (ipv6_get_lladdr(dev, coa) != 0) /* Link local address still tentative */
314 coa->s6_addr32[0] = pfix->s6_addr32[0];
315 coa->s6_addr32[1] = pfix->s6_addr32[1];
317 if (ipv6_chk_addr(coa, dev, 0) == 0) {
318 DEBUG(DBG_WARNING, "care-of address still tentative");
323 DEBUG(DBG_INFO, "Formed new CoA: %x:%x:%x:%x:%x:%x:%x:%x",
328 static inline int rtr_is_gw(struct router *rtr, struct rt6_info *rt)
330 return ((rt->rt6i_flags & RTF_GATEWAY) &&
331 !ipv6_addr_cmp(&rt->rt6i_gateway, &rtr->ll_addr));
334 static inline int is_prefix_route(struct router *rtr, struct rt6_info *rt)
336 return (!(rt->rt6i_flags & RTF_GATEWAY) &&
337 mipv6_prefix_compare(&rt->rt6i_dst.addr, &rtr->raddr,
342 * Function that determines whether given rt6_info should be destroyed
343 * (negative => destroy rt6_info, zero or positive => do nothing)
345 static int mn_route_cleaner(struct rt6_info *rt, void *arg)
349 struct router *rtr = (struct router *)arg;
356 DEBUG(DBG_ERROR, "mn_route_cleaner: rt or rtr NULL");
360 /* Do not delete routes to local addresses or to multicast
361 * addresses, since we need them to get router advertisements
362 * etc. Multicast addresses are more tricky, but we don't
363 * delete them in any case. The routing mechanism is not optimal for
366 * Also keep all new prefix routes, gateway routes through rtr and
367 * all remaining default routes (including those used for reverse
370 type = ipv6_addr_type(&rt->rt6i_dst.addr);
372 if ((type & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) ||
373 rt->rt6i_dev == &loopback_dev || rtr_is_gw(rtr, rt) ||
374 is_prefix_route(rtr, rt) || (rt->rt6i_flags & RTF_DEFAULT))
377 /* delete all others */
379 if (rt->rt6i_dev != &loopback_dev) {
383 "gw: %x:%x:%x:%x:%x:%x:%x:%x,\n"
386 "src: %x:%x:%x:%x:%x:%x:%x:%x,\n"
387 "dst: %x:%x:%x:%x:%x:%x:%x:%x,\n"
389 (ret ? "Deleting" : "Keeping"),
391 NIPV6ADDR(&rt->rt6i_gateway),
394 NIPV6ADDR(&rt->rt6i_src.addr),
395 NIPV6ADDR(&rt->rt6i_dst.addr),
404 static __inline__ void delete_routes(struct router *rtr)
408 /* Routing table is locked to ensure that nobody uses its */
409 write_lock_bh(&rt6_lock);
410 DEBUG(DBG_INFO, "mipv6: Purging routes");
411 /* TODO: Does not prune, should it? */
412 fib6_clean_tree(&ip6_routing_table,
413 mn_route_cleaner, 0, rtr);
414 write_unlock_bh(&rt6_lock);
419 int next_mdet_state[3][3] = {{CURR_RTR_OK, NO_RTR, NO_RTR},
420 {CURR_RTR_OK, CURR_RTR_OK, NO_RTR},
421 {CURR_RTR_OK, CURR_RTR_OK, RTR_SUSPECT}};
423 char *states[3] = {"NO_RTR", "RTR_SUSPECT", "CURR_RTR_OK"};
424 char *events[3] = {"RA_RCVD", "NA_RCVD", "TIMEOUT"};
427 * NO_RTR, RA_RCVD -> CURR_RTR_OK
428 * NO_RTR, NA_RCVD -> NO_RTR
429 * NO_RTR, TIMEOUT -> NO_RTR
431 * RTR_SUSPECT, RA_RCVD -> CURR_RTR_OK
432 * RTR_SUSPECT, NA_RCVD -> CURR_RTR_OK
433 * RTR_SUSPECT, TIMEOUT -> NO_RTR
435 * CURR_RTR_OK, RA_RCVD -> CURR_RTR_OK
436 * CURR_RTR_OK, NA_RCVD -> CURR_RTR_OK
437 * CURR_RTR_OK, TIMEOUT -> RTR_SUSPECT
441 * State diagram has been changed to :
442 * STOP_RTR_PROBE, RA_RCVD -> CURR_RTR_PRESENT
444 * CURR_RTR_NOT_PRESENT, RA_RCVD -> CURR_RTR_PRESENT
445 * CURR_RTR_NOT_PRESENT, RA_TIMEOUT -> CURR_RTR_NOT_PRESENT
446 * CURR_RTR_NOT_PRESENT, RA_TIMEOUT (if _curr_count == 0) -> STOP_RTR_PROBE
448 * CURR_RTR_PRESENT, RA_RCVD -> CURR_RTR_PRESENT
449 * CURR_RTR_PRESENT, RA_TIMEOUT -> CURR_RTR_NOT_PRESENT
451 char *states[3] = {"CURR_RTR_PRESENT", "CURR_RTR_NOT_PRESENT", "STOP_RTR_PROBE"};
452 char *events[3] = {"RA_RCVD", "RA_TIMEOUT"};
454 static int _curr_state = CURR_RTR_NOT_PRESENT;
455 static int _curr_count = MAX_RTR_SOLICITATIONS;
458 /* Needs to be called with router_lock locked */
459 static int mdet_statemachine(int event)
461 if (event > RA_TIMEOUT || _curr_state > STOP_RTR_PROBE) {
462 DEBUG(DBG_ERROR, "Got illegal event or curr_state");
466 DEBUG(DBG_DATADUMP, "Got event %s and curr_state is %s",
467 events[event], states[_curr_state]);
471 _curr_state = CURR_RTR_PRESENT;
472 _curr_count = MAX_RTR_SOLICITATIONS;
475 /* Try for _curr_count before stopping probe */
476 if (_curr_count-- <= 0)
477 _curr_state = STOP_RTR_PROBE;
478 else _curr_state = CURR_RTR_NOT_PRESENT;
482 DEBUG(DBG_DATADUMP, "Next state is %s", states[_curr_state]);
488 * Changes the router, called from ndisc.c if mipv6_router_event
492 static void mipv6_change_router(void)
501 if (next_router == NULL)
504 spin_lock(&router_lock);
507 if (curr_router != NULL &&
508 !ipv6_addr_cmp(&curr_router->ll_addr, &next_router->ll_addr)) {
509 DEBUG(DBG_INFO,"Trying to handoff from: "
510 "%x:%x:%x:%x:%x:%x:%x:%x",
511 NIPV6ADDR(&curr_router->ll_addr));
512 DEBUG(DBG_INFO,"Trying to handoff to: "
513 "%x:%x:%x:%x:%x:%x:%x:%x",
514 NIPV6ADDR(&next_router->ll_addr));
515 next_router = NULL; /* Let's not leave dangling pointers */
516 spin_unlock(&router_lock);
520 ret = form_coa(&next_router->CoA, &next_router->raddr, 64,
521 next_router->ifindex);
523 DEBUG(DBG_ERROR, "handoff: Creation of coa failed");
524 spin_unlock(&router_lock);
527 next_router->flags |= COA_TENTATIVE;
529 mdet_statemachine(RA_RCVD); /* TODO: What if DAD fails... */
530 if (next_router->interval)
531 mod_timer(&r_timer, jiffies +
532 (next_router->interval * HZ)/1000);
534 mod_timer(&r_timer, jiffies + max_rtr_reach_time);
537 ipv6_addr_copy(&coa, &next_router->CoA);
538 ifindex = next_router->ifindex;
539 spin_unlock(&router_lock);
540 mipv6_mdet_finalize_ho(&coa, ifindex);
543 spin_unlock(&router_lock);
547 static void coa_timer_handler(unsigned long dummy)
549 spin_lock_bh(&ho_lock);
551 DEBUG(DBG_INFO, "Starting handoff after DAD");
552 mipv6_mobile_node_moved(_ho);
556 spin_unlock_bh(&ho_lock);
558 static void timer_handler(unsigned long foo)
560 unsigned long timeout = 0;
563 spin_lock_bh(&router_lock);
564 state = mdet_statemachine(RA_TIMEOUT);
565 if (state == CURR_RTR_NOT_PRESENT)
567 if (state != STOP_RTR_PROBE) {
569 timeout = curr_router->interval ?
570 curr_router->interval * HZ / 1000 :
573 DEBUG(DBG_ERROR, "mdetect timeout < 0.02s");
577 timeout = max_rtr_reach_time;
582 mod_timer(&r_timer, jiffies + timeout);
583 spin_unlock_bh(&router_lock);
587 * mipv6_get_care_of_address - get node's care-of primary address
588 * @homeaddr: one of node's home addresses
589 * @coaddr: buffer to store care-of address
591 * Stores the current care-of address in the @coaddr, assumes
592 * addresses in EUI-64 format. Since node might have several home
593 * addresses caller MUST supply @homeaddr. If node is at home
594 * @homeaddr is stored in @coaddr. Returns 0 on success, otherwise a
597 int mipv6_get_care_of_address(
598 struct in6_addr *homeaddr, struct in6_addr *coaddr)
603 if (homeaddr == NULL)
605 spin_lock_bh(&router_lock);
606 if (curr_router == NULL || mipv6_mn_is_at_home(homeaddr) ||
607 mipv6_prefix_compare(homeaddr, &curr_router->raddr, 64) ||
608 curr_router->flags&COA_TENTATIVE) {
610 "mipv6_get_care_of_address: returning home address");
611 ipv6_addr_copy(coaddr, homeaddr);
612 spin_unlock_bh(&router_lock);
617 /* At home or address check failure probably due to dad wait */
618 if (mipv6_prefix_compare(&curr_router->raddr, homeaddr,
619 curr_router->pfix_len)
620 || (dad == RESPECT_DAD &&
621 (ipv6_chk_addr(coaddr, NULL, 0) == 0))) {
622 ipv6_addr_copy(coaddr, homeaddr);
624 ipv6_addr_copy(coaddr, &curr_router->CoA);
627 spin_unlock_bh(&router_lock);
630 int mipv6_mdet_del_if(int ifindex)
632 struct router *curr = NULL;
633 struct list_head *lh, *lh_tmp;
637 spin_lock_bh(&router_lock);
638 list_for_each_safe(lh, lh_tmp, &rtr_list) {
639 curr = list_entry(lh, struct router, list);
640 if (curr->ifindex == ifindex) {
642 list_del_init(&curr->list);
643 DEBUG(DBG_DATADUMP, "Deleting router %x:%x:%x:%x:%x:%x:%x:%x on interface %d",
644 NIPV6ADDR(&curr->raddr), ifindex);
645 if (curr_router == curr)
650 spin_unlock_bh(&router_lock);
653 int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex)
659 spin_lock_bh(&router_lock);
660 if (next_router && mipv6_prefix_compare(coa, &next_router->CoA,
661 next_router->pfix_len)) {
664 curr_router = next_router;
665 curr_router->is_current = 1;
667 curr_router->flags &= ~COA_TENTATIVE;
668 delete_routes(curr_router);
670 struct net_device *dev = dev_get_by_index(tmp->ifindex);
671 struct rt6_info *rt = NULL;
673 rt = rt6_get_dflt_router(&tmp->ll_addr, dev);
677 ip6_del_rt(rt, NULL, NULL);
681 ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy);
682 ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy);
685 memcpy(&ho.rtr_addr, &curr_router->raddr, sizeof(ho.rtr_addr));
686 ho.coa = &curr_router->CoA;
687 ho.plen = curr_router->pfix_len;
688 ho.ifindex = curr_router->ifindex;
689 ipv6_addr_copy(&ho.rtr_addr, &curr_router->raddr);
690 ho.home_address = (curr_router->glob_addr &&
691 curr_router->flags&ND_RA_FLAG_HA);
693 spin_unlock_bh(&router_lock);
694 mipv6_mobile_node_moved(&ho);
696 spin_unlock_bh(&router_lock);
699 /* Decides whether router candidate is the same router as current rtr
700 * based on prefix / global addresses of the routers and their link local
703 static int is_current_rtr(struct router *nrt, struct router *crt)
707 DEBUG(DEBUG_MDETECT, "Current router: "
708 "%x:%x:%x:%x:%x:%x:%x:%x and", NIPV6ADDR(&crt->raddr));
709 DEBUG(DEBUG_MDETECT, "Candidate router: "
710 "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&nrt->raddr));
712 return (!ipv6_addr_cmp(&nrt->raddr,&crt->raddr) &&
713 !ipv6_addr_cmp(&nrt->ll_addr, &crt->ll_addr));
717 * Set next router to nrtr
718 * TODO: Locking to ensure nothing happens to next router
719 * before handoff is done
721 static void set_next_rtr(struct router *nrtr, struct router *ortr)
727 static int clean_ncache(struct router *nrt, struct router *ort, int same_if)
729 struct net_device *ortdev;
732 /* Always call ifdown after a handoff to ensure proper routing */
736 if ((ortdev = dev_get_by_index(ort->ifindex)) == NULL) {
737 DEBUG(DBG_WARNING, "Device is not present");
740 neigh_ifdown(&nd_tbl, ortdev);
745 static int mdet_get_if_preference(int ifi)
751 pref = ma_ctl_get_preference(ifi);
753 DEBUG(DEBUG_MDETECT, "ifi: %d preference %d", ifi, pref);
759 * Called from mipv6_mn_ra_rcv to determine whether to do a handoff.
761 static int mipv6_router_event(struct router *rptr)
763 struct router *nrt = NULL;
764 int new_router = 0, same_if = 1;
765 int ret = MIPV6_IGN_RTR;
766 int oldstate = _curr_state;
767 int addrtype = ipv6_addr_type(&rptr->raddr);
771 if (rptr->lifetime == 0)
773 DEBUG(DEBUG_MDETECT, "Received a RA from router: "
774 "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&rptr->raddr));
775 spin_lock(&router_lock);
777 /* Add or update router entry */
778 if ((nrt = mipv6_rtr_get(&rptr->raddr)) == NULL) {
779 if (addrtype == IPV6_ADDR_ANY || (nrt = mipv6_rtr_add(rptr)) == NULL) {
780 spin_unlock(&router_lock);
783 DEBUG(DBG_INFO, "Router not on list,adding it to the list");
786 nrt->last_ra_rcvd = jiffies;
787 nrt->state = ROUTER_REACHABLE;
788 nrt->interval = rptr->interval;
789 nrt->lifetime = rptr->lifetime;
790 nrt->ifindex = rptr->ifindex;
791 nrt->flags = rptr->flags;
792 nrt->glob_addr = rptr->glob_addr;
794 /* Whether from current router */
795 if (curr_router && is_current_rtr(nrt, curr_router)) {
797 mod_timer(&r_timer, jiffies + (nrt->interval * HZ)/1000);
799 mod_timer(&r_timer, jiffies + max_rtr_reach_time);
800 mdet_statemachine(RA_RCVD);
801 spin_unlock(&router_lock);
802 return MIPV6_ADD_RTR;
803 } else if (oldstate == STOP_RTR_PROBE) {
804 rt6_purge_dflt_routers(0); /* For multiple interface case */
805 DEBUG(DBG_INFO, "No router or router not reachable, switching to new one");
812 /* Router behind same interface as current one ?*/
813 same_if = (nrt->ifindex == curr_router->ifindex);
814 /* Switch to new router behind same interface if eager cell
815 * switching is used or if the interface is preferred
817 if ((new_router && eager_cell_switching && same_if) ||
818 (mdet_get_if_preference(nrt->ifindex) >
819 mdet_get_if_preference(curr_router->ifindex))) {
820 DEBUG(DBG_INFO, "Switching to new router.");
824 /* No handoff, don't add default route */
825 DEBUG(DEBUG_MDETECT, "Ignoring RA");
826 spin_unlock(&router_lock);
829 clean_ncache(nrt, curr_router, same_if);
830 set_next_rtr(nrt, curr_router);
831 spin_unlock(&router_lock);
833 return MIPV6_CHG_RTR;
837 * Called from ndisc.c's router_discovery.
840 static int mipv6_mn_ra_rcv(struct sk_buff *skb)
843 int ifi = ((struct inet6_skb_parm *)skb->cb)->iif;
844 struct ra_msg *ra = (struct ra_msg *) skb->h.raw;
845 struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
847 __u8 * opt = (__u8 *)(ra + 1);
851 memset(&nrt, 0, sizeof(struct router));
853 if (ra->icmph.icmp6_home_agent) {
854 nrt.flags |= ND_RA_FLAG_HA;
855 DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_HA up");
858 if (ra->icmph.icmp6_addrconf_managed) {
859 nrt.flags |= ND_RA_FLAG_MANAGED;
860 DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_MANAGED up");
863 if (ra->icmph.icmp6_addrconf_other) {
864 nrt.flags |= ND_RA_FLAG_OTHER;
865 DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_OTHER up");
868 ipv6_addr_copy(&nrt.ll_addr, saddr);
870 nrt.lifetime = ntohs(ra->icmph.icmp6_rt_lifetime);
872 optlen = (skb->tail - (unsigned char *)ra) - sizeof(struct ra_msg);
875 int len = (opt[1] << 3);
877 return MIPV6_IGN_RTR;
881 if (opt[0] == ND_OPT_PREFIX_INFO) {
882 struct prefix_info *pinfo;
884 if (len < sizeof(struct prefix_info))
885 return MIPV6_IGN_RTR;
887 pinfo = (struct prefix_info *) opt;
889 if (!pinfo->autoconf) {
890 /* Autonomous not set according to
896 /* use first prefix with widest scope */
897 if (ipv6_addr_any(&nrt.raddr) ||
898 ((ipv6_addr_type(&nrt.raddr) != IPV6_ADDR_UNICAST) &&
899 (ipv6_addr_type(&pinfo->prefix) == IPV6_ADDR_UNICAST))) {
900 ipv6_addr_copy(&nrt.raddr, &pinfo->prefix);
901 nrt.pfix_len = pinfo->prefix_len;
902 if (pinfo->router_address)
906 DEBUG(DBG_DATADUMP, "Address of the received "
907 "prefix info option: %x:%x:%x:%x:%x:%x:%x:%x",
908 NIPV6ADDR(&nrt.raddr));
909 DEBUG(DBG_DATADUMP, "the length of the prefix is %d",
913 if (opt[0] == ND_OPT_SOURCE_LL_ADDR) {
914 nrt.link_addr_len = skb->dev->addr_len;
915 memcpy(nrt.link_addr, opt + 2, nrt.link_addr_len);
917 if (opt[0] == ND_OPT_RTR_ADV_INTERVAL) {
918 nrt.interval = ntohl(*(__u32 *)(opt+4));
920 "received router interval option with interval : %d ",
923 if (nrt.interval > MAX_RADV_INTERVAL) {
925 DEBUG(DBG_DATADUMP, "but we are using: %d, "
926 "because interval>MAX_RADV_INTERVAL",
935 return mipv6_router_event(&nrt);
938 int __init mipv6_initialize_mdetect(void)
943 spin_lock_init(&router_lock);
944 spin_lock_init(&ho_lock);
945 init_timer(&coa_timer);
946 init_timer(&r_timer);
947 r_timer.expires = jiffies + HZ;
950 /* Actual HO, also deletes old routes after the addition of new ones
952 MIPV6_SETCALL(mipv6_change_router, mipv6_change_router);
954 MIPV6_SETCALL(mipv6_ra_rcv, mipv6_mn_ra_rcv);
959 int __exit mipv6_shutdown_mdetect()
964 MIPV6_RESETCALL(mipv6_ra_rcv);
965 MIPV6_RESETCALL(mipv6_change_router);
966 spin_lock_bh(&router_lock);
968 del_timer(&coa_timer);
970 /* Free the memory allocated by router list */
971 list_free(&curr_router);
974 spin_unlock(&ho_lock);
975 spin_unlock_bh(&router_lock);