diff -r 82fcd4bea972 NEWS.MCOA
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NEWS.MCOA	Fri Jan 11 17:02:25 2008 +0100
@@ -0,0 +1,103 @@
+mipv6-daemon-umip-0.4-nepl-mcoa-20080108.patch
+News and Bugfixes list since mipv6-daemon-umip-0.3-nepl-mcoa-20071022.patch:
+
+* Updated code on top of the latest NEPL patch
+* No new features
+
+-----------------------------------------------------------------------
+mipv6-daemon-umip-0.4-nepl-mcoa-20071022.patch
+News and Bugfixes list since mipv6-daemon-umip-0.3-nepl-mcoa-20070613.patch:
+
+* Ported the code to UMIP 0.4. MCoA now runs on a 2.6.23 kernel, 
+  with an UMIP 0.4-based code for the userland.
+* Merged a fix to the proxy ND code for the HA (provided by 
+  Tobias HOF): the HA can now be used as legacy node on the 
+  Home Link (whereas it only worked as a gateway before).
+
+-----------------------------------------------------------------------
+mipv6-daemon-umip-0.3-nepl-mcoa-20070613.patch
+News and Bugfixes list since nemo-0.2-mcoa-beta2-20060630:
+
+* Ported the code to UMIP. MCoA now runs on a 2.6.21.3 kernel, 
+  with an UMIP-based code for the userland.
+* MCoA is now provided as a patch for the UMIP userland. 
+* The MCoA kernel patch is not needed anymore as it has been 
+  integrated in the mainline kernel.
+* No new functionnalities were added.
+
+-----------------------------------------------------------------------
+nemo-0.2-mcoa-beta3-20070118
+News and Bugfixes list since nemo-0.2-mcoa-beta2-20060630:
+
+* Base code:
+- Userland runs on a 2.6.16 kernel 
+- Upgraded fwmark patch for 2.6.16 kernel
+- MCoA userland code updated to nemo-20060725
+
+* Features:
+- New "Reliable" option that allows to use the BU as a 
+  heartbeat, and to automatically divert the traffic to 
+  another interface when one MR-HA path fails. 
+  See the manpage for details.
+- MPS/MPA can now be used with MCoA.
+- When handover occurs on an interface, forwarded traffic is now
+  blocked only for the interface performing the handover (whereas 
+  it was blocked globally before). The local traffic is also 
+  blocked for the interface that performs the handover, whereas 
+  it was blocked for the HoA interface before.
+- Merged patch to support HoA from MNP (for Mobile Routers)
+- Merged patch to support SIT tunnels
+- Merged patch to fix the MR behaviour in the Home Network
+  (the MR was not correctly forwarding packets from the MNNs)
+
+* Bugfixes:
+- The source address used to send the BU was sometimes outdated 
+  because the CoA was not correctly updated in the XFRM kernel 
+  state.
+- When packets originated from the MR were sent to the HA, the 
+  HA did not behave correctly because the source address of the 
+  packet may not match the one in the kernel XFRM state (that 
+  can only store one CoA). Thus the MR behaviour has been 
+  slightly changed to NOT use HAO dest. option when sending 
+  packet to the HA (This behaviour seems to respect the MIP6 
+  spec).
+- When packets originated from the MR were sent to the HA, and 
+  the MR handoffs, the HA still sent replied packets to the 
+  old CoA (using Routing Header Type 2). The HA now updates 
+  correctly the CoA stored in the kernel, but as the kernel 
+  can only store 1 CoA at a time, when the HA replies to the 
+  MR the path may be asymetric.
+- Added more checks in the configuration file grammar to 
+  avoid configuration errors for MCoA.
+- Solved infinite loop when interface was declared as Interface
+  but not in IfMultipleCoA.
+- Fixed some bugs when MCoA was disabled. It now works fine when 
+  not using MCoA. Interface prefence works also fine.
+- Fixed bugs when one entry was invalidated: rules are now 
+  correctly deleted.
+- Fixed bugs when choosing the default rule: invalid BULE are 
+  not looked up when searching for the default rule.
+- Improved the MR and HA behaviour regarding error management.
+
+
+-----------------------------------------------------------------------
+nemo-0.2-mcoa-beta2-20060630
+News and Bugfixes list since nemo-0.2-mcoa-beta1-20060531:
+
+- The MR was in some cases installing the same rules twice.
+- The MR had a problem to install some rules after recovering a failure.
+- The MR was stuck in an infinite loop at startup if MCoA was disabled
+- The MR could not clean properly tunnels if registration failed with HA
+- Policy issues when Returning home with a usual MR but using MCoA code
+- When the MR only have 1 active interface, handoff policy is different 
+  (rules not erased and dereg bu not send during handoff time)
+- When the MR receives a BAck without BID option, whereas it sent a BU
+  with a BID option, the MR only registers the prefered interface
+
+- If HA is not configured for MCoA, or MCoA not allowed for the HoA, then 
+  BU is accepted but no BID option is sent in BAck
+
+- Merged the OptimisticHandoff patch 
+- Merged the PPP patch
+
+-----------------------------------------------------------------------
diff -r 82fcd4bea972 README.MCOA
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.MCOA	Fri Jan 11 17:02:25 2008 +0100
@@ -0,0 +1,154 @@
+--------------------------------------------
+Multiple CoA implementation on UMIP
+<http://software.nautilus6.org/MCoA/>
+ver. 20080108
+--------------------------------------------
+Romain KUNTZ <kuntz(at)sfc.wide.ad.jp>
+Jean LORCHAT <lorchat(at)sfc.wide.ad.jp>
+Nautilus6 Project <http://www.nautilus6.org>
+--------------------------------------------
+
+
+1. What is Multiple CoA?
+------------------------
+
+Multiple CoA (Multiple Care-of Addresses Registration, MCoA) allows 
+a Mobile Node to register multiple Care-of Addresses at the same 
+time to its Home Agent. Main benefits, amongst others, are policy 
+routing and fault tolerance for the Mobile Router.
+
+The current implementation is based on 
+draft-ietf-monami6-multiplecoa-01 and is shipped as a tarball
+that contains kernel patches and the userland.
+
+This implementation is based on UMIP, a Mobility platform on Linux.
+More information about UMIP can be found on 
+<http://www.linux-ipv6.org/umip-0.3-ann.html.en>
+
+
+2. Limitations 
+--------------
+
+Due to its early stage of development, the MCoA implementation still 
+have some important limitations. Please read them carefully to 
+understand what you cannot do with this implementation:
+
+- IPsec is not yet supported. It will in the future, but we now 
+  concentrate on the non-IPsec part.
+  
+- Returning home is not supported. Do not try to boot your MN at home 
+  or to return home with one of its interface, this will not work. 
+  This feature will be available in the future.
+  
+- Once the MN has established multiple tunnels, the policy routing
+  only works FOR THE FORWARDED TRAFFIC. The traffic generated by the 
+  MN itself will not be routed according to your policies. Only the 
+  traffic from the MNN, routed via the Mobile Router, will be routed 
+  according to the policies you installed on the MR. You can expect 
+  some improvements in the next releases.
+
+- CN are not supported, ie the MN cannot register multiple CoA to 
+  the CN.
+
+- The BID for each interface must be chosen between 1 and 251. This 
+  limitation is due to the implementation design. We will try to 
+  improve this point in the future.
+  
+- This implementation does not support yet the Bulk Registration,
+  mainly because Bulk registration is still under discussion at 
+  the IETF.
+
+
+3. Known Bugs
+-------------
+
+- Binding Refresh Request do not include the BID option
+- Binding Error do not include the BID option
+- The BU is sometimes sent via another interface than expected. 
+- Certainly many others!
+
+4. Instructions
+---------------
+
+a. Testbed Setup
+----------------
+
+Testbed setup, kernel and userlan compilation, configuration 
+files etc. are explained in the NEPL Howto available on:
+http://www.nautilus6.org/doc/nepl-howto/
+
+Please check this document (especially section 6) to setup 
+your MCoA testbed.
+
+b. Policy Routing
+-----------------
+
+Once your MN has registered multiple CoA to your Home Agent, it can
+use its multiple MN-HA tunnel for policy routing.
+
+The policy routing part is managed with ip6tables. Be sure to install 
+this tool on both your HA and MN. 
+
+- How does it work?
+-------------------
+
+The BID that you assigned to each interface will be used to mark the 
+packets. Packets marked with BID X will be routed through the 
+interface whose BID is X. If this interface is not available (because 
+it is down), then the packet will be routed through the most prefered
+interface (the one with the best BidPriority).
+
+NOTE:
+At the moment, ONLY PACKETS FORWARDED BY THE MR (ie packets sent by MNN)
+can benefit from the policy routing. Packets generated by the MR itself 
+will not be routed according to your rules. We are currently working to 
+improve the current situation to also allow the MR to benefit from the 
+policy routing.
+
+
+- How to mark a packet?
+-----------------------
+
+Use the ip6tables tool and the MARK target to mark your packets with 
+the BID. Ip6tables rules must be done in the PREROUTING chain, in the
+mangle table.
+
+For example, on the MN, to mark as 100 all icmpv6 packets whose
+destination is 2001:a:b::1000, you can do:
+
+ip6tables -A PREROUTING -t mangle 
+          -p icmpv6 --destination 2001:a:b::1000 
+	  -j MARK --set-mark 100
+
+Those packets will be sent through the interface whose BID is 100.
+You will also need to create the "symetric" rule on the Home Agent:
+
+ip6tables -A PREROUTING -t mangle 
+          -p icmpv6 --source 2001:a:b::1000 
+	  -j MARK --set-mark 100
+
+As you see, one current limitation is that each rules created on the 
+MN must be also created on the Home Agent. We plan to support in the 
+future some policy exchange mechanism between the MN and the HA in 
+order to configure automatically the HA.
+
+Read carefully the ip6tables manpage. You will be able to create 
+rules based on many parameters, and thus create a very subtle policy 
+routing on your MN.
+
+
+5. Support
+----------
+
+The MCoA implementation is not supported by the UMIP team, so please 
+do not complain on their Mailing List. If you have any questions 
+regarding this work, or any BUG report, feel free to subscribe to 
+our support and announce mailing lists:
+
+http://www.nautilus6.org/ml.php
+
+--------------------------------------------
+Romain KUNTZ <kuntz(at)sfc.wide.ad.jp>
+Jean LORCHAT <lorchat(at)sfc.wide.ad.jp>
+Nautilus6 Project <http://www.nautilus6.org>
+--------------------------------------------
diff -r 82fcd4bea972 include/netinet/ip6mh.h
--- a/include/netinet/ip6mh.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/include/netinet/ip6mh.h	Fri Jan 11 17:02:25 2008 +0100
@@ -146,6 +146,25 @@ struct ip6_mh_opt_mob_net_prefix {
 } __attribute__ ((packed));
 
 /*
+ * MCoA registration
+ * Binding Unique Identifier sub-option
+ */
+struct ip6_mh_opt_bid {
+	uint8_t ip6mobid_type;
+	uint8_t ip6mobid_len;
+	uint16_t ip6mobid_bid;
+	uint8_t ip6mobid_priority;
+	uint8_t ip6mobid_reserved;
+} __attribute__((packed));
+
+/* Binding Unique Identifier sub-option flags */
+#if BYTE_ORDER == BIG_ENDIAN
+#define IP6_OPT_BID_BULK		0x80  /*  Bulk Registration */
+#else	/* BYTE_ORDER == LITTLE_ENDIAN */
+#define IP6_OPT_BID_BULK		0x80  /*  Bulk Registration */
+#endif
+
+/*
  *     Mobility Header Message Types
  */
 #define IP6_MH_TYPE_BRR		0	/* Binding Refresh Request */
@@ -167,6 +186,7 @@ struct ip6_mh_opt_mob_net_prefix {
 #define IP6_MHOPT_NONCEID	0x04	/* Nonce Index */
 #define IP6_MHOPT_BAUTH		0x05	/* Binding Auth Data */
 #define IP6_MHOPT_MOB_NET_PRFX	0x06	/* Mobile Network Prefix */
+#define IP6_MHOPT_BID		0x07	/* Binding Unique Identifier */
 
 /*
  *    Status values accompanied with Mobility Binding Acknowledgement
@@ -191,6 +211,9 @@ struct ip6_mh_opt_mob_net_prefix {
 #define IP6_MH_BAS_INVAL_PRFX		141	/* Invalid Prefix */
 #define IP6_MH_BAS_NOT_AUTH_FOR_PRFX	142	/* Not Authorized for Prefix */
 #define IP6_MH_BAS_FWDING_FAILED	143	/* Forwarding Setup failed */
+#define IP6_MH_BAS_CONFLICT_BINDING	144	/* (TBD) MCoA Conflict between bindings */
+#define IP6_MH_BAS_MCOA_BULK_FAILED	145	/* (TBD) MCoA Bulk registration failed */
+
 /*
  *    Status values for the Binding Error mobility messages
  */
diff -r 82fcd4bea972 man/mip6d.conf.tmpl
--- a/man/mip6d.conf.tmpl	Fri Jan 11 16:55:08 2008 +0100
+++ b/man/mip6d.conf.tmpl	Fri Jan 11 17:02:25 2008 +0100
@@ -57,6 +57,9 @@ These options are used both in the Home 
 .BR "Interface " "name" " {"
 .BR "	MnIfPreference " "number" ";"
 .BR "	IfType " "CN | HA | MN" ";"
+.BR "	Bid " "number" ";"
+.BR "	BidPriority " "number" ";"
+.BR "	Reliable " "boolean" ";"
 .B }
 .fi
 
@@ -74,9 +77,10 @@ sets the interface preference value for 
 sets the interface preference value for an interface in a multi-homed
 Mobile Node.  The most preferred intefaces have preference 1, the
 second most preferred have 2, etc.  A preference of zero means the
-interface will not be used.
+interface will not be used. Do not use this option if you use the Multiple
+Care-of Address registration on your Mobile Node with this interface.
 
-Default: 5
+Default: 0
 
 .B IfType
 overrides the default node behavior for this interface. If a MN doesn't
@@ -86,6 +90,43 @@ Default: same as
 Default: same as
 .B NodeConfig
 
+.B Bid
+Sets the BID for this interface. The BID must be unique within all 
+interface definitions. This BID will be used as an Interface Identifier
+that the user can use for policy routing. If this option is defined, 
+also set the 
+.B BidPriority 
+option, and do not set the 
+.B MnIfPreference
+option.
+.B Bid 
+must be set between 1 and 251 (this is one of the current limitation 
+with this MCoA implementation).
+
+Default: 0 (no BID)
+
+.B BidPriority
+Sets the priority of the binding (the higher the better). When no 
+policies are define for some flows, the binding with the higher 
+preference is used. If this option is defined, do not set the 
+.B MnIfPreference
+option for this interface. 
+.B BidPriority 
+must be set between 1 and 255.
+
+Default: 0
+
+.B Reliable
+allows you to specify if the interface can be considered as reliable
+when using MCoA. If set to false, the traffic will be redirected to 
+another interface during the BU-BAck exchange. You can thus use the 
+BU as a heartbeat (by specifying a small
+.B MnMaxHaBindingLife
+), which can be pretty useful when one of your MR-HA path is not 
+reliable: traffic is redirected to the interface with the best 
+priority as long as no BAck is received.
+
+Default: true
 
 .TP
 .BR "UseMnHaIPsec " "boolean" ";"
@@ -191,6 +232,13 @@ Default: disabled;
 Default: disabled;
 
 .TP
+.BR "HaAcceptMCoAReg enabled | disabled"
+
+Indicates if the HA accepts Multiple Care-of Addresses registration.
+
+Default: disabled;
+
+.TP
 .BR "HaServedPrefix " "prefix/length" ";"
 
 Prefix is an IPv6 prefix and length is the prefix length. Defines the whole
@@ -199,7 +247,7 @@ Mobile Network Prefixes, instead of one 
 Mobile Network Prefixes, instead of one of the home link prefixes.
 
 .TP
-.BR "BindingAclPolicy " "address MNP list " "allow | deny"
+.BR "BindingAclPolicy " "address MNP list " "MCoAReg | NoMCoAReg " "allow | deny"
 
 Defines if a MN is allowed to register with the HA or not. The home address
 of the MN is given in the address field.  The mobile network prefixes
@@ -207,6 +255,12 @@ be an empty string or a comma separated 
 be an empty string or a comma separated list of network prefixes
 enclosed in braces, for example:
 .B "(3ffe:2620:6:3::/64, 3ffe:2620:6:4::/64)"
+The HA can forbid or allow the MN to register Multiple Care-of Addresses by
+setting the 
+.B NoMCoAReg 
+or 
+.B MCoAReg
+flag.
 
 .TP
 .BR "DefaultBindingAclPolicy allow | deny"
@@ -368,6 +422,31 @@ Default: disabled
 Default: disabled
 
 .TP
+.BR "RegMultipleCoA enabled | disabled"
+
+Defines if the Mobile Node should try to register Multiple Care-of 
+Addresses to its Home Agent. If enabled, use also the
+.B IfMultipleCoA
+option. If disabled, the node will only register one CoA at a time.
+
+Default: disabled;
+
+.TP
+.BR "IfMultipleCoA " "name1, name2, ..." ";"
+
+Defines the list of interfaces that will be used for Multiple Care-of 
+Addresses registration. This list must be composed with the names 
+(enclosed in double quotes) of the interfaces (eg. "eth1"), separated 
+with a coma. Each interface listed must have an 
+.B Interface
+definition properly filled with the Multiple Care-of Address options
+(
+.B Bid
+and 
+.B BidPriority
+).
+
+.TP
 The route optimization policies are of the form:
 
 .TP
@@ -389,7 +468,7 @@ matching this entry.
 .SH EXAMPLES
 
 .TP
-.BR "A NEMO Home Agent example:"
+.BR "A NEMO Home Agent example with MCoA support:"
 
 .nf
 NodeConfig HA;
@@ -397,18 +476,19 @@ Interface "eth0";
 Interface "eth0";
 
 HaAcceptMobRtr enabled;
+HaAcceptMCoAReg enabled;
 
 HaServedPrefix 3ffe:2620:6::/48;
 
 DefaultBindingAclPolicy deny;
-BindingAclPolicy 3ffe:2620:6:1::1234 (3ffe:2620:6:2::/64, 3ffe:2620:6:3::/64) allow;
+BindingAclPolicy 3ffe:2620:6:1::1234 (3ffe:2620:6:2::/64, 3ffe:2620:6:3::/64) MCoAReg allow;
 BindingAclPolicy 3ffe:2620:6:1::1235 allow;
 
 UseMnHaIPsec disabled;
 .fi
 
 .TP
-.BR "A NEMO Mobile Router example:"
+.BR "A NEMO Mobile Router example with MCoA support:"
 
 .nf
 NodeConfig MN;
@@ -416,7 +496,15 @@ DoRouteOptimizationCN disabled;
 DoRouteOptimizationCN disabled;
 DoRouteOptimizationMN disabled;
 
-Interface "eth0";
+Interface "eth0" {
+        Bid 111;
+        BidPriority 10;
+};
+
+Interface "eth1" {
+        Bid 222;
+        BidPriority 5;
+};
 
 MnRouterProbes 1;
 
@@ -426,6 +514,8 @@ MnHomeLink "eth0" {
         IsMobRtr enabled;
         HomeAgentAddress 3ffe:2620:6:1::1;
         HomeAddress 3ffe:2620:6:1::1234/64 (3ffe:2620:6:2::/64, 3ffe:2620:6:3::/64);
+        RegMultipleCoA enabled;
+        IfMultipleCoA "eth0", "eth1";
 }
 
 UseMnHaIPsec disabled;
diff -r 82fcd4bea972 src/bcache.c
--- a/src/bcache.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/bcache.c	Fri Jan 11 17:02:25 2008 +0100
@@ -48,6 +48,7 @@ static int bcache_count = 0;
 static int bcache_count = 0;
 
 pthread_rwlock_t bc_lock; /* Protects binding cache */
+int bce_next_entry(void *bce_v, void *next_bce_v);
 
 /** 
  * get_bcache_count - returns number of home and cache entries
@@ -90,6 +91,12 @@ void dump_bce(void *bce, void *os)
 	fprintf(out, " lifetime %ld\n ", e->lifetime.tv_sec);
 	fprintf(out, " seqno %d\n", e->seqno);
 
+ 	if (e->bid)
+ 		fprintf(out, " BID = %d, Priority = %d\n", 
+ 			e->bid, e->priority);
+ 	else
+ 		fprintf(out, " No BID assigned\n");
+ 
 	if (e->flags & IP6_MH_BA_MR) {
 		struct list_head *list;
 
@@ -201,6 +208,7 @@ void bcache_free(struct bcentry *bce)
  * @hinfo: home address info, optional if our_addr is present
  * @our_addr: local address (home address)
  * @peer_addr: address of CN
+ * @bid: BID of the bce
  *
  * Returns reference to non-null entry on success and null on failure.
  * If caller adjusts lifetime of entry, caller must call
@@ -208,7 +216,8 @@ void bcache_free(struct bcentry *bce)
  * call bcache_release_entry() after entry is not used anymore.
  **/
 struct bcentry *bcache_get(const struct in6_addr *our_addr,
-			   const struct in6_addr *peer_addr)
+ 			   const struct in6_addr *peer_addr,
+ 			   uint16_t bid)
 {
 	struct bcentry *bce;
 
@@ -216,7 +225,7 @@ struct bcentry *bcache_get(const struct 
 
 	pthread_rwlock_rdlock(&bc_lock);
 
-	bce = hash_get(&bc_hash, our_addr, peer_addr);
+	bce = hash_get(&bc_hash, our_addr, peer_addr, bid);
 
 	if (bce) 
 		pthread_rwlock_wrlock(&bce->lock);
@@ -240,16 +249,19 @@ void bcache_release_entry(struct bcentry
  * bce_type - get type of binding cache entry
  * @our_addr: our IPv6 address
  * @peer_addr: peer's IPv6 address
+ * @bid: BID of the bce
  *
  * Looks up entry from binding cache and returns its type.  If not
  * found, returns -%ENOENT.
  **/
-int bce_type(const struct in6_addr *our_addr, const struct in6_addr *peer_addr)
+int bce_type(const struct in6_addr *our_addr, 
+ 	     const struct in6_addr *peer_addr,
+ 	     uint16_t bid)
 {
         struct bcentry *bce;
         int type;
 
-        bce = bcache_get(our_addr, peer_addr);
+ 	bce = bcache_get(our_addr, peer_addr, bid);
 
         if (bce == NULL)
                 return -ENOENT;
@@ -264,7 +276,8 @@ static int __bcache_insert(struct bcentr
 {
 	int ret;
 
-	ret = hash_add(&bc_hash, bce, &bce->our_addr, &bce->peer_addr);
+	ret = hash_add(&bc_hash, bce, &bce->our_addr, 
+ 		       &bce->peer_addr, &bce->bid);
 	if (ret)
 		return ret;
 
@@ -282,7 +295,14 @@ static int __bcache_start(struct bcentry
 	      bce->type == BCE_HOMEREG ? bce->lifetime : tmp,
 	      expires);
 	add_task_abs(&expires, &bce->tqe, _expire); 
-	xfrm_add_bce(&bce->our_addr, &bce->peer_addr, &bce->coa, 0);
+ 	/* MCoA 
+ 	 * Due to the BID, the BC may have several entries with the 
+ 	 * same peer_addr. Thus we add states and policies only if 
+ 	 * there are no entries yet for the our_addr/peer_addr pair 
+ 	 * (see bce->xfrm_create).
+ 	 */
+ 	xfrm_add_bce(&bce->our_addr, &bce->peer_addr, &bce->coa, 0, 
+ 		     bce->xfrm_create);
 	return 0;
 }
 
@@ -350,22 +370,26 @@ int bcache_update_expire(struct bcentry 
 	}
 	tsadd(expires, bce->add_time, expires);
 	add_task_abs(&expires, &bce->tqe, _expire);	
-	xfrm_add_bce(&bce->our_addr, &bce->peer_addr, &bce->coa, 1);
+ 	xfrm_add_bce(&bce->our_addr, &bce->peer_addr, &bce->coa, 1, 0);
 
 	return 0;
 }
 
 /**
  * bcache_delete - deletes a bul entry
+ * If bid == 0, all entries that match our_addr/peer_addr are deleted
  **/
 void bcache_delete(const struct in6_addr *our_addr,
-		   const struct in6_addr *peer_addr)
+		   const struct in6_addr *peer_addr,
+		   uint16_t bid)
 {
 	struct bcentry *bce;
 	pthread_rwlock_wrlock(&bc_lock);
-	bce = hash_get(&bc_hash, our_addr, peer_addr);
-	if (bce)
-		bce_delete(bce, 0);
+ 	while ((bce = hash_get(&bc_hash, our_addr, peer_addr, bid))) {
+		if (bce)
+ 			bce_delete(bce, 0);
+ 	}
+ 
 	pthread_rwlock_unlock(&bc_lock);
 }
 
@@ -398,8 +422,27 @@ static void bce_delete(struct bcentry *b
 	pthread_rwlock_wrlock(&bce->lock);
 	if (bce->type != BCE_DAD) {
 		del_task(&bce->tqe);
-		if (bce->type != BCE_NONCE_BLOCK)
-			xfrm_del_bce(&bce->our_addr, &bce->peer_addr);
+ 		if (bce->type != BCE_NONCE_BLOCK) {
+			/* MCoA 
+			 * Due to the BID, the BC can have several entries 
+			 * with the same our_addr/peer_addr pair. We delete 
+			 * the xfrm data if we do not have any BCE for 
+			 * this peer_addr anymore, or we update it (we need 
+			 * to update the kernel's CoA to a valid one).
+			 */
+			if(mcoa_bce_count(&bce->peer_addr) == 1)
+				xfrm_del_bce(&bce->our_addr, &bce->peer_addr);
+			else {
+				struct bcentry *next = bce;
+				/* Look for another valid BCE */
+				bcache_iterate_in(&bce->peer_addr, 
+						  bce_next_entry, &next);
+				/* Replace the CoA */
+				xfrm_add_bce(&next->our_addr, 
+					     &next->peer_addr, 
+					     &next->coa, 1, 0);
+			}
+		}
 	}
 	if (bce->cleanup)
 		bce->cleanup(bce);
@@ -414,7 +457,7 @@ static void bce_delete(struct bcentry *b
 		}
 	}
 	bcache_count--;
-	hash_delete(&bc_hash, &bce->our_addr, &bce->peer_addr);
+	hash_delete(&bc_hash, &bce->our_addr, &bce->peer_addr, bce->bid);
 	pthread_rwlock_unlock(&bce->lock);
 	bcache_free(bce);
 }
@@ -458,3 +501,71 @@ int bcache_iterate(int (* func)(void *, 
 	pthread_rwlock_unlock(&bc_lock);
 	return err;
 }
+
+/**
+ * bcache_iterate_in - apply function to every BC entry that match peer_addr
+ * @peer_addr: the peer_addr of the hash
+ * @func: function to apply
+ * @arg: extra data for @func
+ *
+ * Iterates through binding cache, calling @func for each entry that 
+ * match peer_addr. Extra data may be passed to @func in @arg.  @func 
+ * takes a bcentry as its first argument and @arg as second argument.
+ *
+ * Be sure that bc_lock is locked before using this function, and 
+ * unlocked after using it.
+ **/
+int bcache_iterate_in(const struct in6_addr *peer_addr, 
+ 		      int (* func)(void *, void *), 
+ 		      void *arg)
+{
+	int err;
+	err = hash_iterate_in(&bc_hash, peer_addr, func, arg);
+	return err;	
+}
+
+int bce_highest_priority(void *bce_v, void *best_bce_v)
+{
+	struct bcentry *bce = bce_v;
+	struct bcentry **best_bce = best_bce_v;
+	
+	assert(bce);
+	assert(best_bce);
+
+	if (!(*best_bce) || (bce->priority > (*best_bce)->priority))
+		(*best_bce) = bce;
+
+	return 0;
+}
+
+int bce_next_entry(void *bce_v, void *next_bce_v)
+{
+	struct bcentry *bce = bce_v;
+	struct bcentry **next_bce = next_bce_v;
+	
+	assert(bce);
+	assert(next_bce);
+	
+	if (bce->bid != (*next_bce)->bid) {
+		(*next_bce) = bce;
+		return 1;
+	}
+	
+	return 0;
+}
+
+int mcoa_bce_inc_counter(void *bule_v, void *counter_v)
+{
+	int *counter = counter_v;
+	(*counter)++;
+	return 0;
+}
+
+int mcoa_bce_count(const struct in6_addr *peer_addr)
+{
+	int count = 0;
+	bcache_iterate_in(peer_addr, mcoa_bce_inc_counter, &count);
+
+	return count;
+}
+
diff -r 82fcd4bea972 src/bcache.h
--- a/src/bcache.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/bcache.h	Fri Jan 11 17:02:25 2008 +0100
@@ -20,6 +20,12 @@ struct bcentry {
 	uint16_t nonce_hoa;
 	uint16_t type;     		/* Entry type */
 	uint16_t nemo_type;    		/* NEMO registration type */
+	uint16_t bid; 			/* MCoA: Binding Identifier */
+	uint8_t priority;		/* MCoA: BID priority */
+	int table;			/* MCoA: Table id to where are stored
+					   the routes */
+	int xfrm_create;		/* MCoA: If xfrm policies/states must
+					   be created */
 	int unreach;			/* ICMP dest unreach count */
 	int tunnel;			/* Tunnel interface index */
 	int link;			/* Home link interface index */
@@ -48,12 +54,16 @@ struct bcentry {
 #define BCE_NEMO_IMPLICIT 2
 #define BCE_NEMO_DYNAMIC 3
 
+#define BCE_TABLE_MIN 1
+#define BCE_TABLE_MAX 251
+
 struct bcentry *bcache_alloc(int type);
 
 void bcache_free(struct bcentry *bce);
 
 struct bcentry *bcache_get(const struct in6_addr *our_addr,
-			   const struct in6_addr *peer_addr);
+			   const struct in6_addr *peer_addr,
+			   uint16_t bid);
 
 int bcache_add(struct bcentry *bce);
 
@@ -63,28 +73,37 @@ int bcache_update_expire(struct bcentry 
 int bcache_update_expire(struct bcentry *bce);
 
 void bcache_delete(const struct in6_addr *our_addr,
-		   const struct in6_addr *peer_addr);
+		   const struct in6_addr *peer_addr,
+		   uint16_t bid);
 
 int bcache_init(void);
 void bcache_flush(void);
 void bcache_cleanup(void);
 
 int bcache_iterate(int (* func)(void *, void *), void *arg);
+int bcache_iterate_in(const struct in6_addr *peer_addr, 
+		      int (* func)(void *, void *), 
+		      void *arg);
 
 void bcache_release_entry(struct bcentry *bce);
 
 int bce_type(const struct in6_addr *our_addr,
-	     const struct in6_addr *peer_addr);
+	     const struct in6_addr *peer_addr,
+	     uint16_t bid);
 
 static inline int bce_exists(const struct in6_addr *our_addr,
-			     const struct in6_addr *peer_addr)
+			     const struct in6_addr *peer_addr,
+			     uint16_t bid)
 {
-	return bce_type(our_addr, peer_addr) >= BCE_NONCE_BLOCK;
+	return bce_type(our_addr, peer_addr, bid) >= BCE_NONCE_BLOCK;
 }
+
+int bce_highest_priority(void *bce_v, void *best_bce_v);
 
 void dump_bce(void *bce, void *os);
 
 int get_bcache_count(int type);
+int mcoa_bce_count(const struct in6_addr *peer_addr);
 
 extern pthread_rwlock_t bc_lock; /* Protects binding cache */
 
diff -r 82fcd4bea972 src/bul.c
--- a/src/bul.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/bul.c	Fri Jan 11 17:02:25 2008 +0100
@@ -38,6 +38,8 @@
 #include "xfrm.h"
 #include "debug.h"
 #include "retrout.h"
+#include "tunnelctl.h"
+#include "rtnl.h"
 #ifdef ENABLE_VT
 #include "vt.h"
 #endif
@@ -55,7 +57,8 @@ static struct hash bul_hash;
 static struct hash bul_hash;
 
 struct bulentry *create_bule(const struct in6_addr *hoa,
-			     const struct in6_addr *cn_addr)
+			     const struct in6_addr *cn_addr, 
+			     u_int16_t bid)
 {
 	struct bulentry *bule;
 	if ((bule = malloc(sizeof(*bule))) != NULL) {
@@ -63,6 +66,11 @@ struct bulentry *create_bule(const struc
 		bule->hoa = *hoa;
 		bule->last_coa = *hoa;
 		bule->peer_addr = *cn_addr;
+		bule->bid = bid;
+		bule->if_tunnel = 0;
+		bule->rules = 0;
+		bule->home_block = 0;
+		bule->mcoa_dereg = 1;
 		INIT_LIST_HEAD(&bule->tqe.list);
 		bule->seq = random();
 	}
@@ -97,6 +105,11 @@ void dump_bule(void *bule, void *os)
 		NIP6ADDR(&e->peer_addr));
 	fprintf(out, " lifetime = %ld, ", e->lifetime.tv_sec);
 	fprintf(out, " delay = %ld\n", tstomsec(e->delay));
+	if (e->bid)
+		fprintf(out, " BID = %d, Priority = %d\n", 
+			e->bid, e->priority);
+	else
+		fprintf(out, " No BID assigned\n");	
 	fprintf(out, " flags: ");
 	if (e->flags & IP6_MH_BU_HOME)
 		fprintf(out, "IP6_MH_BU_HOME ");
@@ -118,21 +131,23 @@ void dump_bule(void *bule, void *os)
  * @hinfo: home address info, optional if our_addr is present
  * @our_addr: local address (home address)
  * @peer_addr: address of CN
+ * @bid: Binding Unique Identifier
  *
  * Returns non-null entry on success and null on failure. Caller must
  * call del_task and add_task, if lifetime of the entry is changed.
  **/
 struct bulentry *bul_get(struct home_addr_info *hinfo,
 			 const struct in6_addr *our_addr,
-			 const struct in6_addr *peer_addr)
+			 const struct in6_addr *peer_addr,
+			 u_int16_t bid)
 {
 	struct bulentry *bule;
 
 	assert(hinfo || our_addr);
 
 	if (hinfo)
-		bule = hash_get(&hinfo->bul, NULL, peer_addr);
-	else bule = hash_get(&bul_hash, our_addr, peer_addr);
+		bule = hash_get(&hinfo->bul, NULL, peer_addr, bid);
+	else bule = hash_get(&bul_hash, our_addr, peer_addr, bid);
 	return bule;
 }
 
@@ -175,9 +190,11 @@ int bul_add(struct bulentry *bule)
 
 	assert(bule && tsisset(bule->lifetime) && hai);
 	
-	if ((ret = hash_add(&bul_hash, bule, &bule->hoa, &bule->peer_addr)) < 0)
+	if ((ret = hash_add(&bul_hash, bule, &bule->hoa, &bule->peer_addr, 
+			    &bule->bid)) < 0)
 		return ret;
-	if ((ret = hash_add(&hai->bul, bule, NULL, &bule->peer_addr)) < 0)
+	if ((ret = hash_add(&hai->bul, bule, NULL, &bule->peer_addr,
+			    &bule->bid)) < 0)
 		goto bul_free;
 
 	clock_gettime(CLOCK_REALTIME, &bule->lastsent);
@@ -197,9 +214,9 @@ int bul_add(struct bulentry *bule)
 	add_task_abs(&timer_expire, &bule->tqe, bule->callback);
 	return 0;
 home_bul_free:
-	hash_delete(&hai->bul, &bule->hoa, &bule->peer_addr);
+	hash_delete(&hai->bul, &bule->hoa, &bule->peer_addr, bule->bid);
 bul_free:
-	hash_delete(&bul_hash, &bule->hoa, &bule->peer_addr);
+	hash_delete(&bul_hash, &bule->hoa, &bule->peer_addr, bule->bid);
 	return ret; 
 }
 
