Selaa lähdekoodia

FreeBSD support.

ambrop7 15 vuotta sitten
vanhempi
sitoutus
76f53f6510

+ 16 - 8
CMakeLists.txt

@@ -40,14 +40,22 @@ if (WIN32)
 else ()
     link_libraries(rt)
 
-    check_include_files(sys/signalfd.h HAVE_SYS_SIGNALFD_H)
-    if (NOT HAVE_SYS_SIGNALFD_H)
-        message(FATAL_ERROR "signalfd is required")
-    endif ()
+    if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+        add_definitions(-DBADVPN_LINUX)
+
+        check_include_files(sys/signalfd.h HAVE_SYS_SIGNALFD_H)
+        if (NOT HAVE_SYS_SIGNALFD_H)
+            message(FATAL_ERROR "signalfd is required")
+        endif ()
+        add_definitions(-DBADVPN_USE_SIGNALFD)
 
-    check_include_files(sys/epoll.h HAVE_SYS_EPOLL_H)
-    if (NOT HAVE_SYS_EPOLL_H)
-        message(FATAL_ERROR "epoll is required")
+        check_include_files(sys/epoll.h HAVE_SYS_EPOLL_H)
+        if (NOT HAVE_SYS_EPOLL_H)
+            message(FATAL_ERROR "epoll is required")
+        endif ()
+        add_definitions(-DBADVPN_USE_EPOLL)
+    elseif (CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
+        add_definitions(-DBADVPN_FREEBSD -DBADVPN_USE_KEVENT)
     endif ()
 
     if (NOT DEFINED BADVPN_WITHOUT_CRYPTODEV)
@@ -113,6 +121,6 @@ add_subdirectory(flooder)
 add_subdirectory(tun2socks)
 
 # ncd
-if (NOT WIN32)
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
     add_subdirectory(ncd)
 endif ()

+ 5 - 2
client/DatagramPeerIO.c

@@ -299,11 +299,14 @@ int DatagramPeerIO_Connect (DatagramPeerIO *o, BAddr addr)
     }
     
     // connect the socket
