]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Implemented ARP sniffing and spoofing functionality
authorMartin Willi <martin@revosec.ch>
Fri, 19 Mar 2010 15:56:21 +0000 (16:56 +0100)
committerMartin Willi <martin@revosec.ch>
Thu, 25 Mar 2010 13:39:32 +0000 (14:39 +0100)
src/libcharon/plugins/farp/Makefile.am
src/libcharon/plugins/farp/farp_plugin.c
src/libcharon/plugins/farp/farp_spoofer.c [new file with mode: 0644]
src/libcharon/plugins/farp/farp_spoofer.h [new file with mode: 0644]

index 63221891814a8becbf56ef356146691756fd8ff8..6da64509f0bd34c8774bbf6ec06b3c779de68114 100644 (file)
@@ -10,6 +10,6 @@ plugin_LTLIBRARIES = libstrongswan-farp.la
 endif
 
 libstrongswan_farp_la_SOURCES = farp_plugin.h farp_plugin.c \
-       farp_listener.h farp_listener.c
+       farp_listener.h farp_listener.c farp_spoofer.h farp_spoofer.c
 
 libstrongswan_farp_la_LDFLAGS = -module -avoid-version
index 82f1fb2aa1ee0292c486a500d5d3ede89e8558c3..01c2a39c8bd081c7f5f510582d714fd21e36d816 100644 (file)
@@ -16,6 +16,7 @@
 #include "farp_plugin.h"
 
 #include "farp_listener.h"
+#include "farp_spoofer.h"
 
 #include <daemon.h>
 
@@ -35,11 +36,17 @@ struct private_farp_plugin_t {
         * Listener registering active virtual IPs
         */
        farp_listener_t *listener;
+
+       /**
+        * Spoofer listening and spoofing ARP messages
+        */
+       farp_spoofer_t *spoofer;
 };
 
 METHOD(plugin_t, destroy, void,
        private_farp_plugin_t *this)
 {
+       DESTROY_IF(this->spoofer);
        charon->bus->remove_listener(charon->bus, &this->listener->listener);
        this->listener->destroy(this->listener);
        free(this);
@@ -59,6 +66,12 @@ plugin_t *farp_plugin_create()
 
        charon->bus->add_listener(charon->bus, &this->listener->listener);
 
+       this->spoofer = farp_spoofer_create(this->listener);
+       if (!this->spoofer)
+       {
+               destroy(this);
+               return NULL;
+       }
        return &this->public.plugin;
 }
 
