/* * 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 . * * 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 #include #include #include #include #include #include #include #include #include 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_JGE+BPF_K, sizeof(arp_t), 0, 1), BPF_STMT(BPF_RET+BPF_K, sizeof(arp_t)), 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; }