aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/charon/kernel/kernel_interface.c80
-rw-r--r--src/charon/kernel/kernel_interface.h12
-rw-r--r--src/charon/sa/ike_sa.c2
-rw-r--r--src/charon/sa/tasks/ike_natd.c50
4 files changed, 125 insertions, 19 deletions
diff --git a/src/charon/kernel/kernel_interface.c b/src/charon/kernel/kernel_interface.c
index 919dad855..641ed837b 100644
--- a/src/charon/kernel/kernel_interface.c
+++ b/src/charon/kernel/kernel_interface.c
@@ -1342,6 +1342,84 @@ static status_t manage_srcroute(private_kernel_interface_t *this, int nlmsg_type
return netlink_send_ack(this, this->socket_rt, hdr);
}
+/**
+ * Implementation of kernel_interface_t.get_source_addr.
+ */
+static host_t* get_source_addr(private_kernel_interface_t *this, host_t *dest)
+{
+ unsigned char request[BUFFER_SIZE];
+ struct nlmsghdr *hdr, *out, *current;
+ struct rtmsg *msg;
+ chunk_t chunk;
+ size_t len;
+ host_t *source = NULL;
+
+ memset(&request, 0, sizeof(request));
+
+ hdr = (struct nlmsghdr*)request;
+ hdr->nlmsg_flags = NLM_F_REQUEST;
+ hdr->nlmsg_type = RTM_GETROUTE;
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+
+ msg = (struct rtmsg*)NLMSG_DATA(hdr);
+ msg->rtm_family = dest->get_family(dest);
+ msg->rtm_dst_len = msg->rtm_family == AF_INET ? 32 : 128;
+ msg->rtm_table = RT_TABLE_MAIN;
+ msg->rtm_protocol = RTPROT_STATIC;
+ msg->rtm_type = RTN_UNICAST;
+ msg->rtm_scope = RT_SCOPE_UNIVERSE;
+
+ chunk = dest->get_address(dest);
+ add_attribute(hdr, RTA_DST, chunk, sizeof(request));
+
+ if (netlink_send(this, this->socket_rt, hdr, &out, &len) != SUCCESS)
+ {
+ DBG1(DBG_KNL, "getting source address to %H failed", dest);
+ return NULL;
+ }
+ current = out;
+ while (NLMSG_OK(current, len))
+ {
+ switch (current->nlmsg_type)
+ {
+ case NLMSG_DONE:
+ break;
+ case RTM_NEWROUTE:
+ {
+ struct rtattr *rta;
+ size_t rtasize;
+
+ msg = (struct rtmsg*)(NLMSG_DATA(current));
+ rta = RTM_RTA(msg);
+ rtasize = RTM_PAYLOAD(current);
+ while(RTA_OK(rta, rtasize))
+ {
+ switch (rta->rta_type)
+ {
+ case RTA_PREFSRC:
+ chunk.ptr = RTA_DATA(rta);
+ chunk.len = RTA_PAYLOAD(rta);
+ source = host_create_from_chunk(msg->rtm_family,
+ chunk, 0);
+ break;
+ }
+ rta = RTA_NEXT(rta, rtasize);
+ }
+ break;
+ }
+ default:
+ current = NLMSG_NEXT(current, len);
+ continue;
+ }
+ break;
+ }
+ if (source == NULL)
+ {
+ DBG1(DBG_KNL, "no route found to %H", dest);
+ }
+ free(out);
+ return source;
+}
/**
* Implementation of kernel_interface_t.add_ip.
@@ -2209,9 +2287,9 @@ kernel_interface_t *kernel_interface_create()
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,bool))add_policy;
this->public.query_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t,u_int32_t*))query_policy;
this->public.del_policy = (status_t(*)(kernel_interface_t*,traffic_selector_t*,traffic_selector_t*,policy_dir_t))del_policy;
-
this->public.get_interface = (char*(*)(kernel_interface_t*,host_t*))get_interface_name;
this->public.create_address_iterator = (iterator_t*(*)(kernel_interface_t*))create_address_iterator;
+ this->public.get_source_addr = (host_t*(*)(kernel_interface_t*, host_t *dest))get_source_addr;
this->public.add_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) add_ip;
this->public.del_ip = (status_t(*)(kernel_interface_t*,host_t*,host_t*)) del_ip;
this->public.destroy = (void(*)(kernel_interface_t*)) destroy;
diff --git a/src/charon/kernel/kernel_interface.h b/src/charon/kernel/kernel_interface.h
index ef576b9ef..39eb6d475 100644
--- a/src/charon/kernel/kernel_interface.h
+++ b/src/charon/kernel/kernel_interface.h
@@ -268,6 +268,18 @@ struct kernel_interface_t {
policy_dir_t direction);
/**
+ * @brief Get our outgoing source address for a destination.
+ *
+ * Does a route lookup to get the source address used to reach dest.
+ * The returned host is allocated and must be destroyed.
+ *
+ * @param this calling object
+ * @param dest target destination address
+ * @return outgoing source address, NULL if unreachable
+ */
+ host_t* (*get_source_addr)(kernel_interface_t *this, host_t *dest);
+
+ /**
* @brief Get the interface name of a local address.
*
* @param this calling object
diff --git a/src/charon/sa/ike_sa.c b/src/charon/sa/ike_sa.c
index 46dc94c55..e4e076297 100644
--- a/src/charon/sa/ike_sa.c
+++ b/src/charon/sa/ike_sa.c
@@ -763,7 +763,7 @@ static status_t process_message(private_ike_sa_t *this, message_t *message)
}
/* check if message is trustworthy, and update host information */
- if (this->state == IKE_CREATED ||
+ if (this->state == IKE_CREATED || this->state == IKE_CONNECTING ||
message->get_exchange_type(message) != IKE_SA_INIT)
{
update_hosts(this, me, other);
diff --git a/src/charon/sa/tasks/ike_natd.c b/src/charon/sa/tasks/ike_natd.c
index 9b355d773..5ae3666ea 100644
--- a/src/charon/sa/tasks/ike_natd.c
+++ b/src/charon/sa/tasks/ike_natd.c
@@ -243,29 +243,45 @@ static status_t build_i(private_ike_natd_t *this, message_t *message)
iterator_t *iterator;
host_t *host;
- /* include one notify if our address is defined, all addresses otherwise */
+ /* destination is always set */
+ host = this->ike_sa->get_other_host(this->ike_sa);
+ notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host);
+ message->add_payload(message, (payload_t*)notify);
+
+ /* source may be any, we have 3 possibilities to get our source address:
+ * 1. It is defined in the config => use the one of the IKE_SA
+ * 2. We do a routing lookup in the kernel interface
+ * 3. Include all possbile addresses
+ */
host = this->ike_sa->get_my_host(this->ike_sa);
- if (host->is_anyaddr(host))
+ if (!host->is_anyaddr(host))
+ { /* 1. */
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
+ message->add_payload(message, (payload_t*)notify);
+ }
+ else
{
- iterator = charon->kernel_interface->create_address_iterator(
- charon->kernel_interface);
- while (iterator->iterate(iterator, (void**)&host))
- {
+ host = charon->kernel_interface->get_source_addr(
+ charon->kernel_interface,
+ this->ike_sa->get_other_host(this->ike_sa));
+ if (host)
+ { /* 2. */
notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
message->add_payload(message, (payload_t*)notify);
+ host->destroy(host);
+ }
+ else
+ { /* 3. */
+ iterator = charon->kernel_interface->create_address_iterator(
+ charon->kernel_interface);
+ while (iterator->iterate(iterator, (void**)&host))
+ {
+ notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
+ message->add_payload(message, (payload_t*)notify);
+ }
+ iterator->destroy(iterator);
}
- iterator->destroy(iterator);
- }
- else
- {
- notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
- message->add_payload(message, (payload_t*)notify);
}
-
- host = this->ike_sa->get_other_host(this->ike_sa);
- notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host);
- message->add_payload(message, (payload_t*)notify);
-
return NEED_MORE;
}