diff options
Diffstat (limited to 'src/charon/kernel/kernel_interface.c')
-rw-r--r-- | src/charon/kernel/kernel_interface.c | 90 |
1 files changed, 58 insertions, 32 deletions
diff --git a/src/charon/kernel/kernel_interface.c b/src/charon/kernel/kernel_interface.c index 0e06368fd..50be720de 100644 --- a/src/charon/kernel/kernel_interface.c +++ b/src/charon/kernel/kernel_interface.c @@ -615,7 +615,8 @@ static void process_link(private_kernel_interface_t *this, /* send an update to all IKE_SAs */ if (update && event) { - charon->processor->queue_job(charon->processor, (job_t*)roam_job_create()); + charon->processor->queue_job(charon->processor, + (job_t*)roam_job_create(TRUE)); } } @@ -633,7 +634,7 @@ static void process_addr(private_kernel_interface_t *this, iface_entry_t *iface; addr_entry_t *addr; chunk_t local = chunk_empty, address = chunk_empty; - bool update = FALSE, found = FALSE; + bool update = FALSE, found = FALSE, changed = FALSE; while(RTA_OK(rta, rtasize)) { @@ -681,6 +682,7 @@ static void process_addr(private_kernel_interface_t *this, found = TRUE; if (hdr->nlmsg_type == RTM_DELADDR) { + changed = TRUE; addrs->remove(addrs); addr_entry_destroy(addr); DBG1(DBG_KNL, "%H disappeared from %s", host, iface->ifname); @@ -694,6 +696,7 @@ static void process_addr(private_kernel_interface_t *this, if (!found) { found = TRUE; + changed = TRUE; addr = malloc_thing(addr_entry_t); addr->ip = host->clone(host); addr->virtual = FALSE; @@ -718,9 +721,10 @@ static void process_addr(private_kernel_interface_t *this, host->destroy(host); /* send an update to all IKE_SAs */ - if (update && event) + if (update && event && changed) { - charon->processor->queue_job(charon->processor, (job_t*)roam_job_create()); + charon->processor->queue_job(charon->processor, + (job_t*)roam_job_create(TRUE)); } } @@ -817,7 +821,7 @@ static job_requeue_t receive_events(private_kernel_interface_t *this) case RTM_NEWROUTE: case RTM_DELROUTE: charon->processor->queue_job(charon->processor, - (job_t*)roam_job_create()); + (job_t*)roam_job_create(FALSE)); break; default: break; @@ -1763,11 +1767,11 @@ static status_t add_sa(private_kernel_interface_t *this, return FAILED; } - struct xfrm_encap_tmpl* encap = (struct xfrm_encap_tmpl*)RTA_DATA(rthdr); - encap->encap_type = UDP_ENCAP_ESPINUDP; - encap->encap_sport = htons(src->get_port(src)); - encap->encap_dport = htons(dst->get_port(dst)); - memset(&encap->encap_oa, 0, sizeof (xfrm_address_t)); + struct xfrm_encap_tmpl* tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rthdr); + tmpl->encap_type = UDP_ENCAP_ESPINUDP; + tmpl->encap_sport = htons(src->get_port(src)); + tmpl->encap_dport = htons(dst->get_port(dst)); + memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t)); /* encap_oa could probably be derived from the * traffic selectors [rfc4306, p39]. In the netlink kernel implementation * pluto does the same as we do here but it uses encap_oa in the @@ -1794,13 +1798,16 @@ static status_t add_sa(private_kernel_interface_t *this, static status_t update_sa(private_kernel_interface_t *this, u_int32_t spi, protocol_id_t protocol, host_t *src, host_t *dst, - host_t *new_src, host_t *new_dst) + host_t *new_src, host_t *new_dst, bool encap) { - unsigned char request[BUFFER_SIZE]; + unsigned char request[BUFFER_SIZE], *pos; struct nlmsghdr *hdr, *out = NULL; struct xfrm_usersa_id *sa_id; - struct xfrm_usersa_info *sa = NULL; + struct xfrm_usersa_info *out_sa = NULL, *sa; size_t len; + struct rtattr *rta; + size_t rtasize; + struct xfrm_encap_tmpl* tmpl = NULL; memset(&request, 0, sizeof(request)); @@ -1827,7 +1834,7 @@ static status_t update_sa(private_kernel_interface_t *this, { case XFRM_MSG_NEWSA: { - sa = NLMSG_DATA(hdr); + out_sa = NLMSG_DATA(hdr); break; } case NLMSG_ERROR: @@ -1846,7 +1853,7 @@ static status_t update_sa(private_kernel_interface_t *this, break; } } - if (sa == NULL || + if (out_sa == NULL || this->public.del_sa(&this->public, dst, spi, protocol) != SUCCESS) { DBG1(DBG_KNL, "unable to update SAD entry with SPI 0x%x", spi); @@ -1857,10 +1864,14 @@ static status_t update_sa(private_kernel_interface_t *this, DBG2(DBG_KNL, "updating SAD entry with SPI 0x%x from %#H..%#H to %#H..%#H", spi, src, dst, new_src, new_dst); - /* update the values in the queried SA */ - hdr = out; + /* copy over the SA from out to request */ + hdr = (struct nlmsghdr*)request; + memcpy(hdr, out, min(out->nlmsg_len, sizeof(request))); hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; hdr->nlmsg_type = XFRM_MSG_NEWSA; + hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_usersa_info)); + sa = NLMSG_DATA(hdr); + sa->family = new_dst->get_family(new_dst); if (!src->ip_equals(src, new_src)) { @@ -1871,24 +1882,39 @@ static status_t update_sa(private_kernel_interface_t *this, host2xfrm(new_dst, &sa->id.daddr); } - if (src->get_port(src) != new_src->get_port(new_src) || - dst->get_port(dst) != new_dst->get_port(new_dst)) + rta = XFRM_RTA(out, struct xfrm_usersa_info); + rtasize = XFRM_PAYLOAD(out, struct xfrm_usersa_info); + pos = (u_char*)XFRM_RTA(hdr, struct xfrm_usersa_info); + while(RTA_OK(rta, rtasize)) { - struct rtattr *rtattr = XFRM_RTA(hdr, struct xfrm_usersa_info); - size_t rtsize = XFRM_PAYLOAD(hdr, struct xfrm_usersa_info); - while (RTA_OK(rtattr, rtsize)) + /* copy all attributes, but not XFRMA_ENCAP if we are disabling it */ + if (rta->rta_type != XFRMA_ENCAP || encap) { - if (rtattr->rta_type == XFRMA_ENCAP) - { - struct xfrm_encap_tmpl* encap; - encap = (struct xfrm_encap_tmpl*)RTA_DATA(rtattr); - encap->encap_sport = ntohs(new_src->get_port(new_src)); - encap->encap_dport = ntohs(new_dst->get_port(new_dst)); - break; - } - rtattr = RTA_NEXT(rtattr, rtsize); + if (rta->rta_type == XFRMA_ENCAP) + { /* update encap tmpl */ + tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta); + tmpl->encap_sport = ntohs(new_src->get_port(new_src)); + tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); + } + memcpy(pos, rta, rta->rta_len); + pos += rta->rta_len; + hdr->nlmsg_len += rta->rta_len; } + rta = RTA_NEXT(rta, rtasize); + } + if (tmpl == NULL && encap) + { /* add tmpl if we are enabling it */ + rta = (struct rtattr*)pos; + rta->rta_type = XFRMA_ENCAP; + rta->rta_len = RTA_LENGTH(sizeof(struct xfrm_encap_tmpl)); + hdr->nlmsg_len += rta->rta_len; + tmpl = (struct xfrm_encap_tmpl*)RTA_DATA(rta); + tmpl->encap_type = UDP_ENCAP_ESPINUDP; + tmpl->encap_sport = ntohs(new_src->get_port(new_src)); + tmpl->encap_dport = ntohs(new_dst->get_port(new_dst)); + memset(&tmpl->encap_oa, 0, sizeof (xfrm_address_t)); } + if (netlink_send_ack(this, this->socket_xfrm, hdr) != SUCCESS) { DBG1(DBG_KNL, "unable to update SAD entry with SPI 0x%x", spi); @@ -2320,7 +2346,7 @@ kernel_interface_t *kernel_interface_create() /* public functions */ this->public.get_spi = (status_t(*)(kernel_interface_t*,host_t*,host_t*,protocol_id_t,u_int32_t,u_int32_t*))get_spi; this->public.add_sa = (status_t(*)(kernel_interface_t *,host_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t,u_int64_t,u_int64_t,algorithm_t*,algorithm_t*,prf_plus_t*,mode_t,bool,bool))add_sa; - this->public.update_sa = (status_t(*)(kernel_interface_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_t*,host_t*))update_sa; + this->public.update_sa = (status_t(*)(kernel_interface_t*,u_int32_t,protocol_id_t,host_t*,host_t*,host_t*,host_t*,bool))update_sa; this->public.query_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t,u_int32_t*))query_sa; this->public.del_sa = (status_t(*)(kernel_interface_t*,host_t*,u_int32_t,protocol_id_t))del_sa; this->public.add_policy = (status_t(*)(kernel_interface_t*,host_t*,host_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,protocol_id_t,u_int32_t,bool,mode_t))add_policy; |