-    // Windows needs this or receive will fail
-    if (BSocket_Connect(&o->sock, &addr) < 0) {
+    // Windows needs this or receive will fail; however, FreeBSD will refuse to send
+    // if this is done
+    #ifdef BADVPN_USE_WINAPI
+    if (BSocket_Connect(&o->sock, &addr, 0) < 0) {
         BLog(BLOG_ERROR, "BSocket_Connect failed");
         goto fail2;
     }
+    #endif
     
     // init receiving
     init_receiving(o);

+ 1 - 1
client/StreamPeerIO.c

@@ -646,7 +646,7 @@ int StreamPeerIO_Connect (StreamPeerIO *pio, BAddr addr, uint64_t password, CERT
     }
     
     // attempt connection
-    if (BSocket_Connect(&pio->connect.sock.sock, &addr) >= 0 || BSocket_GetError(&pio->connect.sock.sock) != BSOCKET_ERROR_IN_PROGRESS) {
+    if (BSocket_Connect(&pio->connect.sock.sock, &addr, 1) >= 0 || BSocket_GetError(&pio->connect.sock.sock) != BSOCKET_ERROR_IN_PROGRESS) {
         BLog(BLOG_NOTICE, "BSocket_Connect failed");
         goto fail1;
     }

+ 1 - 1
dhcpclient/CMakeLists.txt

@@ -3,7 +3,7 @@ add_library(dhcpclientcore
 )
 target_link_libraries(dhcpclientcore system flow security)
 
-if (NOT WIN32)
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
     add_library(dhcpclient
         BDHCPClient.c
         DHCPIpUdpEncoder.c

+ 5 - 3
examples/CMakeLists.txt

@@ -34,9 +34,11 @@ if (NOT WIN32)
     add_executable(ipc_client ipc_client.c)
     target_link_libraries(ipc_client ipc)
 
-    add_executable(dhcpclient_test dhcpclient_test.c)
-    target_link_libraries(dhcpclient_test dhcpclient)
-
     add_executable(bprocess_example bprocess_example.c)
     target_link_libraries(bprocess_example system process)
 endif ()
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+    add_executable(dhcpclient_test dhcpclient_test.c)
+    target_link_libraries(dhcpclient_test dhcpclient)
+endif ()

+ 5 - 1
lwip/custom/arch/cc.h

@@ -58,8 +58,12 @@
 // for BYTE_ORDER
 #ifdef BADVPN_USE_WINAPI
     #include <sys/param.h>
-#else
+#endif
+#ifdef BADVPN_LINUX
     #include <endian.h>
 #endif
+#ifdef BADVPN_FREEBSD
+    #include <machine/endian.h>
+#endif
 
 #endif

+ 1 - 1
server_connection/ServerConnection.c

@@ -499,7 +499,7 @@ int ServerConnection_Init (
     }
     
     // start connecting
-    int res = BSocket_Connect(&o->sock, &addr);
+    int res = BSocket_Connect(&o->sock, &addr, 1);
     if (res != -1 || BSocket_GetError(&o->sock) != BSOCKET_ERROR_IN_PROGRESS) {
         BLog(BLOG_ERROR, "BSocket_Connect failed (%d)", BSocket_GetError(&o->sock));
         goto fail1;

+ 1 - 1
socksclient/BSocksClient.c

@@ -337,7 +337,7 @@ int BSocksClient_Init (BSocksClient *o, BAddr server_addr, BAddr dest_addr, BSoc
     }
     
     // connect socket
-    if (BSocket_Connect(&o->sock, &server_addr) >= 0 || BSocket_GetError(&o->sock) != BSOCKET_ERROR_IN_PROGRESS) {
+    if (BSocket_Connect(&o->sock, &server_addr, 1) >= 0 || BSocket_GetError(&o->sock) != BSOCKET_ERROR_IN_PROGRESS) {
         BLog(BLOG_NOTICE, "BSocket_Connect failed");
         goto fail1;
     }

+ 6 - 3
system/BAddr.h

@@ -40,6 +40,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <netdb.h>
+#include <netinet/in.h>
 #endif
 
 #include <misc/byteorder.h>
@@ -50,6 +51,8 @@
 #define BADDR_TYPE_IPV6 2
 #ifndef BADVPN_USE_WINAPI
     #define BADDR_TYPE_UNIX 3 // only a domain number for BSocket
+#endif
+#ifdef BADVPN_LINUX
     #define BADDR_TYPE_PACKET 4
 #endif
 
@@ -468,7 +471,7 @@ void BAddr_InitIPv6 (BAddr *addr, uint8_t *ip, uint16_t port)
     addr->ipv6.port = port;
 }
 
-#ifndef BADVPN_USE_WINAPI
+#ifdef BADVPN_LINUX
 
 void BAddr_InitPacket (BAddr *addr, uint16_t phys_proto, int interface_index, int header_type, int packet_type, uint8_t *phys_addr)
 {
@@ -493,7 +496,7 @@ void BAddr_Assert (BAddr *addr)
         case BADDR_TYPE_NONE:
         case BADDR_TYPE_IPV4:
         case BADDR_TYPE_IPV6:
-        #ifndef BADVPN_USE_WINAPI
+        #ifdef BADVPN_LINUX
         case BADDR_TYPE_PACKET:
         #endif
             return;
@@ -529,7 +532,7 @@ void BAddr_Print (BAddr *addr, char *out)
             BIPAddr_Print(&ipaddr, out);
             sprintf(out + strlen(out), ":%"PRIu16, ntoh16(addr->ipv6.port));
             break;
-        #ifndef BADVPN_USE_WINAPI
+        #ifdef BADVPN_LINUX
         case BADDR_TYPE_PACKET:
             ASSERT(addr->packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET)
             sprintf(out, "proto=%"PRIu16",ifindex=%d,htype=eth,ptype=%d,addr=%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8":%02"PRIx8,

+ 378 - 73
system/BReactor.c

@@ -43,15 +43,8 @@
 
 #include <generated/blog_channel_BReactor.h>
 
-#ifdef BADVPN_USE_WINAPI
-typedef DWORD btimeout_t;
-#define BTIMEOUT_T_MAX ((DWORD)INFINITE - 1)
-#define WAITRES_TIMED_OUT(_res) ((_res) == WAIT_TIMEOUT)
-#else
-typedef int btimeout_t;
-#define BTIMEOUT_T_MAX INT_MAX
-#define WAITRES_TIMED_OUT(_res) ((_res) == 0)
-#endif
+#define KEVENT_TAG_FD 1
+#define KEVENT_TAG_KEVENT 2
 
 static int timer_comparator (void *user, btime_t *val1, btime_t *val2)
 {
@@ -133,9 +126,9 @@ static void move_first_timers (BReactor *bsys)
     }
 }
 
-#ifndef BADVPN_USE_WINAPI
+#ifdef BADVPN_USE_EPOLL
 
-static void set_fd_pointers (BReactor *bsys)
+static void set_epoll_fd_pointers (BReactor *bsys)
 {
     // Write pointers to our entry pointers into file descriptors.
     // If a handler function frees some other file descriptor, the
@@ -152,6 +145,74 @@ static void set_fd_pointers (BReactor *bsys)
 
 #endif
 
+#ifdef BADVPN_USE_KEVENT
+
+static void set_kevent_fd_pointers (BReactor *bsys)
+{
+    for (int i = 0; i < bsys->kevent_results_num; i++) {
+        struct kevent *event = &bsys->kevent_results[i];
+        ASSERT(event->udata)
+        int *tag = event->udata;
+        switch (*tag) {
+            case KEVENT_TAG_FD: {
+                BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag);
+                ASSERT(bfd->active)
+                ASSERT(!bfd->kevent_returned_ptr)
+                bfd->kevent_returned_ptr = (int **)&event->udata;
+            } break;
+            
+            case KEVENT_TAG_KEVENT: {
+                BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag);
+                ASSERT(kev->reactor == bsys)
+                ASSERT(!kev->kevent_returned_ptr)
+                kev->kevent_returned_ptr = (int **)&event->udata;
+            } break;
+            
+            default:
+                ASSERT(0);
+        }
+    }
+}
+
+static void update_kevent_fd_events (BReactor *bsys, BFileDescriptor *bs, int events)
+{
+    struct kevent event;
+    
+    if (!(bs->waitEvents & BREACTOR_READ) && (events & BREACTOR_READ)) {
+        memset(&event, 0, sizeof(event));
+        event.ident = bs->fd;
+        event.filter = EVFILT_READ;
+        event.flags = EV_ADD;
+        event.udata = &bs->kevent_tag;
+        ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
+    }
+    else if ((bs->waitEvents & BREACTOR_READ) && !(events & BREACTOR_READ)) {
+        memset(&event, 0, sizeof(event));
+        event.ident = bs->fd;
+        event.filter = EVFILT_READ;
+        event.flags = EV_DELETE;
+        ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
+    }
+    
+    if (!(bs->waitEvents & BREACTOR_WRITE) && (events & BREACTOR_WRITE)) {
+        memset(&event, 0, sizeof(event));
+        event.ident = bs->fd;
+        event.filter = EVFILT_WRITE;
+        event.flags = EV_ADD;
+        event.udata = &bs->kevent_tag;
+        ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
+    }
+    else if ((bs->waitEvents & BREACTOR_WRITE) && !(events & BREACTOR_WRITE)) {
+        memset(&event, 0, sizeof(event));
+        event.ident = bs->fd;
+        event.filter = EVFILT_WRITE;
+        event.flags = EV_DELETE;
+        ASSERT_FORCE(kevent(bsys->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
+    }
+}
+
+#endif
+
 static void wait_for_events (BReactor *bsys)
 {
     // must have processed all pending events
@@ -159,16 +220,26 @@ static void wait_for_events (BReactor *bsys)
     ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list))
     #ifdef BADVPN_USE_WINAPI
     ASSERT(!bsys->returned_object)
-    #else
+    #endif
+    #ifdef BADVPN_USE_EPOLL
     ASSERT(bsys->epoll_results_pos == bsys->epoll_results_num)
     #endif
+    #ifdef BADVPN_USE_KEVENT
+    ASSERT(bsys->kevent_results_pos == bsys->kevent_results_num)
+    #endif
 
     // clean up epoll results
-    #ifndef BADVPN_USE_WINAPI
+    #ifdef BADVPN_USE_EPOLL
     bsys->epoll_results_num = 0;
     bsys->epoll_results_pos = 0;
     #endif
     
+    // clean up kevent results
+    #ifdef BADVPN_USE_KEVENT
+    bsys->kevent_results_num = 0;
+    bsys->kevent_results_pos = 0;
+    #endif
+    
     // timeout vars
     int have_timeout = 0;
     btime_t timeout_abs;
@@ -192,44 +263,58 @@ static void wait_for_events (BReactor *bsys)
         timeout_abs = first_timer->absTime;
     }
     
-    int timed_out;
-    
-    #ifdef BADVPN_USE_WINAPI
-    int handle_index;
-    #else
-    int epoll_num_results;
-    #endif
-    
     // wait until the timeout is reached or the file descriptor / handle in ready
     while (1) {
         // compute timeout
-        btimeout_t timeout_arg;
         btime_t timeout_rel;
+        btime_t timeout_rel_trunc;
         if (have_timeout) {
             timeout_rel = timeout_abs - now;
-            if (timeout_rel > BTIMEOUT_T_MAX) {
-                timeout_arg = BTIMEOUT_T_MAX;
-            } else {
-                timeout_arg = timeout_rel;
-            }
+            timeout_rel_trunc = timeout_rel;
         }
         
         // perform wait
         
         #ifdef BADVPN_USE_WINAPI
         
+        if (have_timeout) {
+            if (timeout_rel_trunc > INFINITE - 1) {
+                timeout_rel_trunc = INFINITE - 1;
+            }
+        }
+        
         BLog(BLOG_DEBUG, "Calling WaitForMultipleObjects on %d handles", bsys->enabled_num);
         
-        DWORD waitres = WaitForMultipleObjects(bsys->enabled_num, bsys->enabled_handles, FALSE, (have_timeout ? timeout_arg : INFINITE));
+        DWORD waitres = WaitForMultipleObjects(bsys->enabled_num, bsys->enabled_handles, FALSE, (have_timeout ? timeout_rel_trunc : INFINITE));
         ASSERT_FORCE(waitres != WAIT_FAILED)
         ASSERT_FORCE(!(waitres == WAIT_TIMEOUT) || have_timeout)
         ASSERT_FORCE(!(waitres != WAIT_TIMEOUT) || (waitres >= WAIT_OBJECT_0 && waitres < WAIT_OBJECT_0 + bsys->enabled_num))
         
-        #else
+        if (waitres != WAIT_TIMEOUT || timeout_rel_trunc == timeout_rel) {
+            if (waitres != WAIT_TIMEOUT) {
+                int handle_index = waitres - WAIT_OBJECT_0;
+                BLog(BLOG_DEBUG, "WaitForMultipleObjects returned handle %d", handle_index);
+                bsys->returned_object = bsys->enabled_objects[handle_index];
+            } else {
+                BLog(BLOG_DEBUG, "WaitForMultipleObjects timed out");
+                move_first_timers(bsys);
+            }
+            break;
+        }
+        
+        #endif
+        
+        #ifdef BADVPN_USE_EPOLL
+        
+        if (have_timeout) {
+            if (timeout_rel_trunc > INT_MAX) {
+                timeout_rel_trunc = INT_MAX;
+            }
+        }
         
         BLog(BLOG_DEBUG, "Calling epoll_wait");
         
-        int waitres = epoll_wait(bsys->efd, bsys->epoll_results, BSYSTEM_MAX_RESULTS, (have_timeout ? timeout_arg : -1));
+        int waitres = epoll_wait(bsys->efd, bsys->epoll_results, BSYSTEM_MAX_RESULTS, (have_timeout ? timeout_rel_trunc : -1));
         if (waitres < 0) {
             int error = errno;
             if (error == EINTR) {
@@ -243,48 +328,73 @@ static void wait_for_events (BReactor *bsys)
         ASSERT_FORCE(!(waitres == 0) || have_timeout)
         ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS)
         
+        if (waitres != 0 || timeout_rel_trunc == timeout_rel) {
+            if (waitres != 0) {
+                BLog(BLOG_DEBUG, "epoll_wait returned %d file descriptors", waitres);
+                bsys->epoll_results_num = waitres;
+                set_epoll_fd_pointers(bsys);
+            } else {
+                BLog(BLOG_DEBUG, "epoll_wait timed out");
+                move_first_timers(bsys);
+            }
+            break;
+        }
+        
         #endif
         
-        if (!WAITRES_TIMED_OUT(waitres) || timeout_rel <= BTIMEOUT_T_MAX) {
-            timed_out = WAITRES_TIMED_OUT(waitres);
-            if (!timed_out) {
-                #ifdef BADVPN_USE_WINAPI
-                handle_index = waitres - WAIT_OBJECT_0;
-                #else
-                epoll_num_results = waitres;
-                #endif
+        #ifdef BADVPN_USE_KEVENT
+        
+        struct timespec ts;
+        if (have_timeout) {
+            if (timeout_rel_trunc > 86400000) {
+                timeout_rel_trunc = 86400000;
+            }
+            ts.tv_sec = timeout_rel_trunc / 1000;
+            ts.tv_nsec = (timeout_rel_trunc % 1000) * 1000000;
+        }
+        
+        BLog(BLOG_DEBUG, "Calling kevent");
+        
+        int waitres = kevent(bsys->kqueue_fd, NULL, 0, bsys->kevent_results, BSYSTEM_MAX_RESULTS, (have_timeout ? &ts : NULL));
+        if (waitres < 0) {
+            int error = errno;
+            if (error == EINTR) {
+                BLog(BLOG_DEBUG, "kevent interrupted");
+                goto try_again;
+            }
+            perror("kevent");
+            ASSERT_FORCE(0)
+        }
+        
+        ASSERT_FORCE(!(waitres == 0) || have_timeout)
+        ASSERT_FORCE(waitres <= BSYSTEM_MAX_RESULTS)
+        
+        if (waitres != 0 || timeout_rel_trunc == timeout_rel) {
+            if (waitres != 0) {
+                BLog(BLOG_DEBUG, "kevent returned %d events", waitres);
+                bsys->kevent_results_num = waitres;
+                set_kevent_fd_pointers(bsys);
+            } else {
+                BLog(BLOG_DEBUG, "kevent timed out");
+                move_first_timers(bsys);
             }
             break;
         }
         
+        #endif
+        
     try_again:
         if (have_timeout) {
             // get current time
             now = btime_gettime();
             // check if we already reached the time we're waiting for
             if (now >= timeout_abs) {
-                timed_out = 1;
+                BLog(BLOG_DEBUG, "already timed out while trying again");
+                move_first_timers(bsys);
                 break;
             }
         }
     }
-    
-    if (timed_out) {
-        // timed out, expire first timers
-        BLog(BLOG_DEBUG, "Wait timed out");
-        move_first_timers(bsys);
-    } else {
-        #ifdef BADVPN_USE_WINAPI
-        // user's handle got signalled
-        BLog(BLOG_DEBUG, "Wait returned handle %d", handle_index);
-        bsys->returned_object = bsys->enabled_objects[handle_index];
-        #else
-        // setup returned file descriptors list
-        BLog(BLOG_DEBUG, "Wait returned %d file descriptors", epoll_num_results);
-        bsys->epoll_results_num = epoll_num_results;
-        set_fd_pointers(bsys);
-        #endif
-    }
 }
 
 #ifdef BADVPN_USE_WINAPI
@@ -344,7 +454,9 @@ int BReactor_Init (BReactor *bsys)
     bsys->enabled_num = 0;
     bsys->returned_object = NULL;
     
-    #else
+    #endif
+    
+    #ifdef BADVPN_USE_EPOLL
     
     // create epoll fd
     if ((bsys->efd = epoll_create(10)) < 0) {
@@ -356,12 +468,29 @@ int BReactor_Init (BReactor *bsys)
     bsys->epoll_results_num = 0;
     bsys->epoll_results_pos = 0;
     
-    DebugCounter_Init(&bsys->d_fds_counter);
+    #endif
+    
+    #ifdef BADVPN_USE_KEVENT
+    
+    // create kqueue fd
+    if ((bsys->kqueue_fd = kqueue()) < 0) {
+        BLog(BLOG_ERROR, "kqueue failed");
+        goto fail0;
+    }
+    
+    // init results array
+    bsys->kevent_results_num = 0;
+    bsys->kevent_results_pos = 0;
     
     #endif
     
-    // init debug object
     DebugObject_Init(&bsys->d_obj);
+    #ifndef BADVPN_USE_WINAPI
+    DebugCounter_Init(&bsys->d_fds_counter);
+    #endif
+    #ifdef BADVPN_USE_KEVENT
+    DebugCounter_Init(&bsys->d_kevent_ctr);
+    #endif
     
     return 1;
     
@@ -377,22 +506,33 @@ void BReactor_Free (BReactor *bsys)
     ASSERT(!BPendingGroup_HasJobs(&bsys->pending_jobs))
     ASSERT(!BHeap_GetFirst(&bsys->timers_heap))
     ASSERT(LinkedList1_IsEmpty(&bsys->timers_expired_list))
-    DebugObject_Free(&bsys->d_obj);
     #ifdef BADVPN_USE_WINAPI
     ASSERT(bsys->num_handles == 0)
-    #else
+    #endif
+    DebugObject_Free(&bsys->d_obj);
+    #ifndef BADVPN_USE_WINAPI
     DebugCounter_Free(&bsys->d_fds_counter);
     #endif
+    #ifdef BADVPN_USE_KEVENT
+    DebugCounter_Free(&bsys->d_kevent_ctr);
+    #endif
     
     BLog(BLOG_DEBUG, "Reactor freeing");
     
-    #ifndef BADVPN_USE_WINAPI
+    #ifdef BADVPN_USE_EPOLL
     
     // close epoll fd
     ASSERT_FORCE(close(bsys->efd) == 0)
     
     #endif
     
+    #ifdef BADVPN_USE_KEVENT
+    
+    // close kqueue fd
+    ASSERT_FORCE(close(bsys->kqueue_fd) == 0)
+    
+    #endif
+    
     // free jobs
     BPendingGroup_Free(&bsys->pending_jobs);
 }
@@ -444,7 +584,9 @@ int BReactor_Exec (BReactor *bsys)
             continue;
         }
         
-        #else
+        #endif
+        
+        #ifdef BADVPN_USE_EPOLL
         
         // dispatch file descriptor
         if (bsys->epoll_results_pos < bsys->epoll_results_num) {
@@ -490,6 +632,73 @@ int BReactor_Exec (BReactor *bsys)
         
         #endif
         
+        #ifdef BADVPN_USE_KEVENT
+        
+        // dispatch kevent
+        if (bsys->kevent_results_pos < bsys->kevent_results_num) {
+            // grab event
+            struct kevent *event = &bsys->kevent_results[bsys->kevent_results_pos];
+            bsys->kevent_results_pos++;
+            
+            // check if the event was removed
+            if (!event->udata) {
+                continue;
+            }
+            
+            // check tag
+            int *tag = event->udata;
+            switch (*tag) {
+                case KEVENT_TAG_FD: {
+                    // get BFileDescriptor
+                    BFileDescriptor *bfd = UPPER_OBJECT(tag, BFileDescriptor, kevent_tag);
+                    ASSERT(bfd->active)
+                    ASSERT(bfd->kevent_returned_ptr == (int **)&event->udata)
+                    
+                    // zero pointer to the kevent entry
+                    bfd->kevent_returned_ptr = NULL;
+                    
+                    // calculate event to report
+                    int events = 0;
+                    if ((bfd->waitEvents&BREACTOR_READ) && event->filter == EVFILT_READ) {
+                        events |= BREACTOR_READ;
+                    }
+                    if ((bfd->waitEvents&BREACTOR_WRITE) && event->filter == EVFILT_WRITE) {
+                        events |= BREACTOR_WRITE;
+                    }
+                    
+                    if (!events) {
+                        BLog(BLOG_ERROR, "no events detected?");
+                        continue;
+                    }
+                    
+                    // call handler
+                    BLog(BLOG_DEBUG, "Dispatching file descriptor");
+                    bfd->handler(bfd->user, events);
+                    continue;
+                } break;
+                
+                case KEVENT_TAG_KEVENT: {
+                    // get BReactorKEvent
+                    BReactorKEvent *kev = UPPER_OBJECT(tag, BReactorKEvent, kevent_tag);
+                    ASSERT(kev->reactor == bsys)
+                    ASSERT(kev->kevent_returned_ptr == (int **)&event->udata)
+                    
+                    // zero pointer to the kevent entry
+                    kev->kevent_returned_ptr = NULL;
+                    
+                    // call handler
+                    BLog(BLOG_DEBUG, "Dispatching kevent");
+                    kev->handler(kev->user, event->fflags, event->data);
+                    continue;
+                } break;
+                
+                default:
+                    ASSERT(0);
+            }
+        }
+        
+        #endif
+        
         wait_for_events(bsys);
     }
 
@@ -666,6 +875,8 @@ int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs)
 {
     ASSERT(!bs->active)
     
+    #ifdef BADVPN_USE_EPOLL
+    
     // add epoll entry
     struct epoll_event event;
     memset(&event, 0, sizeof(event));
@@ -677,32 +888,58 @@ int BReactor_AddFileDescriptor (BReactor *bsys, BFileDescriptor *bs)
         return 0;
     }
     
+    // set epoll returned pointer
+    bs->epoll_returned_ptr = NULL;
+    
+    #endif
+    
+    #ifdef BADVPN_USE_KEVENT
+    
+    // set kevent tag
+    bs->kevent_tag = KEVENT_TAG_FD;
+    
+    // set kevent returned pointer
+    bs->kevent_returned_ptr = NULL;
+    
+    #endif
+    
     bs->active = 1;
     bs->waitEvents = 0;
-    bs->epoll_returned_ptr = NULL;
     
     DebugCounter_Increment(&bsys->d_fds_counter);
-    
     return 1;
 }
 
 void BReactor_RemoveFileDescriptor (BReactor *bsys, BFileDescriptor *bs)
 {
     ASSERT(bs->active)
+    DebugCounter_Decrement(&bsys->d_fds_counter);
 
     bs->active = 0;
 
+    #ifdef BADVPN_USE_EPOLL
+    
     // delete epoll entry
     ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_DEL, bs->fd, NULL) == 0)
     
-    // The user can now free the file descriptor object, however the file descriptor
-    // can still be in the list of returned events. To prevent the event dispatcher
-    // from crashing, zero its pointer to the file descriptor.
+    // write through epoll returned pointer
     if (bs->epoll_returned_ptr) {
         *bs->epoll_returned_ptr = NULL;
     }
     
-    DebugCounter_Decrement(&bsys->d_fds_counter);
+    #endif
+    
+    #ifdef BADVPN_USE_KEVENT
+    
+    // delete kevents
+    update_kevent_fd_events(bsys, bs, 0);
+    
+    // write through kevent returned pointer
+    if (bs->kevent_returned_ptr) {
+        *bs->kevent_returned_ptr = NULL;
+    }
+    
+    #endif
 }
 
 void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int events)
