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);