]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
farp plugin sends ARP responses for any tunneled address, not only virtual IPs
authorMartin Willi <martin@revosec.ch>
Fri, 10 Feb 2012 15:50:18 +0000 (16:50 +0100)
committerMartin Willi <martin@revosec.ch>
Tue, 6 Mar 2012 15:06:33 +0000 (16:06 +0100)
src/libcharon/plugins/farp/farp_listener.c
src/libcharon/plugins/farp/farp_listener.h
src/libcharon/plugins/farp/farp_spoofer.c

index 8eed497785707f0bf2770a7121fa9caf0d4420a1..d1df4cc278a8ea8a17b089eda1dd6a434ecd91cf 100644 (file)
@@ -15,7 +15,7 @@
 
 #include "farp_listener.h"
 
-#include <utils/hashtable.h>
+#include <utils/linked_list.h>
 #include <threading/rwlock.h>
 
 typedef struct private_farp_listener_t private_farp_listener_t;
@@ -31,9 +31,9 @@ struct private_farp_listener_t {
        farp_listener_t public;
 
        /**
-        * Hashtable with active virtual IPs
+        * List with entry_t
         */
-       hashtable_t *ips;
+       linked_list_t *entries;
 
        /**
         * RWlock for IP list
@@ -42,88 +42,99 @@ struct private_farp_listener_t {
 };
 
 /**
- * Hashtable hash function
+ * Traffic selector cache entry
  */
-static u_int hash(host_t *key)
+typedef struct {
+       /** list of local selectors */
+       linked_list_t *local;
+       /** list of remote selectors */
+       linked_list_t *remote;
+       /** reqid of CHILD_SA */
+       u_int32_t reqid;
+} entry_t;
+
+METHOD(listener_t, child_updown, bool,
+       private_farp_listener_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
+       bool up)
 {
-       return chunk_hash(key->get_address(key));
-}
-
-/**
- * Hashtable equals function
- */
-static bool equals(host_t *a, host_t *b)
-{
-       return a->ip_equals(a, b);
-}
+       enumerator_t *enumerator;
+       entry_t *entry;
 
-METHOD(listener_t, ike_updown, bool,
-       private_farp_listener_t *this, ike_sa_t *ike_sa, bool up)
-{
-       if (!up)
+       if (up)
        {
-               host_t *ip;
-
-               ip = ike_sa->get_virtual_ip(ike_sa, FALSE);
-               if (ip)
-               {
-                       this->lock->write_lock(this->lock);
-                       ip = this->ips->remove(this->ips, ip);
-                       this->lock->unlock(this->lock);
-                       DESTROY_IF(ip);
-               }
+               INIT(entry,
+                       .local = child_sa->get_traffic_selectors(child_sa, TRUE),
+                       .remote = child_sa->get_traffic_selectors(child_sa, FALSE),
+                       .reqid = child_sa->get_reqid(child_sa),
+               );
+               entry->local = entry->local->clone_offset(entry->local,
+                                                                               offsetof(traffic_selector_t, clone));
+               entry->remote = entry->remote->clone_offset(entry->remote,
+                                                                               offsetof(traffic_selector_t, clone));
+
+               this->lock->write_lock(this->lock);
+               this->entries->insert_last(this->entries, entry);
+               this->lock->unlock(this->lock);
        }
-       return TRUE;
-}
-
-METHOD(listener_t, message_hook, bool,
-       private_farp_listener_t *this, ike_sa_t *ike_sa,
-       message_t *message, bool incoming)
-{
-       if (ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
-               message->get_exchange_type(message) == IKE_AUTH &&
-               !message->get_request(message))
+       else
        {
-               host_t *ip;
-
-               ip = ike_sa->get_virtual_ip(ike_sa, FALSE);
-               if (ip)
+               this->lock->write_lock(this->lock);
+               enumerator = this->entries->create_enumerator(this->entries);
+               while (enumerator->enumerate(enumerator, &entry))
                {
-                       ip = ip->clone(ip);
-                       this->lock->write_lock(this->lock);
-                       ip = this->ips->put(this->ips, ip, ip);
-                       this->lock->unlock(this->lock);
-                       DESTROY_IF(ip);
+                       if (entry->reqid == child_sa->get_reqid(child_sa))
+                       {
+                               this->entries->remove_at(this->entries, enumerator);
+                               entry->local->destroy_offset(entry->local,
+                                                                               offsetof(traffic_selector_t, destroy));
+                               entry->remote->destroy_offset(entry->remote,
+                                                                               offsetof(traffic_selector_t, destroy));
+                               free(entry);
+                       }
                }
+               enumerator->destroy(enumerator);
+               this->lock->unlock(this->lock);
        }
        return TRUE;
 }
 
-METHOD(farp_listener_t, is_active, bool,
-       private_farp_listener_t *this, host_t *ip)
+METHOD(farp_listener_t, has_tunnel, bool,
+       private_farp_listener_t *this, host_t *local, host_t *remote)
 {
-       bool active;
+       enumerator_t *entries, *locals, *remotes;
+       traffic_selector_t *ts;
+       bool found = FALSE;
+       entry_t *entry;
 
        this->lock->read_lock(this->lock);
-       active = this->ips->get(this->ips, ip) != NULL;
+       entries = this->entries->create_enumerator(this->entries);
+       while (!found && entries->enumerate(entries, &entry))
+       {
+               remotes = entry->remote->create_enumerator(entry->remote);
+               while (!found && remotes->enumerate(remotes, &ts))
+               {
+                       if (ts->includes(ts, remote))
+                       {
+                               locals = entry->local->create_enumerator(entry->local);
+                               while (!found && locals->enumerate(locals, &ts))
+                               {
+                                       found = ts->includes(ts, local);
+                               }
+                               locals->destroy(locals);
+                       }
+               }
+               remotes->destroy(remotes);
+       }
+       entries->destroy(entries);
        this->lock->unlock(this->lock);
-       return active;
+
+       return found;
 }
 
 METHOD(farp_listener_t, destroy, void,
        private_farp_listener_t *this)
 {
-       enumerator_t *enumerator;
-       host_t *key, *value;
-
-       enumerator = this->ips->create_enumerator(this->ips);
-       while (enumerator->enumerate(enumerator, &key, &value))
-       {
-               value->destroy(value);
-       }
-       enumerator->destroy(enumerator);
-       this->ips->destroy(this->ips);
-
+       this->entries->destroy(this->entries);
        this->lock->destroy(this->lock);
        free(this);
 }
@@ -138,14 +149,12 @@ farp_listener_t *farp_listener_create()
        INIT(this,
                .public = {
                        .listener = {
-                               .ike_updown = _ike_updown,
-                               .message = _message_hook,
+                               .child_updown = _child_updown,
                        },
-                       .is_active = _is_active,
+                       .has_tunnel = _has_tunnel,
                        .destroy = _destroy,
                },
-               .ips = hashtable_create((hashtable_hash_t)hash,
-                                                               (hashtable_equals_t)equals, 8),
+               .entries = linked_list_create(),
                .lock = rwlock_create(RWLOCK_TYPE_DEFAULT),
        );
 
index bd96d7a1ca88ab064927b47aa35a2d2b1b90524a..3155f60e25ae5fa166559c205ec94fcc787ef9d4 100644 (file)
@@ -37,12 +37,13 @@ struct farp_listener_t {
        listener_t listener;
 
        /**
-        * Check if a given IP is currently used as virtual IP by a peer.
+        * Check if we have a tunnel between two IP addresses.
         *
-        * @param ip            IP to check
-        * @return                      TRUE if IP is an active virtual IP
+        * @param local         local IP
+        * @param remote        remote IP
+        * @return                      TRUE if a tunnel is active
         */
-       bool (*is_active)(farp_listener_t *this, host_t *ip);
+       bool (*has_tunnel)(farp_listener_t *this, host_t *local, host_t *remote);
 
        /**
         * Destroy a farp_listener_t.
index 7a8ca850b6de545a30d367dfff33ab3ad90db8c5..587a3a74e4af978809f060ff8d3eadf658a5cfee 100644 (file)
@@ -108,7 +108,7 @@ static job_requeue_t receive_arp(private_farp_spoofer_t *this)
        arp_t arp;
        int oldstate;
        ssize_t len;
-       host_t *ip;
+       host_t *local, *remote;
 
        oldstate = thread_cancelability(TRUE);
        len = recvfrom(this->skt, &arp, sizeof(arp), 0,
@@ -117,16 +117,16 @@ static job_requeue_t receive_arp(private_farp_spoofer_t *this)
 
        if (len == sizeof(arp))
        {
-               ip = host_create_from_chunk(AF_INET,
+               local = host_create_from_chunk(AF_INET,
+                                                                       chunk_create((char*)&arp.sender_ip, 4), 0);
+               remote = host_create_from_chunk(AF_INET,
                                                                        chunk_create((char*)&arp.target_ip, 4), 0);
-               if (ip)
+               if (this->listener->has_tunnel(this->listener, local, remote))
                {
-                       if (this->listener->is_active(this->listener, ip))
-                       {
-                               send_arp(this, &arp, &addr);
-                       }
-                       ip->destroy(ip);
+                       send_arp(this, &arp, &addr);
                }
+               local->destroy(local);
+               remote->destroy(remote);
        }
 
        return JOB_REQUEUE_DIRECT;