summaryrefslogtreecommitdiff
path: root/src/libcharon/plugins/farp/farp_spoofer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libcharon/plugins/farp/farp_spoofer.c')
-rw-r--r--src/libcharon/plugins/farp/farp_spoofer.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/src/libcharon/plugins/farp/farp_spoofer.c b/src/libcharon/plugins/farp/farp_spoofer.c
new file mode 100644
index 000000000..29e64e32d
--- /dev/null
+++ b/src/libcharon/plugins/farp/farp_spoofer.c
@@ -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;
+}
+