@@ -714,15 +951,14 @@ void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int
         return;
     }
     
-    // update events
-    bs->waitEvents = events;
+    #ifdef BADVPN_USE_EPOLL
     
     // calculate epoll events
     int eevents = 0;
-    if (bs->waitEvents&BREACTOR_READ) {
+    if ((events & BREACTOR_READ)) {
         eevents |= EPOLLIN;
     }
-    if (bs->waitEvents&BREACTOR_WRITE) {
+    if ((events & BREACTOR_WRITE)) {
         eevents |= EPOLLOUT;
     }
     
@@ -732,6 +968,75 @@ void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int
     event.events = eevents;
     event.data.ptr = bs;
     ASSERT_FORCE(epoll_ctl(bsys->efd, EPOLL_CTL_MOD, bs->fd, &event) == 0)
+    
+    #endif
+    
+    #ifdef BADVPN_USE_KEVENT
+    
+    update_kevent_fd_events(bsys, bs, events);
+    
+    #endif
+    
+    // update events
+    bs->waitEvents = events;
+}
+
+#endif
+
+#ifdef BADVPN_USE_KEVENT
+
+int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data)
+{
+    DebugObject_Access(&reactor->d_obj);
+    
+    // init arguments
+    o->reactor = reactor;
+    o->handler = handler;
+    o->user = user;
+    o->ident = ident;
+    o->filter = filter;
+    
+    // add kevent
+    struct kevent event;
+    memset(&event, 0, sizeof(event));
+    event.ident = o->ident;
+    event.filter = o->filter;
+    event.flags = EV_ADD;
+    event.fflags = fflags;
+    event.data = data;
+    event.udata = &o->kevent_tag;
+    if (kevent(o->reactor->kqueue_fd, &event, 1, NULL, 0, NULL) < 0) {
+        return 0;
+    }
+    
+    // set kevent tag
+    o->kevent_tag = KEVENT_TAG_KEVENT;
+    
+    // set kevent returned pointer
+    o->kevent_returned_ptr = NULL;
+    
+    DebugObject_Init(&o->d_obj);
+    DebugCounter_Increment(&o->reactor->d_kevent_ctr);
+    return 1;
+}
+
+void BReactorKEvent_Free (BReactorKEvent *o)
+{
+    DebugObject_Free(&o->d_obj);
+    DebugCounter_Decrement(&o->reactor->d_kevent_ctr);
+    
+    // write through kevent returned pointer
+    if (o->kevent_returned_ptr) {
+        *o->kevent_returned_ptr = NULL;
+    }
+    
+    // delete kevent
+    struct kevent event;
+    memset(&event, 0, sizeof(event));
+    event.ident = o->ident;
+    event.filter = o->filter;
+    event.flags = EV_DELETE;
+    ASSERT_FORCE(kevent(o->reactor->kqueue_fd, &event, 1, NULL, 0, NULL) == 0)
 }
 
 #endif

