dhcp: Port the plugin to FreeBSD/macOS
This also refactors the BPF handling so it can be shared between the dhcp and farp plugins. The latter is adapted accordingly. Closes strongswan/strongswan#2047 Co-authored-by: Tobias Brunner <[email protected]>
This commit is contained in:
committed by
Tobias Brunner
co-authored by
Tobias Brunner
parent
10a876d54c
commit
187c72d1af
@@ -1853,6 +1853,7 @@ AM_CONDITIONAL(USE_ATTR, test x$attr = xtrue)
|
|||||||
AM_CONDITIONAL(USE_ATTR_SQL, test x$attr_sql = xtrue)
|
AM_CONDITIONAL(USE_ATTR_SQL, test x$attr_sql = xtrue)
|
||||||
AM_CONDITIONAL(USE_COUNTERS, test x$counters = xtrue)
|
AM_CONDITIONAL(USE_COUNTERS, test x$counters = xtrue)
|
||||||
AM_CONDITIONAL(USE_SELINUX, test x$selinux = xtrue)
|
AM_CONDITIONAL(USE_SELINUX, test x$selinux = xtrue)
|
||||||
|
AM_CONDITIONAL(USE_PF_HANDLER, test x$dhcp = xtrue -o x$farp = xtrue)
|
||||||
|
|
||||||
# other options
|
# other options
|
||||||
# ---------------
|
# ---------------
|
||||||
|
|||||||
@@ -147,6 +147,11 @@ if USE_SYSLOG
|
|||||||
bus/listeners/sys_logger.c bus/listeners/sys_logger.h
|
bus/listeners/sys_logger.c bus/listeners/sys_logger.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if USE_PF_HANDLER
|
||||||
|
libcharon_la_SOURCES += \
|
||||||
|
network/pf_handler.c network/pf_handler.h
|
||||||
|
endif
|
||||||
|
|
||||||
daemon.lo : $(top_builddir)/config.status
|
daemon.lo : $(top_builddir)/config.status
|
||||||
|
|
||||||
AM_CPPFLAGS = \
|
AM_CPPFLAGS = \
|
||||||
|
|||||||
@@ -0,0 +1,649 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021-2024 Tobias Brunner
|
||||||
|
* Copyright (C) 2020-2023 Dan James <[email protected]>
|
||||||
|
* Copyright (C) 2010 Martin Willi
|
||||||
|
*
|
||||||
|
* Copyright (C) secunet Security Networks 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 "pf_handler.h"
|
||||||
|
|
||||||
|
#include <library.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
|
#include <linux/if_arp.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#else
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <ifaddrs.h>
|
||||||
|
#include <net/bpf.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_dl.h>
|
||||||
|
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <net/ethernet.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of interfaces to cache in LFU cache
|
||||||
|
*/
|
||||||
|
#define IFACE_CACHE_SIZE 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data for a cached interface on which packets were received
|
||||||
|
*/
|
||||||
|
struct cached_iface_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of the interface
|
||||||
|
*/
|
||||||
|
int if_index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the interface
|
||||||
|
*/
|
||||||
|
char if_name[IFNAMSIZ];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hardware (mac) address of the interface
|
||||||
|
*/
|
||||||
|
u_char hwaddr[ETHER_ADDR_LEN];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of the times this info has been used, for LFU cache
|
||||||
|
*/
|
||||||
|
int used;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct cached_iface_t cached_iface_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct private_pf_handler_t private_pf_handler_t;
|
||||||
|
|
||||||
|
struct private_pf_handler_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public interface
|
||||||
|
*/
|
||||||
|
pf_handler_t public;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name for this handler
|
||||||
|
*/
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered callback
|
||||||
|
*/
|
||||||
|
pf_packet_handler_t handler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context to pass to callback
|
||||||
|
*/
|
||||||
|
void *ctx;
|
||||||
|
|
||||||
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AF_PACKET receive socket
|
||||||
|
*/
|
||||||
|
int receive;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache of frequently used interface information
|
||||||
|
*/
|
||||||
|
cached_iface_t ifaces[IFACE_CACHE_SIZE];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of currently cached interface information
|
||||||
|
*/
|
||||||
|
int cached;
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BPF sockets (one per interface), pf_socket_t
|
||||||
|
*/
|
||||||
|
linked_list_t *pf_sockets;
|
||||||
|
|
||||||
|
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
|
};
|
||||||
|
|
||||||
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the index of the slot that was least frequently used
|
||||||
|
*/
|
||||||
|
static int find_least_used_cache_entry(private_pf_handler_t *this)
|
||||||
|
{
|
||||||
|
int i, idx = 0, least_used = 0;
|
||||||
|
|
||||||
|
if (this->cached < IFACE_CACHE_SIZE)
|
||||||
|
{
|
||||||
|
/* not all slots used, choose the next unused slot */
|
||||||
|
idx = this->cached++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* all slots in use, choose the one with the lowest usage */
|
||||||
|
for (i = 0; i < this->cached; i++)
|
||||||
|
{
|
||||||
|
if (this->ifaces[i].used < least_used)
|
||||||
|
{
|
||||||
|
idx = i;
|
||||||
|
least_used = this->ifaces[i].used;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve information about the interface on which a packet was received
|
||||||
|
*/
|
||||||
|
static cached_iface_t *find_interface(private_pf_handler_t *this, int fd,
|
||||||
|
int ifindex)
|
||||||
|
{
|
||||||
|
struct ifreq req = {
|
||||||
|
.ifr_ifindex = ifindex,
|
||||||
|
};
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
for (idx = 0; idx < this->cached; idx++)
|
||||||
|
{
|
||||||
|
if (this->ifaces[idx].if_index == ifindex)
|
||||||
|
{
|
||||||
|
this->ifaces[idx].used++;
|
||||||
|
return &this->ifaces[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(fd, SIOCGIFNAME, &req) == 0 &&
|
||||||
|
ioctl(fd, SIOCGIFHWADDR, &req) == 0 &&
|
||||||
|
req.ifr_hwaddr.sa_family == ARPHRD_ETHER)
|
||||||
|
{
|
||||||
|
idx = find_least_used_cache_entry(this);
|
||||||
|
|
||||||
|
this->ifaces[idx].if_index = ifindex;
|
||||||
|
memcpy(this->ifaces[idx].if_name, req.ifr_name, IFNAMSIZ);
|
||||||
|
memcpy(this->ifaces[idx].hwaddr, req.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
|
||||||
|
this->ifaces[idx].used = 1;
|
||||||
|
return &this->ifaces[idx];
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CALLBACK(receive_packet, bool,
|
||||||
|
private_pf_handler_t *this, int fd, watcher_event_t event)
|
||||||
|
{
|
||||||
|
cached_iface_t *iface;
|
||||||
|
struct sockaddr_ll addr;
|
||||||
|
socklen_t addr_len = sizeof(addr);
|
||||||
|
uint8_t packet[1500];
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
len = recvfrom(fd, &packet, sizeof(packet), MSG_DONTWAIT,
|
||||||
|
(struct sockaddr*)&addr, &addr_len);
|
||||||
|
|
||||||
|
if (len >= 0)
|
||||||
|
{
|
||||||
|
iface = find_interface(this, fd, addr.sll_ifindex);
|
||||||
|
if (iface)
|
||||||
|
{
|
||||||
|
this->handler(this->ctx, iface->if_name, iface->if_index,
|
||||||
|
chunk_create(iface->hwaddr, ETHER_ADDR_LEN), fd,
|
||||||
|
chunk_create(packet, len));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(pf_handler_t, destroy, void,
|
||||||
|
private_pf_handler_t *this)
|
||||||
|
{
|
||||||
|
if (this->receive >= 0)
|
||||||
|
{
|
||||||
|
lib->watcher->remove(lib->watcher, this->receive);
|
||||||
|
close(this->receive);
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup capturing via AF_PACKET socket
|
||||||
|
*/
|
||||||
|
static bool setup_internal(private_pf_handler_t *this, char *iface,
|
||||||
|
struct sock_fprog *packet_filter)
|
||||||
|
{
|
||||||
|
int protocol = strcmp(this->name, "ARP") ? ETH_P_IP : ETH_P_ARP;
|
||||||
|
|
||||||
|
this->receive = socket(AF_PACKET, SOCK_DGRAM, htons(protocol));
|
||||||
|
if (this->receive == -1)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "opening %s packet socket failed: %s", this->name,
|
||||||
|
strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (setsockopt(this->receive, SOL_SOCKET, SO_ATTACH_FILTER, packet_filter,
|
||||||
|
sizeof(struct sock_fprog)) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "installing %s packet socket filter failed: %s",
|
||||||
|
this->name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
if (iface && !bind_to_device(this->receive, iface))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
lib->watcher->add(lib->watcher, this->receive, WATCHER_READ,
|
||||||
|
receive_packet, this);
|
||||||
|
DBG2(DBG_NET, "listening for %s (protocol=0x%04x) requests on fd=%d",
|
||||||
|
this->name, protocol, this->receive);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Described in header
|
||||||
|
*/
|
||||||
|
bool bind_to_device(int fd, char *iface)
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
struct ifreq ifreq = {};
|
||||||
|
|
||||||
|
if (strlen(iface) > sizeof(ifreq.ifr_name))
|
||||||
|
{
|
||||||
|
DBG1(DBG_CFG, "name for interface too long: '%s'", iface);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
memcpy(ifreq.ifr_name, iface, sizeof(ifreq.ifr_name));
|
||||||
|
status = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq));
|
||||||
|
if (status)
|
||||||
|
{
|
||||||
|
DBG1(DBG_CFG, "binding socket to '%s' failed: %s",
|
||||||
|
iface, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A BPF socket is required for each interface.
|
||||||
|
*/
|
||||||
|
struct pf_socket_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to the private packet filter handler
|
||||||
|
*/
|
||||||
|
private_pf_handler_t *this;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the interface
|
||||||
|
*/
|
||||||
|
char *if_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of the interface
|
||||||
|
*/
|
||||||
|
int if_index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Ethernet MAC address of the interface
|
||||||
|
*/
|
||||||
|
chunk_t mac;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IPv4 address of the interface
|
||||||
|
*/
|
||||||
|
host_t *ipv4;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The BPF file descriptor for this interface
|
||||||
|
*/
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The BPF packet buffer length as read from the BPF fd
|
||||||
|
*/
|
||||||
|
size_t buflen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An allocated buffer for receiving packets from BPF
|
||||||
|
*/
|
||||||
|
uint8_t *bufdat;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct pf_socket_t pf_socket_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free resources used by a socket.
|
||||||
|
*/
|
||||||
|
CALLBACK(destroy_pf_socket, void,
|
||||||
|
pf_socket_t *socket)
|
||||||
|
{
|
||||||
|
if (socket->fd >= 0)
|
||||||
|
{
|
||||||
|
lib->watcher->remove(lib->watcher, socket->fd);
|
||||||
|
close(socket->fd);
|
||||||
|
}
|
||||||
|
DESTROY_IF(socket->ipv4);
|
||||||
|
chunk_free(&socket->mac);
|
||||||
|
free(socket->bufdat);
|
||||||
|
free(socket->if_name);
|
||||||
|
free(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the handler for the named interface, creating one if needed.
|
||||||
|
*/
|
||||||
|
static pf_socket_t *get_pf_socket(private_pf_handler_t *this, char *if_name)
|
||||||
|
{
|
||||||
|
pf_socket_t *socket, *found = NULL;
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
|
||||||
|
enumerator = this->pf_sockets->create_enumerator(this->pf_sockets);
|
||||||
|
while (enumerator->enumerate(enumerator, &socket))
|
||||||
|
{
|
||||||
|
if (streq(socket->if_name, if_name))
|
||||||
|
{
|
||||||
|
found = socket;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
INIT(found,
|
||||||
|
.this = this,
|
||||||
|
.if_name = strdup(if_name),
|
||||||
|
.fd = -1,
|
||||||
|
);
|
||||||
|
this->pf_sockets->insert_last(this->pf_sockets, found);
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find and open an available BPF device.
|
||||||
|
*/
|
||||||
|
static int bpf_open()
|
||||||
|
{
|
||||||
|
static int no_cloning_bpf = 0;
|
||||||
|
/* enough space for: /dev/bpf000\0 */
|
||||||
|
char device[12];
|
||||||
|
int n = no_cloning_bpf ? 0 : -1;
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if (n < 0)
|
||||||
|
{
|
||||||
|
snprintf(device, sizeof(device), "/dev/bpf");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(device, sizeof(device), "/dev/bpf%d", n);
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(device, O_RDWR);
|
||||||
|
|
||||||
|
if (n++ < 0 && fd < 0 && errno == ENOENT)
|
||||||
|
{
|
||||||
|
no_cloning_bpf = 1;
|
||||||
|
errno = EBUSY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (fd < 0 && errno == EBUSY && n < 1000);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
CALLBACK(handler_onpkt, bool,
|
||||||
|
pf_socket_t *socket, int fd, watcher_event_t event)
|
||||||
|
{
|
||||||
|
struct bpf_hdr *bh;
|
||||||
|
void *a;
|
||||||
|
uint8_t *p = socket->bufdat;
|
||||||
|
ssize_t n;
|
||||||
|
size_t pktlen;
|
||||||
|
|
||||||
|
n = read(socket->fd, socket->bufdat, socket->buflen);
|
||||||
|
if (n <= 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "reading %s request from %s failed: %s",
|
||||||
|
socket->this->name, socket->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (p < socket->bufdat + n)
|
||||||
|
{
|
||||||
|
bh = (struct bpf_hdr*) p;
|
||||||
|
a = (void*)(p + bh->bh_hdrlen + sizeof(struct ether_header));
|
||||||
|
pktlen = bh->bh_caplen - sizeof(struct ether_header);
|
||||||
|
|
||||||
|
socket->this->handler(socket->this->ctx, socket->if_name,
|
||||||
|
socket->if_index, socket->mac,
|
||||||
|
socket->fd, chunk_create(a, pktlen));
|
||||||
|
|
||||||
|
p += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and initialize a BPF socket for the interface specified in the given
|
||||||
|
* struct. This entails opening a BPF device, binding it to the interface,
|
||||||
|
* setting the packet filter, and allocating a buffer for receiving packets.
|
||||||
|
*/
|
||||||
|
static bool setup_pf_socket(pf_socket_t *socket, pf_program_t *program)
|
||||||
|
{
|
||||||
|
struct ifreq req;
|
||||||
|
uint32_t disable = 1;
|
||||||
|
uint32_t enable = 1;
|
||||||
|
uint32_t dlt = 0;
|
||||||
|
|
||||||
|
snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", socket->if_name);
|
||||||
|
|
||||||
|
if ((socket->fd = bpf_open()) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "bpf_open(%s): %s", socket->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(socket->fd, BIOCSETIF, &req) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "BIOCSETIF(%s): %s", socket->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(socket->fd, BIOCSHDRCMPLT, &enable) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "BIOCSHDRCMPLT(%s): %s", socket->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(socket->fd, BIOCSSEESENT, &disable) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "BIOCSSEESENT(%s): %s", socket->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(socket->fd, BIOCIMMEDIATE, &enable) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "BIOCIMMEDIATE(%s): %s", socket->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(socket->fd, BIOCGDLT, &dlt) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "BIOCGDLT(%s): %s", socket->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
else if (dlt != DLT_EN10MB)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(socket->fd, BIOCSETF, program) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "BIOCSETF(%s): %s", socket->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ioctl(socket->fd, BIOCGBLEN, &socket->buflen) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "BIOCGBLEN(%s): %s", socket->if_name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
socket->bufdat = malloc(socket->buflen);
|
||||||
|
|
||||||
|
lib->watcher->add(lib->watcher, socket->fd, WATCHER_READ,
|
||||||
|
handler_onpkt, socket);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a socket for each BPF capable interface. The interface must have an
|
||||||
|
* Ethernet MAC address, an IPv4 address, and use an Ethernet data link layer.
|
||||||
|
*/
|
||||||
|
static bool setup_internal(private_pf_handler_t *this, char *iface,
|
||||||
|
pf_program_t *program)
|
||||||
|
{
|
||||||
|
struct ifaddrs *ifas;
|
||||||
|
struct ifaddrs *ifa;
|
||||||
|
struct sockaddr_dl *dl;
|
||||||
|
pf_socket_t *socket;
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
host_t *ipv4;
|
||||||
|
|
||||||
|
if (getifaddrs(&ifas) < 0)
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "%s cannot find interfaces: %s", this->name, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
this->pf_sockets = linked_list_create();
|
||||||
|
for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next)
|
||||||
|
{
|
||||||
|
switch (ifa->ifa_addr->sa_family)
|
||||||
|
{
|
||||||
|
case AF_LINK:
|
||||||
|
dl = (struct sockaddr_dl *)ifa->ifa_addr;
|
||||||
|
if (dl->sdl_alen == ETHER_ADDR_LEN)
|
||||||
|
{
|
||||||
|
socket = get_pf_socket(this, ifa->ifa_name);
|
||||||
|
socket->if_index = dl->sdl_index;
|
||||||
|
socket->mac = chunk_clone(chunk_create(LLADDR(dl),
|
||||||
|
dl->sdl_alen));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AF_INET:
|
||||||
|
ipv4 = host_create_from_sockaddr(ifa->ifa_addr);
|
||||||
|
if (ipv4 && !ipv4->is_anyaddr(ipv4))
|
||||||
|
{
|
||||||
|
socket = get_pf_socket(this, ifa->ifa_name);
|
||||||
|
if (!socket->ipv4)
|
||||||
|
{
|
||||||
|
socket->ipv4 = ipv4->clone(ipv4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DESTROY_IF(ipv4);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeifaddrs(ifas);
|
||||||
|
|
||||||
|
enumerator = this->pf_sockets->create_enumerator(this->pf_sockets);
|
||||||
|
while (enumerator->enumerate(enumerator, &socket))
|
||||||
|
{
|
||||||
|
if (socket->mac.ptr && socket->ipv4 &&
|
||||||
|
(!iface || streq(socket->if_name, iface)) &&
|
||||||
|
setup_pf_socket(socket, program))
|
||||||
|
{
|
||||||
|
DBG2(DBG_NET, "listening for %s requests on %s (%H, %#B)",
|
||||||
|
this->name, socket->if_name, socket->ipv4, &socket->mac);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->pf_sockets->remove_at(this->pf_sockets, enumerator);
|
||||||
|
destroy_pf_socket(socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
|
return this->pf_sockets->get_count(this->pf_sockets) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
METHOD(pf_handler_t, destroy, void,
|
||||||
|
private_pf_handler_t *this)
|
||||||
|
{
|
||||||
|
DESTROY_FUNCTION_IF(this->pf_sockets, destroy_pf_socket);
|
||||||
|
free(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Described in header
|
||||||
|
*/
|
||||||
|
bool bind_to_device(int fd, char *iface)
|
||||||
|
{
|
||||||
|
#if defined(__FreeBSD__)
|
||||||
|
DBG1(DBG_CFG, "binding socket to '%s' failed: IP_SENDIF not implemented yet.", iface);
|
||||||
|
return FALSE;
|
||||||
|
#else /* defined(__FreeBSD__) */
|
||||||
|
unsigned int idx = if_nametoindex(iface);
|
||||||
|
if (setsockopt(fd, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx)) == -1)
|
||||||
|
{
|
||||||
|
DBG1(DBG_CFG, "binding socket to '%s' failed: %s",
|
||||||
|
iface, strerror(errno));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
#endif /* defined(__FreeBSD__) */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Described in header
|
||||||
|
*/
|
||||||
|
pf_handler_t *pf_handler_create(const char *name, char *iface,
|
||||||
|
pf_packet_handler_t handler, void *ctx,
|
||||||
|
pf_program_t *program)
|
||||||
|
{
|
||||||
|
private_pf_handler_t *this;
|
||||||
|
|
||||||
|
INIT(this,
|
||||||
|
.public = {
|
||||||
|
.destroy = _destroy,
|
||||||
|
},
|
||||||
|
.name = name,
|
||||||
|
.handler = handler,
|
||||||
|
.ctx = ctx,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!setup_internal(this, iface, program))
|
||||||
|
{
|
||||||
|
destroy(this);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &this->public;
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 Tobias Brunner
|
||||||
|
* Copyright (C) 2020-2023 Dan James <[email protected]>
|
||||||
|
*
|
||||||
|
* Copyright (C) secunet Security Networks 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PF_HANDLER_H_
|
||||||
|
#define PF_HANDLER_H_
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <utils/chunk.h>
|
||||||
|
|
||||||
|
typedef struct pf_handler_t pf_handler_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BPF implementation for different platforms
|
||||||
|
*/
|
||||||
|
struct pf_handler_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy this instance.
|
||||||
|
*/
|
||||||
|
void (*destroy)(pf_handler_t *this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that's called for received packets.
|
||||||
|
*
|
||||||
|
* @param ctx context as passed in the constructor
|
||||||
|
* @param if_name name of the interface on which the packet was received
|
||||||
|
* @param if_index index of the interface on which the packet was received
|
||||||
|
* @param mac MAC address of the interface on which the packet was received
|
||||||
|
* @param fd file descriptor of the receiving socket (may be used to send
|
||||||
|
* a response)
|
||||||
|
* @param packet the received packet
|
||||||
|
*/
|
||||||
|
typedef void (*pf_packet_handler_t)(void *ctx, char *if_name, int if_index,
|
||||||
|
chunk_t mac, int fd, chunk_t packet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for BFP programs on different platforms
|
||||||
|
*/
|
||||||
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
|
typedef struct sock_fprog pf_program_t;
|
||||||
|
#else
|
||||||
|
typedef struct bpf_program pf_program_t;
|
||||||
|
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a pf_handler_t instance.
|
||||||
|
*
|
||||||
|
* @param name name to identify this handler ("ARP" is treated specially)
|
||||||
|
* @param iface optional interface to limit capturing to
|
||||||
|
* @param handler handler for received packets
|
||||||
|
* @param ctx context passed to handler
|
||||||
|
* @param program BPF filter program
|
||||||
|
*/
|
||||||
|
pf_handler_t *pf_handler_create(const char *name, char *iface,
|
||||||
|
pf_packet_handler_t handler, void *ctx,
|
||||||
|
pf_program_t *program);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind a socket to a particular network interface
|
||||||
|
*
|
||||||
|
* @param fd file descriptor of the socket
|
||||||
|
* @param iface name of the interface
|
||||||
|
* @return whether the socket was successfully bound
|
||||||
|
*/
|
||||||
|
bool bind_to_device(int fd, char *iface);
|
||||||
|
|
||||||
|
#endif /** PF_HANDLER_H_ */
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2012-2018 Tobias Brunner
|
* Copyright (C) 2023 Dan James <[email protected]>
|
||||||
|
* Copyright (C) 2012-2024 Tobias Brunner
|
||||||
* Copyright (C) 2010 Martin Willi
|
* Copyright (C) 2010 Martin Willi
|
||||||
*
|
*
|
||||||
* Copyright (C) secunet Security Networks AG
|
* Copyright (C) secunet Security Networks AG
|
||||||
@@ -19,13 +20,20 @@
|
|||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <netinet/ip.h>
|
#include <netinet/ip.h>
|
||||||
#include <netinet/udp.h>
|
#include <netinet/udp.h>
|
||||||
|
|
||||||
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
|
#include <string.h>
|
||||||
#include <linux/if_arp.h>
|
#include <linux/if_arp.h>
|
||||||
#include <linux/if_ether.h>
|
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
|
#else
|
||||||
|
#include <net/bpf.h>
|
||||||
|
#include <net/ethernet.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_arp.h>
|
||||||
|
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
|
|
||||||
#include <collections/linked_list.h>
|
#include <collections/linked_list.h>
|
||||||
#include <utils/identification.h>
|
#include <utils/identification.h>
|
||||||
@@ -35,6 +43,7 @@
|
|||||||
|
|
||||||
#include <daemon.h>
|
#include <daemon.h>
|
||||||
#include <processing/jobs/callback_job.h>
|
#include <processing/jobs/callback_job.h>
|
||||||
|
#include <network/pf_handler.h>
|
||||||
|
|
||||||
#define DHCP_SERVER_PORT 67
|
#define DHCP_SERVER_PORT 67
|
||||||
#define DHCP_CLIENT_PORT 68
|
#define DHCP_CLIENT_PORT 68
|
||||||
@@ -93,9 +102,9 @@ struct private_dhcp_socket_t {
|
|||||||
int send;
|
int send;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DHCP receive socket
|
* BPF handler to receive DHCP messages
|
||||||
*/
|
*/
|
||||||
int receive;
|
pf_handler_t *pf_handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do we use per-identity or random leases (and MAC addresses)
|
* Do we use per-identity or random leases (and MAC addresses)
|
||||||
@@ -178,7 +187,7 @@ typedef struct __attribute__((packed)) {
|
|||||||
uint32_t your_address;
|
uint32_t your_address;
|
||||||
uint32_t server_address;
|
uint32_t server_address;
|
||||||
uint32_t gateway_address;
|
uint32_t gateway_address;
|
||||||
char client_hw_addr[6];
|
uint8_t client_hw_addr[6];
|
||||||
char client_hw_padding[10];
|
char client_hw_padding[10];
|
||||||
char server_hostname[64];
|
char server_hostname[64];
|
||||||
char boot_filename[128];
|
char boot_filename[128];
|
||||||
@@ -199,14 +208,14 @@ static inline bool is_broadcast(host_t *host)
|
|||||||
/**
|
/**
|
||||||
* Prepare a DHCP message for a given transaction
|
* Prepare a DHCP message for a given transaction
|
||||||
*/
|
*/
|
||||||
static int prepare_dhcp(private_dhcp_socket_t *this,
|
static size_t prepare_dhcp(private_dhcp_socket_t *this,
|
||||||
dhcp_transaction_t *transaction,
|
dhcp_transaction_t *transaction,
|
||||||
dhcp_message_type_t type, dhcp_t *dhcp)
|
dhcp_message_type_t type, dhcp_t *dhcp)
|
||||||
{
|
{
|
||||||
chunk_t chunk;
|
chunk_t chunk;
|
||||||
identification_t *identity;
|
identification_t *identity;
|
||||||
dhcp_option_t *option;
|
dhcp_option_t *option;
|
||||||
int optlen = 0, remaining;
|
size_t optlen = 0, remaining;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
|
||||||
memset(dhcp, 0, sizeof(*dhcp));
|
memset(dhcp, 0, sizeof(*dhcp));
|
||||||
@@ -281,7 +290,7 @@ static int prepare_dhcp(private_dhcp_socket_t *this,
|
|||||||
* Send a DHCP message with given options length
|
* Send a DHCP message with given options length
|
||||||
*/
|
*/
|
||||||
static bool send_dhcp(private_dhcp_socket_t *this,
|
static bool send_dhcp(private_dhcp_socket_t *this,
|
||||||
dhcp_transaction_t *transaction, dhcp_t *dhcp, int optlen)
|
dhcp_transaction_t *transaction, dhcp_t *dhcp, size_t optlen)
|
||||||
{
|
{
|
||||||
host_t *dst;
|
host_t *dst;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
@@ -304,7 +313,7 @@ static bool discover(private_dhcp_socket_t *this,
|
|||||||
{
|
{
|
||||||
dhcp_option_t *option;
|
dhcp_option_t *option;
|
||||||
dhcp_t dhcp;
|
dhcp_t dhcp;
|
||||||
int optlen;
|
size_t optlen;
|
||||||
|
|
||||||
optlen = prepare_dhcp(this, transaction, DHCP_DISCOVER, &dhcp);
|
optlen = prepare_dhcp(this, transaction, DHCP_DISCOVER, &dhcp);
|
||||||
|
|
||||||
@@ -340,7 +349,7 @@ static bool request(private_dhcp_socket_t *this,
|
|||||||
dhcp_t dhcp;
|
dhcp_t dhcp;
|
||||||
host_t *offer, *server;
|
host_t *offer, *server;
|
||||||
chunk_t chunk;
|
chunk_t chunk;
|
||||||
int optlen;
|
size_t optlen;
|
||||||
|
|
||||||
optlen = prepare_dhcp(this, transaction, DHCP_REQUEST, &dhcp);
|
optlen = prepare_dhcp(this, transaction, DHCP_REQUEST, &dhcp);
|
||||||
|
|
||||||
@@ -483,7 +492,7 @@ METHOD(dhcp_socket_t, release, void,
|
|||||||
dhcp_t dhcp;
|
dhcp_t dhcp;
|
||||||
host_t *release, *server;
|
host_t *release, *server;
|
||||||
chunk_t chunk;
|
chunk_t chunk;
|
||||||
int optlen;
|
size_t optlen;
|
||||||
|
|
||||||
optlen = prepare_dhcp(this, transaction, DHCP_RELEASE, &dhcp);
|
optlen = prepare_dhcp(this, transaction, DHCP_RELEASE, &dhcp);
|
||||||
|
|
||||||
@@ -517,7 +526,7 @@ METHOD(dhcp_socket_t, release, void,
|
|||||||
/**
|
/**
|
||||||
* Handle a DHCP OFFER
|
* Handle a DHCP OFFER
|
||||||
*/
|
*/
|
||||||
static void handle_offer(private_dhcp_socket_t *this, dhcp_t *dhcp, int optlen)
|
static void handle_offer(private_dhcp_socket_t *this, dhcp_t *dhcp, size_t optlen)
|
||||||
{
|
{
|
||||||
dhcp_transaction_t *transaction = NULL;
|
dhcp_transaction_t *transaction = NULL;
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
@@ -551,7 +560,7 @@ static void handle_offer(private_dhcp_socket_t *this, dhcp_t *dhcp, int optlen)
|
|||||||
|
|
||||||
if (transaction)
|
if (transaction)
|
||||||
{
|
{
|
||||||
int optsize, optpos = 0, pos;
|
size_t optsize, optpos = 0, pos;
|
||||||
dhcp_option_t *option;
|
dhcp_option_t *option;
|
||||||
|
|
||||||
while (optlen > sizeof(dhcp_option_t))
|
while (optlen > sizeof(dhcp_option_t))
|
||||||
@@ -596,7 +605,7 @@ static void handle_offer(private_dhcp_socket_t *this, dhcp_t *dhcp, int optlen)
|
|||||||
/**
|
/**
|
||||||
* Handle a DHCP ACK
|
* Handle a DHCP ACK
|
||||||
*/
|
*/
|
||||||
static void handle_ack(private_dhcp_socket_t *this, dhcp_t *dhcp, int optlen)
|
static void handle_ack(private_dhcp_socket_t *this, dhcp_t *dhcp)
|
||||||
{
|
{
|
||||||
dhcp_transaction_t *transaction;
|
dhcp_transaction_t *transaction;
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
@@ -618,33 +627,31 @@ static void handle_ack(private_dhcp_socket_t *this, dhcp_t *dhcp, int optlen)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive DHCP responses
|
* Complete DHCP packet
|
||||||
*/
|
*/
|
||||||
static bool receive_dhcp(private_dhcp_socket_t *this, int fd,
|
struct __attribute__((packed)) dhcp_packet_t {
|
||||||
watcher_event_t event)
|
struct ip ip;
|
||||||
|
struct udphdr udp;
|
||||||
|
dhcp_t dhcp;
|
||||||
|
};
|
||||||
|
typedef struct dhcp_packet_t dhcp_packet_t;
|
||||||
|
|
||||||
|
CALLBACK(receive_dhcp, void,
|
||||||
|
private_dhcp_socket_t *this, char *if_name, int if_index, chunk_t mac,
|
||||||
|
int fd, chunk_t pkt)
|
||||||
{
|
{
|
||||||
struct sockaddr_ll addr;
|
dhcp_packet_t *packet = (dhcp_packet_t*)pkt.ptr;
|
||||||
socklen_t addr_len = sizeof(addr);
|
size_t optlen, origoptlen, optpos = 0, optsize;
|
||||||
struct __attribute__((packed)) {
|
|
||||||
struct iphdr ip;
|
|
||||||
struct udphdr udp;
|
|
||||||
dhcp_t dhcp;
|
|
||||||
} packet;
|
|
||||||
int optlen, origoptlen, optsize, optpos = 0;
|
|
||||||
ssize_t len;
|
|
||||||
dhcp_option_t *option;
|
dhcp_option_t *option;
|
||||||
|
|
||||||
len = recvfrom(fd, &packet, sizeof(packet), MSG_DONTWAIT,
|
if (pkt.len >= sizeof(struct ip) + sizeof(struct udphdr) +
|
||||||
(struct sockaddr*)&addr, &addr_len);
|
|
||||||
|
|
||||||
if (len >= sizeof(struct iphdr) + sizeof(struct udphdr) +
|
|
||||||
offsetof(dhcp_t, options))
|
offsetof(dhcp_t, options))
|
||||||
{
|
{
|
||||||
origoptlen = optlen = len - sizeof(struct iphdr) +
|
origoptlen = optlen = pkt.len - sizeof(struct ip) +
|
||||||
sizeof(struct udphdr) + offsetof(dhcp_t, options);
|
sizeof(struct udphdr) + offsetof(dhcp_t, options);
|
||||||
while (optlen > sizeof(dhcp_option_t))
|
while (optlen > sizeof(dhcp_option_t))
|
||||||
{
|
{
|
||||||
option = (dhcp_option_t*)&packet.dhcp.options[optpos];
|
option = (dhcp_option_t*)&packet->dhcp.options[optpos];
|
||||||
optsize = sizeof(dhcp_option_t) + option->len;
|
optsize = sizeof(dhcp_option_t) + option->len;
|
||||||
if (option->type == DHCP_OPTEND || optlen < optsize)
|
if (option->type == DHCP_OPTEND || optlen < optsize)
|
||||||
{
|
{
|
||||||
@@ -655,10 +662,10 @@ static bool receive_dhcp(private_dhcp_socket_t *this, int fd,
|
|||||||
switch (option->data[0])
|
switch (option->data[0])
|
||||||
{
|
{
|
||||||
case DHCP_OFFER:
|
case DHCP_OFFER:
|
||||||
handle_offer(this, &packet.dhcp, origoptlen);
|
handle_offer(this, &packet->dhcp, origoptlen);
|
||||||
break;
|
break;
|
||||||
case DHCP_ACK:
|
case DHCP_ACK:
|
||||||
handle_ack(this, &packet.dhcp, origoptlen);
|
handle_ack(this, &packet->dhcp);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -668,7 +675,6 @@ static bool receive_dhcp(private_dhcp_socket_t *this, int fd,
|
|||||||
optpos += optsize;
|
optpos += optsize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(dhcp_socket_t, destroy, void,
|
METHOD(dhcp_socket_t, destroy, void,
|
||||||
@@ -682,11 +688,6 @@ METHOD(dhcp_socket_t, destroy, void,
|
|||||||
{
|
{
|
||||||
close(this->send);
|
close(this->send);
|
||||||
}
|
}
|
||||||
if (this->receive > 0)
|
|
||||||
{
|
|
||||||
lib->watcher->remove(lib->watcher, this->receive);
|
|
||||||
close(this->receive);
|
|
||||||
}
|
|
||||||
this->mutex->destroy(this->mutex);
|
this->mutex->destroy(this->mutex);
|
||||||
this->condvar->destroy(this->condvar);
|
this->condvar->destroy(this->condvar);
|
||||||
this->discover->destroy_offset(this->discover,
|
this->discover->destroy_offset(this->discover,
|
||||||
@@ -695,33 +696,12 @@ METHOD(dhcp_socket_t, destroy, void,
|
|||||||
offsetof(dhcp_transaction_t, destroy));
|
offsetof(dhcp_transaction_t, destroy));
|
||||||
this->completed->destroy_offset(this->completed,
|
this->completed->destroy_offset(this->completed,
|
||||||
offsetof(dhcp_transaction_t, destroy));
|
offsetof(dhcp_transaction_t, destroy));
|
||||||
|
DESTROY_IF(this->pf_handler);
|
||||||
DESTROY_IF(this->rng);
|
DESTROY_IF(this->rng);
|
||||||
DESTROY_IF(this->dst);
|
DESTROY_IF(this->dst);
|
||||||
free(this);
|
free(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind a socket to a particular interface name
|
|
||||||
*/
|
|
||||||
static bool bind_to_device(int fd, char *iface)
|
|
||||||
{
|
|
||||||
struct ifreq ifreq = {};
|
|
||||||
|
|
||||||
if (strlen(iface) > sizeof(ifreq.ifr_name))
|
|
||||||
{
|
|
||||||
DBG1(DBG_CFG, "name for DHCP interface too long: '%s'", iface);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
memcpy(ifreq.ifr_name, iface, sizeof(ifreq.ifr_name));
|
|
||||||
if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof(ifreq)))
|
|
||||||
{
|
|
||||||
DBG1(DBG_CFG, "binding DHCP socket to '%s' failed: %s",
|
|
||||||
iface, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See header
|
* See header
|
||||||
*/
|
*/
|
||||||
@@ -738,29 +718,26 @@ dhcp_socket_t *dhcp_socket_create()
|
|||||||
socklen_t addr_len;
|
socklen_t addr_len;
|
||||||
char *iface;
|
char *iface;
|
||||||
int on = 1, rcvbuf = 0;
|
int on = 1, rcvbuf = 0;
|
||||||
|
|
||||||
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
|
const size_t skip_ip4 = sizeof(struct iphdr);
|
||||||
|
const size_t skip_udp = skip_ip4 + sizeof(struct udphdr);
|
||||||
struct sock_filter dhcp_filter_code[] = {
|
struct sock_filter dhcp_filter_code[] = {
|
||||||
BPF_STMT(BPF_LD+BPF_B+BPF_ABS,
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, offsetof(struct iphdr, protocol)),
|
||||||
offsetof(struct iphdr, protocol)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_UDP, 0, 16),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_UDP, 0, 16),
|
||||||
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, sizeof(struct iphdr) +
|
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, skip_ip4 + offsetof(struct udphdr, source)),
|
||||||
offsetof(struct udphdr, source)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_SERVER_PORT, 0, 14),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_SERVER_PORT, 0, 14),
|
||||||
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, sizeof(struct iphdr) +
|
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, skip_ip4 + offsetof(struct udphdr, dest)),
|
||||||
offsetof(struct udphdr, dest)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_CLIENT_PORT, 2, 0),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_CLIENT_PORT, 2, 0),
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_SERVER_PORT, 1, 0),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_SERVER_PORT, 1, 0),
|
||||||
BPF_JUMP(BPF_JMP+BPF_JA, 10, 0, 0),
|
BPF_JUMP(BPF_JMP+BPF_JA, 10, 0, 0),
|
||||||
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, sizeof(struct iphdr) +
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, skip_udp + offsetof(dhcp_t, opcode)),
|
||||||
sizeof(struct udphdr) + offsetof(dhcp_t, opcode)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, BOOTREPLY, 0, 8),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, BOOTREPLY, 0, 8),
|
||||||
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, sizeof(struct iphdr) +
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, skip_udp + offsetof(dhcp_t, hw_type)),
|
||||||
sizeof(struct udphdr) + offsetof(dhcp_t, hw_type)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPHRD_ETHER, 0, 6),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPHRD_ETHER, 0, 6),
|
||||||
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, sizeof(struct iphdr) +
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, skip_udp + offsetof(dhcp_t, hw_addr_len)),
|
||||||
sizeof(struct udphdr) + offsetof(dhcp_t, hw_addr_len)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 6, 0, 4),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 6, 0, 4),
|
||||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, sizeof(struct iphdr) +
|
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, skip_udp + offsetof(dhcp_t, magic_cookie)),
|
||||||
sizeof(struct udphdr) + offsetof(dhcp_t, magic_cookie)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x63825363, 0, 2),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x63825363, 0, 2),
|
||||||
BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0),
|
BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0),
|
||||||
BPF_STMT(BPF_RET+BPF_A, 0),
|
BPF_STMT(BPF_RET+BPF_A, 0),
|
||||||
@@ -770,6 +747,38 @@ dhcp_socket_t *dhcp_socket_create()
|
|||||||
sizeof(dhcp_filter_code) / sizeof(struct sock_filter),
|
sizeof(dhcp_filter_code) / sizeof(struct sock_filter),
|
||||||
dhcp_filter_code,
|
dhcp_filter_code,
|
||||||
};
|
};
|
||||||
|
#else
|
||||||
|
const size_t skip_eth = sizeof(struct ether_header);
|
||||||
|
const size_t skip_ip4 = skip_eth + sizeof(struct ip);
|
||||||
|
const size_t skip_udp = skip_ip4 + sizeof(struct udphdr);
|
||||||
|
struct bpf_insn instructions[] = {
|
||||||
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, skip_eth + offsetof(struct ip, ip_p)),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, IPPROTO_UDP, 0, 16),
|
||||||
|
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, skip_ip4 + offsetof(struct udphdr, uh_sport)),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_SERVER_PORT, 0, 14),
|
||||||
|
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, skip_ip4 + offsetof(struct udphdr, uh_dport)),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_CLIENT_PORT, 2, 0),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, DHCP_SERVER_PORT, 1, 0),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JA, 10, 0, 0),
|
||||||
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, skip_udp + offsetof(dhcp_t, opcode)),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, BOOTREPLY, 0, 8),
|
||||||
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, skip_udp + offsetof(dhcp_t, hw_type)),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPHRD_ETHER, 0, 6),
|
||||||
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, skip_udp + offsetof(dhcp_t, hw_addr_len)),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 6, 0, 4),
|
||||||
|
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, skip_udp + offsetof(dhcp_t, magic_cookie)),
|
||||||
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x63825363, 0, 2),
|
||||||
|
BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0),
|
||||||
|
BPF_STMT(BPF_RET+BPF_A, 0),
|
||||||
|
BPF_STMT(BPF_RET+BPF_K, 0),
|
||||||
|
};
|
||||||
|
struct bpf_program dhcp_filter = {
|
||||||
|
sizeof(instructions) / sizeof(struct bpf_insn),
|
||||||
|
&instructions[0],
|
||||||
|
};
|
||||||
|
/* 0 receive buffer is not accepted */
|
||||||
|
rcvbuf = 1;
|
||||||
|
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
|
|
||||||
INIT(this,
|
INIT(this,
|
||||||
.public = {
|
.public = {
|
||||||
@@ -864,25 +873,16 @@ dhcp_socket_t *dhcp_socket_create()
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
this->receive = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_IP));
|
this->pf_handler = pf_handler_create("DHCP", iface, receive_dhcp, this,
|
||||||
if (this->receive == -1)
|
&dhcp_filter);
|
||||||
|
if (!this->pf_handler)
|
||||||
{
|
{
|
||||||
DBG1(DBG_NET, "opening DHCP receive socket failed: %s", strerror(errno));
|
|
||||||
destroy(this);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (setsockopt(this->receive, SOL_SOCKET, SO_ATTACH_FILTER,
|
|
||||||
&dhcp_filter, sizeof(dhcp_filter)) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_CFG, "installing DHCP socket filter failed: %s",
|
|
||||||
strerror(errno));
|
|
||||||
destroy(this);
|
destroy(this);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (iface)
|
if (iface)
|
||||||
{
|
{
|
||||||
if (!bind_to_device(this->send, iface) ||
|
if (!bind_to_device(this->send, iface))
|
||||||
!bind_to_device(this->receive, iface))
|
|
||||||
{
|
{
|
||||||
destroy(this);
|
destroy(this);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -908,8 +908,5 @@ dhcp_socket_t *dhcp_socket_create()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lib->watcher->add(lib->watcher, this->receive, WATCHER_READ,
|
|
||||||
(watcher_cb_t)receive_dhcp, this);
|
|
||||||
|
|
||||||
return &this->public;
|
return &this->public;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2021 Tobias Brunner
|
* Copyright (C) 2021-2024 Tobias Brunner
|
||||||
|
* Copyright (C) 2020-2023 Dan James <[email protected]>
|
||||||
* Copyright (C) 2010 Martin Willi
|
* Copyright (C) 2010 Martin Willi
|
||||||
*
|
*
|
||||||
* Copyright (C) secunet Security Networks AG
|
* Copyright (C) secunet Security Networks AG
|
||||||
@@ -15,30 +16,6 @@
|
|||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
|
||||||
* For the Apple BPF implementation.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2020 Dan James <[email protected]>
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "farp_spoofer.h"
|
#include "farp_spoofer.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
@@ -51,18 +28,16 @@
|
|||||||
#include <linux/if_ether.h>
|
#include <linux/if_ether.h>
|
||||||
#include <linux/filter.h>
|
#include <linux/filter.h>
|
||||||
#else
|
#else
|
||||||
#include <fcntl.h>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <net/bpf.h>
|
#include <net/bpf.h>
|
||||||
#include <net/ethernet.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#include <net/if_arp.h>
|
#include <net/if_arp.h>
|
||||||
#include <net/if_dl.h>
|
#include <net/if_dl.h>
|
||||||
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
|
|
||||||
|
#include <net/ethernet.h>
|
||||||
#include <daemon.h>
|
#include <daemon.h>
|
||||||
#include <threading/thread.h>
|
#include <threading/thread.h>
|
||||||
#include <processing/jobs/callback_job.h>
|
#include <processing/jobs/callback_job.h>
|
||||||
|
#include <network/pf_handler.h>
|
||||||
|
|
||||||
typedef struct private_farp_spoofer_t private_farp_spoofer_t;
|
typedef struct private_farp_spoofer_t private_farp_spoofer_t;
|
||||||
|
|
||||||
@@ -81,17 +56,10 @@ struct private_farp_spoofer_t {
|
|||||||
*/
|
*/
|
||||||
farp_listener_t *listener;
|
farp_listener_t *listener;
|
||||||
|
|
||||||
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
|
||||||
/**
|
/**
|
||||||
* RAW socket for ARP requests
|
* BPF handler for ARP requests
|
||||||
*/
|
*/
|
||||||
int skt;
|
pf_handler_t *pf_handler;
|
||||||
#else
|
|
||||||
/**
|
|
||||||
* Linked list of interface handlers
|
|
||||||
*/
|
|
||||||
linked_list_t *handlers;
|
|
||||||
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,74 +71,145 @@ typedef struct __attribute__((packed)) {
|
|||||||
uint8_t hardware_size;
|
uint8_t hardware_size;
|
||||||
uint8_t protocol_size;
|
uint8_t protocol_size;
|
||||||
uint16_t opcode;
|
uint16_t opcode;
|
||||||
uint8_t sender_mac[6];
|
uint8_t sender_mac[ETHER_ADDR_LEN];
|
||||||
uint8_t sender_ip[4];
|
uint8_t sender_ip[4];
|
||||||
uint8_t target_mac[6];
|
uint8_t target_mac[ETHER_ADDR_LEN];
|
||||||
uint8_t target_ip[4];
|
uint8_t target_ip[4];
|
||||||
} arp_t;
|
} arp_t;
|
||||||
|
|
||||||
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send faked ARP response
|
* Send faked ARP response
|
||||||
*/
|
*/
|
||||||
static void send_arp(private_farp_spoofer_t *this,
|
static void send_arp(char *if_name, int if_index, chunk_t mac, int fd,
|
||||||
arp_t *arp, struct sockaddr_ll *addr)
|
arp_t *arp, host_t *sender, host_t *target)
|
||||||
{
|
{
|
||||||
struct ifreq req;
|
struct sockaddr_ll addr = {
|
||||||
|
.sll_family = AF_PACKET,
|
||||||
|
.sll_protocol = htons(ETH_P_ARP),
|
||||||
|
.sll_ifindex = if_index,
|
||||||
|
.sll_halen = ETHER_ADDR_LEN,
|
||||||
|
};
|
||||||
char tmp[4];
|
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CALLBACK(receive_arp, bool,
|
|
||||||
private_farp_spoofer_t *this, int fd, watcher_event_t event)
|
|
||||||
{
|
|
||||||
struct sockaddr_ll addr;
|
|
||||||
socklen_t addr_len = sizeof(addr);
|
|
||||||
arp_t arp;
|
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
host_t *local, *remote;
|
|
||||||
|
|
||||||
len = recvfrom(this->skt, &arp, sizeof(arp), MSG_DONTWAIT,
|
#if DEBUG_LEVEL >= 2
|
||||||
(struct sockaddr*)&addr, &addr_len);
|
chunk_t sender_mac = chunk_create((u_char*)arp->sender_mac, ETHER_ADDR_LEN);
|
||||||
if (len == sizeof(arp))
|
|
||||||
|
DBG2(DBG_NET, "replying with %#B to ARP request for %H from %H (%#B) on %s",
|
||||||
|
&mac, target, sender, &sender_mac, if_name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(addr.sll_addr, arp->sender_mac, ETHER_ADDR_LEN);
|
||||||
|
|
||||||
|
memcpy(arp->target_mac, arp->sender_mac, 6);
|
||||||
|
memcpy(arp->sender_mac, mac.ptr, 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);
|
||||||
|
|
||||||
|
len = sendto(fd, arp, sizeof(*arp), 0,
|
||||||
|
(const struct sockaddr*)&addr, sizeof(addr));
|
||||||
|
|
||||||
|
if (len != sizeof(*arp))
|
||||||
{
|
{
|
||||||
local = host_create_from_chunk(AF_INET,
|
DBG1(DBG_NET, "failed to send ARP reply: %s", strerror(errno));
|
||||||
chunk_create((char*)&arp.sender_ip, 4), 0);
|
|
||||||
remote = host_create_from_chunk(AF_INET,
|
|
||||||
chunk_create((char*)&arp.target_ip, 4), 0);
|
|
||||||
if (this->listener->has_tunnel(this->listener, local, remote))
|
|
||||||
{
|
|
||||||
send_arp(this, &arp, &addr);
|
|
||||||
}
|
|
||||||
local->destroy(local);
|
|
||||||
remote->destroy(remote);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(farp_spoofer_t, destroy, void,
|
#else /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
private_farp_spoofer_t *this)
|
|
||||||
|
/**
|
||||||
|
* An Ethernet frame for an ARP packet.
|
||||||
|
*/
|
||||||
|
struct frame_t {
|
||||||
|
struct ether_header e;
|
||||||
|
arp_t a;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct frame_t frame_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an ARP response for the given ARP request.
|
||||||
|
*/
|
||||||
|
static void send_arp(char *if_name, int if_index, chunk_t mac, int fd,
|
||||||
|
const arp_t *arpreq, host_t *sender, host_t *target)
|
||||||
{
|
{
|
||||||
lib->watcher->remove(lib->watcher, this->skt);
|
frame_t frame;
|
||||||
close(this->skt);
|
ssize_t n;
|
||||||
|
|
||||||
|
#if DEBUG_LEVEL >= 2
|
||||||
|
chunk_t sender_mac = chunk_create((u_char*)arpreq->sender_mac, ETHER_ADDR_LEN);
|
||||||
|
|
||||||
|
DBG2(DBG_NET, "replying with %#B to ARP request for %H from %H (%#B) on %s",
|
||||||
|
&mac, target, sender, &sender_mac, if_name);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memcpy(frame.e.ether_dhost, arpreq->sender_mac, ETHER_ADDR_LEN);
|
||||||
|
memcpy(frame.e.ether_shost, mac.ptr, ETHER_ADDR_LEN);
|
||||||
|
frame.e.ether_type = htons(ETHERTYPE_ARP);
|
||||||
|
|
||||||
|
frame.a.hardware_type = htons(1);
|
||||||
|
frame.a.protocol_type = htons(ETHERTYPE_IP);
|
||||||
|
frame.a.hardware_size = arpreq->hardware_size;
|
||||||
|
frame.a.protocol_size = arpreq->protocol_size;
|
||||||
|
frame.a.opcode = htons(ARPOP_REPLY);
|
||||||
|
memcpy(frame.a.sender_mac, mac.ptr, ETHER_ADDR_LEN);
|
||||||
|
memcpy(frame.a.sender_ip, arpreq->target_ip, sizeof(arpreq->target_ip));
|
||||||
|
memcpy(frame.a.target_mac, arpreq->sender_mac, sizeof(arpreq->sender_mac));
|
||||||
|
memcpy(frame.a.target_ip, arpreq->sender_ip, sizeof(arpreq->sender_ip));
|
||||||
|
|
||||||
|
n = write(fd, &frame, sizeof(frame));
|
||||||
|
if (n != sizeof(frame))
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "sending ARP reply failed: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
||||||
|
|
||||||
|
CALLBACK(handle_arp_pkt, void,
|
||||||
|
private_farp_spoofer_t *this, char *if_name, int if_index, chunk_t mac,
|
||||||
|
int fd, chunk_t packet)
|
||||||
|
{
|
||||||
|
arp_t *a = (arp_t*)packet.ptr;
|
||||||
|
host_t *sender, *target;
|
||||||
|
|
||||||
|
if (packet.len == sizeof(arp_t))
|
||||||
|
{
|
||||||
|
sender = host_create_from_chunk(AF_INET,
|
||||||
|
chunk_create((char*)a->sender_ip, 4), 0);
|
||||||
|
target = host_create_from_chunk(AF_INET,
|
||||||
|
chunk_create((char*)a->target_ip, 4), 0);
|
||||||
|
if (this->listener->has_tunnel(this->listener, sender, target))
|
||||||
|
{
|
||||||
|
send_arp(if_name, if_index, mac, fd, a, sender, target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG2(DBG_NET, "not sending ARP reply, no tunnel between %H -> %H",
|
||||||
|
sender, target);
|
||||||
|
}
|
||||||
|
target->destroy(target);
|
||||||
|
sender->destroy(sender);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG1(DBG_NET, "ARP request with invalid size %d received (expected: %d)",
|
||||||
|
packet.len, sizeof(arp_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleanup the handlers used by this plugin.
|
||||||
|
*/
|
||||||
|
METHOD(farp_spoofer_t, destroy, void, private_farp_spoofer_t *this)
|
||||||
|
{
|
||||||
|
this->pf_handler->destroy(this->pf_handler);
|
||||||
free(this);
|
free(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,7 +218,7 @@ METHOD(farp_spoofer_t, destroy, void,
|
|||||||
*/
|
*/
|
||||||
farp_spoofer_t *farp_spoofer_create(farp_listener_t *listener)
|
farp_spoofer_t *farp_spoofer_create(farp_listener_t *listener)
|
||||||
{
|
{
|
||||||
private_farp_spoofer_t *this;
|
#if !defined(__APPLE__) && !defined(__FreeBSD__)
|
||||||
struct sock_filter arp_request_filter_code[] = {
|
struct sock_filter arp_request_filter_code[] = {
|
||||||
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, offsetof(arp_t, protocol_type)),
|
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_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETH_P_IP, 0, 9),
|
||||||
@@ -198,443 +237,29 @@ farp_spoofer_t *farp_spoofer_create(farp_listener_t *listener)
|
|||||||
sizeof(arp_request_filter_code) / sizeof(struct sock_filter),
|
sizeof(arp_request_filter_code) / sizeof(struct sock_filter),
|
||||||
arp_request_filter_code,
|
arp_request_filter_code,
|
||||||
};
|
};
|
||||||
|
#else
|
||||||
INIT(this,
|
const size_t skip_eth = sizeof(struct ether_header);
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
lib->watcher->add(lib->watcher, this->skt, WATCHER_READ, receive_arp, this);
|
|
||||||
|
|
||||||
return &this->public;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A handler is required for each interface.
|
|
||||||
*/
|
|
||||||
struct farp_handler_t {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to the private farp spoofer.
|
|
||||||
*/
|
|
||||||
private_farp_spoofer_t *this;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the interface to be handled.
|
|
||||||
*/
|
|
||||||
char *name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The IPv4 address of this interface.
|
|
||||||
*/
|
|
||||||
host_t *ipv4;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Ethernet MAC address of this interface.
|
|
||||||
*/
|
|
||||||
chunk_t mac;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The BPF file descriptor for this interface.
|
|
||||||
*/
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The BPF packet buffer length as read from the BPF fd.
|
|
||||||
*/
|
|
||||||
size_t buflen;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An allocated buffer for receiving packets from BPF.
|
|
||||||
*/
|
|
||||||
uint8_t *bufdat;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct farp_handler_t farp_handler_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An Ethernet frame for an ARP packet.
|
|
||||||
*/
|
|
||||||
struct frame_t {
|
|
||||||
struct ether_header e;
|
|
||||||
arp_t a;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct frame_t frame_t;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find and open an available BPF device.
|
|
||||||
*/
|
|
||||||
static int bpf_open()
|
|
||||||
{
|
|
||||||
static int no_cloning_bpf = 0;
|
|
||||||
/* enough space for: /dev/bpf000\0 */
|
|
||||||
char device[12];
|
|
||||||
int n = no_cloning_bpf ? 0 : -1;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (n < 0)
|
|
||||||
{
|
|
||||||
snprintf(device, sizeof(device), "/dev/bpf");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
snprintf(device, sizeof(device), "/dev/bpf%d", n);
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = open(device, O_RDWR);
|
|
||||||
|
|
||||||
if (n++ < 0 && fd < 0 && errno == ENOENT)
|
|
||||||
{
|
|
||||||
no_cloning_bpf = 1;
|
|
||||||
errno = EBUSY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (fd < 0 && errno == EBUSY && n < 1000);
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free resources used by a handler.
|
|
||||||
*/
|
|
||||||
static void handler_destroy(farp_handler_t *handler)
|
|
||||||
{
|
|
||||||
if (handler->fd >= 0)
|
|
||||||
{
|
|
||||||
lib->watcher->remove(lib->watcher, handler->fd);
|
|
||||||
close(handler->fd);
|
|
||||||
}
|
|
||||||
DESTROY_IF(handler->ipv4);
|
|
||||||
chunk_free(&handler->mac);
|
|
||||||
free(handler->bufdat);
|
|
||||||
free(handler->name);
|
|
||||||
free(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find the handler for the named interface, creating one if needed.
|
|
||||||
*/
|
|
||||||
static farp_handler_t *get_handler(private_farp_spoofer_t* this,
|
|
||||||
char *interface_name)
|
|
||||||
{
|
|
||||||
farp_handler_t *handler, *found = NULL;
|
|
||||||
enumerator_t *enumerator;
|
|
||||||
|
|
||||||
enumerator = this->handlers->create_enumerator(this->handlers);
|
|
||||||
while (enumerator->enumerate(enumerator, &handler))
|
|
||||||
{
|
|
||||||
if (streq(handler->name, interface_name))
|
|
||||||
{
|
|
||||||
found = handler;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enumerator->destroy(enumerator);
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
INIT(found,
|
|
||||||
.this = this,
|
|
||||||
.name = strdup(interface_name),
|
|
||||||
.fd = -1,
|
|
||||||
);
|
|
||||||
this->handlers->insert_last(this->handlers, found);
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send an ARP response for the given ARP request.
|
|
||||||
*/
|
|
||||||
static void handler_send(farp_handler_t *handler, arp_t *arpreq, host_t *lcl,
|
|
||||||
host_t *rmt)
|
|
||||||
{
|
|
||||||
frame_t frame;
|
|
||||||
chunk_t mac;
|
|
||||||
ssize_t n;
|
|
||||||
|
|
||||||
memcpy(frame.e.ether_dhost, arpreq->sender_mac, ETHER_ADDR_LEN);
|
|
||||||
mac = chunk_create(frame.e.ether_dhost, ETHER_ADDR_LEN);
|
|
||||||
memcpy(frame.e.ether_shost, handler->mac.ptr, ETHER_ADDR_LEN);
|
|
||||||
frame.e.ether_type = htons(ETHERTYPE_ARP);
|
|
||||||
|
|
||||||
frame.a.hardware_type = htons(1);
|
|
||||||
frame.a.protocol_type = htons(ETHERTYPE_IP);
|
|
||||||
frame.a.hardware_size = arpreq->hardware_size;
|
|
||||||
frame.a.protocol_size = arpreq->protocol_size;
|
|
||||||
frame.a.opcode = htons(ARPOP_REPLY);
|
|
||||||
memcpy(frame.a.sender_mac, handler->mac.ptr, ETHER_ADDR_LEN);
|
|
||||||
memcpy(frame.a.sender_ip, arpreq->target_ip, sizeof(arpreq->target_ip));
|
|
||||||
memcpy(frame.a.target_mac, arpreq->sender_mac, sizeof(arpreq->sender_mac));
|
|
||||||
memcpy(frame.a.target_ip, arpreq->sender_ip, sizeof(arpreq->sender_ip));
|
|
||||||
|
|
||||||
DBG2(DBG_NET, "replying to ARP request for %H from %H (%#B) on %s",
|
|
||||||
rmt, lcl, &mac, handler->name);
|
|
||||||
|
|
||||||
n = write(handler->fd, &frame, sizeof(frame));
|
|
||||||
if (n != sizeof(frame))
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "sending ARP reply failed: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Receive and examine the available ARP requests. If a tunnel exists, send an
|
|
||||||
* ARP response back out the same interface.
|
|
||||||
*/
|
|
||||||
CALLBACK(handler_onarp, bool,
|
|
||||||
farp_handler_t *handler, int fd, watcher_event_t event)
|
|
||||||
{
|
|
||||||
struct bpf_hdr *bh;
|
|
||||||
arp_t *a;
|
|
||||||
host_t *lcl, *rmt;
|
|
||||||
uint8_t *p = handler->bufdat;
|
|
||||||
ssize_t n;
|
|
||||||
|
|
||||||
n = read(handler->fd, handler->bufdat, handler->buflen);
|
|
||||||
if (n <= 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "reading ARP request from %s failed: %s", handler->name,
|
|
||||||
strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (p < handler->bufdat + n)
|
|
||||||
{
|
|
||||||
bh = (struct bpf_hdr*)p;
|
|
||||||
a = (arp_t*)(p + bh->bh_hdrlen + sizeof(struct ether_header));
|
|
||||||
|
|
||||||
lcl = host_create_from_chunk(AF_INET, chunk_create(a->sender_ip, 4), 0);
|
|
||||||
rmt = host_create_from_chunk(AF_INET, chunk_create(a->target_ip, 4), 0);
|
|
||||||
if (lcl && rmt &&
|
|
||||||
handler->this->listener->has_tunnel(handler->this->listener,
|
|
||||||
lcl, rmt))
|
|
||||||
{
|
|
||||||
handler_send(handler, a, lcl, rmt);
|
|
||||||
}
|
|
||||||
DESTROY_IF(rmt);
|
|
||||||
DESTROY_IF(lcl);
|
|
||||||
|
|
||||||
p += BPF_WORDALIGN(bh->bh_hdrlen + bh->bh_caplen);
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an initialize a BPF handler for the interface specified in the farp
|
|
||||||
* handler. This entails opening a BPF device, binding it to the interface,
|
|
||||||
* setting the packet filter, and allocating a buffer for receiving packets.
|
|
||||||
*/
|
|
||||||
static bool setup_handler(private_farp_spoofer_t *this, farp_handler_t *handler)
|
|
||||||
{
|
|
||||||
struct bpf_insn instructions[] = {
|
struct bpf_insn instructions[] = {
|
||||||
BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0),
|
BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0),
|
||||||
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K,
|
BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, skip_eth + sizeof(arp_t), 0, 11),
|
||||||
sizeof(struct ether_header) + sizeof(arp_t), 0, 11),
|
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, offsetof(struct ether_header, ether_type)),
|
||||||
BPF_STMT(BPF_LD+BPF_H+BPF_ABS,
|
|
||||||
offsetof(struct ether_header, ether_type)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_ARP, 0, 9),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_ARP, 0, 9),
|
||||||
BPF_STMT(BPF_LD+BPF_H+BPF_ABS,
|
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, skip_eth + offsetof(arp_t, protocol_type)),
|
||||||
sizeof(struct ether_header) + offsetof(arp_t, protocol_type)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_IP, 0, 7),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_IP, 0, 7),
|
||||||
BPF_STMT(BPF_LD+BPF_B+BPF_ABS,
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, skip_eth + offsetof(arp_t, hardware_size)),
|
||||||
sizeof(struct ether_header) + offsetof(arp_t, hardware_size)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 6, 0, 5),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 6, 0, 5),
|
||||||
BPF_STMT(BPF_LD+BPF_B+BPF_ABS,
|
BPF_STMT(BPF_LD+BPF_B+BPF_ABS, skip_eth + offsetof(arp_t, protocol_size)),
|
||||||
sizeof(struct ether_header) + offsetof(arp_t, protocol_size)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 4, 0, 3),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 4, 0, 3),
|
||||||
BPF_STMT(BPF_LD+BPF_H+BPF_ABS,
|
BPF_STMT(BPF_LD+BPF_H+BPF_ABS, skip_eth + offsetof(arp_t, opcode)),
|
||||||
sizeof(struct ether_header) + offsetof(arp_t, opcode)),
|
|
||||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPOP_REQUEST, 0, 1),
|
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARPOP_REQUEST, 0, 1),
|
||||||
BPF_STMT(BPF_RET+BPF_K, 14 + sizeof(arp_t)),
|
BPF_STMT(BPF_RET+BPF_K, 14 + sizeof(arp_t)),
|
||||||
BPF_STMT(BPF_RET+BPF_K, 0)
|
BPF_STMT(BPF_RET+BPF_K, 0)
|
||||||
};
|
};
|
||||||
struct bpf_program program;
|
struct bpf_program arp_request_filter = {
|
||||||
struct ifreq req;
|
sizeof(instructions) / sizeof(struct bpf_insn),
|
||||||
uint32_t disable = 1;
|
&instructions[0]
|
||||||
uint32_t enable = 1;
|
};
|
||||||
uint32_t dlt = 0;
|
#endif
|
||||||
|
|
||||||
snprintf(req.ifr_name, sizeof(req.ifr_name), "%s", handler->name);
|
|
||||||
|
|
||||||
if ((handler->fd = bpf_open()) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "bpf_open(%s): %s", handler->name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctl(handler->fd, BIOCSETIF, &req) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "BIOCSETIF(%s): %s", handler->name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctl(handler->fd, BIOCSHDRCMPLT, &enable) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "BIOCSHDRCMPLT(%s): %s", handler->name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctl(handler->fd, BIOCSSEESENT, &disable) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "BIOCSSEESENT(%s): %s", handler->name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctl(handler->fd, BIOCIMMEDIATE, &enable) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "BIOCIMMEDIATE(%s): %s", handler->name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctl(handler->fd, BIOCGDLT, &dlt) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "BIOCGDLT(%s): %s", handler->name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
else if (dlt != DLT_EN10MB)
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
program.bf_len = sizeof(instructions) / sizeof(struct bpf_insn);
|
|
||||||
program.bf_insns = &instructions[0];
|
|
||||||
|
|
||||||
if (ioctl(handler->fd, BIOCSETF, &program) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "BIOCSETF(%s): %s", handler->name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctl(handler->fd, BIOCGBLEN, &handler->buflen) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "BIOCGBLEN(%s): %s", handler->name, strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
handler->bufdat = malloc(handler->buflen);
|
|
||||||
|
|
||||||
lib->watcher->add(lib->watcher, handler->fd, WATCHER_READ,
|
|
||||||
handler_onarp, handler);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a handler for each BPF capable interface. The interface must have an
|
|
||||||
* Ethernet MAC address, an IPv4 address, and use an Ethernet data link layer.
|
|
||||||
*/
|
|
||||||
static bool setup_handlers(private_farp_spoofer_t *this)
|
|
||||||
{
|
|
||||||
struct ifaddrs *ifas;
|
|
||||||
struct ifaddrs *ifa;
|
|
||||||
struct sockaddr_dl *dl;
|
|
||||||
farp_handler_t* handler;
|
|
||||||
enumerator_t *enumerator;
|
|
||||||
host_t *ipv4;
|
|
||||||
|
|
||||||
if (getifaddrs(&ifas) < 0)
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "farp cannot find interfaces: %s", strerror(errno));
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
for (ifa = ifas; ifa != NULL; ifa = ifa->ifa_next)
|
|
||||||
{
|
|
||||||
switch (ifa->ifa_addr->sa_family)
|
|
||||||
{
|
|
||||||
case AF_LINK:
|
|
||||||
dl = (struct sockaddr_dl*)ifa->ifa_addr;
|
|
||||||
if (dl->sdl_alen == ETHER_ADDR_LEN)
|
|
||||||
{
|
|
||||||
handler = get_handler(this, ifa->ifa_name);
|
|
||||||
handler->mac = chunk_clone(chunk_create(LLADDR(dl),
|
|
||||||
dl->sdl_alen));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AF_INET:
|
|
||||||
ipv4 = host_create_from_sockaddr(ifa->ifa_addr);
|
|
||||||
if (ipv4 && !ipv4->is_anyaddr(ipv4))
|
|
||||||
{
|
|
||||||
handler = get_handler(this, ifa->ifa_name);
|
|
||||||
if (!handler->ipv4)
|
|
||||||
{
|
|
||||||
handler->ipv4 = ipv4->clone(ipv4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DESTROY_IF(ipv4);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
freeifaddrs(ifas);
|
|
||||||
|
|
||||||
enumerator = this->handlers->create_enumerator(this->handlers);
|
|
||||||
while (enumerator->enumerate(enumerator, &handler))
|
|
||||||
{
|
|
||||||
if (handler->mac.ptr && handler->ipv4 &&
|
|
||||||
setup_handler(this, handler))
|
|
||||||
{
|
|
||||||
DBG1(DBG_NET, "listening for ARP requests on %s (%H, %#B)",
|
|
||||||
handler->name, handler->ipv4, &handler->mac);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->handlers->remove_at(this->handlers, enumerator);
|
|
||||||
handler_destroy(handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
enumerator->destroy(enumerator);
|
|
||||||
|
|
||||||
return this->handlers->get_count(this->handlers) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleanup the handlers used by this plugin.
|
|
||||||
*/
|
|
||||||
METHOD(farp_spoofer_t, destroy, void, private_farp_spoofer_t *this)
|
|
||||||
{
|
|
||||||
enumerator_t *enumerator;
|
|
||||||
farp_handler_t *handler;
|
|
||||||
|
|
||||||
enumerator = this->handlers->create_enumerator(this->handlers);
|
|
||||||
while (enumerator->enumerate(enumerator, &handler))
|
|
||||||
{
|
|
||||||
handler_destroy(handler);
|
|
||||||
}
|
|
||||||
enumerator->destroy(enumerator);
|
|
||||||
this->handlers->destroy(this->handlers);
|
|
||||||
free(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* See header
|
|
||||||
*/
|
|
||||||
farp_spoofer_t *farp_spoofer_create(farp_listener_t *listener)
|
|
||||||
{
|
|
||||||
private_farp_spoofer_t *this;
|
private_farp_spoofer_t *this;
|
||||||
|
|
||||||
INIT(this,
|
INIT(this,
|
||||||
@@ -642,15 +267,14 @@ farp_spoofer_t *farp_spoofer_create(farp_listener_t *listener)
|
|||||||
.destroy = _destroy,
|
.destroy = _destroy,
|
||||||
},
|
},
|
||||||
.listener = listener,
|
.listener = listener,
|
||||||
.handlers = linked_list_create(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!setup_handlers(this))
|
this->pf_handler = pf_handler_create("ARP", NULL, handle_arp_pkt, this,
|
||||||
|
&arp_request_filter);
|
||||||
|
if (!this->pf_handler)
|
||||||
{
|
{
|
||||||
destroy(this);
|
destroy(this);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return &this->public;
|
return &this->public;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */
|
|
||||||
|
|||||||
Reference in New Issue
Block a user