Преглед изворни кода

ncd: add net.watch_interfaces module

ambrop7 пре 15 година
родитељ
комит
a00fe52aea

+ 2 - 1
blog_channels.txt

@@ -30,8 +30,9 @@ ncd_net_up 4
 ncd_net_dns 4
 ncd_net_iptables 4
 ncd_net_ipv4_addr 4
-ncd_net_ipv4_dhcp 4
 ncd_net_ipv4_route 4
+ncd_net_ipv4_dhcp 4
+ncd_net_watch_interfaces 4
 ncd_sys_evdev 4
 ncd_sys_watch_directory 4
 StreamPeerIO 4

+ 4 - 0
generated/blog_channel_ncd_net_watch_interfaces.h

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

+ 33 - 32
generated/blog_channels_defines.h

@@ -30,35 +30,36 @@
 #define BLOG_CHANNEL_ncd_net_dns 29
 #define BLOG_CHANNEL_ncd_net_iptables 30
 #define BLOG_CHANNEL_ncd_net_ipv4_addr 31
-#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 32
-#define BLOG_CHANNEL_ncd_net_ipv4_route 33
-#define BLOG_CHANNEL_ncd_sys_evdev 34
-#define BLOG_CHANNEL_ncd_sys_watch_directory 35
-#define BLOG_CHANNEL_StreamPeerIO 36
-#define BLOG_CHANNEL_DatagramPeerIO 37
-#define BLOG_CHANNEL_BReactor 38
-#define BLOG_CHANNEL_BSignal 39
-#define BLOG_CHANNEL_FragmentProtoAssembler 40
-#define BLOG_CHANNEL_BPredicate 41
-#define BLOG_CHANNEL_ServerConnection 42
-#define BLOG_CHANNEL_Listener 43
-#define BLOG_CHANNEL_DataProto 44
-#define BLOG_CHANNEL_FrameDecider 45
-#define BLOG_CHANNEL_BSocksClient 46
-#define BLOG_CHANNEL_BDHCPClientCore 47
-#define BLOG_CHANNEL_BDHCPClient 48
-#define BLOG_CHANNEL_NCDIfConfig 49
-#define BLOG_CHANNEL_BUnixSignal 50
-#define BLOG_CHANNEL_BProcess 51
-#define BLOG_CHANNEL_StreamSocketSink 52
-#define BLOG_CHANNEL_StreamSocketSource 53
-#define BLOG_CHANNEL_DatagramSocketSink 54
-#define BLOG_CHANNEL_DatagramSocketSource 55
-#define BLOG_CHANNEL_PRStreamSink 56
-#define BLOG_CHANNEL_PRStreamSource 57
-#define BLOG_CHANNEL_BSocketPRFileDesc 58
-#define BLOG_CHANNEL_PacketProtoDecoder 59
-#define BLOG_CHANNEL_DPRelay 60
-#define BLOG_CHANNEL_BThreadWork 61
-#define BLOG_CHANNEL_DPReceive 62
-#define BLOG_NUM_CHANNELS 63
+#define BLOG_CHANNEL_ncd_net_ipv4_route 32
+#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 33
+#define BLOG_CHANNEL_ncd_net_watch_interfaces 34
+#define BLOG_CHANNEL_ncd_sys_evdev 35
+#define BLOG_CHANNEL_ncd_sys_watch_directory 36
+#define BLOG_CHANNEL_StreamPeerIO 37
+#define BLOG_CHANNEL_DatagramPeerIO 38
+#define BLOG_CHANNEL_BReactor 39
+#define BLOG_CHANNEL_BSignal 40
+#define BLOG_CHANNEL_FragmentProtoAssembler 41
+#define BLOG_CHANNEL_BPredicate 42
+#define BLOG_CHANNEL_ServerConnection 43
+#define BLOG_CHANNEL_Listener 44
+#define BLOG_CHANNEL_DataProto 45
+#define BLOG_CHANNEL_FrameDecider 46
+#define BLOG_CHANNEL_BSocksClient 47
+#define BLOG_CHANNEL_BDHCPClientCore 48
+#define BLOG_CHANNEL_BDHCPClient 49
+#define BLOG_CHANNEL_NCDIfConfig 50
+#define BLOG_CHANNEL_BUnixSignal 51
+#define BLOG_CHANNEL_BProcess 52
+#define BLOG_CHANNEL_StreamSocketSink 53
+#define BLOG_CHANNEL_StreamSocketSource 54
+#define BLOG_CHANNEL_DatagramSocketSink 55
+#define BLOG_CHANNEL_DatagramSocketSource 56
+#define BLOG_CHANNEL_PRStreamSink 57
+#define BLOG_CHANNEL_PRStreamSource 58
+#define BLOG_CHANNEL_BSocketPRFileDesc 59
+#define BLOG_CHANNEL_PacketProtoDecoder 60
+#define BLOG_CHANNEL_DPRelay 61
+#define BLOG_CHANNEL_BThreadWork 62
+#define BLOG_CHANNEL_DPReceive 63
+#define BLOG_NUM_CHANNELS 64