+ 56 - 7
system/BReactor.h

@@ -28,12 +28,24 @@
 #ifndef BADVPN_SYSTEM_BREACTOR_H
 #define BADVPN_SYSTEM_BREACTOR_H
 
+#if (defined(BADVPN_USE_WINAPI) + defined(BADVPN_USE_EPOLL) + defined(BADVPN_USE_KEVENT)) != 1
+#error Unknown event backend or too many event backends
+#endif
+
 #ifdef BADVPN_USE_WINAPI
 #include <windows.h>
-#else
+#endif
+
+#ifdef BADVPN_USE_EPOLL
 #include <sys/epoll.h>
 #endif
 
+#ifdef BADVPN_USE_KEVENT
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#endif
+
 #include <stdint.h>
 
 #include <misc/debug.h>
@@ -159,7 +171,15 @@ typedef struct BFileDescriptor_t {
     void *user;
     int active;
     int waitEvents;
+    
+    #ifdef BADVPN_USE_EPOLL
     struct BFileDescriptor_t **epoll_returned_ptr;
+    #endif
+    
+    #ifdef BADVPN_USE_KEVENT
+    int kevent_tag;
+    int **kevent_returned_ptr;
+    #endif
 } BFileDescriptor;
 
 /**
@@ -185,8 +205,6 @@ void BFileDescriptor_Init (BFileDescriptor *bs, int fd, BFileDescriptor_handler
  * and timers.
  */
 typedef struct {
-    DebugObject d_obj;
-    
     int exiting;
     int exit_code;
     
@@ -198,22 +216,33 @@ typedef struct {
     LinkedList1 timers_expired_list;
     
     #ifdef BADVPN_USE_WINAPI
-    
     int num_handles; // number of user handles
     int enabled_num; // number of user handles in the enabled array
     HANDLE enabled_handles[BSYSTEM_MAX_HANDLES]; // enabled user handles
     BHandle *enabled_objects[BSYSTEM_MAX_HANDLES]; // objects corresponding to enabled handles
     BHandle *returned_object;
+    #endif
     
-    #else
-    
+    #ifdef BADVPN_USE_EPOLL
     int efd; // epoll fd
     struct epoll_event epoll_results[BSYSTEM_MAX_RESULTS]; // epoll returned events buffer
     int epoll_results_num; // number of events in the array
     int epoll_results_pos; // number of events processed so far
+    #endif
     
-    DebugCounter d_fds_counter;
+    #ifdef BADVPN_USE_KEVENT
+    int kqueue_fd;
+    struct kevent kevent_results[BSYSTEM_MAX_RESULTS];
+    int kevent_results_num;
+    int kevent_results_pos;
+    #endif
     
+    DebugObject d_obj;
+    #ifndef BADVPN_USE_WINAPI
+    DebugCounter d_fds_counter;
+    #endif
+    #ifdef BADVPN_USE_KEVENT
+    DebugCounter d_kevent_ctr;
     #endif
 } BReactor;
 
@@ -420,4 +449,24 @@ void BReactor_SetFileDescriptorEvents (BReactor *bsys, BFileDescriptor *bs, int
 
 #endif
 
+#ifdef BADVPN_USE_KEVENT
+
+typedef void (*BReactorKEvent_handler) (void *user, u_int fflags, intptr_t data);
+
+typedef struct {
+    BReactor *reactor;
+    BReactorKEvent_handler handler;
+    void *user;
+    uintptr_t ident;
+    short filter;
+    int kevent_tag;
+    int **kevent_returned_ptr;
+    DebugObject d_obj;
+} BReactorKEvent;
+
+int BReactorKEvent_Init (BReactorKEvent *o, BReactor *reactor, BReactorKEvent_handler handler, void *user, uintptr_t ident, short filter, u_int fflags, intptr_t data);
+void BReactorKEvent_Free (BReactorKEvent *o);
+
+#endif
+
 #endif

+ 115 - 13
system/BSocket.c

@@ -32,6 +32,9 @@
 #include <fcntl.h>
 #include <errno.h>
 #include <sys/socket.h>
+#endif
+
+#ifdef BADVPN_LINUX
 #include <netpacket/packet.h>
 #include <net/ethernet.h>
 #endif
@@ -50,6 +53,11 @@
 #define HANDLER_CONNECT 3
 #define HANDLER_ERROR 4
 
+#define CONNECT_STATUS_NONE 0
+#define CONNECT_STATUS_CONNECTING 1
+#define CONNECT_STATUS_DONE 2
+#define CONNECT_STATUS_DONE_IMMEDIATELY 3
+
 static int get_event_index (int event)
 {
     switch (event) {
@@ -102,6 +110,9 @@ static int set_pktinfo (int s)
     #ifdef BADVPN_USE_WINAPI
     DWORD opt = 1;
     int res = setsockopt(s, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt));
+    #elif defined(BADVPN_FREEBSD)
+    int opt = 1;
+    int res = setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt)); // IP_SENDSRCADDR is the same
     #else
     int opt = 1;
     int res = setsockopt(s, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt));