diff --git a/src/libcharon/plugins/farp/farp_spoofer.c b/src/libcharon/plugins/farp/farp_spoofer.c
new file mode 100644 (file)
index 0000000..29e64e3
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+#include "farp_spoofer.h"
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/filter.h>
+#include <sys/ioctl.h>
+
+#include <daemon.h>
+#include <threading/thread.h>
+#include <processing/jobs/callback_job.h>
+
+typedef struct private_farp_spoofer_t private_farp_spoofer_t;
+
+/**
+ * Private data of an farp_spoofer_t object.
+ */
+struct private_farp_spoofer_t {
+
+       /**
+        * Public farp_spoofer_t interface.
+        */
+       farp_spoofer_t public;
+
+       /**
+        * Listener that knows active addresses
+        */
+       farp_listener_t *listener;
+
+       /**
+        * Callback job to read ARP requests
+        */
+       callback_job_t *job;
+
+       /**
+        * RAW socket for ARP requests
+        */
+       int skt;
+};
+
+/**
+ * IP over Ethernet ARP message
+ */
+typedef struct __attribute__((packed)) {
+       u_int16_t hardware_type;
+       u_int16_t protocol_type;
+       u_int8_t hardware_size;
+       u_int8_t protocol_size;
+       u_int16_t opcode;
+       u_int8_t sender_mac[6];
+       u_int8_t sender_ip[4];
+       u_int8_t target_mac[6];
+       u_int8_t target_ip[4];
+} arp_t;
+
+/**
+ * Send faked ARP response
+ */
+static void send_arp(private_farp_spoofer_t *this,
+                                        arp_t *arp, struct sockaddr_ll *addr)
+{
+       struct ifreq req;
+       char tmp[4];
+
+       req.ifr_ifindex = addr->sll_ifindex;
+       if (ioctl(this->skt, SIOCGIFNAME, &req) == 0 &&
+               ioctl(this->skt, SIOCGIFHWADDR, &req) == 0 &&
+               req.ifr_hwaddr.sa_family == ARPHRD_ETHER)
+       {
+               memcpy(arp->target_mac, arp->sender_mac, 6);
+               memcpy(arp->sender_mac, req.ifr_hwaddr.sa_data, 6);
+
+               memcpy(tmp, arp->sender_ip, 4);
+               memcpy(arp->sender_ip, arp->target_ip, 4);
+               memcpy(arp->target_ip, tmp, 4);
+
+               arp->opcode = htons(ARPOP_REPLY);
+
+               sendto(this->skt, arp, sizeof(*arp), 0,
+                          (struct sockaddr*)addr, sizeof(*addr));
+       }
+}
+
+/**
+ * ARP request receiving
+ */
+static job_requeue_t receive_arp(private_farp_spoofer_t *this)
+{
+       struct sockaddr_ll addr;
+       socklen_t addr_len = sizeof(addr);
+       arp_t arp;
+       int oldstate;
+       ssize_t len;
+       host_t *ip;
+
+       oldstate = thread_cancelability(TRUE);
+       len = recvfrom(this->skt, &arp, sizeof(arp), 0,
+                                  (struct sockaddr*)&addr, &addr_len);
+       thread_cancelability(oldstate);
+
+       if (len == sizeof(arp))
+       {
+               ip = host_create_from_chunk(AF_INET,
+                                                                       chunk_create((char*)&arp.target_ip, 4), 0);
+               if (ip)
+               {
+                       if (this->listener->is_active(this->listener, ip))
+                       {
+                               send_arp(this, &arp, &addr);
+                       }
+                       ip->destroy(ip);
+               }
+       }
+
+       return JOB_REQUEUE_DIRECT;
+}
+
+METHOD(farp_spoofer_t, destroy, void,
+       private_farp_spoofer_t *this)
+{
+       this->job->cancel(this->job);
+       close(this->skt);
+       free(this);
+}
+
+/**
+ * See header
+ */
+farp_spoofer_t *farp_spoofer_create(farp_listener_t *listener)
+{
+       private_farp_spoofer_t *this;
+       struct sock_filter arp_request_filter_code[] = {
+               BPF_STMT(BPF_LD+BPF_H+BPF_ABS, offsetof(arp_t, protocol_type)),
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_P_IP, 0, 9),
+               BPF_STMT(BPF_LD+BPF_B+BPF_ABS, offsetof(arp_t, hardware_size)),
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 6, 0, 7),
+               BPF_STMT(BPF_LD+BPF_B+BPF_ABS, offsetof(arp_t, protocol_size)),
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 4, 0, 4),
+               BPF_STMT(BPF_LD+BPF_H+BPF_ABS, offsetof(arp_t, opcode)),
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPOP_REQUEST, 0, 3),
+               BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0),
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 28, 0, 1),
+               BPF_STMT(BPF_RET+BPF_A, 0),
+               BPF_STMT(BPF_RET+BPF_K, 0),
+       };
+       struct sock_fprog arp_request_filter = {
+               sizeof(arp_request_filter_code) / sizeof(struct sock_filter),
+               arp_request_filter_code,
+       };
+
+       INIT(this,
+               .public = {
+                       .destroy = _destroy,
+               },
+               .listener = listener,
+       );
+
+       this->skt = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
+       if (this->skt == -1)
+       {
+               DBG1(DBG_NET, "opening ARP packet socket failed: %s", strerror(errno));
+               free(this);
+               return NULL;
+       }
+
+       if (setsockopt(this->skt, SOL_SOCKET, SO_ATTACH_FILTER,
+                                  &arp_request_filter, sizeof(arp_request_filter)) < 0)
+       {
+               DBG1(DBG_NET, "installing ARP packet filter failed: %s", strerror(errno));
+               close(this->skt);
+               free(this);
+               return NULL;
+       }
+
+       this->job = callback_job_create((callback_job_cb_t)receive_arp,
+                                                                       this, NULL, NULL);
+       charon->processor->queue_job(charon->processor, (job_t*)this->job);
+
+       return &this->public;
+}
+
diff --git a/src/libcharon/plugins/farp/farp_spoofer.h b/src/libcharon/plugins/farp/farp_spoofer.h
new file mode 100644 (file)
index 0000000..c91fb3b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 Martin Willi
+ * Copyright (C) 2010 revosec AG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.  See <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ */
+
+/**
+ * @defgroup farp_spoofer farp_spoofer
+ * @{ @ingroup farp
+ */
+
+#ifndef FARP_SPOOFER_H_
+#define FARP_SPOOFER_H_
+
+#include "farp_listener.h"
+
+typedef struct farp_spoofer_t farp_spoofer_t;
+
+/**
+ * Listen to ARP requests and spoof responses, if required.
+ */
+struct farp_spoofer_t {
+
+       /**
+        * Destroy a farp_spoofer_t.
+        */
+       void (*destroy)(farp_spoofer_t *this);
+};
+
+/**
+ * Create a farp_spoofer instance.
+ *
+ * @param listener             listener to check for addresses to spoof
+ * @return                             spoofer instance
+ */
+farp_spoofer_t *farp_spoofer_create(farp_listener_t *listener);
+
+#endif /** FARP_SPOOFER_H_ @}*/