Index: src/mh.c
===================================================================
--- src/mh.c
+++ src/mh.c
@@ -627,7 +627,7 @@
 	struct iovec iov[2*(IP6_MHOPT_MAX+1)];
 	struct msghdr msg;
 	struct cmsghdr *cmsg;
-	int cmsglen;
+	int cmsglen, dstlen = 0;
 	struct in6_pktinfo pinfo;
 	int ret = 0, on = 1;
 	struct ip6_mh *mh;
@@ -698,10 +698,17 @@
 	daddr.sin6_port = htons(IPPROTO_MH);
 
 	memset(&pinfo, 0, sizeof(pinfo));
-	pinfo.ipi6_addr = *addrs->src;
+	pinfo.ipi6_addr = addrs->local_coa ? *addrs->local_coa : *addrs->src;
 	pinfo.ipi6_ifindex = oif;
 
 	cmsglen = CMSG_SPACE(sizeof(pinfo));
+
+	if (addrs->local_coa != NULL) {
+		dstlen = sizeof(struct ip6_ext) + sizeof(_pad4) +
+            sizeof(struct ip6_opt_home_address);
+		cmsglen += CMSG_SPACE(dstlen);
+    }
+
 	if (addrs->remote_coa != NULL) {
 		rthlen = inet6_rth_space(IPV6_RTHDR_TYPE_2, 1);
 		if (!rthlen) {
@@ -731,6 +738,29 @@
 	cmsg->cmsg_type = IPV6_PKTINFO;
 	memcpy(CMSG_DATA(cmsg), &pinfo, sizeof(pinfo));
 
+	if (addrs->local_coa != NULL) {
+		cmsg = CMSG_NXTHDR(&msg, cmsg);
+		if (cmsg == NULL) {
+			free(msg.msg_control);
+			MDBG("internal error\n");
+			return -2;
+		}
+		cmsg->cmsg_len = CMSG_LEN(dstlen);
+		cmsg->cmsg_level = IPPROTO_IPV6;
+		cmsg->cmsg_type = IPV6_DSTOPTS;
+
+        struct ip6_ext *ext = (struct ip6_ext *)CMSG_DATA(cmsg);
+        ext->ip6e_nxt = IPPROTO_MH;
+        ext->ip6e_len = 2;
+        unsigned char *pad = (unsigned char *)(ext + 1);
+        memcpy(pad, _pad4, sizeof(_pad4));
+        struct ip6_opt_home_address *hao =
+            (struct ip6_opt_home_address *)(pad + sizeof(_pad4));
+        hao->ip6oha_type = IP6OPT_HOME_ADDRESS;
+        hao->ip6oha_len = sizeof(hao->ip6oha_addr);
+        memcpy(hao->ip6oha_addr, addrs->src, sizeof(hao->ip6oha_addr));
+	}
+
 	if (addrs->remote_coa != NULL) {
 		void *rthp;
 
Index: src/xfrm.c
===================================================================
--- src/xfrm.c
+++ src/xfrm.c
@@ -1836,6 +1836,28 @@
 	bule->xfrm_state = 0;
 }
 
+static void _xfrm_del_bule_sig_coa(struct bulentry *bule)
+{
+    if (bule->xfrm_state & BUL_XFRM_STATE_SIG) { 
+        struct xfrm_selector sel;
+        /* BU from CoA. */
+        set_selector(&bule->peer_addr, &bule->last_coa, IPPROTO_MH,
+                IP6_MH_TYPE_BU, 0, 0, &sel);
+        XDBG("removing xfrm BU out policy CoA %x:%x:%x:%x:%x:%x:%x:%x\n",
+            NIP6ADDR(&bule->last_coa));
+        xfrm_mip_policy_del(&sel, XFRM_POLICY_OUT);
+        if (bule->flags & IP6_MH_BU_ACK) {
+            /* BA to CoA. */
+            set_selector(&bule->last_coa, &bule->peer_addr,
+                    IPPROTO_MH, IP6_MH_TYPE_BACK,
+                    0, 0, &sel);
+            XDBG("removing xfrm BA in policy CoA %x:%x:%x:%x:%x:%x:%x:%x\n",
+                NIP6ADDR(&bule->last_coa));
+            xfrm_mip_policy_del(&sel, XFRM_POLICY_IN);
+        }
+    }
+}
+
 void xfrm_del_bule(struct bulentry *bule) 
 {
 	if (bule->xfrm_state & BUL_XFRM_STATE_DATA)
@@ -1844,6 +1866,7 @@
 	 * XFRM policies are deleted only when the last BULE for the 
 	 * same peer is deleted, or when returning home
 	 */
+    _xfrm_del_bule_sig_coa(bule);
 	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);
@@ -1857,7 +1880,8 @@
 	struct xfrm_user_tmpl tmpl;
 	int rsig = bule->xfrm_state & BUL_XFRM_STATE_SIG;
 	int rdata = bule->xfrm_state & BUL_XFRM_STATE_DATA;
-	int prio;
+	int prio = (bule->flags & IP6_MH_BU_HOME ?
+			MIP6_PRIO_HOME_SIG : MIP6_PRIO_RO_SIG);
 	int exist = 0;
 	int bule_count = mcoa_bule_count(bule);
 
@@ -1897,8 +1921,6 @@
 		 * 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,
@@ -1915,6 +1937,26 @@
 			return -1;
 	}
 
+    /* BU from CoA. */
+    XDBG("adding xfrm BU out policy update %d coa %x:%x:%x:%x:%x:%x:%x:%x\n",
+            rsig, NIP6ADDR(&bule->coa));
+    set_selector(&bule->peer_addr, &bule->coa, IPPROTO_MH,
+            IP6_MH_TYPE_BU, 0, 0, &sel);
+    if (xfrm_mip_policy_add(&sel, rsig, XFRM_POLICY_OUT,
+                XFRM_POLICY_ALLOW, prio, NULL, 0))
+        return -1;
+    if (bule->flags & IP6_MH_BU_ACK) {
+        /* BA to CoA. */
+        create_rh_tmpl(&tmpl);
+        XDBG("adding xfrm BA in policy update %d coa %x:%x:%x:%x:%x:%x:%x:%x\n",
+            rsig, NIP6ADDR(&bule->coa));
+        set_selector(&bule->coa, &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;
+    }
+
 	if (!(bule->flags & IP6_MH_BU_HOME)) {
 		struct bcentry *bce = bcache_get(&bule->hoa, &bule->peer_addr, 
 						 MCOA_NO_BID);