@@ -185,7 +196,7 @@ struct sys_addr {
         struct sockaddr generic;
         struct sockaddr_in ipv4;
         struct sockaddr_in6 ipv6;
-        #ifndef BADVPN_USE_WINAPI
+        #ifdef BADVPN_LINUX
         struct sockaddr_ll packet;
         #endif
     } addr;
@@ -210,7 +221,7 @@ static void addr_socket_to_sys (struct sys_addr *out, BAddr *addr)
             memcpy(out->addr.ipv6.sin6_addr.s6_addr, addr->ipv6.ip, 16);
             out->addr.ipv6.sin6_scope_id = 0;
             break;
-        #ifndef BADVPN_USE_WINAPI
+        #ifdef BADVPN_LINUX
         case BADDR_TYPE_PACKET:
             ASSERT(addr->packet.header_type == BADDR_PACKET_HEADER_TYPE_ETHERNET)
             memset(&out->addr.packet, 0, sizeof(out->addr.packet));
@@ -271,6 +282,7 @@ static void dispatch_event (BSocket *bs)
     ASSERT(bs->current_event_index >= 0)
     ASSERT(bs->current_event_index < BSOCKET_NUM_EVENTS)
     ASSERT(((bs->ready_events)&~(bs->waitEvents)) == 0)
