ambrop7 14 سال پیش
والد
کامیت
d95c8da2f9

+ 3 - 0
CMakeLists.txt

@@ -179,6 +179,7 @@ install(
 # building examples and tests which require libraries that are not available.
 set(BUILDING_SECURITY 0)
 set(BUILDING_DHCPCLIENT 0)
+set(BUILDING_ARPPROBE 0)
 set(BUILDING_BKIO 0)
 set(BUILDING_PREDICATE 0)
 set(BUILDING_UDEVMONITOR 0)
@@ -212,10 +213,12 @@ if (BUILD_CLIENT OR BUILD_FLOODER)
 endif ()
 if (BUILD_NCD)
     set(BUILDING_DHCPCLIENT 1)
+    set(BUILDING_ARPPROBE 1)
     set(BUILDING_UDEVMONITOR 1)
     add_subdirectory(stringmap)
     add_subdirectory(udevmonitor)
     add_subdirectory(dhcpclient)
+    add_subdirectory(arpprobe)
 endif ()
 if (BUILD_TUN2SOCKS)
     add_subdirectory(socksclient)

+ 402 - 0
arpprobe/BArpProbe.c

@@ -0,0 +1,402 @@
+/**
+ * @file BArpProbe.c
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN 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.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <sys/ioctl.h>
+#include <linux/filter.h>
+
+#include <misc/debug.h>
+#include <misc/byteorder.h>
+#include <misc/ethernet_proto.h>
+#include <misc/ipv4_proto.h>
+#include <misc/udp_proto.h>
+#include <base/BLog.h>
+
+#include "BArpProbe.h"
+
+#include <generated/blog_channel_BArpProbe.h>
+
+#define STATE_INITIAL 1
+#define STATE_NOEXIST 2
+#define STATE_EXIST 3
+#define STATE_EXIST_PANIC 4
+
+static int get_iface_info (const char *ifname, uint8_t *out_mac, int *out_mtu, int *out_ifindex)
+{
+    struct ifreq ifr;
+    
+    int s = socket(AF_INET, SOCK_DGRAM, 0);
+    if (!s) {
+        BLog(BLOG_ERROR, "socket failed");
+        goto fail0;
+    }
+    
+    // get MAC
+    memset(&ifr, 0, sizeof(ifr));
+    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname);
+    if (ioctl(s, SIOCGIFHWADDR, &ifr)) {
+        BLog(BLOG_ERROR, "ioctl(SIOCGIFHWADDR) failed");
+        goto fail1;
+    }
+    if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+        BLog(BLOG_ERROR, "hardware address not ethernet");
+        goto fail1;
+    }
+    memcpy(out_mac, ifr.ifr_hwaddr.sa_data, 6);
+    
+    // get MTU
+    memset(&ifr, 0, sizeof(ifr));
+    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname);
+    if (ioctl(s, SIOCGIFMTU, &ifr)) {
+        BLog(BLOG_ERROR, "ioctl(SIOCGIFMTU) failed");
+        goto fail1;
+    }
+    *out_mtu = ifr.ifr_mtu;
+    
+    // get interface index
+    memset(&ifr, 0, sizeof(ifr));
+    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname);
+    if (ioctl(s, SIOCGIFINDEX, &ifr)) {
+        BLog(BLOG_ERROR, "ioctl(SIOCGIFINDEX) failed");
+        goto fail1;
+    }
+    *out_ifindex = ifr.ifr_ifindex;
+    
+    close(s);
+    
+    return 1;
+    
+fail1:
+    close(s);
+fail0:
+    return 0;
+}
+
+static void dgram_handler (BArpProbe *o, int event)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    BLog(BLOG_ERROR, "packet socket error");
+    
+    // report error
+    DEBUGERROR(&o->d_err, o->handler(o->user, BARPPROBE_EVENT_ERROR));
+    return;
+}
+
+static void send_request (BArpProbe *o)
+{
+    if (o->send_sending) {
+        BLog(BLOG_ERROR, "cannot send packet while another packet is being sent!");
+        return;
+    }
+    
+    // build packet
+    struct arp_packet *arp = &o->send_packet;
+    arp->hardware_type = hton16(ARP_HARDWARE_TYPE_ETHERNET);
+    arp->protocol_type = hton16(ETHERTYPE_IPV4);
+    arp->hardware_size = hton8(6);
+    arp->protocol_size = hton8(4);
+    arp->opcode = hton16(ARP_OPCODE_REQUEST);
+    memcpy(arp->sender_mac, o->if_mac, 6);
+    arp->sender_ip = hton32(0);
+    memset(arp->target_mac, 0, sizeof(arp->target_mac));
+    arp->target_ip = o->addr;
+    
+    // send packet
+    PacketPassInterface_Sender_Send(o->send_if, (uint8_t *)&o->send_packet, sizeof(o->send_packet));
+    
+    // set sending
+    o->send_sending = 1;
+}
+
+static void send_if_handler_done (BArpProbe *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->send_sending)
+    
+    // set not sending
+    o->send_sending = 0;
+}
+
+static void recv_if_handler_done (BArpProbe *o, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(data_len >= 0)
+    ASSERT(data_len <= sizeof(struct arp_packet))
+    
+    // receive next packet
+    PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)&o->recv_packet);
+    
+    if (data_len != sizeof(struct arp_packet)) {
+        BLog(BLOG_WARNING, "receive: wrong size");
+        return;
+    }
+    
+    struct arp_packet *arp = &o->recv_packet;
+    
+    if (ntoh16(arp->hardware_type) != ARP_HARDWARE_TYPE_ETHERNET) {
+        BLog(BLOG_WARNING, "receive: wrong hardware type");
+        return;
+    }
+    
+    if (ntoh16(arp->protocol_type) != ETHERTYPE_IPV4) {
+        BLog(BLOG_WARNING, "receive: wrong protocol type");
+        return;
+    }
+    
+    if (ntoh8(arp->hardware_size) != 6) {
+        BLog(BLOG_WARNING, "receive: wrong hardware size");
+        return;
+    }
+    
+    if (ntoh8(arp->protocol_size) != 4) {
+        BLog(BLOG_WARNING, "receive: wrong protocol size");
+        return;
+    }
+    
+    if (ntoh16(arp->opcode) != ARP_OPCODE_REPLY) {
+        return;
+    }
+    
+    if (arp->sender_ip != o->addr) {
+        return;
+    }
+    
+    int old_state = o->state;
+    
+    // set minus one missed
+    o->num_missed = -1;
+    
+    // set timer
+    BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_WAITSEND);
+    
+    // set state exist
+    o->state = STATE_EXIST;
+    
+    // report exist if needed
+    if (old_state == STATE_INITIAL || old_state == STATE_NOEXIST) {
+        o->handler(o->user, BARPPROBE_EVENT_EXIST);
+        return;
+    }
+}
+
+static void timer_handler (BArpProbe *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    // send request
+    send_request(o);
+    
+    switch (o->state) {
+        case STATE_INITIAL: {
+            ASSERT(o->num_missed >= 0)
+            ASSERT(o->num_missed < BARPPROBE_INITIAL_NUM_ATTEMPTS)
+            
+            // increment missed
+            o->num_missed++;
+            
+            // all attempts failed?
+            if (o->num_missed == BARPPROBE_INITIAL_NUM_ATTEMPTS) {
+                // set timer
+                BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV);
+                
+                // set state noexist
+                o->state = STATE_NOEXIST;
+                
+                // report noexist
+                o->handler(o->user, BARPPROBE_EVENT_NOEXIST);
+                return;
+            }
+            
+            // set timer
+            BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_INITIAL_WAITRECV);
+        } break;
+        
+        case STATE_NOEXIST: {
+            // set timer
+            BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV);
+        } break;
+        
+        case STATE_EXIST: {
+            ASSERT(o->num_missed >= -1)
+            ASSERT(o->num_missed < BARPPROBE_EXIST_NUM_NOREPLY)
+            
+            // increment missed
+            o->num_missed++;
+            
+            // all missed?
+            if (o->num_missed == BARPPROBE_EXIST_NUM_NOREPLY) {
+                // set timer
+                BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_PANIC_WAITRECV);
+                
+                // set zero missed
+                o->num_missed = 0;
+                
+                // set state panic
+                o->state = STATE_EXIST_PANIC;
+                return;
+            }
+            
+            // set timer
+            BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_WAITRECV);
+        } break;
+        
+        case STATE_EXIST_PANIC: {
+            ASSERT(o->num_missed >= 0)
+            ASSERT(o->num_missed < BARPPROBE_EXIST_PANIC_NUM_NOREPLY)
+            
+            // increment missed
+            o->num_missed++;
+            
+            // all missed?
+            if (o->num_missed == BARPPROBE_EXIST_PANIC_NUM_NOREPLY) {
+                // set timer
+                BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_NOEXIST_WAITRECV);
+                
+                // set state panic
+                o->state = STATE_NOEXIST;
+                
+                // report noexist
+                o->handler(o->user, BARPPROBE_EVENT_NOEXIST);
+                return;
+            }
+            
+            // set timer
+            BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_EXIST_PANIC_WAITRECV);
+        } break;
+    }
+}
+
+int BArpProbe_Init (BArpProbe *o, const char *ifname, uint32_t addr, BReactor *reactor, void *user, BArpProbe_handler handler)
+{
+    ASSERT(ifname)
+    ASSERT(handler)
+    
+    // init arguments
+    o->addr = addr;
+    o->reactor = reactor;
+    o->user = user;
+    o->handler = handler;
+    
+    // get interface information
+    int if_mtu;
+    int if_index;
+    if (!get_iface_info(ifname, o->if_mac, &if_mtu, &if_index)) {
+        BLog(BLOG_ERROR, "failed to get interface information");
+        goto fail0;
+    }
+    
+    uint8_t *if_mac = o->if_mac;
+    BLog(BLOG_INFO, "if_mac=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8" if_mtu=%d if_index=%d",
+         if_mac[0], if_mac[1], if_mac[2], if_mac[3], if_mac[4], if_mac[5], if_mtu, if_index);
+    
+    // check MTU
+    if (if_mtu < sizeof(struct arp_packet)) {
+        BLog(BLOG_ERROR, "MTU is too small for ARP !?!");
+        goto fail0;
+    }
+    
+    // init dgram
+    if (!BDatagram_Init(&o->dgram, BADDR_TYPE_PACKET, o->reactor, o, (BDatagram_handler)dgram_handler)) {
+        BLog(BLOG_ERROR, "BDatagram_Init failed");
+        goto fail0;
+    }
+    
+    // bind dgram
+    BAddr bind_addr;
+    BAddr_InitPacket(&bind_addr, hton16(ETHERTYPE_ARP), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_HOST, if_mac);
+    if (!BDatagram_Bind(&o->dgram, bind_addr)) {
+        BLog(BLOG_ERROR, "BDatagram_Bind failed");
+        goto fail1;
+    }
+    
+    // set dgram send addresses
+    BAddr dest_addr;
+    uint8_t broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    BAddr_InitPacket(&dest_addr, hton16(ETHERTYPE_ARP), if_index, BADDR_PACKET_HEADER_TYPE_ETHERNET, BADDR_PACKET_PACKET_TYPE_BROADCAST, broadcast_mac);
+    BIPAddr local_addr;
+    BIPAddr_InitInvalid(&local_addr);
+    BDatagram_SetSendAddrs(&o->dgram, dest_addr, local_addr);
+    
+    // init send interface
+    BDatagram_SendAsync_Init(&o->dgram, sizeof(struct arp_packet));
+    o->send_if = BDatagram_SendAsync_GetIf(&o->dgram);
+    PacketPassInterface_Sender_Init(o->send_if, (PacketPassInterface_handler_done)send_if_handler_done, o);
+    
+    // set not sending
+    o->send_sending = 0;
+    
+    // init recv interface
+    BDatagram_RecvAsync_Init(&o->dgram, sizeof(struct arp_packet));
+    o->recv_if = BDatagram_RecvAsync_GetIf(&o->dgram);
+    PacketRecvInterface_Receiver_Init(o->recv_if, (PacketRecvInterface_handler_done)recv_if_handler_done, o);
+    
+    // init timer
+    BTimer_Init(&o->timer, 0, (BTimer_handler)timer_handler, o);
+    
+    // receive first packet
+    PacketRecvInterface_Receiver_Recv(o->recv_if, (uint8_t *)&o->recv_packet);
+    
+    // send request
+    send_request(o);
+    
+    // set timer
+    BReactor_SetTimerAfter(o->reactor, &o->timer, BARPPROBE_INITIAL_WAITRECV);
+    
+    // set zero missed
+    o->num_missed = 0;
+    
+    // set state initial
+    o->state = STATE_INITIAL;
+    
+    DebugError_Init(&o->d_err, BReactor_PendingGroup(o->reactor));
+    DebugObject_Init(&o->d_obj);
+    return 1;
+    
+fail1:
+    BDatagram_Free(&o->dgram);
+fail0:
+    return 0;
+}
+
+void BArpProbe_Free (BArpProbe *o)
+{
+    DebugObject_Free(&o->d_obj);
+    DebugError_Free(&o->d_err);
+    
+    // free timer
+    BReactor_RemoveTimer(o->reactor, &o->timer);
+    
+    // free recv interface
+    BDatagram_RecvAsync_Free(&o->dgram);
+    
+    // free send interface
+    BDatagram_SendAsync_Free(&o->dgram);
+    
+    // free dgram
+    BDatagram_Free(&o->dgram);
+}

+ 73 - 0
arpprobe/BArpProbe.h

@@ -0,0 +1,73 @@
+/**
+ * @file BArpProbe.h
+ * @author Ambroz Bizjak <ambrop7@gmail.com>
+ * 
+ * @section LICENSE
+ * 
+ * This file is part of BadVPN.
+ * 
+ * BadVPN is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ * 
+ * BadVPN 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.
+ * 
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef BADVPN_BARPPROBE_H
+#define BADVPN_BARPPROBE_H
+
+#include <stdint.h>
+
+#include <misc/debug.h>
+#include <misc/debugerror.h>
+#include <misc/arp_proto.h>
+#include <misc/ethernet_proto.h>
+#include <base/DebugObject.h>
+#include <system/BDatagram.h>
+#include <system/BReactor.h>
+
+#define BARPPROBE_INITIAL_WAITRECV 1000
+#define BARPPROBE_INITIAL_NUM_ATTEMPTS 6
+#define BARPPROBE_NOEXIST_WAITRECV 15000
+#define BARPPROBE_EXIST_WAITSEND 15000
+#define BARPPROBE_EXIST_WAITRECV 10000
+#define BARPPROBE_EXIST_NUM_NOREPLY 2
+#define BARPPROBE_EXIST_PANIC_WAITRECV 1000
+#define BARPPROBE_EXIST_PANIC_NUM_NOREPLY 6
+
+#define BARPPROBE_EVENT_EXIST 1
+#define BARPPROBE_EVENT_NOEXIST 2
+#define BARPPROBE_EVENT_ERROR 3
+
+typedef void (*BArpProbe_handler) (void *user, int event);
+
+typedef struct {
+    uint32_t addr;
+    BReactor *reactor;
+    void *user;
+    BArpProbe_handler handler;
+    BDatagram dgram;
+    uint8_t if_mac[6];
+    PacketPassInterface *send_if;
+    int send_sending;
+    struct arp_packet send_packet;
+    PacketRecvInterface *recv_if;
+    struct arp_packet recv_packet;
+    BTimer timer;
+    int state;
+    int num_missed;
+    DebugError d_err;
+    DebugObject d_obj;
+} BArpProbe;
+
+int BArpProbe_Init (BArpProbe *o, const char *ifname, uint32_t addr, BReactor *reactor, void *user, BArpProbe_handler handler) WARN_UNUSED;
+void BArpProbe_Free (BArpProbe *o);
+
+#endif

+ 2 - 0
arpprobe/CMakeLists.txt

@@ -0,0 +1,2 @@
+add_library(arpprobe BArpProbe.c)
+target_link_libraries(arpprobe base system flow)

+ 1 - 0
blog_channels.txt

@@ -89,3 +89,4 @@ BConnection 4
 BSSLConnection 4
 BDatagram 4
 PeerChat 4
+BArpProbe 4

+ 4 - 0
generated/blog_channel_BArpProbe.h

@@ -0,0 +1,4 @@
+#ifdef BLOG_CURRENT_CHANNEL
+#undef BLOG_CURRENT_CHANNEL
+#endif
+#define BLOG_CURRENT_CHANNEL BLOG_CHANNEL_BArpProbe

+ 2 - 1
generated/blog_channels_defines.h

@@ -89,4 +89,5 @@
 #define BLOG_CHANNEL_BSSLConnection 88
 #define BLOG_CHANNEL_BDatagram 89
 #define BLOG_CHANNEL_PeerChat 90
-#define BLOG_NUM_CHANNELS 91
+#define BLOG_CHANNEL_BArpProbe 91
+#define BLOG_NUM_CHANNELS 92

+ 1 - 0
generated/blog_channels_list.h

@@ -89,3 +89,4 @@
 {.name = "BSSLConnection", .loglevel = 4},
 {.name = "BDatagram", .loglevel = 4},
 {.name = "PeerChat", .loglevel = 4},
+{.name = "BArpProbe", .loglevel = 4},