+ 2 - 1
generated/blog_channels_list.h

@@ -30,8 +30,9 @@
 {.name = "ncd_net_dns", .loglevel = 4},
 {.name = "ncd_net_iptables", .loglevel = 4},
 {.name = "ncd_net_ipv4_addr", .loglevel = 4},
-{.name = "ncd_net_ipv4_dhcp", .loglevel = 4},
 {.name = "ncd_net_ipv4_route", .loglevel = 4},
+{.name = "ncd_net_ipv4_dhcp", .loglevel = 4},
+{.name = "ncd_net_watch_interfaces", .loglevel = 4},
 {.name = "ncd_sys_evdev", .loglevel = 4},
 {.name = "ncd_sys_watch_directory", .loglevel = 4},
 {.name = "StreamPeerIO", .loglevel = 4},

+ 1 - 0
ncd/CMakeLists.txt

@@ -56,6 +56,7 @@ add_executable(badvpn-ncd
     modules/net_ipv4_addr.c
     modules/net_ipv4_route.c
     modules/net_ipv4_dhcp.c
+    modules/net_watch_interfaces.c
     ${NCD_ADDITIONAL_SOURCES}
 )
 target_link_libraries(badvpn-ncd system dhcpclient ncdconfig process)

+ 2 - 0
ncd/modules/modules.h

@@ -58,6 +58,7 @@ extern const struct NCDModuleGroup ncdmodule_net_iptables;
 extern const struct NCDModuleGroup ncdmodule_net_ipv4_addr;
 extern const struct NCDModuleGroup ncdmodule_net_ipv4_route;
 extern const struct NCDModuleGroup ncdmodule_net_ipv4_dhcp;
+extern const struct NCDModuleGroup ncdmodule_net_watch_interfaces;
 #ifdef BADVPN_USE_LINUX_INPUT
 extern const struct NCDModuleGroup ncdmodule_sys_evdev;
 #endif
@@ -97,6 +98,7 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_net_ipv4_addr,
     &ncdmodule_net_ipv4_route,
     &ncdmodule_net_ipv4_dhcp,
+    &ncdmodule_net_watch_interfaces,
 #ifdef BADVPN_USE_LINUX_INPUT
     &ncdmodule_sys_evdev,
 #endif

+ 308 - 0
ncd/modules/net_watch_interfaces.c