+    ASSERT(!BPending_IsSet(&bs->job))
     
     do {
         // get event
@@ -314,6 +326,7 @@ static void job_handler (BSocket *bs)
 static void dispatch_events (BSocket *bs, int events)
 {
     ASSERT((events&~(bs->waitEvents)) == 0)
+    ASSERT(!BPending_IsSet(&bs->job))
     
     // reset recv number
     bs->recv_num = 0;
@@ -332,6 +345,24 @@ static void dispatch_events (BSocket *bs, int events)
     return;
 }
 
+static void connect_job_handler (BSocket *bs)
+{
+    ASSERT(bs->connecting_status == CONNECT_STATUS_DONE_IMMEDIATELY)
+    ASSERT((bs->waitEvents & BSOCKET_CONNECT))
+    DebugObject_Access(&bs->d_obj);
+    
+    // allow retrieving the result
+    bs->connecting_status = CONNECT_STATUS_DONE;
+    
+    if (bs->global_handler) {
+        bs->global_handler(bs->global_handler_user, BSOCKET_CONNECT);
+        return;
+    }
+    
+    bs->handlers[HANDLER_CONNECT](bs->handlers_user[HANDLER_CONNECT], BSOCKET_CONNECT);
+    return;
+}
+
 #ifdef BADVPN_USE_WINAPI
 
 static long get_wsa_events (int sock_events)
@@ -356,6 +387,7 @@ static long get_wsa_events (int sock_events)
 
 static void handle_handler (BSocket *bs)
 {
+    ASSERT(!BPending_IsSet(&bs->job))
     DebugObject_Access(&bs->d_obj);
     
     // enumerate network events and reset event
@@ -381,8 +413,8 @@ static void handle_handler (BSocket *bs)
         returned_events |= BSOCKET_CONNECT;
         
         // read connection attempt result
-        ASSERT(bs->connecting_status == 1)
-        bs->connecting_status = 2;
+        ASSERT(bs->connecting_status == CONNECT_STATUS_CONNECTING)
+        bs->connecting_status = CONNECT_STATUS_DONE;
         if (events.iErrorCode[FD_CONNECT_BIT] == 0) {
             bs->connecting_result = BSOCKET_ERROR_NONE;
         } else {
@@ -416,6 +448,7 @@ static int get_reactor_fd_events (int sock_events)
 
 static void file_descriptor_handler (BSocket *bs, int events)
 {
+    ASSERT(!BPending_IsSet(&bs->job))
     DebugObject_Access(&bs->d_obj);
     
     int returned_events = 0;
@@ -436,8 +469,8 @@ static void file_descriptor_handler (BSocket *bs, int events)
         returned_events |= BSOCKET_CONNECT;
         
         // read connection attempt result
-        ASSERT(bs->connecting_status == 1)
-        bs->connecting_status = 2;
+        ASSERT(bs->connecting_status == CONNECT_STATUS_CONNECTING)
+        bs->connecting_status = CONNECT_STATUS_DONE;
         int result;
         socklen_t result_len = sizeof(result);
         int res = getsockopt(bs->socket, SOL_SOCKET, SO_ERROR, &result, &result_len);
@@ -599,6 +632,8 @@ int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type)
         case BADDR_TYPE_UNIX:
             sys_domain = AF_UNIX;
             break;
+        #endif
+        #ifdef BADVPN_LINUX
         case BADDR_TYPE_PACKET:
             sys_domain = AF_PACKET;
             break;
@@ -651,7 +686,7 @@ int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type)
     bs->error = BSOCKET_ERROR_NONE;
     init_handlers(bs);
     bs->waitEvents = 0;
-    bs->connecting_status = 0;
+    bs->connecting_status = CONNECT_STATUS_NONE;
     bs->recv_max = BSOCKET_DEFAULT_RECV_MAX;
     bs->recv_num = 0;
     bs->ready_events = 0; // just initialize it so we can clear them safely from BSocket_DisableEvent
@@ -659,6 +694,9 @@ int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type)
     // init job
     BPending_Init(&bs->job, BReactor_PendingGroup(bsys), (BPending_handler)job_handler, bs);
     
+    // init connect job
+    BPending_Init(&bs->connect_job, BReactor_PendingGroup(bsys), (BPending_handler)connect_job_handler, bs);
+    
     // initialize event backend
     if (!init_event_backend(bs)) {
         DEBUG("WARNING: init_event_backend failed");
@@ -670,6 +708,7 @@ int BSocket_Init (BSocket *bs, BReactor *bsys, int domain, int type)
     return 0;
     
 fail2:
+    BPending_Free(&bs->connect_job);
     BPending_Free(&bs->job);
 fail1:
     close_socket(fd);
@@ -684,6 +723,9 @@ void BSocket_Free (BSocket *bs)
     // free event backend
     free_event_backend(bs);
     
+    // free connect job
+    BPending_Free(&bs->connect_job);
+    
     // free job
     BPending_Free(&bs->job);
     
@@ -823,6 +865,11 @@ void BSocket_EnableEvent (BSocket *bs, uint8_t event)
     
     // give new events to event backend
     update_event_backend(bs);
+    
+    // set connect job
+    if (bs->connecting_status == CONNECT_STATUS_DONE_IMMEDIATELY && event == BSOCKET_CONNECT) {
+        BPending_Set(&bs->connect_job);
+    }
 }
 
 void BSocket_DisableEvent (BSocket *bs, uint8_t event)
@@ -844,13 +891,19 @@ void BSocket_DisableEvent (BSocket *bs, uint8_t event)
     
     // give new events to event backend
     update_event_backend(bs);
+    
+    // unset connect job
+    if (!(bs->connecting_status == CONNECT_STATUS_DONE_IMMEDIATELY && (bs->waitEvents & BSOCKET_CONNECT))) {
+        BPending_Unset(&bs->connect_job);
+    }
 }
 
-int BSocket_Connect (BSocket *bs, BAddr *addr)
+int BSocket_Connect (BSocket *bs, BAddr *addr, int later)
 {
     ASSERT(addr)
     ASSERT(!BAddr_IsInvalid(addr))
-    ASSERT(bs->connecting_status == 0)
+    ASSERT(later == 0 || later == 1)
+    ASSERT(bs->connecting_status == CONNECT_STATUS_NONE)
     DebugObject_Access(&bs->d_obj);
     
     struct sys_addr sysaddr;
@@ -861,14 +914,14 @@ int BSocket_Connect (BSocket *bs, BAddr *addr)
         #ifdef BADVPN_USE_WINAPI
         switch ((error = WSAGetLastError())) {
             case WSAEWOULDBLOCK:
-                bs->connecting_status = 1;
+                bs->connecting_status = CONNECT_STATUS_CONNECTING;
                 bs->error = BSOCKET_ERROR_IN_PROGRESS;
                 return -1;
         }
         #else
         switch ((error = errno)) {
             case EINPROGRESS:
-                bs->connecting_status = 1;
+                bs->connecting_status = CONNECT_STATUS_CONNECTING;
                 bs->error = BSOCKET_ERROR_IN_PROGRESS;
                 return -1;
         }
@@ -877,6 +930,21 @@ int BSocket_Connect (BSocket *bs, BAddr *addr)
         bs->error = translate_error(error);
         return -1;
     }
+    
+    if (later) {
+        // set connect result
+        bs->connecting_status = CONNECT_STATUS_DONE_IMMEDIATELY;
+        bs->connecting_result = BSOCKET_ERROR_NONE;
+        
+        // set connect job
+        if ((bs->waitEvents & BSOCKET_CONNECT)) {
+            BPending_Set(&bs->connect_job);
+        }
+        
+        // return in progress error
+        bs->error = BSOCKET_ERROR_IN_PROGRESS;
+        return -1;
+    }
 
     bs->error = BSOCKET_ERROR_NONE;
     return 0;
@@ -884,10 +952,10 @@ int BSocket_Connect (BSocket *bs, BAddr *addr)
 
 int BSocket_GetConnectResult (BSocket *bs)
 {
-    ASSERT(bs->connecting_status == 2)
+    ASSERT(bs->connecting_status == CONNECT_STATUS_DONE)
     DebugObject_Access(&bs->d_obj);
 
-    bs->connecting_status = 0;
+    bs->connecting_status = CONNECT_STATUS_NONE;
     
     return bs->connecting_result;
 }
@@ -1015,6 +1083,9 @@ int BSocket_Accept (BSocket *bs, BSocket *newsock, BAddr *addr)
         
         // init job
         BPending_Init(&newsock->job, BReactor_PendingGroup(bs->bsys), (BPending_handler)job_handler, newsock);