@@ -209,8 +226,8 @@ void bul_delete(struct bulentry *bule)
 	struct home_addr_info *hai = bule->home;
 
 	del_task(&bule->tqe);
-	hash_delete(&bul_hash, &bule->hoa, &bule->peer_addr);
-	hash_delete(&hai->bul, NULL, &bule->peer_addr);
+	hash_delete(&bul_hash, &bule->hoa, &bule->peer_addr, bule->bid);
+	hash_delete(&hai->bul, NULL, &bule->peer_addr, bule->bid);
 
 	if (!IN6_ARE_ADDR_EQUAL(&bule->hoa, &bule->coa)) {
 		bule->last_coa = bule->coa;
@@ -222,6 +239,7 @@ void bul_delete(struct bulentry *bule)
 		if (!(bule->flags & IP6_MH_BU_HOME))
 			mn_rr_delete_bule(bule);
 	}
+
 	if (bule->flags & IP6_MH_BU_HOME) {
 		if (bule->type == UNREACH_ENTRY) {
 			pthread_mutex_lock(&hai->ha_list.c_lock);
@@ -232,16 +250,28 @@ void bul_delete(struct bulentry *bule)
 		} else {
 			if (hai->home_block & HOME_LINK_BLOCK)
 				xfrm_unblock_link(hai);
+			if (bule->home_block & HOME_LINK_BLOCK)
+				xfrm_unblock_bule_link(bule);
 			if (hai->home_block & HOME_ADDR_BLOCK)
 				xfrm_unblock_hoa(hai);
 			if (hai->home_block & NEMO_RA_BLOCK)
 				xfrm_unblock_ra(hai);
 			if (hai->home_block & NEMO_FWD_BLOCK)
 				xfrm_unblock_fwd(hai);
+			if (bule->home_block & NEMO_FWD_BLOCK)
+				bule_unblock_fwd(bule);
 		}
 	}
 	while (bule->ext_cleanup)
 		bule->ext_cleanup(bule);
+
+	/* MCoA
+	 * If MCoA is used, the tunnel related to this BULE
+	 * has to be deleted.
+	 */
+	if (bule->home->reg_mcoa && bule->type != NON_MIP_CN_ENTRY)
+		tunnel_del(bule->if_tunnel, NULL, NULL);
+
 	dbg("Deleting bule\n");
 	dbg_func(bule, dump_bule);
 	free_bule(bule);
@@ -312,3 +342,129 @@ int bul_iterate(struct hash *h, int (* f
 	struct hash *tmp = h ? h : &bul_hash;
 	return hash_iterate(tmp, func, arg);
 }
+
+/**
+ * bul_iterate_in - apply function to every BUL entry that match peer_addr
+ * @peer_addr: the peer_addr of the hash
+ * @func: function to apply
+ * @arg: extra data for @func
+ *
+ * Iterates through binding update list, calling @func for each entry that 
+ * match peer_addr. Extra data may be passed to @func in @arg.  @func 
+ * takes a BUL entry as its first argument and @arg as second argument.
+ **/
+int bul_iterate_in(struct in6_addr *peer_addr, 
+		   int (* func)(void *, void *), 
+		   void *arg)
+{
+	return hash_iterate_in(&bul_hash, peer_addr, func, arg);
+}
+
+int bul_highest_priority(void *bule_v, void *best_bule_v)
+{
+	struct bulentry *bule = bule_v;
+	struct bulentry **best_bule = best_bule_v;
+	
+	assert(bule);
+	assert(best_bule);
+	
+	if(bule->type == NON_MIP_CN_ENTRY ||
+	   bule->type == UNREACH_ENTRY)
+		return 0;
+
+	if (!(*best_bule)
+	    || (bule->priority > (*best_bule)->priority
+		&& bule->rules))
+		(*best_bule) = bule;
+	
+	return 0;
+}
+
+int mcoa_bule_inc_counter(void *bule_v, void *counter_v)
+{
+	struct bulentry *bule = bule_v;
+	int *counter = counter_v;
+	assert(counter);
+	
+	if(bule->type != NON_MIP_CN_ENTRY)
+		(*counter)++;
+	return 0;
+}
+
+/**
+ * mcoa_bule_count - count the number of entry (!NON_MIP_CN_ENTRY) 
+ * with the same peer_addr.
+ * @bule: the bule where to pick up the peer address
+ **/
+
+int mcoa_bule_count(struct bulentry *bule)
+{
+	int count = 0;
+	assert(bule);
+	bul_iterate_in(&bule->peer_addr, mcoa_bule_inc_counter, &count);
+	return count;
+}
+
+
+int mcoa_bule_setdereg(void *bule_v, void *dereg_v)
+{
+	int *dereg = dereg_v;
+	struct bulentry *bule = bule_v;
+	
+	assert(dereg);
+	bule->mcoa_dereg = *(dereg);
+	return 0;
+}
+
+/**
+ * mcoa_bule_dereg - Set the BULE mcoa_dereg flag to value for 
+ * all entries that match bule->peer_addr.
+ * @bule: the bule where to pick up the peer address
+ * @value: the value to set
+ **/
+int mcoa_bule_dereg(struct bulentry *bule, int value)
+{
+	bul_iterate_in(&bule->peer_addr, mcoa_bule_setdereg, &value);
+	return 0;
+}
+
+/**
+ * bule_block_fwd - Block forwarding for all MNPs 
+ * via the fwmark (BID) of the bule
+ * @bule: the bule
+ */
+void bule_block_fwd(struct bulentry *bule)
+{
+	struct list_head *l;
+	if (!bule->rules)
+		return;
+	bule->home_block |= NEMO_FWD_BLOCK; 
+	list_for_each(l, &bule->home->mob_net_prefixes) {
+		struct prefix_list_entry *p = NULL;
+		p = list_entry(l, struct prefix_list_entry, list);
+		rule_add(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK,
+			/*fwmark*/ bule->bid, RTN_BLACKHOLE,
+			&p->ple_prefix, p->ple_plen,
+			&in6addr_any, 0, 0);
+	}
+}
+
+/**
+ * bule_unblock_fwd - unblock forwarding for all MNPs 
+ * via the fwmark (BID) of the bule
+ * @bule: the bule
+ */
+void bule_unblock_fwd(struct bulentry *bule)
+{
+	struct list_head *l;
+	bule->home_block &= ~NEMO_FWD_BLOCK;
+	list_for_each(l, &bule->home->mob_net_prefixes) {
+		struct prefix_list_entry *p = NULL;
+		p = list_entry(l, struct prefix_list_entry, list);
+		rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, 
+			/*fwmark*/ bule->bid, RTN_BLACKHOLE,  
+			&p->ple_prefix, p->ple_plen,    
+			&in6addr_any, 0, 0);
+	}               
+}
+
diff -r 82fcd4bea972 src/bul.h
--- a/src/bul.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/bul.h	Fri Jan 11 17:02:25 2008 +0100
@@ -9,6 +9,9 @@
 #include "tqueue.h"
 #include "hash.h"
 #include "list.h"
+
+#define BULE_TABLE_MIN 1
+#define BULE_TABLE_MAX 251
 
 struct home_addr_info;
 
