--- embedaddon/strongswan/src/libcharon/plugins/farp/farp_spoofer.c 2020/06/03 09:46:44 1.1.1.1 +++ embedaddon/strongswan/src/libcharon/plugins/farp/farp_spoofer.c 2021/03/17 00:20:08 1.1.1.2 @@ -1,4 +1,7 @@ /* + * Copyright (C) 2021 Tobias Brunner + * HSR Hochschule fuer Technik Rapperswil + * * Copyright (C) 2010 Martin Willi * Copyright (C) 2010 revosec AG * @@ -13,15 +16,50 @@ * for more details. */ +/* + * For the Apple BPF implementation. + * + * Copyright (C) 2020 Dan James + * + * 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 #include +#include + +#if !defined(__APPLE__) && !defined(__FreeBSD__) #include #include #include #include -#include +#else +#include +#include +#include +#include +#include +#include +#include +#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */ #include #include @@ -44,10 +82,17 @@ struct private_farp_spoofer_t { */ farp_listener_t *listener; +#if !defined(__APPLE__) && !defined(__FreeBSD__) /** * RAW socket for ARP requests */ int skt; +#else + /** + * Linked list of interface handlers + */ + linked_list_t *handlers; +#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */ }; /** @@ -65,6 +110,7 @@ typedef struct __attribute__((packed)) { uint8_t target_ip[4]; } arp_t; +#if !defined(__APPLE__) && !defined(__FreeBSD__) /** * Send faked ARP response */ @@ -174,7 +220,8 @@ farp_spoofer_t *farp_spoofer_create(farp_listener_t *l 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)); + DBG1(DBG_NET, "installing ARP packet filter failed: %s", + strerror(errno)); close(this->skt); free(this); return NULL; @@ -185,3 +232,431 @@ farp_spoofer_t *farp_spoofer_create(farp_listener_t *l 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; + struct ether_header *eh; + 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; + eh = (struct ether_header*)(p + bh->bh_hdrlen); + 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[] = { + BPF_STMT(BPF_LD+BPF_W+BPF_LEN, 0), + BPF_JUMP(BPF_JMP+BPF_JGE+BPF_K, + sizeof(struct ether_header) + sizeof(arp_t), 0, 11), + 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_STMT(BPF_LD+BPF_H+BPF_ABS, + sizeof(struct ether_header) + offsetof(arp_t, protocol_type)), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ETHERTYPE_IP, 0, 7), + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, + sizeof(struct ether_header) + offsetof(arp_t, hardware_size)), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 6, 0, 5), + BPF_STMT(BPF_LD+BPF_B+BPF_ABS, + sizeof(struct ether_header) + offsetof(arp_t, protocol_size)), + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 4, 0, 3), + BPF_STMT(BPF_LD+BPF_H+BPF_ABS, + sizeof(struct ether_header) + offsetof(arp_t, opcode)), + 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, 0) + }; + struct bpf_program 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", 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; + + INIT(this, + .public = { + .destroy = _destroy, + }, + .listener = listener, + .handlers = linked_list_create(), + ); + + if (!setup_handlers(this)) + { + destroy(this); + return NULL; + } + return &this->public; +} + +#endif /* !defined(__APPLE__) && !defined(__FreeBSD__) */