+        
+        // init connect job
+        BPending_Init(&newsock->connect_job, BReactor_PendingGroup(bs->bsys), (BPending_handler)connect_job_handler, newsock);
     
         if (!init_event_backend(newsock)) {
             DEBUG("WARNING: init_event_backend failed");
@@ -1034,6 +1105,7 @@ int BSocket_Accept (BSocket *bs, BSocket *newsock, BAddr *addr)
     return 0;
     
 fail1:
+    BPending_Free(&newsock->connect_job);
     BPending_Free(&newsock->job);
 fail0:
     close_socket(fd);
@@ -1223,7 +1295,11 @@ int BSocket_SendToFrom (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAdd
     iov.iov_len = len;
     
     union {
+        #ifdef BADVPN_FREEBSD
+        char in[CMSG_SPACE(sizeof(struct in_addr))];
+        #else
         char in[CMSG_SPACE(sizeof(struct in_pktinfo))];
+        #endif
         char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
     } cdata;
     
@@ -1244,6 +1320,15 @@ int BSocket_SendToFrom (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAdd
         case BADDR_TYPE_NONE:
             break;
         case BADDR_TYPE_IPV4: {
+            #ifdef BADVPN_FREEBSD
+            memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_addr)));
+            cmsg->cmsg_level = IPPROTO_IP;
+            cmsg->cmsg_type = IP_SENDSRCADDR;
+            cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
+            struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg);
+            addrinfo->s_addr = local_addr->ipv4;;
+            sum += CMSG_SPACE(sizeof(struct in_addr));
+            #else
             memset(cmsg, 0, CMSG_SPACE(sizeof(struct in_pktinfo)));
             cmsg->cmsg_level = IPPROTO_IP;
             cmsg->cmsg_type = IP_PKTINFO;