@@ -0,0 +1,308 @@
+/**
+ * @file net_watch_interfaces.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.
+ * 
+ * @section DESCRIPTION
+ * 
+ * Network interface watcher.
+ * 
+ * Synopsis: net.watch_interfaces()
+ * Description: reports network interface events. Transitions up when an event is detected, and
+ *   goes down waiting for the next event when net.watch_interfaces::nextevent() is called.
+ *   On startup, "added" events are reported for existing interfaces.
+ * Variables:
+ *   string event_type - what happened with the interface: "added" or "removed". This may not be
+ *     consistent across events.
+ *   string devname - interface name
+ * 
+ * Synopsis: net.watch_interfaces::nextevent()
+ * Description: makes the watch_interfaces module transition down in order to report the next event.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include <ncd/NCDInterfaceMonitor.h>
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_net_watch_interfaces.h>
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct instance {
+    NCDModuleInst *i;
+    NCDInterfaceMonitor monitor;
+    FILE *net_file;
+    char line_buf[256];
+    int processing;
+    const char *processing_type;
+    char processing_name[256];
+};
+
+struct nextevent_instance {
+    NCDModuleInst *i;
+};
+
+static void next_file_event (struct instance *o)
+{
+    ASSERT(!o->processing)
+    ASSERT(o->net_file)
+    
+    char *name;
+    
+    while (1) {
+        if (!fgets(o->line_buf, sizeof(o->line_buf), o->net_file)) {
+            // close file
+            fclose(o->net_file);
+            
+            // set no net file
+            o->net_file = NULL;
+            
+            // start processing monitor events
+            NCDInterfaceMonitor_Continue(&o->monitor);
+            
+            return;
+        }
+        
+        // parse line to get interface name
+        char *c = o->line_buf;
+        while (*c && isspace(*c)) {
+            c++;
+        }
+        name = c;
+        while (*c && *c != ':') {
+            c++;
+        }
+        if (*c != ':') {
+            continue;
+        }
+        *c = '\0';
+        
+        break;
+    }
+    
+    // set event
+    o->processing_type = "added";
+    snprintf(o->processing_name, sizeof(o->processing_name), "%s", name);
+    o->processing = 1;
+    
+    // signal up
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+}
+
+static void next_event (struct instance *o)
+{
+    ASSERT(o->processing)
+    
+    // set not processing
+    o->processing = 0;
+    
+    // signal down
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
+    
+    if (o->net_file) {
+        next_file_event(o);
+        return;
+    } else {
+        // continue processing monitor events
+        NCDInterfaceMonitor_Continue(&o->monitor);
+    }
+}
+
+static void monitor_handler (struct instance *o, const char *ifname, int if_flags)
+{
+    ASSERT(!o->processing)
+    ASSERT(!o->net_file)
+    
+    // pause monitor
+    NCDInterfaceMonitor_Pause(&o->monitor);
+    
+    // set event
+    o->processing_type = ((if_flags & NCDIFCONFIG_FLAG_EXISTS) ? "added" : "removed");
+    snprintf(o->processing_name, sizeof(o->processing_name), "%s", ifname);
+    o->processing = 1;
+    
+    // signal up
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+}
+
+static void func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct instance *o = malloc(sizeof(*o));
+    if (!o) {
+        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
+        goto fail0;
+    }
+    NCDModuleInst_Backend_SetUser(i, o);
+    
+    // init arguments
+    o->i = i;
+    
+    // check arguments
+    if (!NCDValue_ListRead(o->i->args, 0)) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    
+    // init monitor
+    if (!NCDInterfaceMonitor_Init(&o->monitor, o->i->reactor, (NCDInterfaceMonitor_handler)monitor_handler, o)) {
+        ModuleLog(o->i, BLOG_ERROR, "NCDInterfaceMonitor_Init failed");
+        goto fail1;
+    }
+    NCDInterfaceMonitor_Pause(&o->monitor);
+    
+    // open /proc/net/dev
+    if (!(o->net_file = fopen("/proc/net/dev", "r"))) {
+        ModuleLog(o->i, BLOG_ERROR, "fopen(/proc/net/dev) failed");
+        goto fail2;
+    }
+    
+    // set not processing
+    o->processing = 0;
+    
+    next_file_event(o);
+    return;
+    
+fail2:
+    NCDInterfaceMonitor_Free(&o->monitor);
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    NCDModuleInst *i = o->i;
+    
+    // close /proc/net/dev
+    if (o->net_file) {
+        fclose(o->net_file);
+    }
+    
+    // free monitor
+    NCDInterfaceMonitor_Free(&o->monitor);
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static int func_getvar (void *vo, const char *name, NCDValue *out)
+{
+    struct instance *o = vo;
+    ASSERT(o->processing)
+    
+    if (!strcmp(name, "event_type")) {
+        if (!NCDValue_InitString(out, o->processing_type)) {
+            ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
+            return 0;
+        }
+        
+        return 1;
+    }
+    
+    if (!strcmp(name, "devname")) {
+        if (!NCDValue_InitString(out, o->processing_name)) {
+            ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitString failed");
+            return 0;
+        }
+        
+        return 1;
+    }
+    
+    return 0;
+}
+
+static void nextevent_func_new (NCDModuleInst *i)
+{
+    // allocate instance
+    struct nextevent_instance *o = malloc(sizeof(*o));
+    if (!o) {
+        ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
+        goto fail0;
+    }
+    NCDModuleInst_Backend_SetUser(i, o);
+    
+    // init arguments
+    o->i = i;
+    
+    // check arguments
+    if (!NCDValue_ListRead(o->i->args, 0)) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    
+    // get method object
+    struct instance *mo = i->method_object->inst_user;
+    ASSERT(mo->processing)
+    
+    // signal up.
+    // Do it before finishing the event so our process does not advance any further if
+    // we would be killed the event provider going down.
+    NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+    
+    // wait for next event
+    next_event(mo);
+    
+    return;
+    
+fail1:
+    free(o);
+fail0:
+    NCDModuleInst_Backend_SetError(i);
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static void nextevent_func_die (void *vo)
+{
+    struct nextevent_instance *o = vo;
+    NCDModuleInst *i = o->i;
+    
+    // free instance
+    free(o);
+    
+    NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "net.watch_interfaces",
+        .func_new = func_new,
+        .func_die = func_die,
+        .func_getvar = func_getvar
+    }, {
+        .type = "net.watch_interfaces::nextevent",
+        .func_new = nextevent_func_new,
+        .func_die = nextevent_func_die
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_net_watch_interfaces = {
+    .modules = modules
+};