source address lookup in kernel interface
authorMartin Willi <martin@strongswan.org>
Mon, 18 Jun 2007 07:25:58 +0000 (07:25 -0000)
committerMartin Willi <martin@strongswan.org>
Mon, 18 Jun 2007 07:25:58 +0000 (07:25 -0000)
  use it for NAT detection if no source address known from config
  support for %any...%any connections

src/charon/kernel/kernel_interface.c
src/charon/kernel/kernel_interface.h
src/charon/sa/ike_sa.c
src/charon/sa/tasks/ike_natd.c

index 919dad8..641ed83 100644 (file)
@@ -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;
index ef576b9..39eb6d4 100644 (file)
@@ -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
index 46dc94c..e4e0762 100644 (file)
@@ -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);
index 9b355d7..5ae3666 100644 (file)
@@ -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;
 }