@@ -1251,6 +1336,7 @@ int BSocket_SendToFrom (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAdd
             struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
             pktinfo->ipi_spec_dst.s_addr = local_addr->ipv4;
             sum += CMSG_SPACE(sizeof(struct in_pktinfo));
+            #endif
         } break;
         case BADDR_TYPE_IPV6: {
             memset(cmsg, 0, CMSG_SPACE(sizeof(struct in6_pktinfo)));
@@ -1267,6 +1353,11 @@ int BSocket_SendToFrom (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAdd
     
     msg.msg_controllen = sum;
     
+    // FreeBSD chokes on empty control block
+    if (msg.msg_controllen == 0) {
+        msg.msg_control = NULL;
+    }
+    
     int bytes = sendmsg(bs->socket, &msg, MSG_NOSIGNAL);
     if (bytes < 0) {
         int error;
@@ -1367,7 +1458,11 @@ int BSocket_RecvFromTo (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAdd
     iov.iov_len = len;
     
     union {
+        #ifdef BADVPN_FREEBSD
+        char in[CMSG_SPACE(sizeof(struct in_addr))];
+        #else
         char in[CMSG_SPACE(sizeof(struct in_pktinfo))];
+        #endif
         char in6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
     } cdata;
     
@@ -1423,10 +1518,17 @@ int BSocket_RecvFromTo (BSocket *bs, uint8_t *data, int len, BAddr *addr, BIPAdd
     
     struct cmsghdr *cmsg;
     for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+        #ifdef BADVPN_FREEBSD
+        if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) {
+            struct in_addr *addrinfo = (struct in_addr *)CMSG_DATA(cmsg);
+            BIPAddr_InitIPv4(local_addr, addrinfo->s_addr);
+        }
+        #else
         if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_PKTINFO) {
             struct in_pktinfo *pktinfo = (struct in_pktinfo *)CMSG_DATA(cmsg);
             BIPAddr_InitIPv4(local_addr, pktinfo->ipi_addr.s_addr);
         }
+        #endif
         else if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) {
             struct in6_pktinfo *pktinfo = (struct in6_pktinfo *)CMSG_DATA(cmsg);
             BIPAddr_InitIPv6(local_addr, pktinfo->ipi6_addr.s6_addr);

+ 4 - 1
system/BSocket.h

@@ -121,6 +121,7 @@ typedef struct BSocket_t {
     int ready_events;
     int current_event_index;
     BPending job;
+    BPending connect_job;
     
     #ifdef BADVPN_USE_WINAPI
     WSAEVENT event;
@@ -266,6 +267,8 @@ void BSocket_DisableEvent (BSocket *bs, uint8_t event);
  *
  * @param bs the object
  * @param addr remote address. Must not be an invalid address.
+ * @param later if true, even if the connection succeeded right away, fail with BSOCKET_ERROR_IN_PROGRESS,
+ *              and report success in the handler. Must be 0 or 1.
  * @return 0 for immediate success,
  *         -1 for failure, where the error code can be:
  *             - BSOCKET_ERROR_IN_PROGRESS the socket is a stream socket and the connection attempt has started.
@@ -273,7 +276,7 @@ void BSocket_DisableEvent (BSocket *bs, uint8_t event);
  *                                         result of attempt with {@link BSocket_GetConnectResult}.
  *             - BSOCKET_ERROR_UNKNOWN unhandled error
  */
-int BSocket_Connect (BSocket *bs, BAddr *addr) WARN_UNUSED;
+int BSocket_Connect (BSocket *bs, BAddr *addr, int later) WARN_UNUSED;
 
 /**
  * Retreives the result of a connection attempt.

+ 93 - 1
system/BUnixSignal.c

@@ -24,15 +24,23 @@
 #include <stdlib.h>
 #include <limits.h>
 #include <errno.h>
-#include <sys/signalfd.h>
 #include <fcntl.h>
 
+#ifdef BADVPN_USE_SIGNALFD
+#include <sys/signalfd.h>
+#endif
+
+#include <misc/balloc.h>
 #include <system/BLog.h>
 
 #include <system/BUnixSignal.h>
 
 #include <generated/blog_channel_BUnixSignal.h>
 
+#define BUNIXSIGNAL_MAX_SIGNALS 64
+
+#ifdef BADVPN_USE_SIGNALFD
+
 static void signalfd_handler (BUnixSignal *o, int events)
 {
     DebugObject_Access(&o->d_obj);
@@ -68,6 +76,22 @@ static void signalfd_handler (BUnixSignal *o, int events)
     return;
 }
 
+#endif
+
+#ifdef BADVPN_USE_KEVENT
+
+static void kevent_handler (struct BUnixSignal_kevent_entry *entry, u_int fflags, intptr_t data)
+{
+    BUnixSignal *o = entry->parent;
+    DebugObject_Access(&o->d_obj);
+    
+    // call signal
+    o->handler(o->user, entry->signo);
+    return;
+}
+
+#endif
+
 int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnixSignal_handler handler, void *user)
 {
     // init arguments
@@ -76,6 +100,8 @@ int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnix
     o->handler = handler;
     o->user = user;
     
+    #ifdef BADVPN_USE_SIGNALFD
+    
     // init signalfd fd
     if ((o->signalfd_fd = signalfd(-1, &o->signals, 0)) < 0) {
         BLog(BLOG_ERROR, "signalfd failed");
@@ -96,6 +122,43 @@ int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnix
     }
     BReactor_SetFileDescriptorEvents(o->reactor, &o->signalfd_bfd, BREACTOR_READ);
     
+    #endif
+    
+    #ifdef BADVPN_USE_KEVENT
+    
+    // count signals
+    int num_signals = 0;
+    for (int i = 0; i < BUNIXSIGNAL_MAX_SIGNALS; i++) {
+        if (!sigismember(&o->signals, i)) {
+            continue;
+        }
+        num_signals++;
+    }
+    
+    // allocate array
+    if (!(o->entries = BAllocArray(num_signals, sizeof(o->entries[0])))) {
+        BLog(BLOG_ERROR, "BAllocArray failed");
+        goto fail0;
+    }
+    
+    // init kevents
+    o->num_entries = 0;
+    for (int i = 0; i < BUNIXSIGNAL_MAX_SIGNALS; i++) {
+        if (!sigismember(&o->signals, i)) {
+            continue;
+        }
+        struct BUnixSignal_kevent_entry *entry = &o->entries[o->num_entries];
+        entry->parent = o;
+        entry->signo = i;
+        if (!BReactorKEvent_Init(&entry->kevent, o->reactor, (BReactorKEvent_handler)kevent_handler, entry, entry->signo, EVFILT_SIGNAL, 0, 0)) {
+            BLog(BLOG_ERROR, "BReactorKEvent_Init failed");
+            goto fail2;
+        }
+        o->num_entries++;
+    }
+    
+    #endif
+    
     // block signals
     if (sigprocmask(SIG_BLOCK, &o->signals, 0) < 0) {
         BLog(BLOG_ERROR, "sigprocmask block failed");
@@ -106,10 +169,22 @@ int BUnixSignal_Init (BUnixSignal *o, BReactor *reactor, sigset_t signals, BUnix
     
     return 1;
     
+    #ifdef BADVPN_USE_SIGNALFD
 fail2:
     BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd);
 fail1:
     ASSERT_FORCE(close(o->signalfd_fd) == 0)
+    #endif
+    
+    #ifdef BADVPN_USE_KEVENT
+fail2:
+    while (o->num_entries > 0) {
+        BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent);
+        o->num_entries--;
+    }
+    BFree(o->entries);
+    #endif
+    
 fail0:
     return 0;
 }
@@ -124,9 +199,26 @@ void BUnixSignal_Free (BUnixSignal *o, int unblock)
         ASSERT_FORCE(sigprocmask(SIG_UNBLOCK, &o->signals, 0) == 0)
     }
     
+    #ifdef BADVPN_USE_SIGNALFD
+    
     // free signalfd BFileDescriptor
     BReactor_RemoveFileDescriptor(o->reactor, &o->signalfd_bfd);
     
     // free signalfd fd
     ASSERT_FORCE(close(o->signalfd_fd) == 0)
+    
+    #endif
+    
+    #ifdef BADVPN_USE_KEVENT
+    
+    // free kevents
+    while (o->num_entries > 0) {
+        BReactorKEvent_Free(&o->entries[o->num_entries - 1].kevent);
+        o->num_entries--;
+    }
+    
+    // free array
+    BFree(o->entries);
+    
+    #endif
 }

+ 24 - 1
system/BUnixSignal.h

@@ -27,6 +27,10 @@
 #ifndef BADVPN_SYSTEM_BUNIXSIGNAL_H
 #define BADVPN_SYSTEM_BUNIXSIGNAL_H
 
+#if (defined(BADVPN_USE_SIGNALFD) + defined(BADVPN_USE_KEVENT)) != 1
+#error Unknown signal backend or too many signal backends
+#endif
+
 #include <unistd.h>
 #include <signal.h>
 
@@ -34,6 +38,8 @@
 #include <system/BReactor.h>
 #include <system/DebugObject.h>
 
+struct BUnixSignal_s;
+
 /**
  * Handler function called when a signal is received.
  * 
@@ -42,16 +48,33 @@
  */
 typedef void (*BUnixSignal_handler) (void *user, int signo);
 
+#ifdef BADVPN_USE_KEVENT
+struct BUnixSignal_kevent_entry {
+    struct BUnixSignal_s *parent;
+    int signo;
+    BReactorKEvent kevent;
+};
+#endif
+
 /**
  * Object for catching unix signals.
  */
-typedef struct {
+typedef struct BUnixSignal_s {
     BReactor *reactor;
     sigset_t signals;
     BUnixSignal_handler handler;
     void *user;
+    
+    #ifdef BADVPN_USE_SIGNALFD
     int signalfd_fd;
     BFileDescriptor signalfd_bfd;
+    #endif
+    
+    #ifdef BADVPN_USE_KEVENT
+    struct BUnixSignal_kevent_entry *entries;
+    int num_entries;
+    #endif
+    
     DebugObject d_obj;
 } BUnixSignal;
 

+ 64 - 17
tuntap/BTap.c

@@ -24,23 +24,29 @@
 #include <stdio.h>
 
 #ifdef BADVPN_USE_WINAPI
-#include <windows.h>
-#include <winioctl.h>
-#include <objbase.h>
-#include <wtypes.h>
-#include "wintap-common.h"
-#include <tuntap/tapwin32-funcs.h>
+    #include <windows.h>
+    #include <winioctl.h>
+    #include <objbase.h>
+    #include <wtypes.h>
+    #include "wintap-common.h"
+    #include <tuntap/tapwin32-funcs.h>
 #else
-#include <linux/if_tun.h>
-#include <net/if.h>
-#include <net/if_arp.h>
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <errno.h>
+    #include <fcntl.h>
+    #include <unistd.h>
+    #include <errno.h>
+    #include <sys/ioctl.h>
+    #include <sys/types.h>
+    #include <sys/stat.h>
+    #include <sys/socket.h>
+    #include <net/if.h>
+    #include <net/if_arp.h>
+    #ifdef BADVPN_LINUX
+        #include <linux/if_tun.h>
+    #endif
+    #ifdef BADVPN_FREEBSD
+        #include <net/if_tun.h>
+        #include <net/if_tap.h>
+    #endif
 #endif
 
 #include <tuntap/BTap.h>
@@ -553,7 +559,11 @@ fail1:
 fail0:
     return 0;
     
-    #else
+    #endif
+    
+    #if defined(BADVPN_LINUX) || defined(BADVPN_FREEBSD)
+    
+    #ifdef BADVPN_LINUX
     
     // open device
     
@@ -583,6 +593,43 @@ fail0:
     
     strcpy(o->devname, ifr.ifr_name);
     
+    #endif
+    
+    #ifdef BADVPN_FREEBSD
+    
+    if (tun) {
+        DEBUG("TUN not supported on FreeBSD");
+        goto fail0;
+    }
+    
+    if (!devname) {
+        DEBUG("no device specified");
+        goto fail0;
+    }
+    
+    // open device
+    
+    char devnode[10 + IFNAMSIZ];
+    snprintf(devnode, sizeof(devnode), "/dev/%s", devname);
+    
+    if ((o->fd = open(devnode, O_RDWR)) < 0) {
+        DEBUG("error opening device");
+        goto fail0;
+    }
+    
+    // get name
+    
+    struct ifreq ifr;
+    memset(&ifr, 0, sizeof(ifr));
+    if (ioctl(o->fd, TAPGIFNAME, (void *)&ifr) < 0) {
+        DEBUG("error configuring device");
+        goto fail1;
+    }
+    
+    strcpy(o->devname, ifr.ifr_name);
+    
+    #endif
+    
     // get MTU
     if (tun) {
         o->frame_mtu = 65535;

+ 4 - 0
tuntap/BTap.h

@@ -27,6 +27,10 @@
 #ifndef BADVPN_TUNTAP_BTAP_H
 #define BADVPN_TUNTAP_BTAP_H
 
+#if (defined(BADVPN_USE_WINAPI) + defined(BADVPN_LINUX) + defined(BADVPN_FREEBSD)) != 1
+#error Unknown TAP backend or too many TAP backends
+#endif
+
 #include <stdint.h>
 
 #ifdef BADVPN_USE_WINAPI