@@ -31,10 +34,16 @@ struct bulentry {
 	struct in6_addr hoa;
 	struct in6_addr coa;		/* care-of address of the sent BU */
 	int if_coa;
+	int if_tunnel;			/* MCoA: Tunnel iface for the BCE */
+	int table;			/* MCoA: Routing table where are 
+					   stored the routes */
+	int rules;			/* MCoA: if rules are installed for 
+					   this BULE */	
 	int type;                       /* BUL / NON_MIP_CN / UNREACH  */
 	uint16_t seq;			/* sequence number of the latest BU */
 	uint16_t flags;			/* BU send flags */
-	struct in6_addr last_coa;        /* Last good coa */      
+	uint16_t bid;			/* MCoA: Binding Identifier */
+	struct in6_addr last_coa;	/* Last good coa */      
 	struct timespec lastsent;
 	struct timespec lifetime;      	/* lifetime sent in this BU */
 	struct timespec delay;		/* call back time in ms*/
@@ -47,7 +56,11 @@ struct bulentry {
 	uint8_t use_alt_coa;            /* Whether to use alt. CoA option */
 	uint8_t dereg;                  /* for calculating BSA key */
 	uint8_t do_send_bu;             /* send bu / not send bu */
-
+	uint8_t priority;		/* MCoA: BID priority */
+	uint8_t home_block;		/* MCoA: NEMO_FWD_BLOCK, ... */  
+	uint8_t mcoa_dereg;		/* MCoA: if BCE needs to be 
+					   deregistered with a BU */
+ 
 	/* Information for return routability */
 	struct retrout_info rr;
 	uint8_t Kbm[HMAC_SHA1_KEY_SIZE];
@@ -84,7 +97,8 @@ enum {
 
 struct bulentry *bul_get(struct home_addr_info *hinfo,
 			 const struct in6_addr *our_addr,
-			 const struct in6_addr *peer_addr);
+			 const struct in6_addr *peer_addr,
+			 u_int16_t bid);
 
 int bul_add(struct bulentry *bule);
 
@@ -94,7 +108,11 @@ void bul_update_expire(struct bulentry *
 void bul_update_expire(struct bulentry *bule);
 
 int bul_iterate(struct hash *h, int (* func)(void *bule, void *arg), void *arg);
+int bul_iterate_in(struct in6_addr *peer_addr, 
+		   int (* func)(void *, void *), 
+		   void *arg);
 
+int bul_highest_priority(void *bule_v, void *best_bule_v);
 int bul_init(void);
 int bul_home_init(struct home_addr_info *home);
 void bul_home_cleanup(struct hash *bul);
@@ -102,7 +120,12 @@ void bul_cleanup(void);
 void bul_cleanup(void);
 void dump_bule(void *bule, void *os);
 struct bulentry *create_bule(const struct in6_addr *hoa,
-			     const struct in6_addr *cn_addr);
+			     const struct in6_addr *cn_addr,
+			     u_int16_t bid);
 void free_bule(struct bulentry *bule);
+int mcoa_bule_count(struct bulentry *bule);
+int mcoa_bule_dereg(struct bulentry *bule_v, int value);
+void bule_block_fwd(struct bulentry *bule);
+void bule_unblock_fwd(struct bulentry *bule);
 
 #endif
diff -r 82fcd4bea972 src/cn.c
--- a/src/cn.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/cn.c	Fri Jan 11 17:02:25 2008 +0100
@@ -67,7 +67,11 @@ static void cn_recv_dst_unreach(const st
 	if (icmp6_parse_data(ip6h, optlen, &laddr, &raddr) < 0)
 		return;
 
-	bce = bcache_get(laddr, raddr);
+	/* MCoA TODO2
+	 * Add MCoA support for the CN
+	 * MCOA_NO_BID must be replaced with the BUI's BID
+	 */
+	bce = bcache_get(laddr, raddr, MCOA_NO_BID);
 
 	if (bce == NULL) 
 		return;
@@ -76,7 +80,7 @@ static void cn_recv_dst_unreach(const st
 	if (bce->unreach > ICMP_ERROR_PERSISTENT_THRESHOLD &&
 	    bce->type != BCE_HOMEREG) {
 		bcache_release_entry(bce);
-		bcache_delete(laddr, raddr);
+		bcache_delete(laddr, raddr, MCOA_NO_BID);
 		dbg("BCE for %x:%x:%x:%x:%x:%x:%x:%x deleted "
 		    "due to receipt of ICMPv6 destination unreach\n", 
 		    NIP6ADDR(raddr));
@@ -156,6 +160,7 @@ void cn_recv_bu(const struct ip6_mh *mh,
 	struct in6_addr_bundle out;
 	struct ip6_mh_binding_update *bu;
 	struct ip6_mh_opt_nonce_index *non_ind;
+	struct ip6_mh_opt_bid *bid_opt = NULL;
 	struct bcentry *bce = NULL;
 	struct timespec lft;
 	int status, new = 0;
@@ -175,7 +180,18 @@ void cn_recv_bu(const struct ip6_mh *mh,
 
 	bu_flags = bu->ip6mhbu_flags;
 	non_ind = mh_opt(&bu->ip6mhbu_hdr, &mh_opts, IP6_MHOPT_NONCEID);
-	bce = bcache_get(out.src, out.dst);
+	/* MCoA TODO2
+	 * MCoA support for CN is not implemented yet
+	 * At the moment we ignore the BUs with BUI option
+	 */
+	if ((bid_opt = mh_opt(&bu->ip6mhbu_hdr, &mh_opts, IP6_MHOPT_BID))) {
+		dbg("Received BU with BUI sub-option, but CN doesn't "
+		    "support MCoA registration. BU skipped.\n");
+		status = IP6_MH_BAS_REG_NOT_ALLOWED;
+		goto send_nack;
+	}
+
+	bce = bcache_get(out.src, out.dst, MCOA_NO_BID);
 	if (bce) {
 		if ((bce->flags^bu_flags) & (IP6_MH_BU_HOME|IP6_MH_BU_MR)) {
 			/* H-bit or R-bit mismatch, flags changed */
@@ -219,7 +235,7 @@ void cn_recv_bu(const struct ip6_mh *mh,
 				goto send_nack;
 			}
 			/* else get rid of it */
-			bcache_delete(out.src, out.dst);
+			bcache_delete(out.src, out.dst, MCOA_NO_BID);
 		}
 	} else {
 		if (bu_flags & IP6_MH_BU_HOME) {
@@ -281,18 +297,18 @@ void cn_recv_bu(const struct ip6_mh *mh,
 		}
 		/* dereg, success */
 		bcache_release_entry(bce);
-		bcache_delete(out.src, out.dst);
+		bcache_delete(out.src, out.dst, MCOA_NO_BID);
 		status = IP6_MH_BAS_ACCEPTED;
 	}
 	if (bu_flags & IP6_MH_BU_ACK)
-		mh_send_ba(&out, status, 0, seqno, &lft, key, iif);
+		mh_send_ba(&out, status, 0, seqno, &lft, 0, 0, key, iif);
 	return;
 send_nack:
 	if (bce) {
 		bcache_release_entry(bce);
-		bcache_delete(out.src, out.dst);
+		bcache_delete(out.src, out.dst, MCOA_NO_BID);
 	}
-	mh_send_ba_err(&out, status, 0, seqno, pkey, iif);
+	mh_send_ba_err(&out, status, 0, seqno, 0, 0, pkey, iif);
 }
 
 static struct mh_handler cn_bu_handler =
diff -r 82fcd4bea972 src/conf.c
--- a/src/conf.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/conf.c	Fri Jan 11 17:02:25 2008 +0100
@@ -74,6 +74,7 @@ static void conf_version(void)
 {
 	fprintf(stderr,
 		"%s (%s) %s\n"
+		"With Multiple Care-of Addresses registration support\n"
 		"%s\n"
 		"This is free software; see the source for copying conditions.  There is NO\n"
 		"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n",
@@ -218,6 +219,7 @@ static void conf_default(struct mip6_con
 
 	/* HA options */
 	c->SendMobPfxAdvs = 1;
+	c->HaAcceptMCoAReg = 0;
 	c->SendUnsolMobPfxAdvs = 1;
 	c->MaxMobPfxAdvInterval = 86400; /* seconds */
 	c->MinMobPfxAdvInterval = 600; /* seconds */
@@ -317,8 +319,89 @@ void conf_show(struct mip6_config *c)
 	dbg("MinMobPfxAdvInterval = %u\n", c->MinMobPfxAdvInterval);
 	dbg("HaMaxBindingLife = %u\n", c->HaMaxBindingLife);
 	dbg("HaAcceptMobRtr = %s\n", CONF_BOOL_STR(c->HaAcceptMobRtr));
+	dbg("HaAcceptMCoAReg = %s\n", CONF_BOOL_STR(c->HaAcceptMCoAReg));
 
 	/* CN options */
 	dbg("DoRouteOptimizationCN = %s\n",
 	    CONF_BOOL_STR(c->DoRouteOptimizationCN));
 }
+
+/* 
+ * get_reliable_from_ifindex - return the reliability value 
+ * from the interface index
+ * @iif: the interface index
+ */ 
+int get_reliable_from_ifindex(int iif)
+{
+	struct list_head *list;
+
+	list_for_each(list, &conf.net_ifaces) {
+		struct net_iface *nif;
+		nif = list_entry(list, struct net_iface, list);
+		if (nif->ifindex == iif) {
+			if (is_if_mn(nif))
+				return nif->mn_if_reliable;
+			return -1;
+		}
+	}
+	return -1;
+}
+
+/* 
+ * get_bid_from_ifindex - return the bid from the interface index
+ * @iif: the interface index
+ */ 
+uint16_t get_bid_from_ifindex(int iif)
+{
+	struct list_head *list;
+
+	list_for_each(list, &conf.net_ifaces) {
+		struct net_iface *nif;
+		nif = list_entry(list, struct net_iface, list);
+		if (nif->ifindex == iif) {
+			if (is_if_mn(nif))
+				return nif->mn_if_bid;
+			return 0;
+		}
+	}
+	return 0;
+}
+
+/* 
+ * get_prio_from_bid - return the priority from the bid
+ * @bid: the bid of the interface
+ */ 
+uint8_t get_prio_from_bid(uint16_t bid)
+{
+	struct list_head *list;
+
+	list_for_each(list, &conf.net_ifaces) {
+		struct net_iface *nif;
+		nif = list_entry(list, struct net_iface, list);
+		if (nif->mn_if_bid == bid) {
+			if (is_if_mn(nif))
+				return nif->mn_if_bidprio;
+			return 0;
+		}
+	}
+	return 0;
+}
+
+/* 
+ * get_highest_prio - return the highest BID priority from the 
+ * interface list
+ */ 
+uint8_t get_highest_prio()
+{
+	struct list_head *list;
+	int priority = 0;
+
+	list_for_each(list, &conf.net_ifaces) {
+		struct net_iface *nif;
+		nif = list_entry(list, struct net_iface, list);
+		if (nif->mn_if_bidprio >= priority)
+			priority = nif->mn_if_bidprio;
+	}
+	return priority;
+}
+
diff -r 82fcd4bea972 src/conf.h
--- a/src/conf.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/conf.h	Fri Jan 11 17:02:25 2008 +0100
@@ -48,6 +48,7 @@ struct mip6_config {
 
 	/* HA options */
 	char HaAcceptMobRtr;
+	char HaAcceptMCoAReg;
 	char SendMobPfxAdvs;
 	char SendUnsolMobPfxAdvs;
 	unsigned int MaxMobPfxAdvInterval;
@@ -66,6 +67,9 @@ struct net_iface {
 	int is_rtr;
 	int mip6_if_entity;
 	int mn_if_preference;
+	uint16_t mn_if_bid;
+	uint8_t mn_if_bidprio;
+	int mn_if_reliable;
 };
 
 extern struct mip6_config conf;
@@ -119,6 +123,11 @@ int conf_parse(struct mip6_config *c, in
 
 void conf_show(struct mip6_config *c);
 
+int get_reliable_from_ifindex(int iif);
+uint16_t get_bid_from_ifindex(int iif);
+uint8_t get_prio_from_bid(uint16_t bid);
+uint8_t get_highest_prio();
+
 int yyparse(void);
 
 int yylex(void);
diff -r 82fcd4bea972 src/dhaad_mn.c
--- a/src/dhaad_mn.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/dhaad_mn.c	Fri Jan 11 17:02:25 2008 +0100
@@ -120,8 +120,9 @@ static void dhaad_resend(struct tq_elem 
 			pthread_rwlock_unlock(&mn_lock);
 			return;
 		}
-		t->dhaad_id = dhaad_send_request(hai->primary_coa.iif,
-						 &hai->primary_coa.addr,
+		assert(hai->current_coa);
+		t->dhaad_id = dhaad_send_request(hai->current_coa->iif,
+						 &hai->current_coa->addr,
 						 &hai->home_prefix,
 						 hai->home_plen,
 						 hai->mob_rtr?
@@ -146,8 +147,9 @@ static void _dhaad_start(struct home_add
 		if (hai->mob_rtr && !(hai->home_block & NEMO_RA_BLOCK))
 			xfrm_block_ra(hai);
 		t->dhaad_resends = 0;
-		t->dhaad_id = dhaad_send_request(hai->primary_coa.iif,
-						 &hai->primary_coa.addr,
+		assert(hai->current_coa);
+		t->dhaad_id = dhaad_send_request(hai->current_coa->iif,
+						 &hai->current_coa->addr,
 						 &hai->home_prefix,
 						 hai->home_plen,
 						 hai->mob_rtr?
diff -r 82fcd4bea972 src/gram.y
--- a/src/gram.y	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/gram.y	Fri Jan 11 17:02:25 2008 +0100
@@ -47,15 +47,20 @@
 #include "util.h"
 #include "ipsec.h"
 #include "rtnl.h"
+#include "bul.h"
 
 struct net_iface ni = {
 	.mip6_if_entity = MIP6_ENTITY_NO,
 	.mn_if_preference = POL_MN_IF_DEF_PREFERENCE,
+	.mn_if_bid = POL_MN_IF_DEF_BID,
+	.mn_if_bidprio = POL_MN_IF_DEF_BID_PRIORITY,
+	.mn_if_reliable = POL_MN_IF_DEF_RELIABLE
 };
 	
 struct home_addr_info hai = {
 	.ro_policies = LIST_HEAD_INIT(hai.ro_policies),
-	.mob_net_prefixes = LIST_HEAD_INIT(hai.mob_net_prefixes)
+	.mob_net_prefixes = LIST_HEAD_INIT(hai.mob_net_prefixes),
+	.mcoa = LIST_HEAD_INIT(hai.mcoa)
 };
 
 LIST_HEAD(prefixes);
@@ -185,6 +190,14 @@ static void uerror(const char *fmt, ...)
 %token		ISMOBRTR
 %token		HASERVEDPREFIX
 %token		MOBRTRUSEEXPLICITMODE
+%token		HAACCEPTMCOAREG
+%token		MCOAREG
+%token		NOMCOAREG
+%token		BID
+%token		BIDPRIORITY
+%token		RELIABLE
+%token		REGMULTIPLECOA
+%token		IFMULTIPLECOA
 
 %token		INV_TOKEN
 
@@ -197,6 +210,7 @@ static void uerror(const char *fmt, ...)
 %type <addr>	mnropolicyaddr
 %type <bool>	dorouteopt
 %type <num>	bindaclpolval
+%type <num>	mcoapolval
 %type <num>	prefixlen
 %type <num>	mip6entity
 %type <bool>	xfrmaction
@@ -306,6 +320,10 @@ topdef		: MIP6ENTITY mip6entity ';'
 		{
 			conf.HaAcceptMobRtr = $2;
 		}
+		| HAACCEPTMCOAREG BOOL ';' 
+		{ 
+			conf.HaAcceptMCoAReg = $2;
+		}
 		| HASERVEDPREFIX prefixlistentry ';'
 		{
 			list_splice(&prefixes,
@@ -375,6 +393,9 @@ ifacedef	: QSTRING ifacesub
 			memset(&ni, 0, sizeof(struct net_iface));
 			ni.mip6_if_entity = MIP6_ENTITY_NO;
 			ni.mn_if_preference = POL_MN_IF_DEF_PREFERENCE;
+			ni.mn_if_bid = POL_MN_IF_DEF_BID;
+			ni.mn_if_bidprio = POL_MN_IF_DEF_BID_PRIORITY;
+			ni.mn_if_reliable = POL_MN_IF_DEF_RELIABLE;
 		}
 		;
 
@@ -392,7 +413,44 @@ ifaceopt	: IFTYPE mip6entity ';'
 		} 
 		| MNIFPREFERENCE NUMBER ';'
 		{
-			ni.mn_if_preference = $2;
+			if (ni.mn_if_bid) {
+				uerror("You cannot set MnIfPreference if BID "
+				       "is set. Use BidPriority instead.");
+				return -1;
+			}
+ 			ni.mn_if_preference = $2;
+		}
+		| BID NUMBER ';'
+		{
+			if (ni.mn_if_preference) {
+				uerror("If BID is set, you cannot set "
+				       "MnIfPreference. Use BidPriority instead.");
+				return -1;
+			}
+			if ($2 < BULE_TABLE_MIN || $2 > BULE_TABLE_MAX) {
+				uerror("BID error (%d <= Bid <= %d)",
+					BULE_TABLE_MIN, BULE_TABLE_MAX);
+				return -1;
+			}
+			ni.mn_if_bid = $2;
+		}
+		| BIDPRIORITY NUMBER ';'
+		{
+			if (ni.mn_if_preference) {
+				uerror("You cannot set BidPriority if "
+				       "MnIfPreference is set.");
+				return -1;
+			}		
+			if ($2 < 1 || $2 > 255) {
+				uerror("BidPriotity error "
+				       "(%d <= BidPriority <= %d)",1, 255);
+				return -1;
+			}
+			ni.mn_if_bidprio = $2;
+		}
+		| RELIABLE BOOL ';'
+		{
+			ni.mn_if_reliable = $2;
 		}
 		;
 
@@ -432,15 +490,28 @@ linksub		: QSTRING '{' linkdefs '}'
 			INIT_LIST_HEAD(&nhai->ro_policies);
 			INIT_LIST_HEAD(&nhai->ha_list.home_agents);
 			INIT_LIST_HEAD(&nhai->mob_net_prefixes);
+			INIT_LIST_HEAD(&nhai->mcoa);
 			nhai->ha_list.dhaad_id = -1;
 			list_splice(&hai.ro_policies, &nhai->ro_policies);
 			list_splice(&hai.mob_net_prefixes,
 				    &nhai->mob_net_prefixes);
+			list_splice(&hai.mcoa, &nhai->mcoa);
+			if (!hai.reg_mcoa) {
+				/* If MCoA registration is not used, we
+				 * initialize nhai->current_coa
+				 */
+				nhai->current_coa = malloc(sizeof(struct mn_addr)); 
+				if (nhai->current_coa == NULL) {
+					uerror("out of memory");
+					return -1;
+				}
+			}
 			list_add_tail(&nhai->list, &conf.home_addrs);
 
 			memset(&hai, 0, sizeof(struct home_addr_info));
 			INIT_LIST_HEAD(&hai.ro_policies);
 			INIT_LIST_HEAD(&hai.mob_net_prefixes);
+			INIT_LIST_HEAD(&hai.mcoa);
 		}
 		;
 
@@ -468,6 +539,11 @@ linkdef		: HOMEAGENTADDRESS ADDR ';'
 			ipv6_addr_prefix(&hai.home_prefix, &$2, $4);
 			hai.home_plen = $4;
 		}
+		| REGMULTIPLECOA BOOL ';'
+		{
+			hai.reg_mcoa = $2;
+		}
+		| IFMULTIPLECOA iflist ';'
 		;
 
 homeaddress	: homeaddrdef prefixlistsub
@@ -695,7 +771,12 @@ bindaclpolval	: BOOL
 		| NUMBER { $$ = $1; }
 		;
 
-bindaclpolicy	: ADDR prefixlistsub bindaclpolval
+mcoapolval	: MCOAREG { $$ = 1; }
+		| NOMCOAREG { $$ = 0; }
+		| '' { $$ = 0; }
+		;
+
+bindaclpolicy	: ADDR prefixlistsub mcoapolval bindaclpolval
 		{
 			bae = malloc(sizeof(struct policy_bind_acl_entry));
 			if (bae == NULL) {
@@ -707,7 +788,8 @@ bindaclpolicy	: ADDR prefixlistsub binda
 			bae->plen = 128;
 			INIT_LIST_HEAD(&bae->mob_net_prefixes);
 			bae->mnp_count = mv_prefixes(&bae->mob_net_prefixes);
-			bae->bind_policy = $3;
+			bae->mcoa_reg = $3;
+			bae->bind_policy = $4;
 			list_add_tail(&bae->list, &conf.bind_acl);
 		}
 		;
@@ -745,4 +827,58 @@ prefixlistentry	: ADDR '/' prefixlen
 			list_add_tail(&p->list, &prefixes);
 		}
 		;
+		
+iflist		: iflistentry
+		| iflist ',' iflistentry
+		;
+
+iflistentry	: QSTRING
+		{
+			struct mn_addr *mncoa;
+			struct list_head *l;
+			int declared = 0, bid = 0;
+			
+			/* Check that this interface has been  
+			 * previously declared as "Interface" */
+			list_for_each(l, &conf.net_ifaces) {
+				struct net_iface *ni;
+				ni = list_entry(l, struct net_iface,
+						list);
+
+				if(if_nametoindex($1) == ni->ifindex) {
+					declared = 1;
+					bid = ni->mn_if_bid;
+					break;
+				}
+			}
+
+			if(!declared) {
+				uerror("Interface %s has not been "
+					"declared as Interface\n", $1);
+				return -1;
+			}
+
+			if(!bid) {
+				uerror("Interface %s has no BID "
+					"although you declared it in "
+					"IfMultipleCoA\n", $1);
+				return -1;
+			}
+			
+			mncoa = malloc(sizeof(struct mn_addr));
+			if (mncoa == NULL) {
+				fprintf(stderr, "%s: out of memory\n", 
+					__FUNCTION__);
+				return -1;
+			}
+			memset(mncoa, 0, sizeof(struct mn_addr));
+			if ((mncoa->iif = if_nametoindex($1)) == 0) {
+				fprintf(stderr, "%s: %s is not a valid "
+					"interface name\n", __FUNCTION__, $1);
+				return -1;
+			}
+			free($1);
+			list_add_tail(&mncoa->list, &hai.mcoa);
+		}
+		;
 %%
diff -r 82fcd4bea972 src/ha.c
--- a/src/ha.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/ha.c	Fri Jan 11 17:02:25 2008 +0100
@@ -59,6 +59,14 @@
 #include "xfrm.h"
 #include "ndisc.h"
 #include "prefix.h"
+
+#define HA_DEBUG_LEVEL 1
+
+#if HA_DEBUG_LEVEL >= 1
+#define MDBG dbg
+#else 
+#define MDBG(...) 
+#endif /* MDBG */
 
 static pthread_mutex_t bu_worker_mutex;
 static volatile unsigned long bu_worker_count = 0;
@@ -504,7 +512,7 @@ static int ha_vt_init(void)
 
 static void nemo_ha_del_mnp_routes(struct list_head *old_mnps,
 				   struct list_head *new_mnps,
-				   int ifindex, int all)
+				   int table, int ifindex, int all)
 {
 	struct list_head *list;
 	list_for_each(list, old_mnps) {
@@ -514,14 +522,22 @@ static void nemo_ha_del_mnp_routes(struc
 		    prefix_list_find(new_mnps, &p->ple_prefix, p->ple_plen))
 			continue;
 
-		route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_FWD,
+		route_del(ifindex, table, IP6_RT_PRIO_MIP6_FWD,
 			  NULL, 0, &p->ple_prefix, p->ple_plen, NULL);
+
+		/* MCoA
+		 * Delete the MNP fwmark rule
+		 */
+		if(table != RT6_TABLE_MIP6)
+			rule_del(NULL, table, IP6_RULE_PRIO_MIP6_FWD_FWM, 
+				 table, RTN_UNICAST, &in6addr_any, 0, 
+			 	 &p->ple_prefix, p->ple_plen, 0);
 	}
 }
 
 static int nemo_ha_add_mnp_routes(struct list_head *old_mnps,
 				  struct list_head *new_mnps,
-				  int ifindex, int all)
+				  int table, int ifindex, int all)
 {
 	struct list_head *list;
 	list_for_each(list, new_mnps) {
@@ -530,11 +546,89 @@ static int nemo_ha_add_mnp_routes(struct
 		if (!all &&
 		    prefix_list_find(old_mnps, &p->ple_prefix, p->ple_plen))
 			continue;
-		if (route_add(ifindex, RT6_TABLE_MIP6, RTPROT_MIP,
+		if (route_add(ifindex, table, RTPROT_MIP,
 			      0, IP6_RT_PRIO_MIP6_FWD,
 			      NULL, 0, &p->ple_prefix, p->ple_plen, NULL) < 0)
 			return -1;
+		/* MCoA
+		 * Add the MNP fwmark rule
+		 */
+		if(table != RT6_TABLE_MIP6
+		   && rule_add(NULL, table, IP6_RULE_PRIO_MIP6_FWD_FWM, 
+			       table, RTN_UNICAST, &in6addr_any, 0, 
+			       &p->ple_prefix, p->ple_plen, 0) < 0)
+			return -1;
 	}
+	return 0;
+}
+
+static int mcoa_ha_init_rt_table(struct bcentry *bce)
+{
+	/* MCoA TODO2
+	 * At the moment, the routing table number is the BID number. 
+	 * BID thus must be between BCE_TABLE_MIN and BCE_TABLE_MAX
+	 */
+	assert(bce);
+
+	/* If BID is not assigned, default table is taken */
+	if (!bce->bid)
+		return (bce->table = RT6_TABLE_MIP6);
+	
+	if (bce->bid >= BCE_TABLE_MIN && bce->bid <= BCE_TABLE_MAX)
+		return (bce->table = bce->bid);
+
+	return -1;
+}
+
+static int mcoa_ha_del_dflt_rules(struct bcentry *bce)
+{
+	struct list_head *list;
+	struct prefix_list_entry *pfx;
+	struct bcentry *best_bce = bce;
+	const struct in6_addr *peer_addr = &bce->peer_addr;
+	int8_t priority = 0;
+	
+	MDBG("Current BCE has the best priority (%d), "
+	     "erasing default rules\n", best_bce->priority);
+	
+	/* Erase the default HoA rule */
+	rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, 
+		RTN_UNICAST, &in6addr_any, 0, peer_addr, 128, 0);
+	
+	/* Erase the default MNP rules */
+	list_for_each(list, &bce->mob_net_prefixes) {
+		pfx = list_entry(list, struct prefix_list_entry, list);
+		rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, 
+			RTN_UNICAST, &in6addr_any, 0, 
+			&pfx->ple_prefix, pfx->ple_plen, 0);
+	}
+		
+	/* Create new rule with next best bce available */
+	priority = bce->priority;
+	bce->priority = 0;
+	bcache_iterate_in(&bce->peer_addr, bce_highest_priority, 
+			&best_bce);
+	bce->priority = priority;
+	
+	if(best_bce != bce) {
+		MDBG("Rules replaced with BCE prio %d\n", 
+			best_bce->priority);
+		rule_add(NULL, best_bce->table, 
+			IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, RTN_UNICAST, 
+			&in6addr_any, 0, peer_addr, 128, 0);
+				
+		/* Default prefix rules */		
+		list_for_each(list, &bce->mob_net_prefixes) {
+			pfx = list_entry(list, 
+				struct prefix_list_entry, list);
+		   	rule_add(NULL, best_bce->table, 
+				IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, 
+				RTN_UNICAST, &in6addr_any, 0, 
+				&pfx->ple_prefix, pfx->ple_plen, 0);
+		}
+	} else
+		MDBG("No other BCE available, no dflt rules added\n");
+
 	return 0;
 }
 
@@ -544,10 +638,49 @@ struct home_tnl_ops_parm {
 	struct list_head mob_net_prefixes;
 };
 
+static int mcoa_ha_add_dflt_rules(struct home_tnl_ops_parm *p)
+{
+	struct list_head *list;
+	struct bcentry *bce = p->bce;
+	const struct in6_addr *peer_addr = &bce->peer_addr;
+	
+	MDBG("BCE has the best priority (%d), adding default HoA "
+		"and prefix rules\n", bce->priority);
+	
+	/* Erase the default HoA rule */
+	rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, 
+		RTN_UNICAST, &in6addr_any, 0, peer_addr, 128, 0);
+	
+	/* Add the default HoA rule */
+	if (rule_add(NULL, bce->table,
+			IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, RTN_UNICAST,
+			&in6addr_any, 0, peer_addr, 128, 0) < 0)
+		return -1;
+	
+	/* Delete and add the default prefix rules */
+	list_for_each(list, &p->mob_net_prefixes) {
+		struct prefix_list_entry *pfx;
+		pfx = list_entry(list, struct prefix_list_entry, list);
+		
+		rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 
+			0, RTN_UNICAST, &in6addr_any, 0, 
+			&pfx->ple_prefix, pfx->ple_plen, 0);
+		
+		if (rule_add(NULL, bce->table, 
+				IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, 
+				RTN_UNICAST, &in6addr_any, 0, 
+				&pfx->ple_prefix, pfx->ple_plen, 0) < 0)
+			return -1;
+	}
+	
+	return 0;
+}
+
 static int home_tnl_del(int old_if, int new_if, struct home_tnl_ops_parm *p)
 {
 	const struct in6_addr *our_addr, *peer_addr, *coa, *old_coa;
 	struct list_head *mnp;
+	struct bcentry *best_bce = NULL;
 
 	assert(old_if);
 
@@ -565,12 +698,33 @@ static int home_tnl_del(int old_if, int 
 		ha_ipsec_tnl_pol_del(our_addr, peer_addr, p->bce->tunnel, mnp);
 	}
 	/* delete HoA route */
+	/* MCoA: The route is deleted in the table given by p->bce->table */
+	/* RKRK: for the moment the route is installed in the MAIN table. 
+	 * See home_tnl_add */
+	//MDBG("Deleting old default route in table %d\n", p->bce->table);
+	MDBG("Deleting old default route in table %d\n", RT6_TABLE_MAIN);
+	//route_del(old_if, p->bce->table,
 	route_del(old_if, RT6_TABLE_MAIN,
 		  IP6_RT_PRIO_MIP6_FWD, NULL, 0, peer_addr, 128, NULL);
-
+ 
 	/* delete MNP routes */
 	nemo_ha_del_mnp_routes(&p->bce->mob_net_prefixes,
-			       &p->mob_net_prefixes, old_if, 1);
+			       &p->mob_net_prefixes, p->bce->table, 
+			       old_if, 1);
+	/* MCoA 
+	 * Delete the HoA and fwmark rules, and if needed replace 
+	 * the default HoA and MNP rules with the best available one
+	 */
+	if(p->bce->table != RT6_TABLE_MIP6) {
+		rule_del(NULL, p->bce->table, IP6_RULE_PRIO_MIP6_FWD_FWM,
+			 p->bce->table, RTN_UNICAST, &in6addr_any, 0, 
+			 peer_addr, 128, 0);
+		best_bce = p->bce;
+		bcache_iterate_in(&p->bce->peer_addr, 
+				  bce_highest_priority, &best_bce);
+		if(best_bce == p->bce)
+			mcoa_ha_del_dflt_rules(p->bce);
+	}
 	/* update tunnel interface */
 	p->bce->tunnel = new_if;
 
@@ -581,6 +735,7 @@ static int home_tnl_add(int old_if, int 
 {
 	const struct in6_addr *our_addr, *peer_addr, *coa, *old_coa;
 	struct list_head *mnp;
+	struct bcentry *best_bce = NULL;
 
 	assert(new_if);
 
@@ -596,7 +751,8 @@ static int home_tnl_add(int old_if, int 
 
 	/* add MNP routes */
 	if (nemo_ha_add_mnp_routes(&p->bce->mob_net_prefixes,
-				   &p->mob_net_prefixes, new_if, 1) < 0) {
+				   &p->mob_net_prefixes, p->bce->table, 
+				   new_if, 1) < 0) {
 		if (p->bce->nemo_type == BCE_NEMO_EXPLICIT)
 			p->ba_status = IP6_MH_BAS_INVAL_PRFX;
 		else
@@ -604,11 +760,38 @@ static int home_tnl_add(int old_if, int 
 		goto err;
 	}
 	/* add HoA route */
+	/* MCoA: The route is added in the table given by p->bce->table */
+	/* RKRK: does not work. Route has to be added in the MAIN table.
+	 * Need to check why! */
+	//MDBG("Adding route in table %d via iface %d\n", p->bce->table, new_if);
+	MDBG("Adding route in table %d via iface %d\n", RT6_TABLE_MAIN, new_if);
+	//if (route_add(new_if, p->bce->table,
 	if (route_add(new_if, RT6_TABLE_MAIN,
 		      RTPROT_MIP, 0, IP6_RT_PRIO_MIP6_FWD,
 		      NULL, 0, peer_addr, 128, NULL) < 0) {
 		p->ba_status = IP6_MH_BAS_INSUFFICIENT;
 		goto err;
+	}
+	/* MCoA
+	 * Add the HoA and fwmark rules, and if needed replace 
+	 * the default HoA and MNP rules with the best available one
+	 */
+	if (p->bce->table != RT6_TABLE_MIP6) {
+		if (rule_add(NULL, p->bce->table,
+			     IP6_RULE_PRIO_MIP6_FWD_FWM, 
+			     p->bce->table, RTN_UNICAST,
+		     	     &in6addr_any, 0, peer_addr, 128, 0) < 0) {
+			p->ba_status = IP6_MH_BAS_INSUFFICIENT;
+			goto err;
+		}
+		best_bce = p->bce; 
+		bcache_iterate_in(&p->bce->peer_addr, 
+				bce_highest_priority, &best_bce);
+		if(best_bce == p->bce 
+		   && mcoa_ha_add_dflt_rules(p) < 0) {
+			p->ba_status = IP6_MH_BAS_INSUFFICIENT;
+			goto err;
+		}
 	}
 	/* add SP entry */	
 	if (conf.UseMnHaIPsec) {
@@ -664,9 +847,11 @@ static int home_tnl_chg(int old_if, int 
 			/* Do the same for routes */
 			nemo_ha_del_mnp_routes(&p->bce->mob_net_prefixes,
 					       &p->mob_net_prefixes,
+					       p->bce->table,
 					       old_if, 0);
 			if (nemo_ha_add_mnp_routes(&p->bce->mob_net_prefixes,
 						   &p->mob_net_prefixes,
+						   p->bce->table,
 						   new_if, 0) < 0) {
 				if (p->bce->nemo_type == BCE_NEMO_EXPLICIT)
 					p->ba_status = IP6_MH_BAS_INVAL_PRFX;
@@ -707,12 +892,22 @@ static int home_tnl_ops(int request, int
 
 static void home_cleanup(struct bcentry *bce)
 {
-	mpd_cancel_mpa(&bce->our_addr, &bce->peer_addr);
+	int nb_entry = mcoa_bce_count(&bce->peer_addr);
+
+	/* MCoA 
+	 * MPA is canceled if this is the last BCE entry
+	 */
+	if (nb_entry == 1)
+		mpd_cancel_mpa(&bce->our_addr, &bce->peer_addr);
 
 	if (bce->link > 0) {
-		route_del(bce->link, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_OUT,
+		route_del(bce->link, bce->table, IP6_RT_PRIO_MIP6_OUT,
 			  &bce->our_addr, 128, &bce->peer_addr, 128, NULL);
-		proxy_nd_stop(bce->link, &bce->peer_addr, bce->flags);
+		/* MCoA
+		 * Proxy ND is stopped if this is the last BCE entry
+		 */
+		if (nb_entry == 1)
+			proxy_nd_stop(bce->link, &bce->peer_addr, bce->flags);
 	}
 	if (bce->tunnel > 0) {
 		struct home_tnl_ops_parm p = {
@@ -724,6 +919,9 @@ static void home_cleanup(struct bcentry 
 	if (conf.UseMnHaIPsec) {
 		ha_mn_ipsec_pol_mod(&bce->our_addr, &bce->peer_addr);
 	}
+
+	/* MCoA: Freeing the BCE's MNPs list */
+	prefix_list_free(&bce->mob_net_prefixes);
 }
 
 
@@ -800,19 +998,28 @@ static void *ha_recv_bu_worker(void *var
 {
 	struct ha_recv_bu_args *arg = varg;
 	struct in6_addr_bundle out;
-	struct bcentry *bce;
+	struct bcentry *bce, *bce_exists;
 	struct timespec lft, tmp;
-	int iif, status, new, home_ifindex;
-	uint16_t bu_flags, seqno;
-	uint8_t ba_flags;
+	int iif, status, status_bid, new, home_ifindex;
+	int update_dflt_table;
+	uint16_t bu_flags, seqno, bid;
+	uint8_t ba_flags, bid_priority, bid_flags;
 	struct home_tnl_ops_parm p;
+	struct ip6_mh_opt_bid *bid_opt;	
 
 	pthread_dbg("thread started");
 restart:	
 	home_ifindex = 0;
 	new = 0;
+	bid = 0; 
+	bid_priority = 0;
+	bid_flags = 0;
+	update_dflt_table = 0;
 	ba_flags = 0;
-	lft = arg->lft;
+	status = IP6_MH_BAS_UNSPECIFIED;
+	status_bid = 0;
+	bce = NULL;
+ 	lft = arg->lft;
 	iif = arg->iif;
 	bu_flags = arg->bu->ip6mhbu_flags;
 	seqno = ntohs(arg->bu->ip6mhbu_seqno);
@@ -827,8 +1034,79 @@ restart:
 	else
 		out.bind_coa = NULL;
 	out.local_coa = NULL;
+	bid_opt = NULL;
 
-	bce = bcache_get(out.src, out.dst);
+	/* MCoA
+	 * We check if a BCE already exists for this HoA 
+	 * We take the first one available 
+	 */
+	bce_exists = bcache_get(out.src, out.dst, MCOA_NO_BID);	
+	if(bce_exists)
+		bcache_release_entry(bce_exists);
+
+	/* MCoA
+	 * we need the BID before looking at the BCE 
+	 */
+	bid_opt = mh_opt(&arg->bu->ip6mhbu_hdr, 
+			&arg->mh_opts, 
+			IP6_MHOPT_BID);
+
+	if (bid_opt && !default_mcoa_reg(out.dst)) {
+		/* MCoA: BAck without BID option will be sent */
+		MDBG("BUI option, but MCoA registration not allowed.\n");
+	} else if (bid_opt) {
+		MDBG("BUI option, and HA is configured for MCoA.\n");
+		bid = ntohs(bid_opt->ip6mobid_bid);
+		bid_priority = bid_opt->ip6mobid_priority;
+		bid_flags = ntohs(bid_opt->ip6mobid_reserved);
+		
+		if (!bid) {
+			MDBG("BID invalid, BU discarded.\n");
+			goto out;
+		}
+		
+		MDBG("BID = %d, Priority = %d\n", bid, bid_priority);
+		/* MCoA TODO1
+		 * if B flag is set but no altcoa option: 
+		 * BAck status 145.
+		 * To update according to latest draft.
+		 */
+		if (bid_flags & IP6_OPT_BID_BULK) {
+			/* MCoA TODO1
+		 	* At the moment we do not check the AltCoA option, 
+		 	* and just reject the BU if B flag is set
+		 	*/
+			MDBG("BULK Reg., but not supported yet.\n");
+				status = IP6_MH_BAS_MCOA_BULK_FAILED;
+				goto send_nack;
+		}
+		if (bce_exists && !bce_exists->bid) {
+			/* MCoA 
+			 * The BCE has a BID = 0, this means no MCoA 
+			 * are registered. We delete the entry and set 
+			 * BA status to 144. 
+			 * New registratration is done later
+			 */
+			MDBG("BID option but BCE has no BID\n");
+			MDBG("Delete BCE and register new one.\n");
+			bcache_delete(out.src, out.dst, MCOA_NO_BID);				
+			status_bid = IP6_MH_BAS_CONFLICT_BINDING;
+		}							
+	} else /* No BUI option in BU */ {
+		if(tsisset(lft) && bce_exists && bce_exists->bid) {
+			/* MCoA
+			 * A BCE exists with a BID != 0 but no BUI 
+			 * sub-option are set in the BU: we delete 
+			 * all existing entries for this HoA. New 
+			 * registration is done later.
+			 */
+			MDBG("No BID option but BCE has BID.\n");
+			MDBG("Delete all BCE and register new one.\n");
+			bcache_delete(out.src, out.dst, MCOA_NO_BID);
+		}
+	}
+	
+	bce = bcache_get(out.src, out.dst, bid);
 	if (bce) {
 		if (bce->type != BCE_NONCE_BLOCK) {
 			/* H-bit or R-bit mismatch, flags changed */
@@ -873,7 +1151,7 @@ restart:
 				goto send_nack;
 			}
 			/* else get rid of it */
-			bcache_delete(out.src, out.dst);
+			bcache_delete(out.src, out.dst, bid);			
 		}
 	} else if (!tsisset(lft)) {
 		status = IP6_MH_BAS_NOT_HA;
@@ -895,6 +1173,10 @@ restart:
 					   out.src, arg->bu, arg->len);
 	if (status >= IP6_MH_BAS_UNSPECIFIED)
 		goto send_nack;
+
+	if(status_bid)
+		status = status_bid;
+
 	/* lifetime may be further decreased by local policy */
 	if (conf.pmgr.max_binding_life(out.dst, out.bind_coa, out.src,
 				       arg->bu, arg->len, &lft, &tmp)) {
@@ -916,24 +1198,38 @@ restart:
 		bce->type = BCE_DAD;
 		bce->cleanup = NULL;
 		bce->link = home_ifindex;
-
+		bce->bid = bid;
+		bce->priority = bid_priority;
+		if(bce_exists)
+			bce->xfrm_create = 0;
+		else
+			bce->xfrm_create = 1;
+ 
+		/* MCoA: Initialize the routing table used by the BCE */
+		if(mcoa_ha_init_rt_table(bce) < 0) {
+ 			free(bce);
+ 			bce = NULL;
+ 			status = IP6_MH_BAS_INSUFFICIENT;
+ 			goto send_nack;
+ 		}
 		if (bcache_add_homereg(bce) < 0) {
 			free(bce);
 			bce = NULL;
 			status = IP6_MH_BAS_INSUFFICIENT;
 			goto send_nack;
 		}
-		if (!(arg->flags & HA_BU_F_SKIP_DAD)) {
+		/* MCoA: Do DAD only if no entries yet for this HoA */		
+		if (!(arg->flags & HA_BU_F_SKIP_DAD) && !bce_exists) {
 			/* Do DAD for home address */
 			if (ndisc_do_dad(home_ifindex, out.dst,
 					 bu_flags & IP6_MH_BU_LLOCAL) < 0) {
-				bcache_delete(out.src, out.dst);
+				bcache_delete(out.src, out.dst, bid);
 				bce = NULL;
 				status =  IP6_MH_BAS_DAD_FAILED;
 				goto send_nack;
 			}
 		}
-		bce = bcache_get(out.src, out.dst);
+		bce = bcache_get(out.src, out.dst, bid);
 		if (!bce) {
 			BUG("BCE deleted before DAD completed!");
 			status =  IP6_MH_BAS_UNSPECIFIED;
@@ -965,6 +1261,7 @@ restart:
 	bce->seqno = seqno;
 	bce->flags = bu_flags;
 	bce->lifetime = lft;
+
 	if (new) {
 		if (tunnel_add(out.src, out.bind_coa, 0, 
 			       home_tnl_ops, &p) < 0) {
@@ -979,7 +1276,8 @@ restart:
 
 		bce->cleanup = home_cleanup;
 
-		if (route_add(bce->link, RT6_TABLE_MIP6,
+		/* MCoA: The route is added in the table given by the BCE */
+		if (route_add(bce->link, bce->table,
 			      RTPROT_MIP, 0, IP6_RT_PRIO_MIP6_OUT,
 			      &bce->our_addr, 128, &bce->peer_addr, 128, 
 			      NULL) < 0) {
@@ -987,16 +1285,23 @@ restart:
 			goto send_nack;
 		}
 
-		if (proxy_nd_start(bce->link, out.dst, out.src,
-				   bu_flags) < 0) {
-			status = IP6_MH_BAS_INSUFFICIENT;
-			goto send_nack;
+		/* MCoA: Do Proxy ND only if no entries yet for this HoA */
+		if (!bce_exists) {
+			if (proxy_nd_start(bce->link, out.dst, 
+					   out.src, bu_flags) < 0) {
+				status = IP6_MH_BAS_INSUFFICIENT;
+				goto send_nack;
+			}
 		}
 		bce->type = BCE_HOMEREG;
 		bcache_complete_homereg(bce);
 	} else {
 		bce->old_coa = bce->coa;
 		bce->coa = *out.bind_coa;
+		if (tsisset(lft)) {
+			bce->bid = bid;
+			bce->priority = bid_priority;
+		}
 		if (tunnel_mod(bce->tunnel, out.src, out.bind_coa, 0,
 			       home_tnl_ops, &p) < 0) { 
 			if (p.ba_status >= IP6_MH_BAS_UNSPECIFIED)
@@ -1006,15 +1311,24 @@ restart:
 			goto send_nack;
 		}
 		/* Now update the MNP list in the BCE */
-		prefix_list_free(&bce->mob_net_prefixes);
-		list_splice(&p.mob_net_prefixes, &bce->mob_net_prefixes);
-
+		/* MCoA: the update is done only if this is not a 
+		 * deregistration BU, otherwise MNPs are erased from the BCE
+		 * (the dereg BU does not have any MNP option), thus there 
+		 * is a problem to update the fwmark MNP rules
+		 */
+		if (tsisset(lft)) {
+			prefix_list_free(&bce->mob_net_prefixes);
+			list_splice(&p.mob_net_prefixes, &bce->mob_net_prefixes);
+		}
 		bcache_update_expire(bce);
 	}
 	/* bce is always valid here */
 	bcache_release_entry(bce);
 	if (!tsisset(lft))
-		bcache_delete(out.src, out.dst);
+		/* MCoA: We delete one specific entry (bid is set) 
+		 * or all entries for this HoA (if bid is set to 0)
+		 */
+		bcache_delete(out.src, out.dst, bid);
 
 	if ((bu_flags & IP6_MH_BU_KEYM) && 
 	    conf.pmgr.use_keymgm(out.dst, out.src))
@@ -1035,12 +1349,16 @@ restart:
 		 * have a binding before sending this Binding Update,
 		 * discard the connections to the home address. */
 	}
- 	if (status < IP6_MH_BAS_UNSPECIFIED && bu_flags & IP6_MH_BU_MR)
+	if ((status < IP6_MH_BAS_UNSPECIFIED 
+	     || status == IP6_MH_BAS_CONFLICT_BINDING)
+	     && bu_flags & IP6_MH_BU_MR)
  		ba_flags |= IP6_MH_BA_MR;
 
 	if (!(arg->flags & HA_BU_F_SKIP_BA))
-		mh_send_ba(&out, status, ba_flags, seqno, &lft, NULL, iif);
-	if (new && tsisset(lft))
+		mh_send_ba(&out, status, ba_flags, seqno, &lft,
+			   bid, bid_priority, NULL, iif);
+	/* MCoA: Start MPA only if no entries yet for this HoA */	
+	if (!bce_exists && new && tsisset(lft))
 		mpd_start_mpa(&bce->our_addr, &bce->peer_addr);
 out:
 	free(arg);
@@ -1060,10 +1378,12 @@ send_nack:
 send_nack:
 	if (bce) {
 		bcache_release_entry(bce);
-		bcache_delete(out.src, out.dst);
+		bcache_delete(out.src, out.dst, bid);
 	}
 	if (!(arg->flags & HA_BU_F_SKIP_BA))
-		mh_send_ba_err(&out, status, 0, seqno, NULL, iif);
+		/* MCoA: Specify the BID and BID priority to the BAck */
+		mh_send_ba_err(&out, status, 0, seqno, bid, 
+			       bid_priority, NULL, iif);
 	goto out;
 }
 
@@ -1089,12 +1409,13 @@ int ha_recv_bu_main(const struct ip6_mh 
 
 	arg = malloc(sizeof(struct ha_recv_bu_args) + len);
 	if (!arg) {
-		if (bce_exists(out.src, out.dst))
-			bcache_delete(out.src, out.dst);
+		if (bce_exists(out.src, out.dst, MCOA_NO_BID))
+			bcache_delete(out.src, out.dst, MCOA_NO_BID);
 
 		if (!(arg->flags & HA_BU_F_SKIP_BA))
 			mh_send_ba_err(&out, IP6_MH_BAS_INSUFFICIENT, 0,
-				       ntohs(bu->ip6mhbu_seqno), NULL, iif);
+				       ntohs(bu->ip6mhbu_seqno), 0, 0,
+				       NULL, iif);
 		return -ENOMEM;
 	}
 	arg->src = *out.src;
@@ -1166,8 +1487,9 @@ int ha_init(void)
 		return -1;
 	if (mpd_ha_init() < 0)
 		return -1;
+	/* MCoA: Rule to the default table (RT6_TABLE_MIP6) */
 	if (rule_add(NULL, RT6_TABLE_MIP6,
-		     IP6_RULE_PRIO_MIP6_FWD, RTN_UNICAST,
+		     IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST,
 		     &in6addr_any, 0, &in6addr_any, 0, 0) < 0)
 		return -1;
 	icmp6_handler_reg(ND_ROUTER_ADVERT, &ha_ra_handler);
@@ -1185,7 +1507,7 @@ void ha_cleanup(void)
 	pthread_mutex_unlock(&bu_worker_mutex);
 	bcache_flush();
 	rule_del(NULL, RT6_TABLE_MIP6,
-		 IP6_RULE_PRIO_MIP6_FWD, RTN_UNICAST,
+		 IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST,
 		 &in6addr_any, 0, &in6addr_any, 0, 0);
 	mpd_ha_cleanup();
 	dhaad_ha_cleanup();
diff -r 82fcd4bea972 src/hash.c
--- a/src/hash.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/hash.c	Fri Jan 11 17:02:25 2008 +0100
@@ -47,6 +47,7 @@ struct hash_entry {
 struct hash_entry {
 	struct in6_addr *our_addr; /* pointer to our_addr in data */
 	struct in6_addr *peer_addr; /* pointer to peer_addr in data */
+	uint16_t *bid; /* pointer to bid in data, can be NULL */
 	struct hash_entry *next;
 	void *data;
 };
@@ -78,7 +79,7 @@ static uint32_t calc_hash1(int s, const 
 }
 
 static int match2(struct hash_entry *h, const struct in6_addr *our_addr, 
-		  const struct in6_addr *peer_addr)
+	          const struct in6_addr *peer_addr, uint16_t bid)
 {
 	assert(h);
 	assert(our_addr && h->our_addr);
@@ -89,9 +90,15 @@ static int match2(struct hash_entry *h, 
 	printf("h->peer %s\n", inet_ntop(AF_INET6, h->peer_addr, s, sizeof(s)));
 	printf("our_addr %s\n", inet_ntop(AF_INET6, our_addr, s, sizeof(s)));
 	printf("h->our_addr %s\n", inet_ntop(AF_INET6, h->our_addr, s, sizeof(s)));
+	printf("bid %d\n", bid);
+	if(h->bid != NULL)
+		printf("h->bid %d\n", *(h->bid));
+	else
+		printf("h->bid is NULL\n");
 #endif
 	if (IN6_ARE_ADDR_EQUAL(h->peer_addr, peer_addr) &&
-	    IN6_ARE_ADDR_EQUAL(h->our_addr, our_addr)) {
+	    IN6_ARE_ADDR_EQUAL(h->our_addr, our_addr) && 
+	    (bid == 0 || (h->bid != NULL && bid == *(h->bid)))) {
 		return 1;
 	}
 	return 0;
@@ -99,7 +106,7 @@ static int match2(struct hash_entry *h, 
 }
 
 static int match1(struct hash_entry *h, const struct in6_addr *dummy,
-		  const struct in6_addr *peer_addr)
+		  const struct in6_addr *peer_addr, uint16_t bid)
 {
 	assert(h);
 	assert(h->peer_addr && peer_addr);
@@ -107,8 +114,14 @@ static int match1(struct hash_entry *h, 
 	char s[INET6_ADDRSTRLEN];
 	printf("match1: peer %s\n", inet_ntop(AF_INET6, peer_addr, s, sizeof(s)));
 	printf("h->peer %s\n", inet_ntop(AF_INET6, h->peer_addr, s, sizeof(s)));
+	printf("bid %d\n", bid);
+	if(h->bid != NULL)
+		printf("h->bid %d\n", *(h->bid));
+	else
+		printf("h->bid is NULL\n");
 #endif
-	if (!IN6_ARE_ADDR_EQUAL(h->peer_addr, peer_addr))
+	if (!IN6_ARE_ADDR_EQUAL(h->peer_addr, peer_addr) ||
+	    (bid !=0 && h->bid != NULL && bid != *(h->bid)))
 		return 0;
 	return 1;
 }
@@ -155,7 +168,8 @@ void hash_cleanup(struct hash *h)
 
 void *hash_get(const struct hash *h,
 	       const struct in6_addr *our_addr, 
-	       const struct in6_addr *peer_addr)
+	       const struct in6_addr *peer_addr,
+	       uint16_t bid)
 {
 	struct hash_entry *hptr = NULL;
 	
@@ -165,7 +179,7 @@ void *hash_get(const struct hash *h,
 	hptr = h->hash_buckets[h->calc_hash(h->buckets, our_addr , peer_addr)];
 
 	while(hptr) {
-		if (h->match(hptr, our_addr, peer_addr))
+		if (h->match(hptr, our_addr, peer_addr, bid))
 			return hptr->data;
 		hptr = hptr->next;
 	}
@@ -173,7 +187,8 @@ void *hash_get(const struct hash *h,
 }
 
 int hash_add(const struct hash *h, void *data,
-	     struct in6_addr *our_addr, struct in6_addr *peer_addr)
+	     struct in6_addr *our_addr, struct in6_addr *peer_addr,
+	     uint16_t *bid)
 {
 	struct hash_entry *hptr = NULL, *new;
 	uint32_t hash_ind;
@@ -185,17 +200,19 @@ int hash_add(const struct hash *h, void 
 	assert(peer_addr);
 
 	if ((new = (struct hash_entry *)malloc(sizeof(struct hash_entry))) == NULL) {
-		dbg("out of memeory\n");
+		dbg("out of memory\n");
 		return -ENOMEM;
 	}
 	new->our_addr = our_addr;
 	new->peer_addr = peer_addr;
 	new->data = data;
+	new->bid = bid;
 	hash_ind  = h->calc_hash(h->buckets, our_addr, peer_addr);
 	hptr = h->hash_buckets[hash_ind];
 	if (hptr) {
 		while (hptr->next){
-			if (h->match(hptr, our_addr, peer_addr)) {
+			if (h->match(hptr, our_addr, peer_addr, 
+				     (bid) ? (*bid) : 0)) {
 				free(new);
 				return -EEXIST;
 			}
@@ -231,9 +248,35 @@ int hash_iterate(const struct hash *h, i
 	return 0;
 }
 
+int hash_iterate_in(const struct hash *h, 
+		    const struct in6_addr *peer_addr, 
+		    int (*func)(void *, void *), 
+		    void *arg)
+{
+	int err = 0;
+	int i;
+	struct hash_entry *hptr, *nptr;
+
+	assert(func);
+	
+	for (i=0; i < h->buckets; i++) {
+		if((hptr = h->hash_buckets[i]) != NULL) {
+			while(hptr) {
+				nptr = hptr->next;
+				if (IN6_ARE_ADDR_EQUAL(hptr->peer_addr, peer_addr)
+				    && (err = func(hptr->data, arg)))
+					return err;
+				hptr = nptr;
+			}
+		}
+	}
+	return 0;
+}
+
 void hash_delete(const struct hash *h,
 		 const struct in6_addr *our_addr,
-		 const struct in6_addr *peer_addr)
+		 const struct in6_addr *peer_addr,
+		 uint16_t bid)
 {
 	struct hash_entry *hptr, *pptr, *head;
 	int hash_ind;
@@ -247,7 +290,7 @@ void hash_delete(const struct hash *h,
 	
 	pptr = hptr = head;
 	while (hptr) {
-		if (h->match(hptr, our_addr, peer_addr)){
+		if (h->match(hptr, our_addr, peer_addr, bid)){
 			if (hptr != head) 
 				pptr->next = hptr->next;
 			else {
diff -r 82fcd4bea972 src/hash.h
--- a/src/hash.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/hash.h	Fri Jan 11 17:02:25 2008 +0100
@@ -20,7 +20,9 @@ struct hash {
 	int buckets;
 	int type; 
 	uint32_t (*calc_hash)(int, const struct in6_addr *, const struct in6_addr *);
-	int (*match)(struct hash_entry *, const struct in6_addr *, const struct in6_addr *);
+	int (*match)(struct hash_entry *, const struct in6_addr *, 
+		     const struct in6_addr *, uint16_t);
+
 	struct hash_entry **hash_buckets;
 };
 
@@ -37,19 +39,24 @@ int hash_init(struct hash *h, int type, 
  */
 void hash_cleanup(struct hash *h);
 /*
- *  Get data stored in a hash entry based on one or two addresses, depending on type of hash
+ * Get data stored in a hash entry based on one or two addresses and the bid,
+ * depending on type of hash. bid=0 means we do not use bid as a search key.
  */
 void *hash_get(const struct hash *h,
 	       const struct in6_addr *our_addr, 
-	       const struct in6_addr *peer_addr) ;
+	       const struct in6_addr *peer_addr,
+	       uint16_t bid);
 
 /* Iterate through the hash and call func for every entry */
 
 int hash_iterate(const struct hash *h, int (*func)(void *data, void *arg), void *arg);
+int hash_iterate_in(const struct hash *h, const struct in6_addr *peer_addr, 
+		    int (*func)(void *, void *), void *arg);
 
 /* Add a hash entry to hash */		 
 int hash_add(const struct hash *h, void  *data,
-	     struct in6_addr *our_addr, struct in6_addr *peer_addr);
+	     struct in6_addr *our_addr, struct in6_addr *peer_addr,
+	     uint16_t *bid);
 
 /*
  *Delete entry from hash
@@ -57,6 +64,7 @@ int hash_add(const struct hash *h, void 
 
 void hash_delete(const struct hash *h,
 		 const struct in6_addr *our_addr, 
-		 const struct in6_addr *peer_addr);
+		 const struct in6_addr *peer_addr,
+		 uint16_t bid);
 
 #endif /* _HASH_H */
diff -r 82fcd4bea972 src/mh.c
--- a/src/mh.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/mh.c	Fri Jan 11 17:02:25 2008 +0100
@@ -77,6 +77,7 @@ int mh_opts_dup_ok[] = {
 	0, /* Nonce Index */
 	0, /* Binding Auth Data */
 	1, /* Mobile Network Prefix */
+	1  /* Binding Unique Identifier */
 };
 
 #define __MH_SENTINEL (IP6_MH_TYPE_MAX + 1)
@@ -443,6 +444,41 @@ int mh_create_opt_mob_net_prefix(struct 
 	return 0;
 }
 
+/* MCoA
+ * mh_create_opt_bid - Create a Binding Unique Identifier Option
+ * MCoA TODO1: need to be adapted for BULK registration
+ */
+int mh_create_opt_bid(struct iovec *iov, uint16_t bid, 
+		      uint8_t priority, uint8_t bid_flags)
+{
+	int optlen = sizeof(struct ip6_mh_opt_bid);
+	struct ip6_mh_opt_bid *opt_bid;
+	uint8_t *data;
+
+	iov->iov_base = malloc(optlen);
+	iov->iov_len = optlen;
+
+	if (iov->iov_base == NULL)
+		return -ENOMEM;
+
+	memset(iov->iov_base, 0, iov->iov_len);
+	data = (uint8_t *)iov->iov_base;
+	
+	opt_bid = (struct ip6_mh_opt_bid *)data;
+
+	opt_bid->ip6mobid_type = IP6_MHOPT_BID;
+	opt_bid->ip6mobid_len = 4;
+	opt_bid->ip6mobid_bid = htons(bid);
+	opt_bid->ip6mobid_priority = priority;
+	opt_bid->ip6mobid_reserved |= bid_flags;
+
+	data += sizeof(struct ip6_mh_opt_bid);
+	MDBG("BUI sub-option created with BID = %d and priority %d\n", 
+	     bid, priority);
+
+	return 0;
+}
+
 static size_t mh_length(struct iovec *vec, int count)
 {
 	size_t len = 0;
@@ -487,6 +523,9 @@ static int mh_try_pad(const struct iovec
 		case IP6_MHOPT_MOB_NET_PRFX:
 			pad = optpad(8, 4, len); /* 8n+4 */
 			break;
+		case IP6_MHOPT_BID:
+			pad = optpad(2, 0, len); /* 2n */
+			break;
 		}
 		if (pad > 0) {
 			create_opt_pad(&out[n++], pad);
@@ -497,9 +536,10 @@ static int mh_try_pad(const struct iovec
 		out[n].iov_base = in[m].iov_base;
 		n++;
 	}
-	if (count == 1) {
+	if (count == 1 || len%8 != 0) {
 		pad = optpad(8, 0, len);
 		create_opt_pad(&out[n++], pad);
+		MDBG("Added %d bytes for padding\n", pad);
 	}
 
 	return n;
@@ -984,11 +1024,14 @@ void mh_send_brr(struct in6_addr *mn_add
 
 void mh_send_ba(const struct in6_addr_bundle *addrs, uint8_t status,
 		uint8_t flags, uint16_t sequence, 
-		const struct timespec *lifetime, const uint8_t *key, int iif)
+		const struct timespec *lifetime, 
+		const uint16_t bid,
+		const uint8_t priority,
+		const uint8_t *key, int iif)
 {
 	int iovlen = 1;
 	struct ip6_mh_binding_ack *ba;
-	struct iovec mh_vec[2];
+	struct iovec mh_vec[3];
 
 	MDBG("status %d\n", status);
 
@@ -1012,6 +1055,9 @@ void mh_send_ba(const struct in6_addr_bu
 	}
 	if (key)
 		mh_create_opt_auth_data(&mh_vec[iovlen++]);
+	/* MCoA: Create Binding Unique Identifier Option if needed */
+	if (bid) 
+		mh_create_opt_bid(&mh_vec[iovlen++], bid, priority, 0);
 	mh_send(addrs, mh_vec, iovlen, key, iif);
 	free_iov_data(mh_vec, iovlen);
 }
@@ -1058,8 +1104,11 @@ int mh_bu_parse(struct ip6_mh_binding_up
 			return -1;
 		if (!IN6_ARE_ADDR_EQUAL(out_addrs->bind_coa, peer_addr)) {
 			/* check that there is no circular reference */
-			if (bce_exists(our_addr, out_addrs->bind_coa))
+			if (bce_exists(our_addr, out_addrs->bind_coa, 
+				       MCOA_NO_BID)) {
+				MDBG("Circular reference found in BC\n");
 				return -1;
+			}
 			tssetsec(*lifetime, ntohs(bu->ip6mhbu_lifetime) << 2);
 		}
 	}
diff -r 82fcd4bea972 src/mh.h
--- a/src/mh.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/mh.h	Fri Jan 11 17:02:25 2008 +0100
@@ -10,7 +10,7 @@
 
 /* If new types or options appear, these should be updated. */
 #define IP6_MH_TYPE_MAX IP6_MH_TYPE_BERROR
-#define IP6_MHOPT_MAX IP6_MHOPT_MOB_NET_PRFX
+#define IP6_MHOPT_MAX IP6_MHOPT_BID
 
 struct in6_addr_bundle {
 	struct in6_addr *src;
@@ -43,14 +43,20 @@ void mh_send_ba(const struct in6_addr_bu
 void mh_send_ba(const struct in6_addr_bundle *addrs, uint8_t status, 
 		uint8_t flags, uint16_t sequence, 
 		const struct timespec *lifetime,
+		const uint16_t bid,
+		const uint8_t priority,
 		const uint8_t *key, int iif);
 
 static inline void mh_send_ba_err(const struct in6_addr_bundle *addrs,
 				  uint8_t status, uint8_t flags,
-				  uint16_t seqno, const uint8_t *key, int iif)
+				  uint16_t seqno, 
+				  const uint16_t bid,
+				  const uint8_t priority,
+				  const uint8_t *key, int iif)
 {
 	struct timespec zero = { 0, 0 };
-	mh_send_ba(addrs, status, flags, seqno, &zero, key, iif);
+	mh_send_ba(addrs, status, flags, seqno, &zero, 
+		   bid, priority, key, iif);
 }
 
 void mh_send_be(struct in6_addr *dst,
@@ -78,6 +84,9 @@ struct list_head;
 
 int mh_create_opt_mob_net_prefix(struct iovec *iov, int mnp_count,
 				 struct list_head *mnps);
+
+int mh_create_opt_bid(struct iovec *iov, uint16_t bid, uint8_t priority, 
+		      uint8_t bid_flags);
 
 static inline void *mh_opt(const struct ip6_mh *mh,
 			   const struct mh_options *mh_opts, uint8_t type)
diff -r 82fcd4bea972 src/mipv6.h
--- a/src/mipv6.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/mipv6.h	Fri Jan 11 17:02:25 2008 +0100
@@ -23,6 +23,7 @@
 /* Constants below have no explicit names in the spec. */
 
 #define MAX_BINDING_LIFETIME     (0xffff << 2) /* seconds */  
+#define MCOA_NO_BID		 0   /* BID is null */
 
 /* Maximum time for a binding to be unused for CN to still send a BRR
  * before the binding expires */
diff -r 82fcd4bea972 src/mn.c
--- a/src/mn.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/mn.c	Fri Jan 11 17:02:25 2008 +0100
@@ -35,6 +35,8 @@
 #include <errno.h>
 #include <time.h>
 #include <unistd.h> 
+#include <sys/types.h>
+#include <signal.h>
 
 #include <netinet/icmp6.h>
 #include <netinet/ip6mh.h>
@@ -94,10 +96,44 @@ const struct timespec min_valid_bu_lifet
 const struct timespec min_valid_bu_lifetime_ts =
 { MIN_VALID_BU_LIFETIME, 0 };
 
+struct flag_hoa_args {
+	struct home_addr_info *target;
+	struct bulentry *bule;
+	int flag;
+};
+
 static int pending_bas = 0;
 
 static void mn_send_home_bu(struct home_addr_info *hai);
 static int mn_ext_tunnel_ops(int request, int old_if, int new_if, void *data);
+static int flag_hoa(struct ifaddrmsg *ifa, struct rtattr *rta_tb[], void *arg);
+static int mcoa_iface_rules_add(int iif, struct home_addr_info *hai);
+static struct bulentry* mcoa_iface_rules_del(int iif, 
+			struct home_addr_info *hai,
+			int replace);
+static void mcoa_mn_rules_del(struct home_addr_info *hinfo);
+static int nemo_mr_rules_add(struct home_addr_info *hinfo);
+static void nemo_mr_rules_del(struct home_addr_info *hinfo);
+
+static int mcoa_mn_init_rt_table(struct bulentry *bule)
+{
+	/* MCoA TODO2
+	 * At the moment, the routing table number 
+	 * is the BID number. BID must be between 
+	 * BCE_TABLE_MIN and BCE_TABLE_MAX
+	 */
+	assert(bule);
+
+	/* If BID is not assigned, default table is taken */
+	if (!bule->bid)
+		return (bule->table = RT6_TABLE_MIP6);
+	
+	if (bule->bid >= BULE_TABLE_MIN && bule->bid <= BULE_TABLE_MAX)
+		return (bule->table = bule->bid);
+
+	MDBG("Routing Table index is out of range");
+	return -1;
+}
 
 static int mn_block_rule_del(struct home_addr_info *hai)
 {
@@ -108,9 +144,9 @@ static int mn_block_rule_del(struct home
 		return ret;
 	}
 
-	if ((ret = rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, RTN_BLACKHOLE,
-		     &hai->hoa.addr, 128, &in6addr_any, 0,
-		     FIB_RULE_FIND_SADDR)) < 0)
+	if ((ret = rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, 0, 
+		     RTN_BLACKHOLE, &hai->hoa.addr, 128, &in6addr_any, 
+		     0, FIB_RULE_FIND_SADDR)) < 0)
 		MDBG("failed to delete blackhole rule.\n");
 	else
 		hai->home_block &= ~HOME_ADDR_RULE_BLOCK;
@@ -126,9 +162,9 @@ static int mn_block_rule_add(struct home
 		MDBG("blackhole is already set.\n");
 		return ret;
 	}
-	if ((ret = rule_add(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, RTN_BLACKHOLE,
-		     &hai->hoa.addr, 128, &in6addr_any, 0,
-		     FIB_RULE_FIND_SADDR)) < 0)
+ 	if ((ret = rule_add(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK, 
+ 			    0, RTN_BLACKHOLE, &hai->hoa.addr, 128, 
+ 			    &in6addr_any, 0, FIB_RULE_FIND_SADDR)) < 0)
 		MDBG("failed to add blackhole rule.\n");
 	else
 		hai->home_block |= HOME_ADDR_RULE_BLOCK;
@@ -156,7 +192,10 @@ static void bule_invalidate(struct bulen
 	struct home_addr_info *hai = e->home;
 	struct in6_addr hoa, peer_addr;
 	int type = e->type;
+	int rules = e->rules;
+	int if_coa = e->if_coa;
 	uint16_t flags = e->flags;
+	uint16_t bid = e->bid;
 
 	if (type != BUL_ENTRY) 
 		return;
@@ -173,7 +212,7 @@ static void bule_invalidate(struct bulen
 	if (hai->at_home)
 		return;
 
-	e = create_bule(&hoa, &peer_addr);
+	e = create_bule(&hoa, &peer_addr, hai->reg_mcoa?bid:0);
 
 	if (e == NULL)
 		return;
@@ -185,6 +224,19 @@ static void bule_invalidate(struct bulen
 	e->lifetime = NON_MIP_CN_LTIME_TS;
 	e->delay = NON_MIP_CN_LTIME_TS;
 	e->callback = bul_expire;
+	e->priority = get_prio_from_bid(e->bid);
+	e->mcoa_dereg = 0;
+	/* MCoA: No tunnels are linked to this BULE anymore */
+	e->if_tunnel = 0;
+	/* MCoA: We prevent MIPL to install rules for this entry */
+	e->rules = 1;
+	mcoa_mn_init_rt_table(e);
+
+	/* MCoA: Erase rules corresponding to this BULE */
+	/* TODO2: this should be done when bul is deleted */
+	if(hai->reg_mcoa && rules)
+		mcoa_iface_rules_del(if_coa, hai, 1);
+
 	if (bul_add(e) < 0)
 		bul_delete(e);
 }
@@ -279,7 +331,10 @@ static void mn_recv_param_prob(const str
 	pthread_rwlock_wrlock(&mn_lock);
 	if (mn_rr_error_check(laddr, raddr, &addr))
 		laddr = &addr;
-	e = bul_get(NULL, laddr, raddr);
+	/* MCoA TODO1
+	 * All bule with same laddr, raddr should be invalidated
+	 */
+	e = bul_get(NULL, laddr, raddr, get_bid_from_ifindex(iif));
 	if (e != NULL) { 
 		if (e->flags & IP6_MH_BU_HOME) {
 			if (!conf.UseMnHaIPsec &&
@@ -328,6 +383,19 @@ static int mn_send_bu_msg(struct bulentr
 	}
 	if (bule->flags & IP6_MH_BU_HOME) {
 		struct home_addr_info *hai = bule->home;
+		/* MCoA TODO1: to update according to latest draft
+		 * Binding Unique Identifier Option 
+		 * For deregistration, BUI option is sent only 
+		 * when some specific entry needs to be deleted.
+		 * If all entries are deleted, no need to send BUI
+		 */
+		if ((bu->ip6mhbu_lifetime || !bule->mcoa_dereg)
+		    && hai->reg_mcoa && bule->bid 
+		    && mh_create_opt_bid(&iov[iov_ind++], bule->bid, 
+					 bule->priority, 0) < 0) {
+			free_iov_data(iov, iov_ind);
+			return -ENOMEM;				
+		}
 		if (bule->flags & IP6_MH_BU_MR && bu->ip6mhbu_lifetime &&
 		    bule->home->mnp_count > 0 && conf.MobRtrUseExplicitMode &&
 		    mh_create_opt_mob_net_prefix(&iov[iov_ind++],
@@ -353,7 +421,29 @@ static int mn_send_bu_msg(struct bulentr
 	addrs.remote_coa = NULL;
 	addrs.bind_coa = &bule->coa;
 
+	/* MCoA
+	 * before sending BU, if !bule->rules then
+	 * add rule from HoA to HA in table bule->table
+	 * This to be able to send a BU if no rules are in 
+	 * the RPDB (eg. because of the Reliability option)
+	 */
+	if(bule->home->reg_mcoa && !bule->rules)
+		/* MCoA: Rule for HoA to HA from the MN */
+		rule_add(NULL, bule->table, 
+			IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, 0, RTN_UNICAST, 
+			&bule->home->hoa.addr, 128, &bule->peer_addr, 128, 
+			FIB_RULE_FIND_SADDR);
+
 	ret = mh_send(&addrs, iov, iov_ind, bind_key, bule->if_coa);
+	
+	/* MCoA 
+	 * Delete the previously installed rule 
+	 */
+	if(bule->home->reg_mcoa && !bule->rules)
+		rule_del(NULL, bule->table, 
+			IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, 0, RTN_UNICAST, 
+			&bule->home->hoa.addr, 128, &bule->peer_addr, 128, 
+			FIB_RULE_FIND_SADDR); 
 
 	if (ret <= 0)
 		MDBG("mh_send failed  ret: %d\n", ret);
@@ -363,15 +453,35 @@ static int mn_send_bu_msg(struct bulentr
 	return ret;
 }
 
+struct mn_addr *mcoa_get_current_coa(struct home_addr_info *hai, int iif)
+{
+	struct list_head *l;
+
+	if(!hai->reg_mcoa)
+		return hai->current_coa;
+
+	list_for_each(l, &hai->mcoa) {
+		struct mn_addr *coa_entry;
+		coa_entry = list_entry(l, struct mn_addr, list);
+		if (coa_entry->iif == iif) 
+			return coa_entry;
+	}
+
+	return NULL;
+}
+
 static int mn_get_home_lifetime(struct home_addr_info *hai,
-				struct timespec *lifetime, int dereg)
+				struct timespec *lifetime, 
+				int iif, int dereg)
 {
 	if (!hai->at_home && !dereg) {
-		struct mn_addr *coa = &hai->primary_coa;
+		/* MCoA: Pick up the CoA from the correct iface */
+		struct mn_addr *coa = mcoa_get_current_coa(hai, iif);
 		struct timespec now;
 		unsigned long coa_lft;
 		unsigned long hoa_lft;
 
+		assert(coa);
 		clock_gettime(CLOCK_REALTIME, &now);		
 
 		coa_lft = mpd_curr_lft(&now, &coa->timestamp,
@@ -403,16 +513,20 @@ static int mn_get_ro_lifetime(struct hom
 	if (!hai->at_home && !dereg) {
 		struct bulentry *e;
 
-		e = bul_get(hai, NULL, &hai->ha_addr);
+		e = bul_get(hai, NULL, &hai->ha_addr, 
+			    hai->reg_mcoa?
+			    get_bid_from_ifindex(hai->current_coa->iif)
+			    :MCOA_NO_BID);
 
 		if (e == NULL || !(e->flags & IP6_MH_BU_HOME)) {
 			MDBG("No valid home registration");
 		} else {
 			struct timespec now;
-			struct mn_addr *coa = &hai->primary_coa;
+			struct mn_addr *coa = hai->current_coa;
 			unsigned long coa_lft;
 			unsigned long home_lft;
 
+			assert(coa);
 			clock_gettime(CLOCK_REALTIME, &now);		
 
 			coa_lft = mpd_curr_lft(&now, &coa->timestamp,
@@ -432,6 +546,21 @@ static int mn_get_ro_lifetime(struct hom
 	return 1;
 }
 
+/* MCoA
+ * Deregister a specific bule
+ */
+static int mn_dereg_bule(struct bulentry *bule)
+{
+	if (bule->type != NON_MIP_CN_ENTRY) {
+		bule->seq++;
+		bule->mcoa_dereg = 0;
+		tsclear(bule->lifetime);
+		mn_send_bu_msg(bule);
+	}
+	bul_delete(bule);
+	return 0;
+}
+
 static int mn_dereg(void *vbule, void *arg)
 {
 	struct bulentry *bule = vbule;
@@ -445,7 +574,20 @@ static int mn_dereg(void *vbule, void *a
 		}
 		bule->seq++;
 		tsclear(bule->lifetime);
-		mn_send_bu_msg(bule);
+		/* MCoA
+	 	 * When a MN wants to deregister Multiple CoA,
+	 	 * one BU is enough to deregister all the CoA
+		 */
+		if(bule->home->reg_mcoa && bule->mcoa_dereg) {	
+			mn_send_bu_msg(bule);
+			/* MCoA
+			 * Flag the other entries that match the
+			 * peer address (to avoid sending multiple
+			 * BUs)
+			 */
+			mcoa_bule_dereg(bule, 0);
+		} else if (!bule->home->reg_mcoa)
+			mn_send_bu_msg(bule);
 	}
 	bul_delete(bule);
 	return 0;
@@ -486,8 +628,9 @@ static int bu_lft_check(struct bulentry 
 	int dereg;
 
 	if (bule->flags & IP6_MH_BU_HOME)
-		dereg = mn_get_home_lifetime(bule->home, &bule->lifetime, 0);
-	else
+		dereg = mn_get_home_lifetime(bule->home, &bule->lifetime, 
+					     bule->if_coa, 0);
+ 	else
 		dereg = mn_get_ro_lifetime(bule->home, &bule->lifetime,
 					   bule->dereg);
 
@@ -544,6 +687,7 @@ static void bu_refresh(struct tq_elem *t
 	if (!task_interrupted()) {
 		struct bulentry *bule = tq_data(tqe, struct bulentry, tqe);
 		int expired;
+		int iif = bule->if_coa;
 		MDBG("Bul refresh type: %d\n", bule->type);
 
 		clock_gettime(CLOCK_REALTIME, &bule->lastsent);
@@ -554,6 +698,10 @@ static void bu_refresh(struct tq_elem *t
 
 		bule->seq++;
 		bule->callback = bu_resend;
+		/* MCoA : Check for interface reliability */
+		if(bule->home->reg_mcoa 
+		   && get_reliable_from_ifindex(iif) == 0)
+			mcoa_iface_rules_del(iif, bule->home, 1);
 		pre_bu_bul_update(bule);
 		mn_send_bu_msg(bule);
 
@@ -626,14 +774,53 @@ static int mv_hoa(struct ifaddrmsg *ifa,
 	return 0;
 }
 
-int nemo_mr_tnl_routes_add(struct home_addr_info *hai, int ifindex)
+/*
+ * Copy home address between two interfaces
+ */
+static int cp_hoa(struct ifaddrmsg *ifa, struct rtattr *rta_tb[], void *arg)
+{
+	struct mv_hoa_args *mha = arg;
+	struct home_addr_info *hai = mha->target;
+	struct mn_addr *hoa = &hai->hoa;
+	int err;
+	struct timespec now;
+	uint32_t preferred = PREFIX_LIFETIME_INFINITE;
+	uint32_t valid = PREFIX_LIFETIME_INFINITE;
+	int plen = (mha->if_next == hai->if_tunnel ? 128 : hai->plen);
+
+	clock_gettime(CLOCK_REALTIME, &now);
+
+	if (hai->lladdr_comp && rta_tb[IFA_CACHEINFO] != NULL) {
+		struct ifa_cacheinfo *ci = RTA_DATA(rta_tb[IFA_CACHEINFO]);
+		mn_update_hoa_lifetime(hoa, &now, 
+				       ci->ifa_valid, ci->ifa_prefered);
+		valid = ci->ifa_valid;
+		preferred = ci->ifa_prefered;
+	}
+	if (mha->if_next == ifa->ifa_index)
+		return 0;
+
+	MDBG("Copy HoA %x:%x:%x:%x:%x:%x:%x:%x/%d from iface %d to %d\n",
+	     NIP6ADDR(&hoa->addr), plen, ifa->ifa_index, mha->if_next);
+
+	err = addr_add(&hoa->addr, plen,
+		       ifa->ifa_flags|IFA_F_HOMEADDRESS,
+		       ifa->ifa_scope, mha->if_next, preferred, valid);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+int nemo_mr_tnl_routes_add(struct home_addr_info *hai,
+			   int ifindex, int rtable)
 {
 	struct list_head *l;
 	struct prefix_list_entry *pe;
 	list_for_each(l, &hai->mob_net_prefixes) {
 		struct prefix_list_entry *p;
 		p = list_entry(l, struct prefix_list_entry, list);
-		if (route_add(ifindex, RT6_TABLE_MIP6, RTPROT_MIP,
+ 		if (route_add(ifindex, rtable, RTPROT_MIP,
 			      0, IP6_RT_PRIO_MIP6_FWD,
 			      &p->ple_prefix, p->ple_plen,
 			      &in6addr_any, 0, NULL) < 0) {
@@ -646,7 +833,7 @@ undo:
 	list_for_each(l, &hai->mob_net_prefixes) {
 		struct prefix_list_entry *p;
 		p = list_entry(l, struct prefix_list_entry, list);
-		route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_FWD,
+ 		route_del(ifindex, rtable, IP6_RT_PRIO_MIP6_FWD, 
 			  &p->ple_prefix, p->ple_plen, &in6addr_any, 0, NULL);
 		if (p == pe)
 			break;
@@ -654,44 +841,47 @@ undo:
 	return -1;
 }
 
-static int mn_tnl_state_add(struct home_addr_info *hai, int ifindex, int all)
+static int mn_tnl_state_add(struct home_addr_info *hai, 
+			    int ifindex, int all, int rtable)
 {
 	int err = 0;
 	if (hai->home_reg_status != HOME_REG_NONE) {
 		if ((err = mn_ro_pol_add(hai, ifindex, all)) < 0)
 			return err;
-		if ((err = route_add(ifindex, RT6_TABLE_MIP6, RTPROT_MIP, 0,
+		if ((err = route_add(ifindex, rtable, RTPROT_MIP, 0,
 				     IP6_RT_PRIO_MIP6_OUT, &in6addr_any, 0,
 				     &in6addr_any, 0, NULL)) < 0) {
 			mn_ro_pol_del(hai, ifindex, all);
 		}
 	}
 	if (hai->mob_rtr &&
-	    (err = nemo_mr_tnl_routes_add(hai, ifindex)) < 0) {
-		route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_OUT,
+	    (err = nemo_mr_tnl_routes_add(hai, ifindex, rtable)) < 0) {
+		route_del(ifindex, rtable, IP6_RT_PRIO_MIP6_OUT, 
 			  &hai->hoa.addr, 128, &in6addr_any, 0, NULL);
 		mn_ro_pol_del(hai, ifindex, all);
 	}
 	return err;
 }
 
-static void nemo_mr_tnl_routes_del(struct home_addr_info *hai, int ifindex)
+static void nemo_mr_tnl_routes_del(struct home_addr_info *hai, 
+				   int ifindex, int rtable)
 {
 	struct list_head *l;
 	list_for_each(l, &hai->mob_net_prefixes) {
 		struct prefix_list_entry *p;
 		p = list_entry(l, struct prefix_list_entry, list);
-		route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_FWD,
+		route_del(ifindex, rtable, IP6_RT_PRIO_MIP6_FWD, 
 			  &p->ple_prefix, p->ple_plen, &in6addr_any, 0, NULL);
 	}
 }
 
-static void mn_tnl_state_del(struct home_addr_info *hai, int ifindex, int all)
+static void mn_tnl_state_del(struct home_addr_info *hai, int ifindex, 
+			     int all, int rtable)
 {
 	if (hai->home_reg_status != HOME_REG_NONE) {
 		if (hai->mob_rtr)
-			nemo_mr_tnl_routes_del(hai, ifindex);
-		route_del(ifindex, RT6_TABLE_MIP6, IP6_RT_PRIO_MIP6_OUT, 
+			nemo_mr_tnl_routes_del(hai, ifindex, rtable);
+		route_del(ifindex, rtable, IP6_RT_PRIO_MIP6_OUT, 
 			  &hai->hoa.addr, 128, &in6addr_any, 0, NULL);
 		mn_ro_pol_del(hai, ifindex, all);
 	}
@@ -715,8 +905,18 @@ static void mn_pol_ext_cleanup(struct bu
 static void mn_pol_ext_cleanup(struct bulentry *bule)
 {
 	MDBG("\n");
-	mpd_cancel_mps(&bule->hoa, &bule->peer_addr);
-	mn_tnl_state_del(bule->home, bule->home->if_tunnel, 0);
+	/* MCoA
+	 * MPS is canceled only if the BULE is the last entry 
+	 * for the HoA, or when at home
+	 */
+	if (mcoa_bule_count(bule) == 0 || bule->home->at_home)
+		mpd_cancel_mps(&bule->hoa, &bule->peer_addr);
+	if (bule->home->reg_mcoa)
+		mn_tnl_state_del(bule->home, bule->if_tunnel, 0, 
+				 bule->table);
+	else
+		mn_tnl_state_del(bule->home, bule->home->if_tunnel, 
+				 0, bule->table);
 
 	if (conf.UseMnHaIPsec) {
 		mn_ipsec_tnl_update(&bule->peer_addr, &bule->hoa, bule);
@@ -730,12 +930,13 @@ static int process_first_home_bu(struct 
 				 struct timespec *lifetime)
 {
 	int err = 0;
+	assert(hai->current_coa);
 	bule->type = BUL_ENTRY;
 	bule->flags = (IP6_MH_BU_HOME | IP6_MH_BU_ACK |
 		       hai->lladdr_comp | hai->mob_rtr);
 	bule->coa_changed = -1;
-	bule->coa = hai->primary_coa.addr;
-	bule->if_coa = hai->primary_coa.iif;
+	bule->coa = hai->current_coa->addr;
+	bule->if_coa = hai->current_coa->iif;
 	bule->lifetime = *lifetime;
 	bule->delay = conf.InitialBindackTimeoutFirstReg_ts;
 	bule->callback = bu_resend;
@@ -744,10 +945,47 @@ static int process_first_home_bu(struct 
 	bule->ext_cleanup = mn_pol_ext_cleanup;
 	bule->home = hai;
 	bule->consecutive_resends = 0;
+	bule->priority = get_prio_from_bid(bule->bid);
 
 	hai->home_reg_status = HOME_REG_UNCERTAIN;
 
-	if ((err = mn_tnl_state_add(hai, hai->if_tunnel, 0)) < 0)
+	/* MCoA: MCoA requires one tunnel per CoA. Need to create a new one */
+	if (bule->if_tunnel == 0 
+	    && hai->reg_mcoa 
+	    && get_bid_from_ifindex(hai->current_coa->iif)) {
+		MDBG("Creating new tunnel for iface %d (BID %d)\n", 
+			hai->current_coa->iif, 
+			get_bid_from_ifindex(hai->current_coa->iif)); 
+		bule->if_tunnel = tunnel_add(&hai->hoa.addr, 
+					&hai->ha_addr,
+					hai->current_coa->iif, 
+					NULL, NULL);
+		if (bule->if_tunnel <= 0) {
+			MDBG("Failed to create MN-HA tunnel\n"); 
+			return -1;
+		} else {
+			MDBG("MN-HA tunnel created (iface tnl = %d)\n", 
+				bule->if_tunnel);
+			struct flag_hoa_args arg;
+			arg.target = hai;
+			arg.flag = 1;
+			arg.bule = bule;
+			if (addr_do(&hai->hoa.addr, 128,
+				    bule->if_tunnel, &arg, 
+				    flag_hoa) < 0) {
+				MDBG("addr_do failed for tunnel %d\n", 
+					bule->if_tunnel);
+				return -1;
+			}
+		}
+	} else if (bule->if_tunnel == 0)
+		/* MCoA
+		 * If MCoA is not used, then initialise the tunnel 
+		 * number to the one in hai
+		 */
+		bule->if_tunnel = hai->if_tunnel;
+		
+	if ((err = mn_tnl_state_add(hai, bule->if_tunnel, 0, bule->table)) < 0)
 		MDBG("Failed to initialize new bule for HA\n");
 	else
 		MDBG("New bule for HA\n");
@@ -781,14 +1019,18 @@ static void mn_send_home_bu(struct home_
 	movement_t type_movement = MIP6_TYPE_MOVEMENT_UNKNOWN;
 
 	TRACE;
+	assert(hai->current_coa);
 
 	if (IN6_IS_ADDR_UNSPECIFIED(&hai->ha_addr)) {
 		MDBG("HA not set for home link\n");
 		return;
 	}
-	mn_get_home_lifetime(hai, &lifetime, 0);
+	mn_get_home_lifetime(hai, &lifetime, hai->current_coa->iif, 0);
 
-	if ((bule = bul_get(hai, NULL, &hai->ha_addr)) == NULL) {
+	if ((bule = bul_get(hai, NULL, &hai->ha_addr, 
+			hai->reg_mcoa?
+			get_bid_from_ifindex(hai->current_coa->iif)
+			:MCOA_NO_BID)) == NULL) {
 		assert(!hai->at_home);
 		/* Create new bul entry for HA */
 		if (!tsisset(lifetime)) {
@@ -807,14 +1049,22 @@ static void mn_send_home_bu(struct home_
 				mn_change_ha(hai);
 			return;
 		}
-		bule = create_bule(&hai->hoa.addr, &hai->ha_addr);
+		bule = create_bule(&hai->hoa.addr, &hai->ha_addr,
+				hai->reg_mcoa?
+				get_bid_from_ifindex(hai->current_coa->iif)
+				:MCOA_NO_BID);
 		if (bule == NULL)
 			return;
-		if (process_first_home_bu(bule, hai, &lifetime) < 0 ||
+		if (mcoa_mn_init_rt_table(bule) < 0 ||
+		    process_first_home_bu(bule, hai, &lifetime) < 0 ||
 		    bul_add(bule) < 0) {
 			bul_delete(bule);
 			return;
 		}
+		/* MCoA: Check for interface reliability */
+		if(get_reliable_from_ifindex(bule->if_coa) > 0)
+			/* MCoA: Add the rules */
+			mcoa_iface_rules_add(hai->current_coa->iif, hai);
 		type_movement = MIP6_TYPE_MOVEMENT_HL2FL;
 		MDBG("New bule for HA\n");
 	} else if (bule->type == BUL_ENTRY) {
@@ -829,8 +1079,8 @@ static void mn_send_home_bu(struct home_
 			}
 			type_movement = MIP6_TYPE_MOVEMENT_HL2FL;
 		} else {
-			bule->coa = hai->primary_coa.addr;
-			bule->if_coa = hai->primary_coa.iif;
+			bule->coa = hai->current_coa->addr;
+			bule->if_coa = hai->current_coa->iif;
 
 			/* Rate limiting home registration binding updates
 			   is necessary for multihomed MNs */
@@ -850,6 +1100,10 @@ static void mn_send_home_bu(struct home_
 			bule->callback = bu_resend;
 		}
 		bule->seq++;
+		/* MCoA: Check for interface reliability */
+		if(bule->home->reg_mcoa 
+		   && get_reliable_from_ifindex(bule->if_coa) == 0)
+			mcoa_iface_rules_del(bule->if_coa, bule->home, 1); 
 		pre_bu_bul_update(bule);
 		MDBG("Bule for HA exists. Updating it.\n");
 	} else {
@@ -885,10 +1139,13 @@ static void mn_send_home_bu(struct home_
 					    &bule->hoa, bule);
 		}
         }
+	/* MCoA: Update the current routing table */
+	hai->table = bule->table;
 	/* Before bul_iterate, tunnel modification should be done. */
-	tunnel_mod(hai->if_tunnel, &hai->primary_coa.addr, &hai->ha_addr,
-		   hai->primary_coa.iif, mn_ext_tunnel_ops, hai);
-		
+	bule->if_tunnel = tunnel_mod(bule->if_tunnel, &hai->current_coa->addr, 
+				     &hai->ha_addr, hai->current_coa->iif, 
+				     mn_ext_tunnel_ops, hai);
+	hai->if_tunnel = bule->if_tunnel;		
 	bule->last_coa = bule->coa;
 	bule->coa_changed = 0;
 
@@ -1017,14 +1274,87 @@ static int mn_chk_bauth(struct ip6_mh_bi
 	return -1;
 }
 
+static int mn_disable_mcoa(struct bulentry *bule)
+{
+/*
+	struct bulentry *bule_del;
+	struct home_addr_info *hai;
+	struct flag_hoa_args arg;
+	struct list_head *l, *tmp;
+	struct net_iface *best_iface = NULL;
+*/
+
+	/* MCoA TODO1: At the moment we simply exit */
+	MDBG("HA does not accept MCoA registration. Check "\
+	     "your HA or change your MR configuration\n");
+	kill(0, SIGINT);
+	return 0;
+
+/*
+	assert(bule);
+	hai = bule->home;
+	mcoa_mn_rules_del(hai);
+        while((bule_del = bul_get(hai, NULL, 
+				&bule->peer_addr, MCOA_NO_BID)))
+		bul_delete(bule_del);
+	
+	hai->if_tunnel = tunnel_add(&hai->hoa.addr, &hai->ha_addr,
+				    hai->if_home, NULL, NULL);
+	if (hai->if_tunnel <= 0) {
+		MDBG("failed to create MN-HA tunnel\n"); 
+		goto clean_err;
+	}
+
+	arg.target = hai;
+	arg.flag = 1;
+	if (addr_do(&hai->hoa.addr, 128, hai->if_tunnel, 
+		    &arg, flag_hoa) < 0)
+		goto clean_err;
+
+	nemo_mr_rules_del(hai);
+	hai->reg_mcoa = 0;
+	nemo_mr_rules_add(hai);
+	if (rule_add(NULL, RT6_TABLE_MIP6, IP6_RULE_PRIO_MIP6_HOA_OUT, 0, 
+			RTN_UNICAST, &hai->hoa.addr, 
+			128, &in6addr_any, 0, FIB_RULE_FIND_SADDR) < 0)
+		goto clean_err;
+
+	list_for_each(l, &conf.net_ifaces) {
+		struct net_iface *iface = NULL;
+		iface = list_entry(l, struct net_iface, list);
+		if (!best_iface 
+		    || iface->mn_if_bidprio > best_iface->mn_if_bidprio)
+			best_iface = iface;
+	}
+	list_for_each_safe(l, tmp, &conf.net_ifaces) {
+		struct net_iface *iface = NULL;
+		iface = list_entry(l, struct net_iface, list);
+		if (iface != best_iface) {
+			MDBG("Deleting iface %d\n", iface->ifindex);
+			list_del(l);
+		}
+	}
+	return 1;
+	
+clean_err:
+	rule_del(NULL, RT6_TABLE_MIP6, 
+		IP6_RULE_PRIO_MIP6_HOA_OUT, 0, RTN_UNICAST, 
+		&hai->hoa.addr, 128, &in6addr_any, 0, FIB_RULE_FIND_SADDR);
+	tunnel_del(hai->if_tunnel, NULL, NULL);
+
+	return 0;
+*/
+}
+
 static void mn_recv_ba(const struct ip6_mh *mh, ssize_t len,
 		       const struct in6_addr_bundle *in, int iif)
 {
 	struct ip6_mh_binding_ack *ba;
+	struct ip6_mh_opt_bid *bui;
 	struct mh_options mh_opts;
 	struct bulentry *bule;
 	struct timespec now, ba_lifetime, br_adv;
-	uint16_t seqno;
+	uint16_t seqno, bid;
 
 	TRACE;
 
@@ -1036,25 +1366,32 @@ static void mn_recv_ba(const struct ip6_
 	ba = (struct ip6_mh_binding_ack *)mh;
 
 	pthread_rwlock_wrlock(&mn_lock);
-	bule = bul_get(NULL, in->dst, in->src);
+
+	/* MCoA: Lookup in BA options for the BUI sub-option and the BID */
+	bui = mh_opt(&ba->ip6mhba_hdr, &mh_opts, IP6_MHOPT_BID);
+	bid = bui ? ntohs(bui->ip6mobid_bid) : MCOA_NO_BID;
+	
+	bule = bul_get(NULL, in->dst, in->src, bid);
 	if (!bule || bule->type != BUL_ENTRY) {
 		MDBG("Got BA without corresponding BUL entry "
 		     "from %x:%x:%x:%x:%x:%x:%x:%x "
 		     "to home address %x:%x:%x:%x:%x:%x:%x:%x "
-		     "with coa %x:%x:%x:%x:%x:%x:%x:%x\n",
+		     "with coa %x:%x:%x:%x:%x:%x:%x:%x"
+		     "and bid %d\n",
 		     NIP6ADDR(in->src),  
 		     NIP6ADDR(in->dst),
 		     NIP6ADDR(in->local_coa != NULL ? 
-			      in->local_coa : &in6addr_any));
+			      in->local_coa : &in6addr_any),
+		     bid);
 		pthread_rwlock_unlock(&mn_lock);
 		return;
 	}
 	dbg("Got BA from %x:%x:%x:%x:%x:%x:%x:%x "
 	    "to home address %x:%x:%x:%x:%x:%x:%x:%x "
-	    "with coa %x:%x:%x:%x:%x:%x:%x:%x and status %d\n",
+	    "with coa %x:%x:%x:%x:%x:%x:%x:%x, bid %d and status %d\n",
 	    NIP6ADDR(in->src), NIP6ADDR(in->dst),
 	    NIP6ADDR(in->local_coa != NULL ? in->local_coa : &in6addr_any),
-	    ba->ip6mhba_status);
+	    bid, ba->ip6mhba_status);
 	dbg("Dumping corresponding BULE\n");
 	dbg_func(bule, dump_bule);
 	/* First check authenticator */
@@ -1076,6 +1413,17 @@ static void mn_recv_ba(const struct ip6_
 			return;
 		}
 	}
+	/* MCoA: Check if BID matches */
+	if (bule->bid && !bui) {
+		/* The BACK did not have any BID suboption whereas 
+		 * we sent a BU with a BID option. MCoA is disabled 
+		 * on the node.
+		 */
+		MDBG("Got BA with no BID option, disabling MCoA registration.\n");
+		mn_disable_mcoa(bule);
+		pthread_rwlock_unlock(&mn_lock);
+		return;
+	}
 	bule->do_send_bu = 0;
 	bule->consecutive_resends = 0;
 	clock_gettime(CLOCK_REALTIME, &now);
@@ -1086,11 +1434,18 @@ static void mn_recv_ba(const struct ip6_
 			bule->seq = seqno + 1;
 			if (bule->flags & IP6_MH_BU_HOME)
 				mn_get_home_lifetime(bule->home,
-						     &bule->lifetime, 0);
+						     &bule->lifetime, 
+						     bule->if_coa,
+						     0);
 			else
 				mn_get_ro_lifetime(bule->home,
 						   &bule->lifetime, 0);
 			bule->callback = bu_resend;
+			/* MCoA: Check for interface reliability */
+			if(bule->home->reg_mcoa 
+		   	   && get_reliable_from_ifindex(bule->if_coa) == 0)
+				mcoa_iface_rules_del(bule->if_coa, 
+						     bule->home, 1);
 			pre_bu_bul_update(bule);
 			mn_send_bu_msg(bule);
 			bule->delay = conf.InitialBindackTimeoutReReg_ts;
@@ -1137,6 +1492,10 @@ static void mn_recv_ba(const struct ip6_
 	tssetsec(ba_lifetime, ntohs(ba->ip6mhba_lifetime) << 2);
 	br_adv = ba_lifetime;
 	tsadd(bule->lastsent, ba_lifetime, bule->hard_expire);
+	/* MCoA: Check for interface reliability */
+	if(bule->home->reg_mcoa 
+	   && get_reliable_from_ifindex(bule->if_coa) == 0)
+		mcoa_iface_rules_add(bule->if_coa, bule->home); 
 	if (!(bule->flags & IP6_MH_BU_HOME) || !conf.OptimisticHandoff)
 		post_ba_bul_update(bule);
 	if (bule->flags & IP6_MH_BU_HOME) {
@@ -1266,11 +1625,6 @@ struct home_addr_info *mn_get_home_addr_
 	return NULL;
 }
 
-struct flag_hoa_args {
-	struct home_addr_info *target;
-	int flag;
-};
-
 static int flag_hoa(struct ifaddrmsg *ifa, struct rtattr *rta_tb[], void *arg)
 {
 	/*
@@ -1279,13 +1633,18 @@ static int flag_hoa(struct ifaddrmsg *if
 
 	struct flag_hoa_args *fhoa = arg;
 	struct home_addr_info *hai = fhoa->target;
+	struct bulentry *bule = fhoa->bule;
 	struct mn_addr *hoa = &hai->hoa;
 	struct in6_addr *addr = RTA_DATA(rta_tb[IFA_ADDRESS]);
 	struct timespec now;
 	uint32_t preferred;
 	uint32_t valid;
-	int err;
-	int plen = (ifa->ifa_index == hai->if_tunnel ? 128 : hai->plen);
+	int err, plen;
+
+	if (hai->reg_mcoa && bule != NULL)
+		plen = (ifa->ifa_index == bule->if_tunnel ? 128 : hai->plen);
+	else
+		plen = (ifa->ifa_index == hai->if_tunnel ? 128 : hai->plen);
 
 	clock_gettime(CLOCK_REALTIME, &now);
 
@@ -1321,6 +1680,220 @@ static int flag_hoa(struct ifaddrmsg *if
 	return 0;
 }
 
+static int mcoa_iface_dflt_hoa_rule_del(struct bulentry *bule, 
+	 				struct bulentry *best_bule,
+					struct home_addr_info *hai,
+					int replace)
+{
+	/* MCoA: Delete the default HoA rule */
+	rule_del(NULL, bule->bid, IP6_RULE_PRIO_MIP6_HOA_OUT, 
+		0, RTN_UNICAST, &hai->hoa.addr, 128,
+		&in6addr_any, 0, FIB_RULE_FIND_SADDR);
+	
+	/* MCoA
+	 * Do not replace the default rules 
+	 * if there are no other best bule 
+ 	 */
+	if(best_bule && best_bule != bule && replace) {
+		if (rule_add(NULL, best_bule->bid, 
+			IP6_RULE_PRIO_MIP6_HOA_OUT, 0, RTN_UNICAST, 
+			&hai->hoa.addr, 128, &in6addr_any, 0, 
+			FIB_RULE_FIND_SADDR) < 0)
+		return -1;
+	}
+	return 0;
+}
+
+static void mcoa_iface_dflt_mnp_rules_del(struct bulentry *bule, 
+					  struct bulentry *best_bule,
+					  struct home_addr_info *hai,
+					  struct prefix_list_entry *p,
+					  int replace)
+{
+	/* MCoA: Delete the default MNP rule */
+	rule_del(NULL, bule->bid, IP6_RULE_PRIO_MIP6_FWD_MCOA, 
+		0, RTN_UNICAST, &p->ple_prefix, p->ple_plen, 
+		&in6addr_any, 0, 0);
+
+	/* MCoA
+	 * Do not replace the default rules 
+	 * if there are no other best bule 
+	 */
+	if(best_bule && best_bule != bule && replace) {
+		rule_add(NULL, best_bule->bid, 
+			IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, RTN_UNICAST, 
+			&p->ple_prefix, p->ple_plen, &in6addr_any, 0, 0);
+	}
+}
+ 
+static struct bulentry* mcoa_iface_rules_del(int iif, 
+					     struct home_addr_info *hai,
+					     int replace)
+{
+	uint16_t bid = hai->reg_mcoa?get_bid_from_ifindex(iif):MCOA_NO_BID;
+	uint8_t prio_tmp = 0;
+	struct bulentry *bule, *best_bule = NULL;
+	struct list_head *l;
+	int delete_dflt_mnp = 0;
+
+	bule = bul_get(hai,  NULL, &hai->ha_addr, bid);
+	if (!hai->reg_mcoa || (bule && !bule->rules))
+		return NULL;
+
+	/* MCoA: Delete rule for HoA traffic from the MN */
+	rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, 
+		/* fwmark */ bid, RTN_UNICAST,
+	     	&hai->hoa.addr, 128, &in6addr_any, 0, 
+		FIB_RULE_FIND_SADDR);
+
+	bul_iterate_in(&hai->ha_addr, bul_highest_priority, 
+			&best_bule);
+	if(bule && best_bule == bule) {
+		delete_dflt_mnp = 1;
+		/* MCoA: search for the next best BULE */
+		prio_tmp = bule->priority;
+		bule->priority = 0;
+		bul_iterate_in(&hai->ha_addr, 
+				bul_highest_priority, &best_bule);
+		bule->priority = prio_tmp;
+		/* MCoA: delete the default HoA rule */
+		mcoa_iface_dflt_hoa_rule_del(bule, best_bule, hai, replace);
+	} 
+
+	list_for_each(l, &hai->mob_net_prefixes) {
+		struct prefix_list_entry *p = NULL;
+		p = list_entry(l, struct prefix_list_entry, list);
+		/* MCoA: Delete MNP rule */
+		rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_FWD_FWM, 
+			/*fwmark*/ bid, RTN_UNICAST, &p->ple_prefix, 
+			p->ple_plen, &in6addr_any, 0, 0);
+		if(delete_dflt_mnp) {
+			/* MCoA: Delete default MNP rule */
+			mcoa_iface_dflt_mnp_rules_del(bule, best_bule, 
+						hai, p, replace);
+		}
+ 	}
+	
+	if (bule) {
+		bule->rules = 0;
+		MDBG("MNP and HoA rules deleted for iface %d bid %d\n", 
+			iif, bule->bid);
+	}
+	
+	return bule;
+}
+ 
+static void mcoa_mn_rules_del(struct home_addr_info *hinfo)
+{
+	struct list_head *l;
+ 
+	list_for_each(l, &hinfo->mcoa) {
+		struct mn_addr *iface = NULL;
+		iface = list_entry(l, struct mn_addr, list);
+		mcoa_iface_rules_del(iface->iif, hinfo, 0);
+	}
+	rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_HOA_OUT, 
+		0, RTN_UNICAST, &hinfo->hoa.addr, 128, 
+		&in6addr_any, 0, FIB_RULE_FIND_SADDR);
+	if (hinfo->at_home && hinfo->reg_mcoa) {
+		list_for_each(l, &hinfo->mob_net_prefixes) {
+			struct prefix_list_entry *p = NULL;
+			p = list_entry(l, struct prefix_list_entry, 
+					list);
+			rule_del(NULL, RT6_TABLE_MIP6,
+				IP6_RULE_PRIO_MIP6_FWD, 0, 
+				RTN_UNICAST, &p->ple_prefix, 
+				p->ple_plen, &in6addr_any, 0, 0);
+		}
+	}
+}
+	
+static int mcoa_iface_rules_add(int iif, struct home_addr_info *hai)
+{
+	uint16_t bid = hai->reg_mcoa?get_bid_from_ifindex(iif):MCOA_NO_BID;
+	struct bulentry *bule, *best_bule = NULL;
+	struct prefix_list_entry *pe = NULL;
+	struct list_head *l;
+	uint8_t prio;
+ 
+	bule = bul_get(hai, NULL, &hai->ha_addr, bid);
+	if (!bid || !hai->reg_mcoa || !bule || (bule && bule->rules))
+		return 0;
+
+	prio = get_prio_from_bid(bid);
+	
+	/* MCoA: Rule for HoA traffic from the MN */
+	if (rule_add(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, 
+			/* fwmark */ bid, RTN_UNICAST,
+	 		&hai->hoa.addr, 128, &in6addr_any, 0, 
+			FIB_RULE_FIND_SADDR) < 0)
+		goto undo;
+	
+	bul_iterate_in(&hai->ha_addr, bul_highest_priority, 
+			&best_bule);
+	
+	if (!best_bule || prio >= best_bule->priority) { 
+		rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_HOA_OUT, 
+				0, RTN_UNICAST, &hai->hoa.addr, 128, 
+				&in6addr_any, 0, FIB_RULE_FIND_SADDR);
+		if (rule_add(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT, 
+				0, RTN_UNICAST, &hai->hoa.addr, 128, 
+				&in6addr_any, 0, FIB_RULE_FIND_SADDR) < 0)
+			goto undo;
+	}
+
+	/* MCoA: Rule for forwarded traffic from the NEMO */
+	list_for_each(l, &hai->mob_net_prefixes) {
+		struct prefix_list_entry *p = NULL;
+		p = list_entry(l, struct prefix_list_entry, list);
+		if (rule_add(NULL, bid, IP6_RULE_PRIO_MIP6_FWD_FWM, 
+				/*fwmark*/ bid, RTN_UNICAST, 
+				&p->ple_prefix, p->ple_plen, 
+				&in6addr_any, 0, 0) < 0) {
+			pe = p;
+			goto undo;
+		}
+		/* MCoA
+		 * Add default rules if the bule has the highest 
+		 * priority
+ 	 	 */
+		if (!best_bule || prio >= best_bule->priority) { 
+			rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 
+				0, RTN_UNICAST, &p->ple_prefix, p->ple_plen, 
+				&in6addr_any, 0, 0);
+			if (rule_add(NULL, bid, IP6_RULE_PRIO_MIP6_FWD_MCOA, 
+				0, RTN_UNICAST, &p->ple_prefix, p->ple_plen, 
+				&in6addr_any, 0, 0) < 0)
+				goto undo;
+		}
+	}
+	
+	bule->rules = 1;
+	MDBG("MNP and HoA Rules added for interface %d.\n", iif);
+ 	return 0;
+
+undo:
+	rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA, 
+		/* fwmark */ bid, RTN_UNICAST,
+		&hai->hoa.addr, 128, &in6addr_any, 0, 
+		FIB_RULE_FIND_SADDR);
+
+	rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_HOA_OUT, 
+		0, RTN_UNICAST, &hai->hoa.addr, 128, 
+		&in6addr_any, 0, FIB_RULE_FIND_SADDR);
+		
+	list_for_each(l, &hai->mob_net_prefixes) {
+		struct prefix_list_entry *p = NULL;
+		p = list_entry(l, struct prefix_list_entry, list);
+		rule_del(NULL, bid, IP6_RULE_PRIO_MIP6_FWD_FWM, 
+			/*fwmark*/ bid, RTN_UNICAST, &p->ple_prefix, 
+			p->ple_plen, &in6addr_any, 0, 0);
+		if (p == pe)
+			break;
+	}
+	return -1;
+}
+
 static void nemo_mr_rules_del(struct home_addr_info *hinfo)
 {
 	struct list_head *l;
@@ -1328,11 +1901,17 @@ static void nemo_mr_rules_del(struct hom
 	list_for_each(l, &hinfo->mob_net_prefixes) {
 		struct prefix_list_entry *p = NULL;
 		p = list_entry(l, struct prefix_list_entry, list);
-		rule_del(NULL, RT6_TABLE_MIP6,
-			 IP6_RULE_PRIO_MIP6_FWD, RTN_UNICAST,
-			 &p->ple_prefix, p->ple_plen, &in6addr_any, 0, 0);
+		if (hinfo->reg_mcoa) {
+			rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_FWD_MCOA, 0, 
+				RTN_UNICAST, &p->ple_prefix, p->ple_plen, 
+				&in6addr_any, 0, 0);
+		} else
+			rule_del(NULL, RT6_TABLE_MIP6,
+				IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST,
+			 	&p->ple_prefix, p->ple_plen, &in6addr_any, 
+				0, 0);
 		rule_del(NULL, RT6_TABLE_MAIN,
-			 IP6_RULE_PRIO_MIP6_MNP_IN, RTN_UNICAST,
+			 IP6_RULE_PRIO_MIP6_MNP_IN, 0, RTN_UNICAST,
 			 &in6addr_any, 0, &p->ple_prefix, p->ple_plen, 0);
 	}
 }
@@ -1346,19 +1925,21 @@ static int nemo_mr_rules_add(struct home
 		struct prefix_list_entry *p = NULL;
 		p = list_entry(l, struct prefix_list_entry, list);
 		if (rule_add(NULL, RT6_TABLE_MAIN,
-			     IP6_RULE_PRIO_MIP6_MNP_IN, RTN_UNICAST,
+			     IP6_RULE_PRIO_MIP6_MNP_IN, 0, RTN_UNICAST,
 			     &in6addr_any, 0,
 			     &p->ple_prefix, p->ple_plen, 0) < 0) {
 			pe = p;
 			goto undo;
 		}
-		if (rule_add(NULL, RT6_TABLE_MIP6,
-			     IP6_RULE_PRIO_MIP6_FWD, RTN_UNICAST,
-			     &p->ple_prefix, p->ple_plen,
-			     &in6addr_any, 0, 0) < 0) {
+		if (!hinfo->reg_mcoa 
+		    && rule_add(NULL, RT6_TABLE_MIP6,
+			     	IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST,
+			     	&p->ple_prefix, p->ple_plen, 
+			     	&in6addr_any, 0, 0) < 0) {
 			rule_del(NULL, RT6_TABLE_MAIN,
-				 IP6_RULE_PRIO_MIP6_MNP_IN, RTN_UNICAST,
-				 &in6addr_any, 0, &p->ple_prefix, p->ple_plen, 0);
+				 IP6_RULE_PRIO_MIP6_MNP_IN, 0, 
+				 RTN_UNICAST, &in6addr_any, 0, 
+				 &p->ple_prefix, p->ple_plen, 0);
 			pe = p;
 			goto undo;
 		}
@@ -1368,17 +1949,25 @@ undo:
 	list_for_each(l, &hinfo->mob_net_prefixes) {
 		struct prefix_list_entry *p = NULL;
 		p = list_entry(l, struct prefix_list_entry, list);
-		rule_del(NULL, RT6_TABLE_MIP6,
-			 IP6_RULE_PRIO_MIP6_FWD,  RTN_UNICAST,
-			 &p->ple_prefix, p->ple_plen, &in6addr_any, 0, 0);
+		if (!hinfo->reg_mcoa) {
+			rule_del(NULL, RT6_TABLE_MIP6,
+				IP6_RULE_PRIO_MIP6_FWD, 0, RTN_UNICAST,
+				&p->ple_prefix, p->ple_plen, 
+				&in6addr_any, 0, 0);
+		}
 		rule_del(NULL, RT6_TABLE_MAIN,
-			 IP6_RULE_PRIO_MIP6_MNP_IN, RTN_UNICAST,
+			 IP6_RULE_PRIO_MIP6_MNP_IN, 0, RTN_UNICAST,
 			 &in6addr_any, 0, &p->ple_prefix, p->ple_plen, 0);
 		if (p == pe)
 			break;
 	}
 	return -1;
 }
+
+struct mcoa_tnl_id {
+	struct list_head list;
+	int tnl_id;
+};
 
 static void clean_home_addr_info(struct home_addr_info *hai)
 {
@@ -1390,17 +1979,24 @@ static void clean_home_addr_info(struct 
 		nemo_mr_rules_del(hai);
 	arg.target = hai;
 	arg.flag = 0;
-	addr_do(&hai->hoa.addr, plen,
-		hai->hoa.iif, &arg, flag_hoa);
+	/* MCoA: Flag HoA on the tunnel iface is not necessary here */
+	if (!hai->reg_mcoa)
+		addr_do(&hai->hoa.addr, plen,
+			hai->hoa.iif, &arg, flag_hoa);
 	bul_iterate(&hai->bul, mn_dereg, NULL);
-	bul_home_cleanup(&hai->bul);
-
+	/* MCoA: Delete all mn rules */
+	if (hai->reg_mcoa)
+		mcoa_mn_rules_del(hai);
 	mn_block_rule_del(hai);
 
-	rule_del(NULL, RT6_TABLE_MIP6,
-		 IP6_RULE_PRIO_MIP6_HOA_OUT, RTN_UNICAST,
-		 &hai->hoa.addr, 128, &in6addr_any, 0, FIB_RULE_FIND_SADDR);
-	tunnel_del(hai->if_tunnel, NULL, NULL);
+	if (!hai->reg_mcoa) {	
+		rule_del(NULL, RT6_TABLE_MIP6,
+			 IP6_RULE_PRIO_MIP6_HOA_OUT, 0, RTN_UNICAST,
+			 &hai->hoa.addr, 128, &in6addr_any, 0, 
+			 FIB_RULE_FIND_SADDR);
+		tunnel_del(hai->if_tunnel, NULL, NULL);
+	}
+	bul_home_cleanup(&hai->bul);
 	dhaad_stop(hai);
 	free(hai);
 }	
@@ -1448,6 +2044,11 @@ static struct home_addr_info *hai_copy(s
 				     &hai->mob_net_prefixes) < 0)
 			goto mutex_undo;
 
+		INIT_LIST_HEAD(&hai->mcoa);
+		if (hai->reg_mcoa && 
+		    prefix_list_copy(&conf_hai->mcoa, &hai->mcoa) < 0)
+			goto mutex_undo;
+
 		INIT_LIST_HEAD(&hai->ro_policies);
 		if (rpl_copy(&conf_hai->ro_policies, &hai->ro_policies) < 0)
 			goto mnp_undo;
@@ -1493,17 +2094,22 @@ static int conf_home_addr_info(struct ho
 		MDBG("HA address %x:%x:%x:%x:%x:%x:%x:%x\n", 
 		     NIP6ADDR(&hai->ha_addr)); 
 	}
-	hai->if_tunnel = tunnel_add(&hai->hoa.addr, &hai->ha_addr,
-				    hai->if_home, NULL, NULL);
+	/* MCoA: tunnel is created only if MCoA registration is not used */
+	if(!hai->reg_mcoa) {
+		hai->if_tunnel = tunnel_add(&hai->hoa.addr, 
+					&hai->ha_addr,
+					hai->if_home, NULL, NULL);
 
-	if (hai->if_tunnel <= 0) {
-		MDBG("failed to create MN-HA tunnel\n"); 
-		goto clean_err;
-	}
-	if (rule_add(NULL, RT6_TABLE_MIP6,
-		     IP6_RULE_PRIO_MIP6_HOA_OUT, RTN_UNICAST,
-		     &hai->hoa.addr, 128, &in6addr_any, 0, FIB_RULE_FIND_SADDR) < 0) {
-		goto clean_err;
+		if (hai->if_tunnel <= 0) {
+			MDBG("failed to create MN-HA tunnel\n"); 
+			goto clean_err;
+		}
+		if (rule_add(NULL, RT6_TABLE_MIP6, 
+			     IP6_RULE_PRIO_MIP6_HOA_OUT, 0, RTN_UNICAST, 
+			     &hai->hoa.addr, 128, &in6addr_any, 0, 
+			     FIB_RULE_FIND_SADDR) < 0) {
+			goto clean_err;
+		}
 	}
 
 	if (mn_block_rule_add(hai) < 0)
@@ -1524,16 +2130,25 @@ static int conf_home_addr_info(struct ho
 			       PREFIX_LIFETIME_INFINITE,
 			       PREFIX_LIFETIME_INFINITE);
 
-	arg.target = hai;
-	arg.flag = 1;
+	/* MCoA */
+	if(!hai->reg_mcoa) {
+		arg.target = hai;
+		arg.flag = 1;
 
-	if (addr_do(&hai->hoa.addr, 128,
-		    hai->if_tunnel, &arg, flag_hoa) < 0) {
-		goto clean_err;
+		if (addr_do(&hai->hoa.addr, 128,
+			    hai->if_tunnel, &arg, flag_hoa) < 0) {
+			goto clean_err;
+		}
 	}
 	if (hai->mob_rtr && nemo_mr_rules_add(hai) < 0) {
 		goto clean_err;
 	}
+	/* MCoA TODO1
+	 * For Home-CoA support, hai->at_home must be initialized to 0
+	 * if(hai->reg_mcoa) hai->at_home = 0; 
+	 * BUT Home-CoA solution seems to be deprecated by the no-NDP 
+	 * solution
+	 */
 	hai->at_home = hai->hoa.iif == hai->if_home;
 	pthread_rwlock_wrlock(&mn_lock);
 	list_add(&hai->list, &home_addr_list);
@@ -1635,7 +2250,11 @@ int mn_update_home_prefix(struct home_ad
 	if (!hai->at_home) {
 		struct bulentry *e;
 		
-		e = bul_get(hai,  NULL, &hai->ha_addr);
+		/* MCoA TODO1
+		 * Do the same for all entries that match &hai->ha_addr 
+		 */
+		e = bul_get(hai, NULL, &hai->ha_addr, MCOA_NO_BID);
+
 		if (e == NULL || !(e->flags & IP6_MH_BU_HOME))
 			return -ENOENT;
 		
@@ -1668,8 +2287,9 @@ static int mn_get_ro_coa(const struct in
 		return ret;
 	hai = mn_get_home_addr(hoa);
 	if (hai) {
-		*coa = hai->primary_coa.addr;
-		ret = hai->primary_coa.iif;
+		assert(hai->current_coa);
+		*coa = hai->current_coa->addr;
+		ret = hai->current_coa->iif;
 	} else {
 		MDBG("Failed to find a home address info\n");
 		ret = -1;
@@ -1683,14 +2303,14 @@ static inline void linklocal_rt_rules_de
 static inline void linklocal_rt_rules_del(void)
 {
 	rule_del(NULL, RT6_TABLE_MAIN,
-		 IP6_RULE_PRIO_MIP6_COA_OUT, RTN_UNICAST,
+		 IP6_RULE_PRIO_MIP6_COA_OUT, 0, RTN_UNICAST,
 		 &linklocal_prefix, 64, &in6addr_any, 0, 0);
 }
 
 static inline int linklocal_rt_rules_add(void)
 {
 	return rule_add(NULL, RT6_TABLE_MAIN,
-			IP6_RULE_PRIO_MIP6_COA_OUT, RTN_UNICAST,
+			IP6_RULE_PRIO_MIP6_COA_OUT, 0, RTN_UNICAST,
 			&linklocal_prefix, 64, &in6addr_any, 0, 0);
 }
 
@@ -1705,12 +2325,13 @@ static int mn_ext_tunnel_ops(int request
 	mha.if_next = new_if;
 	mha.target = hai;
 
+	/* MCoA: Update state for the correct table (hai->table) */	
 	if (hai->hoa.iif == old_if &&
-	    (mn_tnl_state_add(hai, new_if, 1) ||
+	    (mn_tnl_state_add(hai, new_if, 1, hai->table) ||	     
 	     addr_do(&hai->hoa.addr, 128, old_if, &mha, mv_hoa) < 0))
 		goto undo;
 
-	mn_tnl_state_del(hai, old_if, 1);
+	mn_tnl_state_del(hai, old_if, 1, hai->table);
 	hai->if_tunnel = new_if;
 	return 0;
 undo:
@@ -1718,28 +2339,52 @@ undo:
 
 	if (hai->hoa.iif == new_if)
 		addr_do(&hai->hoa.addr, 128, new_if, &mha, mv_hoa); 
-	mn_tnl_state_del(hai, new_if, 1);
+	mn_tnl_state_del(hai, new_if, 1, hai->table);
 	return -1;
 }
 
 static int mn_move(struct home_addr_info *hai)
 {
 	struct mv_hoa_args mha;
+	struct list_head *l;
 
 	mha.target = hai;
 
 	TRACE;
+	assert(hai->current_coa);
 
 	if (hai->at_home) {
 		int plen = (hai->hoa.iif == hai->if_tunnel ? 128 : hai->plen);
 		struct bulentry *e;
 		MDBG("in home net\n");
+		/* MCoA 
+		 * If we are in the home network, add a rule to
+		 * RT6_TABLE_MIP6 to allow routing of the packets from 
+		 * the MNNs (routes in RT6_TABLE_MIP6 are 
+		 * added/deleted in movement.c/md_update_router_stats,
+		 * __md_free_router) and the MR.
+		 */
+		if (hai->reg_mcoa) {
+			list_for_each(l, &hai->mob_net_prefixes) {
+				struct prefix_list_entry *p = NULL;
+				p = list_entry(l, struct prefix_list_entry, 
+						list);
+				rule_add(NULL, RT6_TABLE_MIP6,
+			     		IP6_RULE_PRIO_MIP6_FWD, 0, 
+					RTN_UNICAST, &p->ple_prefix, 
+					p->ple_plen, &in6addr_any, 0, 0);
+			}
+		}
 		if (hai->home_reg_status == HOME_REG_NONE &&
-		    (e = bul_get(hai,  NULL, &hai->ha_addr)) != NULL &&
+		    (e = bul_get(hai, NULL, &hai->ha_addr, 
+				 hai->reg_mcoa?
+				 get_bid_from_ifindex(hai->current_coa->iif)
+				 :MCOA_NO_BID)) 
+		    		 != NULL &&
 		    e->flags & IP6_MH_BU_HOME && e->type != BUL_ENTRY)
 			bul_delete(e);
-		if (hai->hoa.iif != hai->primary_coa.iif) {
-			mha.if_next = hai->primary_coa.iif;
+		if (hai->hoa.iif != hai->current_coa->iif) {
+			mha.if_next = hai->current_coa->iif;
 			addr_do(&hai->hoa.addr, plen,
 				hai->hoa.iif, &mha, mv_hoa);
 			if (hai->home_reg_status == HOME_REG_NONE) {
@@ -1760,10 +2405,33 @@ static int mn_move(struct home_addr_info
 			hai->pend_ba = 0;
 			pending_bas--;
 		}
+		/* MCoA
+		 * When in a foreign network, delete the 
+		 * rules to RT6_TABLE_MIP6
+		 */
+		if (hai->reg_mcoa) {
+			list_for_each(l, &hai->mob_net_prefixes) {
+				struct prefix_list_entry *p = NULL;
+				p = list_entry(l, struct prefix_list_entry, 
+						list);
+				rule_del(NULL, RT6_TABLE_MIP6,
+					IP6_RULE_PRIO_MIP6_FWD, 0, 
+					RTN_UNICAST, &p->ple_prefix, 
+					p->ple_plen, &in6addr_any, 0, 0);
+			}
+		}
 		if (hai->hoa.iif != hai->if_tunnel) {
 			mha.if_next = hai->if_tunnel;
-			addr_do(&hai->hoa.addr, hai->plen,
-				hai->hoa.iif, &mha, mv_hoa);
+			/* MCoA 
+			 * HoA is moved if we do not use MCoA,
+			 * HoA is copied if we use MCoA
+			 */
+			if (hai->reg_mcoa)
+				addr_do(&hai->hoa.addr, hai->plen,
+					hai->hoa.iif, &mha, cp_hoa);
+			else
+				addr_do(&hai->hoa.addr, hai->plen,
+					hai->hoa.iif, &mha, mv_hoa);
 		}
 		do_handoff(hai);
 	}
@@ -1951,6 +2619,7 @@ static int mn_do_dad(struct home_addr_in
 	struct in6_addr solicit;
 	int type = FLUSH_ALL;
 
+	assert(hai->current_coa);	
 	if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
 		return ret;
 
@@ -1963,22 +2632,22 @@ static int mn_do_dad(struct home_addr_in
 		   sizeof(struct icmp6_filter));
 
 	ipv6_addr_solict_mult(&hai->hoa.addr, &solicit);
-	if_mc_group(sock, hai->primary_coa.iif, &in6addr_all_nodes_mc, 
+	if_mc_group(sock, hai->current_coa->iif, &in6addr_all_nodes_mc, 
 		    IPV6_JOIN_GROUP);
-	if_mc_group(sock, hai->primary_coa.iif, &solicit, IPV6_JOIN_GROUP);
+	if_mc_group(sock, hai->current_coa->iif, &solicit, IPV6_JOIN_GROUP);
 
 	if (hai->home_reg_status == HOME_REG_NONE) {
 		if (hai->lladdr_comp) {
 			struct in6_addr lladdr;
 			ipv6_addr_llocal(&hai->hoa.addr, &lladdr);
 			if (mn_addr_do_dad(sock, NULL, &lladdr, 64, 
-					   hai->primary_coa.iif, 0) < 0) {
+					   hai->current_coa->iif, 0) < 0) {
 				MDBG("Link-local DAD failed!\n");
 				goto err;
 			}
 		}
 		if (mn_addr_do_dad(sock, hai, &hai->hoa.addr, hai->plen, 
-				   hai->primary_coa.iif, 0) < 0) {
+				   hai->current_coa->iif, 0) < 0) {
 			MDBG("HoA DAD failed!\n");
 			goto err;
 		}
@@ -1986,7 +2655,7 @@ static int mn_do_dad(struct home_addr_in
 			mn_send_home_na(hai);
 		ret = mn_move(hai);
 	} else if (!mn_addr_do_dad(sock, hai, &hai->hoa.addr, 
-				   hai->plen, hai->primary_coa.iif, 1)) {
+				   hai->plen, hai->current_coa->iif, 1)) {
 		ret = mn_move(hai);
 	}
 out:
@@ -2009,15 +2678,19 @@ static inline void mn_update_coa_lifetim
 
 static void mn_coa_updated(struct home_addr_info *hai)
 {
+	assert(hai->current_coa);
 	if (!hai->at_home) {
 		struct bulentry *e;
 		struct mn_addr *coa;
 		
-		e = bul_get(hai, NULL, &hai->ha_addr);
+		e = bul_get(hai, NULL, &hai->ha_addr, 
+			    hai->reg_mcoa?
+			    get_bid_from_ifindex(hai->current_coa->iif)
+			    :MCOA_NO_BID);
 		if (e == NULL || !(e->flags & IP6_MH_BU_HOME))
 			return;
 		
-		coa = &hai->primary_coa;
+		coa = hai->current_coa;
 		
 		MDBG2("preferred_time %u valid_life %u\n",
 		      coa->preferred_time.tv_sec,
@@ -2075,6 +2748,10 @@ static struct md_inet6_iface *mn_get_ifa
 	list_for_each(l, iface_list) {
 		struct md_inet6_iface *iface;
 		iface = list_entry(l, struct md_inet6_iface, list);
+		/* MCoA TODO1
+		 * If MCoA is used, MCoA preference must be used instead
+		 * of interfaces preference
+		 */
 		if (mn_verify_iface(iface) &&
 		    (best_iface == NULL ||
 		     (best_iface)->preference > iface->preference ||
@@ -2119,6 +2796,106 @@ static struct md_coa *mn_get_coa(const s
 	return best_coa;
 }
 
+static int mcoa_mn_make_ho_verdict(const struct movement_event *me,
+			      const struct home_addr_info *hai, 
+			      struct md_router **next_rtr,
+			      struct md_coa **next_coa)
+{
+	/* MCoA 
+	 * The handoff algorithm is different from 
+	 * mn_make_ho_verdict because we do not perform vertical 
+	 * handover, but only horizontal handovers.
+	 */
+	struct md_router *rtr;
+	struct md_coa *coa = NULL;
+	int force = 0;
+
+	/* MCoA TODO1
+	 * For Home-CoA support, remove this if(){}
+	 * BUT Home-CoA seems to be deprecated
+	 */
+	if (me->iface != NULL && 
+	    (rtr = md_get_first_router(&me->iface->default_rtr)) != NULL &&
+	    mn_is_at_home(&rtr->prefixes, &hai->home_prefix, hai->home_plen)) {
+		*next_rtr = rtr;
+		*next_coa = NULL;
+		return MN_HO_RETURN_HOME;
+	}
+	switch (me->event_type) {
+	case ME_DHAAD:
+		force = 1;
+		break;
+	case ME_IFACE_DOWN:
+	case ME_LINK_DOWN:
+	case ME_RTR_EXPIRED:
+		break;
+	case ME_RTR_NEW:
+		assert(!list_empty(&me->iface->default_rtr));
+	case ME_LINK_UP:
+		assert(me->iface != NULL);
+		break;
+	case ME_RTR_BACK:
+	case ME_RTR_UPDATED:
+		assert(me->iface != NULL);
+		assert(!list_empty(&me->iface->default_rtr));
+
+		*next_coa = md_get_coa(&me->iface->coas,
+				       &hai->current_coa->addr);
+		if (*next_coa == NULL)
+			break;
+		*next_rtr = md_get_first_router(&me->iface->default_rtr);
+		return MN_HO_REESTABLISH;
+	case ME_COA_NEW:
+		assert(me->iface != NULL);
+		assert(me->coa != NULL);
+		assert(me->iface->ifindex == me->coa->ifindex);
+		break;
+	case ME_COA_EXPIRED:
+		assert(me->iface != NULL);
+		assert(me->coa != NULL);
+		assert(me->iface->ifindex == me->coa->ifindex);
+
+		if (IN6_ARE_ADDR_EQUAL(&hai->current_coa->addr, 
+					&me->coa->addr))
+			break;
+		return MN_HO_IGNORE;		
+	case ME_COA_LFT_DEC:
+	case ME_COA_LFT_INC:
+		assert(me->iface != NULL);
+		assert(me->coa != NULL);
+		assert(me->iface->ifindex == me->coa->ifindex);
+
+		if (!IN6_ARE_ADDR_EQUAL(&hai->current_coa->addr,
+					&me->coa->addr)) {
+			return MN_HO_IGNORE;
+		}
+
+		*next_coa = me->coa;
+		return MN_HO_CHECK_LIFETIME;
+	default:
+		return MN_HO_IGNORE;
+	}
+	if (!mn_verify_iface(me->iface))
+		return MN_HO_INVALIDATE;
+
+	coa = mn_get_coa(hai, me->iface->ifindex,
+			 &hai->current_coa->addr, &me->iface->coas);
+
+	if (coa == NULL) 
+		return MN_HO_INVALIDATE;
+
+	if (!force && 
+	    IN6_ARE_ADDR_EQUAL(&coa->addr, &hai->current_coa->addr)) 
+		return MN_HO_IGNORE;
+
+	if (list_empty(&me->iface->default_rtr)) 
+		return MN_HO_IGNORE;
+
+	*next_rtr = md_get_first_router(&me->iface->default_rtr);
+	*next_coa = coa;
+	return MN_HO_PROCEED;
+}
+
 static int mn_make_ho_verdict(const struct movement_event *me,
 			      const struct home_addr_info *hai, 
 			      struct md_router **next_rtr,
@@ -2127,9 +2904,11 @@ static int mn_make_ho_verdict(const stru
 	struct md_inet6_iface *old_iface = NULL, *new_iface = NULL;
 	struct md_router *rtr;
 	struct md_coa *coa = NULL;
-	int pref_iif = hai->primary_coa.iif; /* prefer current CoA interface */
+	int pref_iif;
 	int force = 0;
 
+	assert(hai->current_coa);
+	pref_iif = hai->current_coa->iif; /* prefer current CoA interface */
 	if (me->iface != NULL && 
 	    (rtr = md_get_first_router(&me->iface->default_rtr)) != NULL &&
 	    mn_is_at_home(&rtr->prefixes, &hai->home_prefix, hai->home_plen)) {
@@ -2170,7 +2949,7 @@ static int mn_make_ho_verdict(const stru
 			return MN_HO_IGNORE;
 
 		*next_coa = md_get_coa(&old_iface->coas,
-				       &hai->primary_coa.addr);
+				       &hai->current_coa->addr);
 		if (*next_coa == NULL)
 			break;
 		*next_rtr = md_get_first_router(&old_iface->default_rtr);
@@ -2187,7 +2966,7 @@ static int mn_make_ho_verdict(const stru
 
 		if (old_iface == NULL ||
 		    (old_iface == me->iface &&
-		     IN6_ARE_ADDR_EQUAL(&hai->primary_coa.addr, 
+		     IN6_ARE_ADDR_EQUAL(&hai->current_coa->addr, 
 					&me->coa->addr)))
 			break;
 		return MN_HO_IGNORE;		
@@ -2201,7 +2980,7 @@ static int mn_make_ho_verdict(const stru
 			break;
 
 		if (old_iface != me->iface ||
-		    !IN6_ARE_ADDR_EQUAL(&hai->primary_coa.addr,
+		    !IN6_ARE_ADDR_EQUAL(&hai->current_coa->addr,
 					&me->coa->addr))
 			return MN_HO_IGNORE;
 
@@ -2216,13 +2995,13 @@ static int mn_make_ho_verdict(const stru
 		return MN_HO_INVALIDATE;
 
 	coa = mn_get_coa(hai, new_iface->ifindex,
-			 &hai->primary_coa.addr, &new_iface->coas);
+			 &hai->current_coa->addr, &new_iface->coas);
 
 	if (coa == NULL)
 		return MN_HO_INVALIDATE;
 
 	if (!force && new_iface == old_iface && 
-	    IN6_ARE_ADDR_EQUAL(&coa->addr, &hai->primary_coa.addr))
+	    IN6_ARE_ADDR_EQUAL(&coa->addr, &hai->current_coa->addr))
 		return MN_HO_IGNORE;
 
 	if (list_empty(&new_iface->default_rtr))
@@ -2231,6 +3010,100 @@ static int mn_make_ho_verdict(const stru
 	*next_rtr = md_get_first_router(&new_iface->default_rtr);
 	*next_coa = coa;
 	return MN_HO_PROCEED;
+}
+
+static void mcoa_mn_chk_ho_verdict(struct home_addr_info *hai,
+			      const struct movement_event *event)
+{
+	struct md_router *rtr = NULL;
+	struct md_coa *coa = NULL;
+	struct bulentry *bule;
+	int move_home = 0;	
+	int active_coa = 0;
+
+	if ((bule = bul_get(hai,  NULL, &hai->ha_addr, 
+			hai->reg_mcoa?
+			get_bid_from_ifindex(event->iface->ifindex)
+			:MCOA_NO_BID)))
+		active_coa = mcoa_bule_count(bule);
+
+	if (event->event_type == ME_COA_EXPIRED &&
+	    IN6_ARE_ADDR_EQUAL(&event->coa->addr, &hai->hoa.addr))
+		return;
+
+	hai->current_coa = mcoa_get_current_coa(hai, event->iface->ifindex);
+	if (hai->current_coa == NULL)
+		return;
+
+	hai->verdict = mcoa_mn_make_ho_verdict(event, hai, &rtr, &coa);
+	MDBG2("Verdict %d for iface %d\n", 
+		hai->verdict, hai->current_coa->iif);
+
+	if (hai->verdict == MN_HO_IGNORE)
+		return;
+
+	if (hai->verdict == MN_HO_INVALIDATE) {
+		/* MCoA 
+		 * Unless this is the last available interface
+		 * Rules related to this iface are deleted, 
+		 * BULE is deleted, and deregistration BU is sent
+		 */
+		if (active_coa > 1 
+		    && (bule = mcoa_iface_rules_del(hai->current_coa->iif, 
+						    hai, 1)))
+			/* MCoA TODO2
+			 * Send a deregistration BU 
+			 * Is it the correct place to do that? 
+			 * because when BACK is received, BULE has 
+			 * already been deleted
+			 * (actually BAck cannot be received at the BULE's CoA
+			 * because the iface is down...)
+			 */
+			mn_dereg_bule(bule);
+		hai->current_coa = NULL;
+		return;
+	} 
+	if (rtr != NULL)
+		move_home = mn_home_rtr_chk(hai, rtr);
+
+	if  (hai->verdict == MN_HO_CHECK_LIFETIME) {
+		mn_update_coa_lifetime(hai->current_coa, coa);
+		mn_coa_updated(hai);
+	} else if (hai->verdict == MN_HO_REESTABLISH) {
+		mn_update_coa_lifetime(hai->current_coa, coa);
+		mn_coa_updated(hai);
+	} else { 
+		switch (hai->verdict) {
+		case MN_HO_PROCEED:
+			hai->current_coa->addr = coa->addr;
+			mn_update_coa_lifetime(hai->current_coa, coa);
+			/* MCoA
+			 * Rules related to this iface are added if needed
+			 */
+			mcoa_iface_rules_add(hai->current_coa->iif, hai);
+			break;
+		case MN_HO_RETURN_HOME:
+			hai->current_coa->addr = hai->hoa.addr;
+		        mcoa_iface_rules_del(hai->current_coa->iif, hai, 1);
+			break;
+		default:
+			return;
+		}
+		if (hai->at_home && !hai->pend_ba) {
+			/* check if router is HA */
+			if (hai->home_reg_status != HOME_REG_NONE &&
+			    rtr_addr_chk(rtr, &hai->ha_addr)) {
+				mn_move(hai);
+			} else if (hai->home_reg_status != HOME_REG_NONE ||
+				   move_home) {
+				int type = FLUSH_FAILED;
+				mn_do_dad(hai, 0);
+				if (hai->home_reg_status == HOME_REG_NONE)
+					bul_iterate(&hai->bul,
+						    _bul_flush, &type);
+			}
+		}
+	}
 }
 
 static void mn_chk_ho_verdict(struct home_addr_info *hai,
@@ -2244,35 +3117,36 @@ static void mn_chk_ho_verdict(struct hom
 	    IN6_ARE_ADDR_EQUAL(&event->coa->addr, &hai->hoa.addr))
 		return;
 
+	assert(hai->current_coa);
 	hai->verdict = mn_make_ho_verdict(event, hai, &rtr, &coa);
 
 	if (hai->verdict == MN_HO_IGNORE)
 		return;
 
 	if (hai->verdict == MN_HO_INVALIDATE) {
-		hai->primary_coa.iif = 0;
+		hai->current_coa->iif = 0;
 		return;
 	} 
 	if (rtr != NULL)
 		move_home = mn_home_rtr_chk(hai, rtr);
 
 	if (hai->verdict == MN_HO_CHECK_LIFETIME) {
-		mn_update_coa_lifetime(&hai->primary_coa, coa);
+		mn_update_coa_lifetime(hai->current_coa, coa);
 		mn_coa_updated(hai);
 	} else if (hai->verdict == MN_HO_REESTABLISH) {
-		hai->primary_coa.iif = coa->ifindex;
-		mn_update_coa_lifetime(&hai->primary_coa, coa);
+		hai->current_coa->iif = coa->ifindex;
+		mn_update_coa_lifetime(hai->current_coa, coa);
 		mn_coa_updated(hai);
 	} else { 
 		switch (hai->verdict) {
 		case MN_HO_PROCEED:
-			hai->primary_coa.iif = coa->ifindex;
-			hai->primary_coa.addr = coa->addr;
-			mn_update_coa_lifetime(&hai->primary_coa, coa);
+			hai->current_coa->iif = coa->ifindex;
+			hai->current_coa->addr = coa->addr;
+			mn_update_coa_lifetime(hai->current_coa, coa);
 			break;
 		case MN_HO_RETURN_HOME:
-			hai->primary_coa.iif = rtr->ifindex;
-			hai->primary_coa.addr = hai->hoa.addr;
+			hai->current_coa->iif = rtr->ifindex;
+			hai->current_coa->addr = hai->hoa.addr;
 			break;
 		default:
 			return;
@@ -2311,14 +3185,20 @@ int mn_movement_event(struct movement_ev
 				return 0;
 			}
 			dhaad_stop(hai);
-			mn_chk_ho_verdict(hai, event);
+			if (hai->reg_mcoa)
+				mcoa_mn_chk_ho_verdict(hai, event);
+			else	
+				mn_chk_ho_verdict(hai, event);
 		} else {
 			if (event->event_type == ME_COA_EXPIRED)
 				mn_rr_delete_co(&event->coa->addr);
 			list_for_each(lh, &home_addr_list) {
 				hai = list_entry(lh, 
 						 struct home_addr_info, list);
-				mn_chk_ho_verdict(hai, event);
+				if (hai->reg_mcoa)
+					mcoa_mn_chk_ho_verdict(hai, event);
+				else	
+					mn_chk_ho_verdict(hai, event);
 			}
 		}
 	}
@@ -2397,7 +3277,9 @@ void mn_start_ro(struct in6_addr *cn, st
 		     "from %x:%x:%x:%x:%x:%x:%x:%x\n", 
 		     NIP6ADDR(cn), NIP6ADDR(hoa));
 
-		bule = bul_get(NULL, hoa, cn);
+		bule = bul_get(NULL, hoa, cn, 
+			       hai->reg_mcoa?
+			       get_bid_from_ifindex(iif):MCOA_NO_BID);
 
 		if (bule) {
 			/* If BUL entry exists, RR is done or in progress */
@@ -2405,7 +3287,9 @@ void mn_start_ro(struct in6_addr *cn, st
 			return;
 		}
 
-		bule = create_bule(hoa, cn);
+		bule = create_bule(hoa, cn,
+				   hai->reg_mcoa?
+				   get_bid_from_ifindex(iif):MCOA_NO_BID);
 		if (!bule) {
 			MDBG("Malloc failed at starting of RO\n");
 			pthread_rwlock_unlock(&mn_lock);
@@ -2419,6 +3303,18 @@ void mn_start_ro(struct in6_addr *cn, st
 		bule->coa_changed = -1;
 		bule->home = hai;
 		bule->rr.state = RR_NOT_STARTED;
+		bule->priority = get_prio_from_bid(bule->bid);
+		/* MCoA TODO2
+		 * At the moment we prevent MIPL from installing rules for 
+		 * this entry
+		 */
+		bule->rules = 1;
+		/* MCoA TODO2
+		 * Support for CN
+		 * The table index must be filled with 
+		 * mcoa_mn_init_rt_table(bule)
+		 */
+		bule->table = RT6_TABLE_MIP6;
 
 		mn_reset_ro_bule(bule);
 
@@ -2451,7 +3347,10 @@ static void mn_recv_brr(const struct ip6
 	hoa = in->dst;
 	pthread_rwlock_wrlock(&mn_lock);
 	/* Do we have BUL entry for cn?  If not, drop. */
-	if ((e = bul_get(NULL, hoa, cn)) == NULL) {
+	/* MCoA TODO2
+	 * get BID from BRR BUI option
+	 */
+	if ((e = bul_get(NULL, hoa, cn, get_bid_from_ifindex(iif))) == NULL) {
 		pthread_rwlock_unlock(&mn_lock);
 		return;
 	}
@@ -2499,7 +3398,11 @@ static void mn_recv_be(const struct ip6_
 		hoa = in->dst;
 
 	/* Do we have BUL entry for cn?  If not, drop. */
-	if ((e = bul_get(NULL, hoa, cn)) == NULL || e->type != BUL_ENTRY)
+	/* MCoA TODO2
+	 * get BID from BUI option in BERROR
+	 */
+	if ((e = bul_get(NULL, hoa, cn, get_bid_from_ifindex(iif))) == NULL 
+	    || e->type != BUL_ENTRY)
 		goto out;
 
 	clock_gettime(CLOCK_REALTIME, &now);
diff -r 82fcd4bea972 src/mn.h
--- a/src/mn.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/mn.h	Fri Jan 11 17:02:25 2008 +0100
@@ -52,6 +52,7 @@ struct ha_candidate_list {
 #define	NEMO_FWD_BLOCK	0x10
 
 struct mn_addr {
+	struct list_head list;
 	struct in6_addr addr;
 	int iif;
 	struct timespec timestamp;
@@ -75,7 +76,8 @@ struct home_addr_info {
 	uint8_t home_plen;
 	struct in6_addr home_prefix;
 	struct hash bul; /* Binding Update List */
-	struct mn_addr primary_coa;
+	struct mn_addr *current_coa; /* MCoA: pointer to a member of mcoa */
+	struct list_head mcoa; /* MCoA: list of all active CoAs */
 	struct list_head ro_policies;
 	struct ha_candidate_list ha_list;	
 	struct in6_addr ha_addr;
@@ -84,9 +86,11 @@ struct home_addr_info {
 	int if_tunnel;
 	int if_home;
 	int if_block;
+	int table; /* MCoA: routing table number where routes are stored */
 	short hwalen;
 	uint8_t altcoa;
 	uint16_t mob_rtr;
+	uint8_t reg_mcoa;
 	char name[IF_NAMESIZE];
 	int mnp_count;
 	struct list_head mob_net_prefixes;
diff -r 82fcd4bea972 src/movement.c
--- a/src/movement.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/movement.c	Fri Jan 11 17:02:25 2008 +0100
@@ -165,7 +165,7 @@ static void md_prefix_rule_del(struct pr
 	struct in6_addr prefix;
 	ipv6_addr_prefix(&prefix, &p->ple_prefix, p->ple_plen);
 	rule_del(NULL, RT6_TABLE_MAIN, IP6_RULE_PRIO_MIP6_COA_OUT,
-		 RTN_UNICAST, &prefix, p->ple_plen,
+		 0, RTN_UNICAST, &prefix, p->ple_plen,
 		 &in6addr_any, 0, 0);
 }
 
@@ -312,10 +312,10 @@ static void md_expire_router(struct md_i
 
 static void md_block_rule_del(struct md_inet6_iface *iface)
 {
-	rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK_HOA, RTN_BLACKHOLE,
+ 	rule_del(NULL, 0, IP6_RULE_PRIO_MIP6_BLOCK_HOA, 0, RTN_BLACKHOLE,
 		 &in6addr_any, 0, &in6addr_any, 0, 0);
-	rule_del(NULL, RT6_TABLE_MAIN, IP6_RULE_PRIO_MIP6_COA_OUT, RTN_UNICAST,
-		 &in6addr_any, 128, &in6addr_any, 0, 0);
+ 	rule_del(NULL, RT6_TABLE_MAIN, IP6_RULE_PRIO_MIP6_COA_OUT, 0, 
+		 RTN_UNICAST, &in6addr_any, 128, &in6addr_any, 0, 0);
 	iface->iface_flags &= ~MD_BLOCK_TRAFFIC;
 }
 
@@ -1290,7 +1290,7 @@ static void md_prefix_rule_add(struct pr
 	struct in6_addr prefix;
 	ipv6_addr_prefix(&prefix, &p->ple_prefix, p->ple_plen);
 	rule_add(NULL, RT6_TABLE_MAIN, IP6_RULE_PRIO_MIP6_COA_OUT,
-		 RTN_UNICAST, &prefix, p->ple_plen,
+		 0, RTN_UNICAST, &prefix, p->ple_plen,
 		 &in6addr_any, 0, 0);
 }
 
@@ -1361,13 +1361,13 @@ static int md_block_rule_add(struct md_i
 	iface->iface_flags |= MD_BLOCK_TRAFFIC;
 	/* Allow DAD probes and RS messages */
 	rule_add(NULL, RT6_TABLE_MAIN,
-		 IP6_RULE_PRIO_MIP6_COA_OUT, RTN_UNICAST,
+ 		 IP6_RULE_PRIO_MIP6_COA_OUT, 0, RTN_UNICAST,
 		 &in6addr_any, 128, &in6addr_any, 0, 0);
 	/* drop outgoing global traffic until DAD has been performed
 	   on CoA to make routing and tunnel end-point updates atomic
 	   during handoff */
 	return rule_add(NULL, 0,
-			IP6_RULE_PRIO_MIP6_BLOCK_HOA, RTN_BLACKHOLE,
+ 			IP6_RULE_PRIO_MIP6_BLOCK_HOA, 0, RTN_BLACKHOLE,
 			&in6addr_any, 0, &in6addr_any, 0, 0);
 }
 
diff -r 82fcd4bea972 src/mpdisc_ha.c
--- a/src/mpdisc_ha.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/mpdisc_ha.c	Fri Jan 11 17:02:25 2008 +0100
@@ -68,7 +68,7 @@ static inline struct mpa_entry *mpa_get(
 					const struct in6_addr *hoa)
 					
 {
-	return hash_get(&mpa_hash, ha, hoa);
+	return hash_get(&mpa_hash, ha, hoa, 0);
 }
 
 /* HA functions */
@@ -214,7 +214,7 @@ void mpd_cancel_mpa(const struct in6_add
 	pthread_mutex_lock(&mpa_lock);
 	e = mpa_get(ha, hoa);
 	if (e != NULL) {
-		hash_delete(&mpa_hash, &e->ha, &e->hoa);
+		hash_delete(&mpa_hash, &e->ha, &e->hoa, 0);
 		if (tsisset(e->delay))
 			del_task(&e->tqe);
 		free(e);
@@ -239,7 +239,7 @@ int mpd_start_mpa(const struct in6_addr 
 		memset(e, 0, sizeof(struct mpa_entry));
 		e->ha = *ha;
 		e->hoa = *hoa;
-		if (hash_add(&mpa_hash, e, &e->ha, &e->hoa)) {
+		if (hash_add(&mpa_hash, e, &e->ha, &e->hoa, NULL)) {
 			free(e);
 			goto out;
 		}
diff -r 82fcd4bea972 src/mpdisc_mn.c
--- a/src/mpdisc_mn.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/mpdisc_mn.c	Fri Jan 11 17:02:25 2008 +0100
@@ -65,7 +65,7 @@ static inline struct mps_entry *mps_get(
 static inline struct mps_entry *mps_get(const struct in6_addr *hoa,
 					const struct in6_addr *ha)
 {
-	return hash_get(&mps_hash, hoa, ha);
+	return hash_get(&mps_hash, hoa, ha, 0);
 }
 
 #ifdef ENABLE_VT
@@ -116,7 +116,7 @@ void mpd_cancel_mps(const struct in6_add
 	e = mps_get(hoa, ha);
 	if (e != NULL) {
 		dbg("canceling MPS\n");
-		hash_delete(&mps_hash, &e->hoa, &e->ha);
+		hash_delete(&mps_hash, &e->hoa, &e->ha, 0);
 		if (tsisset(e->delay))
 			del_task(&e->tqe);
 		free(e);
@@ -175,7 +175,7 @@ int mpd_schedule_first_mps(const struct 
 		memset(e, 0, sizeof(struct mps_entry));
 		e->hoa = *hoa;
 		e->ha = *ha;
-		if (hash_add(&mps_hash, e, &e->hoa, &e->ha)) {
+		if (hash_add(&mps_hash, e, &e->hoa, &e->ha, NULL)) {
 			free(e);
 			goto out;
 		}
diff -r 82fcd4bea972 src/ndisc.h
--- a/src/ndisc.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/ndisc.h	Fri Jan 11 17:02:25 2008 +0100
@@ -21,6 +21,7 @@ static inline short nd_get_l2addr_len(un
 		return 1;
 		/* supported virtual devices */
 	case ARPHRD_SIT:
+		return 4;
 	case ARPHRD_TUNNEL6:
 	case ARPHRD_PPP:
 	case ARPHRD_IPGRE:
diff -r 82fcd4bea972 src/policy.c
--- a/src/policy.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/policy.c	Fri Jan 11 17:02:25 2008 +0100
@@ -47,6 +47,25 @@ pthread_rwlock_t policy_lock;
 pthread_rwlock_t policy_lock;
 struct hash policy_bind_acl_hash;
 int def_bind_policy = IP6_MH_BAS_PROHIBIT;
+
+/**
+ * default_mcoa_reg - Check if MCoA reg is allowed
+ * @remote_hoa: remote MN's home address
+ *
+ * Returns 0 if not allowed, 1 if allowed.
+ **/
+int default_mcoa_reg(const struct in6_addr *remote_hoa) 
+{
+	struct policy_bind_acl_entry *acl;
+	int ret = 1;
+
+	pthread_rwlock_rdlock(&policy_lock);
+	acl = hash_get(&policy_bind_acl_hash, NULL, remote_hoa, 0);
+	if (!conf.HaAcceptMCoAReg || (acl && !acl->mcoa_reg))
+		ret = 0;
+	pthread_rwlock_unlock(&policy_lock);
+	return ret;
+}
 
 /**
  * default_best_iface - select best interface during handoff
@@ -148,7 +167,7 @@ int default_discard_binding(const struct
 		return IP6_MH_BAS_MR_OP_NOT_PERMITTED;
 
 	pthread_rwlock_rdlock(&policy_lock);
-	acl = hash_get(&policy_bind_acl_hash, NULL, remote_hoa);
+	acl = hash_get(&policy_bind_acl_hash, NULL, remote_hoa, 0);
 	if (acl != NULL) {
 		ret = acl->bind_policy;
 		if (ret < IP6_MH_BAS_UNSPECIFIED &&
@@ -213,8 +232,16 @@ int default_accept_inet6_iface(int iif)
 		struct net_iface *nif;
 		nif = list_entry(list, struct net_iface, list);
 		if (nif->ifindex == iif) {
-			if (is_if_mn(nif)) 
-				return nif->mn_if_preference;
+			if (is_if_mn(nif)) {
+				/* MCoA 
+				 * Return the BID Priority if the 
+				 * interface preference is 0
+				 */
+				if(nif->mn_if_preference)
+					return nif->mn_if_preference;
+				else
+					return nif->mn_if_bidprio;
+			}
 			return 0;
 		}
 	}
@@ -259,7 +286,7 @@ int default_get_mnp_count(const struct i
 	int ret = 0;
 	struct policy_bind_acl_entry *acl;
 	pthread_rwlock_rdlock(&policy_lock);
-	acl = hash_get(&policy_bind_acl_hash, NULL, hoa);
+	acl = hash_get(&policy_bind_acl_hash, NULL, hoa, 0);
 	if (acl != NULL)
 		ret = acl->mnp_count;
 	pthread_rwlock_unlock(&policy_lock);
@@ -275,7 +302,7 @@ int default_get_mnps(const struct in6_ad
 	struct policy_bind_acl_entry *acl;
 
 	pthread_rwlock_rdlock(&policy_lock);
-	acl = hash_get(&policy_bind_acl_hash, NULL, hoa);
+	acl = hash_get(&policy_bind_acl_hash, NULL, hoa, 0);
 	if (acl != NULL) {
 		struct list_head *l;
 		list_for_each(l, &acl->mob_net_prefixes) {
@@ -314,7 +341,7 @@ static int policy_bind_acl_add(struct po
 static int policy_bind_acl_add(struct policy_bind_acl_entry *acl)
 {
 	int err;
-	err = hash_add(&policy_bind_acl_hash, acl, NULL, &acl->hoa);
+	err = hash_add(&policy_bind_acl_hash, acl, NULL, &acl->hoa, NULL);
 	if (!err) {
 		list_del(&acl->list);
 	}
diff -r 82fcd4bea972 src/policy.h
--- a/src/policy.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/policy.h	Fri Jan 11 17:02:25 2008 +0100
@@ -6,7 +6,13 @@
 #include <netinet/in.h>
 #include "list.h"
 
-#define POL_MN_IF_DEF_PREFERENCE 5
+/* MCoA 
+ * Changed POL_MN_IF_DEF_PREFERENCE from 5 to 0
+ */
+#define POL_MN_IF_DEF_PREFERENCE 0
+#define POL_MN_IF_DEF_BID 0
+#define POL_MN_IF_DEF_BID_PRIORITY 0
+#define POL_MN_IF_DEF_RELIABLE 1
 
 struct ip6_mh_binding_update;
 struct nd_router_advert;
@@ -17,9 +23,18 @@ struct policy_bind_acl_entry {
 	struct in6_addr hoa;
 	int plen;
 	int bind_policy;
+	int mcoa_reg;
 	int mnp_count;
 	struct list_head mob_net_prefixes;
 };
+
+/**
+ * default_mcoa_reg - Check if MCoA registration is allowed
+ * @remote_hoa: remote MN's home address
+ * 
+ * Returns 0 if not allowed, 1 if allowed.
+ **/
+int default_mcoa_reg(const struct in6_addr *remote_hoa);
 
 /**
  * default_best_iface - select best interface during handoff
diff -r 82fcd4bea972 src/retrout.c
--- a/src/retrout.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/retrout.c	Fri Jan 11 17:02:25 2008 +0100
@@ -151,7 +151,7 @@ static struct rrlentry *rre_create(int t
 	rre->own2 = *own2;
 	rre->iif = iif;
 
-	if (hash_add(&rrl_hash, rre, &rre->own1, &rre->peer) < 0) {
+	if (hash_add(&rrl_hash, rre, &rre->own1, &rre->peer, NULL) < 0) {
 		rrl_delete(rre);
 		return NULL;
 	}
@@ -168,7 +168,7 @@ static struct rrlentry *rrl_get(int type
 
 	assert(our_addr);
 
-	rre = (struct rrlentry *)hash_get(&rrl_hash, our_addr, peer_addr);
+	rre = (struct rrlentry *)hash_get(&rrl_hash, our_addr, peer_addr, 0);
 
 	if (rre != NULL && rre->type != type)
 		return NULL;
@@ -188,7 +188,7 @@ static void rrl_delete(struct rrlentry *
 	TRACE;
 
 	del_task(&rre->tqe);
-	hash_delete(&rrl_hash, &rre->own1, &rre->peer);
+	hash_delete(&rrl_hash, &rre->own1, &rre->peer, 0);
 
 	if (rre->type == COT_ENTRY) {
 		struct list_head *list, *n;
@@ -629,7 +629,7 @@ int mn_rr_error_check(const struct in6_a
 		      const struct in6_addr *peer,
 		      struct in6_addr *hoa)
 {
-	struct rrlentry *rre = hash_get(&rrl_hash, own, peer);
+	struct rrlentry *rre = hash_get(&rrl_hash, own, peer, 0);
 
 	if (rre == NULL || !rre->wait)
 		return 0;
@@ -694,7 +694,11 @@ static void mn_recv_cot(const struct ip6
 		struct addr_holder *ah;
 
 		ah = list_entry(list, struct addr_holder, list);
-		bule = bul_get(NULL, &ah->addr, cn_addr);
+		/* MCoA TODO2
+		 * MCoA does not work with RR
+		 * At the moment the first entry that match is chosen
+		 */
+		bule = bul_get(NULL, &ah->addr, cn_addr, MCOA_NO_BID);
 
 		if (bule == NULL || bule->type != BUL_ENTRY ||
 		    !IN6_ARE_ADDR_EQUAL(&rre_co->own1, &bule->coa)) {
@@ -774,7 +778,11 @@ static void mn_recv_hot(const struct ip6
 		pthread_rwlock_unlock(&mn_lock);
 		return;
 	}
-	bule = bul_get(NULL, home_addr, cn_addr);
+	/* MCoA TODO2
+	 * MCoA does not work with RR
+	 * At the moment the first entry that match is chosen
+	 */
+	bule = bul_get(NULL, home_addr, cn_addr, MCOA_NO_BID);
 
 	if (bule == NULL || bule->type != BUL_ENTRY) {
 		rrl_delete(rre_ho);
diff -r 82fcd4bea972 src/rtnl.c
--- a/src/rtnl.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/rtnl.c	Fri Jan 11 17:02:25 2008 +0100
@@ -299,7 +299,7 @@ int route_del(int oif, uint8_t table, ui
 }
 
 static int rule_mod(const char *iface, int cmd, uint8_t table,
-		    uint32_t priority, uint8_t action,
+		    uint32_t priority, uint32_t fwmark, uint8_t action,
 		    const struct in6_addr *src, int src_plen,
 		    const struct in6_addr *dst, int dst_plen, int flags)
 {
@@ -331,6 +331,8 @@ static int rule_mod(const char *iface, i
 		addattr_l(n, sizeof(buf), RTA_SRC, src, sizeof(*src));
 	if (priority)
 		addattr32(n, sizeof(buf), RTA_PRIORITY, priority);
+	if (fwmark)
+		addattr32(n, sizeof(buf), RTA_PROTOINFO, fwmark);
 	if (iface)
 		addattr_l(n, sizeof(buf), RTA_IIF, iface, strlen(iface) + 1);
 
@@ -349,12 +351,12 @@ static int rule_mod(const char *iface, i
  * negative otherwise.
  **/
 int rule_add(const char *iface, uint8_t table,
-	     uint32_t priority, uint8_t action,
+	     uint32_t priority, uint32_t fwmark, uint8_t action,
 	     const struct in6_addr *src, int src_plen,
 	     const struct in6_addr *dst, int dst_plen, int flags)
 {
 	return rule_mod(iface, RTM_NEWRULE, table,
-			priority, action,
+			priority, fwmark, action,
 			src, src_plen, dst, dst_plen, flags);
 }
 
@@ -370,12 +372,12 @@ int rule_add(const char *iface, uint8_t 
  * otherwise.
  **/
 int rule_del(const char *iface, uint8_t table,
-	     uint32_t priority, uint8_t action,
+	     uint32_t priority, uint32_t fwmark, uint8_t action,
 	     const struct in6_addr *src, int src_plen,
 	     const struct in6_addr *dst, int dst_plen, int flags)
 {
 	return rule_mod(iface, RTM_DELRULE, table,
-			priority, action,
+			priority, fwmark, action,
 			src, src_plen, dst, dst_plen, flags);
 }
 
diff -r 82fcd4bea972 src/rtnl.h
--- a/src/rtnl.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/rtnl.h	Fri Jan 11 17:02:25 2008 +0100
@@ -17,11 +17,14 @@
 #define IP6_RT_PRIO_ADDRCONF 256
 
 #define IP6_RULE_PRIO_MIP6_MNP_IN    1000
-#define IP6_RULE_PRIO_MIP6_HOA_OUT   1001
-#define IP6_RULE_PRIO_MIP6_COA_OUT   1002
-#define IP6_RULE_PRIO_MIP6_BLOCK     1003
-#define IP6_RULE_PRIO_MIP6_BLOCK_HOA 1004
-#define IP6_RULE_PRIO_MIP6_FWD       1005
+#define IP6_RULE_PRIO_MIP6_HOA_OUT_MCOA   1001
+#define IP6_RULE_PRIO_MIP6_HOA_OUT   1002
+#define IP6_RULE_PRIO_MIP6_COA_OUT   1003
+#define IP6_RULE_PRIO_MIP6_BLOCK     1004
+#define IP6_RULE_PRIO_MIP6_BLOCK_HOA 1005
+#define IP6_RULE_PRIO_MIP6_FWD_FWM   1006
+#define IP6_RULE_PRIO_MIP6_FWD_MCOA  1007
+#define IP6_RULE_PRIO_MIP6_FWD       1008
 
 static inline int rtnl_route_open(struct rtnl_handle *rth, 
 				  unsigned subscriptions)
@@ -74,12 +77,12 @@ int route_del(int oif, uint8_t table, ui
 	      const struct in6_addr *gateway);
 
 int rule_add(const char *iface, uint8_t table,
-	     uint32_t priority, uint8_t action,
+	     uint32_t priority, uint32_t fwmark, uint8_t action,
 	     const struct in6_addr *src, int src_plen,
 	     const struct in6_addr *dst, int dst_plen, int flags);
 
 int rule_del(const char *iface, uint8_t table,
-	     uint32_t priority, uint8_t action,
+	     uint32_t priority, uint32_t fwmark, uint8_t action,
 	     const struct in6_addr *src, int src_plen,
 	     const struct in6_addr *dst, int dst_plen,int flags);
 
diff -r 82fcd4bea972 src/scan.l
--- a/src/scan.l	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/scan.l	Fri Jan 11 17:02:25 2008 +0100
@@ -137,7 +137,15 @@ MnDiscardHaParamProb		{ return MNDISCARD
 MnDiscardHaParamProb		{ return MNDISCARDHAPARAMPROB; }
 OptimisticHandoff		{ return OPTIMISTICHANDOFF; }
 HaAcceptMobRtr   		{ return HAACCEPTMOBRTR; }
+HaAcceptMCoAReg			{ return HAACCEPTMCOAREG; }
+MCoAReg				{ return MCOAREG; }
+NoMCoAReg			{ return NOMCOAREG; }
 IsMobRtr       			{ return ISMOBRTR; }
+Bid				{ return BID; }
+BidPriority			{ return BIDPRIORITY; }
+Reliable			{ return RELIABLE; }
+RegMultipleCoA			{ return REGMULTIPLECOA; }
+IfMultipleCoA			{ return IFMULTIPLECOA; }
 HaServedPrefix       	       	{ return HASERVEDPREFIX; }
 HomePrefix     			{ return HOMEPREFIX; }
 MobRtrUseExplicitMode    	{ return MOBRTRUSEEXPLICITMODE; }
diff -r 82fcd4bea972 src/tunnelctl.c
--- a/src/tunnelctl.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/tunnelctl.c	Fri Jan 11 17:02:25 2008 +0100
@@ -157,7 +157,7 @@ static int __tunnel_del(struct mip6_tnl 
 	if (tnl->users == 0) {
 		struct ifreq ifr;
 		list_del(&tnl->list);
-		hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr);
+		hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr, 0);
 		strcpy(ifr.ifr_name, tnl->parm.name);
 		if ((res = ioctl(tnl_fd, SIOCDELTUNNEL, &ifr)) < 0) {
 			TDBG("SIOCDELTUNNEL failed status %d %s\n", 
@@ -249,7 +249,7 @@ static struct mip6_tnl *__tunnel_add(str
 		TDBG("no device called %s\n", tnl->parm.name);
 		goto err;
 	}
-	if (hash_add(&tnl_hash, tnl, &tnl->parm.laddr, &tnl->parm.raddr) < 0)
+	if (hash_add(&tnl_hash, tnl, &tnl->parm.laddr, &tnl->parm.raddr, NULL) < 0)
 		goto err;
 	
 	list_add_tail(&tnl->list, &tnl_list);
@@ -287,7 +287,7 @@ int tunnel_add(struct in6_addr *local,
 	int res;
 
 	pthread_mutex_lock(&tnl_lock);
-	if ((tnl = hash_get(&tnl_hash, local, remote)) != NULL) { 
+	if ((tnl = hash_get(&tnl_hash, local, remote, 0)) != NULL) { 
 		tnl->users++;
 		TDBG("tunnel %s (%d) from %x:%x:%x:%x:%x:%x:%x:%x "
 		     "to %x:%x:%x:%x:%x:%x:%x:%x user count increased to %d\n",
@@ -339,9 +339,10 @@ static int __tunnel_mod(struct mip6_tnl 
 		     errno, strerror(errno));
 		return -1;
 	}
-	hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr);
+	hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr, 0);
 	memcpy(&tnl->parm, &parm, sizeof(struct ip6_tnl_parm));
-	if (hash_add(&tnl_hash, tnl, &tnl->parm.laddr, &tnl->parm.raddr) < 0) {
+	if (hash_add(&tnl_hash, tnl, &tnl->parm.laddr, 
+		     &tnl->parm.raddr, NULL) < 0) {
 		free(tnl);
 		return -1;
 	}
@@ -387,7 +388,7 @@ int tunnel_mod(int ifindex,
 	old = get_tnl(ifindex);
 	assert(old != NULL);
 
-	if ((new = hash_get(&tnl_hash, local, remote)) != NULL) {
+	if ((new = hash_get(&tnl_hash, local, remote, 0)) != NULL) {
 		if (new != old) {
 			new->users++;
 			TDBG("tunnel %s (%d) from %x:%x:%x:%x:%x:%x:%x:%x "
@@ -448,7 +449,7 @@ static int tnl_cleanup(void *data, void 
 {
 	struct mip6_tnl *tnl = (struct mip6_tnl *) data;
 	list_del(&tnl->list);
-	hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr);
+	hash_delete(&tnl_hash, &tnl->parm.laddr, &tnl->parm.raddr, 0);
 	free(tnl);
 	return 0;
 }
diff -r 82fcd4bea972 src/vt.c
--- a/src/vt.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/vt.c	Fri Jan 11 17:02:25 2008 +0100
@@ -428,6 +428,12 @@ static int bul_vt_dump(void *data, void 
 		fprintf(vh->vh_stream, " last_coa %x:%x:%x:%x:%x:%x:%x:%x",
 			  NIP6ADDR(&bule->last_coa));
 
+		if (bule->bid) {
+			fprintf(vh->vh_stream, " BID %d", bule->bid);
+			fprintf(vh->vh_stream, " BidPriority %d", 
+				bule->priority);
+		}
+
 		fprintf(vh->vh_stream, "\n");
 
 		if (!(bule->flags & IP6_MH_BU_HOME)) {
@@ -579,6 +585,11 @@ static int bcache_vt_dump(void *data, vo
 
 	fprintf(vh->vh_stream, " coa %x:%x:%x:%x:%x:%x:%x:%x",
 		NIP6ADDR(&bce->coa));
+
+	if (bce->bid) {
+		fprintf(vh->vh_stream, " BID %d", bce->bid);
+		fprintf(vh->vh_stream, " BidPriority %d", bce->priority);
+	}
 
 	if (vh->vh_opt.verbose == VT_BOOL_TRUE)
 		fprintf(vh->vh_stream, " nonce %u", bce->nonce_coa);
diff -r 82fcd4bea972 src/xfrm.c
--- a/src/xfrm.c	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/xfrm.c	Fri Jan 11 17:02:25 2008 +0100
@@ -56,7 +56,7 @@
 #include "conf.h"
 #include "ipsec.h"
 
-#define XFRM_DEBUG_LEVEL 1
+#define XFRM_DEBUG_LEVEL 2
 
 #if XFRM_DEBUG_LEVEL >= 1
 #define XDBG dbg
@@ -1555,7 +1555,7 @@ static int xfrm_bule_bce_update(const st
 	int res = -1;
 	pthread_rwlock_rdlock(&mn_lock);
 	if ((hai = mn_get_home_addr(our_addr)) != NULL) {
-		if ((e = bul_get(hai, NULL, peer_addr)) != NULL) {
+		if ((e = bul_get(hai, NULL, peer_addr, MCOA_NO_BID)) != NULL) {
 			if (e->type == BUL_ENTRY &&
 			    !_xfrm_add_bule_bce(our_addr, peer_addr, replace))
 				res = 0;
@@ -1582,30 +1582,34 @@ int xfrm_add_bce(const struct in6_addr *
 int xfrm_add_bce(const struct in6_addr *our_addr,
 		 const struct in6_addr *peer_addr,
 		 const struct in6_addr *coa,
-		 int replace)
+		 int replace,
+		 int create)
 {
 	struct xfrm_selector sel;
 
 	/* Create policy for outbound RO data traffic */
-	set_selector(peer_addr, our_addr, 0, 0, 0, 0, &sel);
-	if (xfrm_state_add(&sel, IPPROTO_ROUTING, coa, replace, 0)){
-		/* 
-		 * WORKAROUND 
-		 * In some cases, MN fail to add it because of the state
-		 * inserted by kernel when notifying aquire. So,update it.
-		 */
-		if (xfrm_state_add(&sel, IPPROTO_ROUTING, coa, 1, 0))
-			return -1;
-	}
-	set_selector(our_addr, peer_addr, 0, 0, 0, 0, &sel);
-	if (xfrm_state_add(&sel, IPPROTO_DSTOPTS, coa, replace, 0)){
-		/* 
-		 * WORKAROUND 
-		 * In some cases, MN fail to add it because of the state
-		 * inserted by kernel when notifying aquire. So,update it.
-		 */
-		if (xfrm_state_add(&sel, IPPROTO_DSTOPTS, coa, 1, 0))
-		return -1;
+	if (create || replace) {
+		set_selector(peer_addr, our_addr, 0, 0, 0, 0, &sel);
+		if (xfrm_state_add(&sel, IPPROTO_ROUTING, coa, replace, 0)){
+			/* 
+			 * WORKAROUND 
+			 * In some cases, MN fail to add it because of the state
+			 * inserted by kernel when notifying aquire. So,update it.
+			 */
+			if (xfrm_state_add(&sel, IPPROTO_ROUTING, coa, 1, 0))
+				return -1;
+		}
+	
+		set_selector(our_addr, peer_addr, 0, 0, 0, 0, &sel);
+		if (xfrm_state_add(&sel, IPPROTO_DSTOPTS, coa, replace, 0)){
+			/* 
+			 * WORKAROUND 
+			 * In some cases, MN fail to add it because of the state
+			 * inserted by kernel when notifying aquire. So,update it.
+			 */
+			if (xfrm_state_add(&sel, IPPROTO_DSTOPTS, coa, 1, 0))
+				return -1;
+		}
 	}
 	if (is_mn() && !xfrm_bule_bce_update(our_addr, peer_addr, replace))
 		return 0;
@@ -1614,7 +1618,8 @@ int xfrm_add_bce(const struct in6_addr *
 				       _ha_mn_ipsec_pol_mod, NULL) < 0)
 			return -1;
 	}
-	_xfrm_add_bce(our_addr, peer_addr, replace);
+	if (create || replace)
+		_xfrm_add_bce(our_addr, peer_addr, replace);
 	return 0;
 }
 
@@ -1643,7 +1648,7 @@ void xfrm_del_bce(const struct in6_addr 
 		/* for MN-MN communications, checking BUL to insert RO policy */
 		pthread_rwlock_rdlock(&mn_lock);
 		if ((hai = mn_get_home_addr(our_addr)) != NULL) {
-			if ((e = bul_get(hai, NULL, peer_addr)) != NULL) {
+			if ((e = bul_get(hai, NULL, peer_addr, MCOA_NO_BID)) != NULL) {
 				if (e->type == BUL_ENTRY)
 			    	_xfrm_add_bule_bce(our_addr, peer_addr, 0);
 			}
@@ -1736,7 +1741,7 @@ static int _xfrm_bce_reset(struct bulent
 static int _xfrm_bce_reset(struct bulentry *bule)
 {
 	/* for MN - MN communication */
-	struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr);
+	struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr, MCOA_NO_BID);
 	int res = 0;
 	if (!bce)
 		return 0;
@@ -1754,21 +1759,34 @@ static int _xfrm_del_bule_data(struct bu
 static int _xfrm_del_bule_data(struct bulentry *bule)
 {
 	struct xfrm_selector sel;
+	int bule_exists = mcoa_bule_count(bule);
 	int prio;
 
-	set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0, 0, &sel);
-	xfrm_state_del(IPPROTO_DSTOPTS,  &sel);
+	/* MCoA
+	 * XFRM states are deleted only when the last BULE for 
+	 * the same peer is deleted, or when returning home
+	 */
+	if (!bule_exists || !bule->home->reg_mcoa || bule->home->at_home) {
+		XDBG2("Last entry for the peer, deleting XFRM states\n");
+		set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0, 0, &sel);
+		xfrm_state_del(IPPROTO_DSTOPTS, &sel);
+	}
 
 	if (bule->home->home_reg_status != HOME_REG_NONE && 
 	    mn_has_cn_ro_pol(bule)) {
 		struct xfrm_user_tmpl tmpl;
 		create_trig_dstopt_tmpl(&tmpl, &bule->peer_addr, &bule->hoa);
+		/* MCoA: replaced bule->home->if_tunnel with bule->if_tunnel */
 		set_selector(&bule->peer_addr, &bule->hoa,
-			     0, 0, 0, bule->home->if_tunnel, &sel);
+			     0, 0, 0, bule->if_tunnel, &sel);
 		xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT,
 				    XFRM_POLICY_ALLOW, MIP6_PRIO_RO_TRIG, &tmpl, 1);
-	} else
-		_mn_bule_ro_pol_del(bule, bule->home->if_tunnel);
+	} else if(!bule_exists || !bule->home->reg_mcoa || bule->home->at_home) {
+		/* MCoA: policies deleted only if this is the last BULE 
+		 * for the same peer, or when returning home */
+		/* MCoA: replaced bule->home->if_tunnel with bule->if_tunnel */
+		_mn_bule_ro_pol_del(bule, bule->if_tunnel);
+	}
 
 	if (!(bule->flags & IP6_MH_BU_HOME))
 		_xfrm_bce_reset(bule);
@@ -1779,20 +1797,25 @@ static int _xfrm_del_bule_data(struct bu
 	/* MN - CN/HA case, BU out */
 	prio = (bule->flags & IP6_MH_BU_HOME ?
 		MIP6_PRIO_HOME_SIG : MIP6_PRIO_RO_SIG);
-	if (bule->flags & IP6_MH_BU_ACK) {
+	if ((bule->flags & IP6_MH_BU_ACK) 
+	    && (!bule_exists ||	!bule->home->reg_mcoa 
+		|| bule->home->at_home)) {
 		set_selector(&bule->hoa, &bule->peer_addr, IPPROTO_MH,
 			     IP6_MH_TYPE_BACK, 0, 0, &sel);
 		if (xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_IN,
 					XFRM_POLICY_ALLOW, prio, NULL, 0))
 			return -1;
 	}
-	set_selector(&bule->peer_addr, &bule->hoa, IPPROTO_MH,
-		     IP6_MH_TYPE_BU, 0, 0, &sel);
-	if (xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT,
-				XFRM_POLICY_ALLOW, prio, NULL, 0))
-		return -1;
+	if (!bule_exists || !bule->home->reg_mcoa 
+	    || bule->home->at_home) {
+		set_selector(&bule->peer_addr, &bule->hoa, IPPROTO_MH,
+			     IP6_MH_TYPE_BU, 0, 0, &sel);
+		if (xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT, 
+				    XFRM_POLICY_ALLOW, prio, NULL, 0))
+			return -1;
+		bule->xfrm_state &= ~BUL_XFRM_STATE_DATA;
+	}
 
-	bule->xfrm_state &= ~BUL_XFRM_STATE_DATA;
 	return 0;
 }
 
@@ -1817,7 +1840,14 @@ void xfrm_del_bule(struct bulentry *bule
 {
 	if (bule->xfrm_state & BUL_XFRM_STATE_DATA)
 		_xfrm_del_bule_data(bule);
-	_xfrm_del_bule_sig(bule);
+	/* MCoA
+	 * XFRM policies are deleted only when the last BULE for the 
+	 * same peer is deleted, or when returning home
+	 */
+	if (mcoa_bule_count(bule) == 0 || bule->home->at_home) {
+		XDBG2("Last entry for the peer, deleting XFRM policies\n");
+		_xfrm_del_bule_sig(bule);
+	}
 }
 
 /* before sending BU, MN should insert policy/state only for BU/BA */
@@ -1829,14 +1859,26 @@ int xfrm_pre_bu_add_bule(struct bulentry
 	int rdata = bule->xfrm_state & BUL_XFRM_STATE_DATA;
 	int prio;
 	int exist = 0;
+	int bule_count = mcoa_bule_count(bule);
 
 	if (bule->flags & IP6_MH_BU_HOME) {
 		struct home_addr_info *hai = bule->home;
 		if (hai->home_block & HOME_LINK_BLOCK)
 			xfrm_unblock_link(hai);
-		xfrm_block_link(hai);
-		if (hai->mob_rtr && !(hai->home_block & NEMO_FWD_BLOCK))
-			xfrm_block_fwd(hai);
+		if (bule->home_block & HOME_LINK_BLOCK)
+			xfrm_unblock_bule_link(bule);
+		if (!hai->reg_mcoa)
+			xfrm_block_link(hai);
+		else
+			xfrm_block_bule_link(bule);
+		if (hai->mob_rtr) {
+			if (!hai->reg_mcoa && 
+			    !(hai->home_block & NEMO_FWD_BLOCK))
+				xfrm_block_fwd(hai);
+			else if (hai->reg_mcoa &&
+			         !(bule->home_block & NEMO_FWD_BLOCK))
+				bule_block_fwd(bule);
+		}
 	}
 	if (IN6_ARE_ADDR_EQUAL(&bule->hoa, &bule->coa)) {
 		if (rdata)
@@ -1850,25 +1892,32 @@ int xfrm_pre_bu_add_bule(struct bulentry
 			return -1;
 	}
 	/* MN - CN/HA case, BU out */
-	prio = (bule->flags & IP6_MH_BU_HOME ?
-		MIP6_PRIO_HOME_SIG : MIP6_PRIO_RO_SIG);
-	if (bule->flags & IP6_MH_BU_ACK) {
-		create_rh_tmpl(&tmpl);
-		set_selector(&bule->hoa, &bule->peer_addr, IPPROTO_MH,
-			     IP6_MH_TYPE_BACK, 0, 0, &sel);
-		if (xfrm_mip_policy_add(&sel, rsig, XFRM_POLICY_IN,
+	if (bule_count == 1) {
+		/* MCoA
+		 * If another BULE already exists for this HoA/HA addr
+		 * we do not need to install the policies once again
+		 */
+		prio = (bule->flags & IP6_MH_BU_HOME ?
+			MIP6_PRIO_HOME_SIG : MIP6_PRIO_RO_SIG);
+		if (bule->flags & IP6_MH_BU_ACK) {
+			create_rh_tmpl(&tmpl);
+			set_selector(&bule->hoa, &bule->peer_addr, IPPROTO_MH,
+				     IP6_MH_TYPE_BACK, 0, 0, &sel);
+			if (xfrm_mip_policy_add(&sel, rsig, XFRM_POLICY_IN,
+						XFRM_POLICY_ALLOW, prio, &tmpl, 1))
+				return -1;
+		}
+		create_dstopt_tmpl(&tmpl, &bule->peer_addr, &bule->hoa);
+		set_selector(&bule->peer_addr, &bule->hoa, IPPROTO_MH,
+		     IP6_MH_TYPE_BU, 0, 0, &sel);
+		if (xfrm_mip_policy_add(&sel, rsig, XFRM_POLICY_OUT,
 					XFRM_POLICY_ALLOW, prio, &tmpl, 1))
 			return -1;
 	}
-	create_dstopt_tmpl(&tmpl, &bule->peer_addr, &bule->hoa);
-	set_selector(&bule->peer_addr, &bule->hoa, IPPROTO_MH,
-		     IP6_MH_TYPE_BU, 0, 0, &sel);
-	if (xfrm_mip_policy_add(&sel, rsig, XFRM_POLICY_OUT,
-				XFRM_POLICY_ALLOW, prio, &tmpl, 1))
-		return -1;
 
 	if (!(bule->flags & IP6_MH_BU_HOME)) {
-		struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr);
+		struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr, 
+						 MCOA_NO_BID);
 		if (bce) {
 			if (bce->type >  BCE_NONCE_BLOCK &&
 			    _xfrm_add_bce(&bule->hoa, &bule->peer_addr, 1)) {
@@ -1879,11 +1928,16 @@ int xfrm_pre_bu_add_bule(struct bulentry
 			exist = 1;
 		}
 	}
-	if(!exist &&_mn_bule_ro_pol_add(bule, bule->home->if_tunnel, rdata))
+	/* MCoA: Replaced bule->home->if_tunnel with bule->if_tunnel */
+	if(!exist && bule_count == 1
+	   && _mn_bule_ro_pol_add(bule, bule->if_tunnel, rdata))
 		return -1;
 	set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0, 0, &sel);
 	/* XXX: acquired state is already inserted */
-	if (!(bule->flags & IP6_MH_BU_HOME)) {
+	/* MCoA: Add the states if there is only 1 BCE for the peer,
+	 * otherwise just update the CoA
+	 */
+	if (!(bule->flags & IP6_MH_BU_HOME) || mcoa_bule_count(bule) > 1) {
 		XDBG2("%s: original rdata = %d\n", __FUNCTION__, rdata);
 		rdata = 1;
 	}
@@ -1902,15 +1956,20 @@ int xfrm_post_ba_mod_bule(struct bulentr
 		struct home_addr_info *hai = bule->home;
 		if (hai->home_block & HOME_LINK_BLOCK)
 			xfrm_unblock_link(hai);
+		if (bule->home_block & HOME_LINK_BLOCK)
+			xfrm_unblock_bule_link(bule);
 		if (hai->home_block & NEMO_FWD_BLOCK)
 			xfrm_unblock_fwd(hai);
+		if (bule->home_block & NEMO_FWD_BLOCK)
+			bule_unblock_fwd(bule);
 	}
 	/* check if XFRM policies and states have already been cleaned up */
 	if (IN6_ARE_ADDR_EQUAL(&bule->hoa, &bule->coa))
 		return 0;
 
 	if (!(bule->flags & IP6_MH_BU_HOME)) {
-		struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr);
+		struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr,
+						 MCOA_NO_BID);
 		if (bce) {
 			if (bce->type >  BCE_NONCE_BLOCK &&
 			    _xfrm_add_bule_bce(&bule->hoa,
@@ -1925,11 +1984,27 @@ int xfrm_post_ba_mod_bule(struct bulentr
 	}
 	prio = (bule->flags & IP6_MH_BU_HOME ?
 		MIP6_PRIO_HOME_DATA : MIP6_PRIO_RO_BULE_DATA);
-	set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0,
-		     bule->home->if_tunnel, &sel);
-	create_dstopt_tmpl(&tmpls[0], &bule->peer_addr, &bule->hoa);
-	ret = xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT,
-				   XFRM_POLICY_ALLOW, prio, tmpls, 1);
+
+	/* MCoA: Replaced bule->home->if_tunnel with bule->if_tunnel */
+	set_selector(&bule->peer_addr, &bule->hoa, 0, 0, 0, 
+		     bule->if_tunnel, &sel);
+
+	/* MCoA TODO1: to update according to the latest draft.
+	 * When MCoA is used, do not use HAO dest. option 
+	 * for traffic from the MN/MR to the HA. Tunnel packets 
+	 * instead.
+	 */
+	if(bule->home->reg_mcoa) {
+		ret = xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT, 
+					   XFRM_POLICY_ALLOW, prio, 
+					   NULL, 0);
+	} else {
+		create_dstopt_tmpl(&tmpls[0], &bule->peer_addr, &bule->hoa);
+		ret = xfrm_mip_policy_add(&sel, 1, XFRM_POLICY_OUT,
+					  XFRM_POLICY_ALLOW, prio, 
+					  tmpls, 1);
+	}
+
 	if (ret)
 		XDBG("failed to insert outbound policy\n");
 
@@ -2126,6 +2201,31 @@ void xfrm_cleanup(void)
 	xfrm_cn_cleanup();
 }
 
+/* blocking all payload packets from MN via a specific interface */
+int xfrm_block_bule_link(struct bulentry *bule)
+{
+	int ret = 0;
+	struct xfrm_selector sel;
+	bule->home_block |= HOME_LINK_BLOCK;
+	/* allow MN to send NA messages */
+	/* RK: such policy is already installed in xfrm_cn_init at startup,
+	 * so we update it (update field to 1) */
+	set_selector(&in6addr_any, &in6addr_any, IPPROTO_ICMPV6, 
+		     ND_NEIGHBOR_ADVERT, 0, bule->if_coa, &sel);
+	if ((ret = xfrm_mip_policy_add(&sel, 1 /* update */, XFRM_POLICY_OUT, 
+				       XFRM_POLICY_ALLOW,
+				       MIP6_PRIO_HOME_SIG_ANY, NULL, 0)))
+		return ret;
+
+	/* block any packets from HoA to the CN */
+	set_selector(&in6addr_any, &in6addr_any, 0, 0, 0, bule->if_coa, &sel);
+	if ((ret = xfrm_mip_policy_add(&sel, 0, XFRM_POLICY_OUT, 
+				   XFRM_POLICY_BLOCK,
+				   MIP6_PRIO_HOME_BLOCK, NULL, 0)))
+		return ret;
+	return ret;
+}
+
 /* blocking all payload packets from MN */
 int xfrm_block_link(struct home_addr_info *hai)
 {
@@ -2141,6 +2241,21 @@ int xfrm_block_link(struct home_addr_inf
 				       MIP6_PRIO_HOME_BLOCK, NULL, 0)))
 		return ret;
 	return ret;
+}
+
+void xfrm_unblock_bule_link(struct bulentry *bule)
+{
+	struct xfrm_selector sel;
+	set_selector(&in6addr_any, &in6addr_any, 0, 0, 0, bule->if_coa, &sel);
+	xfrm_mip_policy_del(&sel, XFRM_POLICY_OUT);
+	/* RK: instead of deleting the policy, update it to its previous state 
+	 * (the one installed in xfrm_cn_init) */
+	set_selector(&in6addr_any, &in6addr_any, IPPROTO_ICMPV6, 
+		     ND_NEIGHBOR_ADVERT, 0, 0, &sel);
+	if (xfrm_mip_policy_add(&sel, 1 /* update */, XFRM_POLICY_OUT, XFRM_POLICY_ALLOW,
+				MIP6_PRIO_NO_RO_SIG_ANY, NULL, 0) < 0)
+		XDBG("Could not update NA policy\n");
+	bule->home_block &= ~HOME_LINK_BLOCK;
 }
 
 void xfrm_unblock_link(struct home_addr_info *hai)
diff -r 82fcd4bea972 src/xfrm.h
--- a/src/xfrm.h	Fri Jan 11 16:55:08 2008 +0100
+++ b/src/xfrm.h	Fri Jan 11 17:02:25 2008 +0100
@@ -51,7 +51,8 @@ int xfrm_add_bce(const struct in6_addr *
 int xfrm_add_bce(const struct in6_addr *our_addr,
 		 const struct in6_addr *peer_addr,
 		 const struct in6_addr *coa,
-		 int replace);
+		 int replace,
+		 int create);
 
 void xfrm_del_bce(const struct in6_addr *our_addr,
 		  const struct in6_addr *peer_addr);
@@ -82,7 +83,9 @@ int cn_wildrecv_bu_pol_add(void);
 int cn_wildrecv_bu_pol_add(void);
 void cn_wildrecv_bu_pol_del(void);
 
+int xfrm_block_bule_link(struct bulentry *bule);
 int xfrm_block_link(struct home_addr_info *hai);
+void xfrm_unblock_bule_link(struct bulentry *bule);
 void xfrm_unblock_link(struct home_addr_info *hai);
 
 int xfrm_block_hoa(struct home_addr_info *hai);
