Pārlūkot izejas kodu

ncd: add net.backend.wpa_supplicant module

ambrop7 15 gadi atpakaļ
vecāks
revīzija
e3c70bb4ba

+ 1 - 0
blog_channels.txt

@@ -16,6 +16,7 @@ ncd_run 4
 ncd_net_backend_physical 4
 ncd_net_backend_waitdevice 4
 ncd_net_backend_badvpn 4
+ncd_net_backend_wpa_supplicant 4
 ncd_net_backend_rfkill 4
 ncd_net_dns 4
 ncd_net_iptables 4

+ 4 - 0
generated/blog_channel_ncd_net_backend_wpa_supplicant.h

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

+ 35 - 34
generated/blog_channels_defines.h

@@ -16,37 +16,38 @@
 #define BLOG_CHANNEL_ncd_net_backend_physical 15
 #define BLOG_CHANNEL_ncd_net_backend_waitdevice 16
 #define BLOG_CHANNEL_ncd_net_backend_badvpn 17
-#define BLOG_CHANNEL_ncd_net_backend_rfkill 18
-#define BLOG_CHANNEL_ncd_net_dns 19
-#define BLOG_CHANNEL_ncd_net_iptables 20
-#define BLOG_CHANNEL_ncd_net_ipv4_addr 21
-#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 22
-#define BLOG_CHANNEL_ncd_net_ipv4_route 23
-#define BLOG_CHANNEL_StreamPeerIO 24
-#define BLOG_CHANNEL_DatagramPeerIO 25
-#define BLOG_CHANNEL_BReactor 26
-#define BLOG_CHANNEL_BSignal 27
-#define BLOG_CHANNEL_FragmentProtoAssembler 28
-#define BLOG_CHANNEL_BPredicate 29
-#define BLOG_CHANNEL_ServerConnection 30
-#define BLOG_CHANNEL_Listener 31
-#define BLOG_CHANNEL_DataProto 32
-#define BLOG_CHANNEL_FrameDecider 33
-#define BLOG_CHANNEL_BSocksClient 34
-#define BLOG_CHANNEL_BDHCPClientCore 35
-#define BLOG_CHANNEL_BDHCPClient 36
-#define BLOG_CHANNEL_NCDIfConfig 37
-#define BLOG_CHANNEL_BUnixSignal 38
-#define BLOG_CHANNEL_BProcess 39
-#define BLOG_CHANNEL_StreamSocketSink 40
-#define BLOG_CHANNEL_StreamSocketSource 41
-#define BLOG_CHANNEL_DatagramSocketSink 42
-#define BLOG_CHANNEL_DatagramSocketSource 43
-#define BLOG_CHANNEL_PRStreamSink 44
-#define BLOG_CHANNEL_PRStreamSource 45
-#define BLOG_CHANNEL_BSocketPRFileDesc 46
-#define BLOG_CHANNEL_PacketProtoDecoder 47
-#define BLOG_CHANNEL_DPRelay 48
-#define BLOG_CHANNEL_BThreadWork 49
-#define BLOG_CHANNEL_DPReceive 50
-#define BLOG_NUM_CHANNELS 51
+#define BLOG_CHANNEL_ncd_net_backend_wpa_supplicant 18
+#define BLOG_CHANNEL_ncd_net_backend_rfkill 19
+#define BLOG_CHANNEL_ncd_net_dns 20
+#define BLOG_CHANNEL_ncd_net_iptables 21
+#define BLOG_CHANNEL_ncd_net_ipv4_addr 22
+#define BLOG_CHANNEL_ncd_net_ipv4_dhcp 23
+#define BLOG_CHANNEL_ncd_net_ipv4_route 24
+#define BLOG_CHANNEL_StreamPeerIO 25
+#define BLOG_CHANNEL_DatagramPeerIO 26
+#define BLOG_CHANNEL_BReactor 27
+#define BLOG_CHANNEL_BSignal 28
+#define BLOG_CHANNEL_FragmentProtoAssembler 29
+#define BLOG_CHANNEL_BPredicate 30
+#define BLOG_CHANNEL_ServerConnection 31
+#define BLOG_CHANNEL_Listener 32
+#define BLOG_CHANNEL_DataProto 33
+#define BLOG_CHANNEL_FrameDecider 34
+#define BLOG_CHANNEL_BSocksClient 35
+#define BLOG_CHANNEL_BDHCPClientCore 36
+#define BLOG_CHANNEL_BDHCPClient 37
+#define BLOG_CHANNEL_NCDIfConfig 38
+#define BLOG_CHANNEL_BUnixSignal 39
+#define BLOG_CHANNEL_BProcess 40
+#define BLOG_CHANNEL_StreamSocketSink 41
+#define BLOG_CHANNEL_StreamSocketSource 42
+#define BLOG_CHANNEL_DatagramSocketSink 43
+#define BLOG_CHANNEL_DatagramSocketSource 44
+#define BLOG_CHANNEL_PRStreamSink 45
+#define BLOG_CHANNEL_PRStreamSource 46
+#define BLOG_CHANNEL_BSocketPRFileDesc 47
+#define BLOG_CHANNEL_PacketProtoDecoder 48
+#define BLOG_CHANNEL_DPRelay 49
+#define BLOG_CHANNEL_BThreadWork 50
+#define BLOG_CHANNEL_DPReceive 51
+#define BLOG_NUM_CHANNELS 52

+ 1 - 0
generated/blog_channels_list.h

@@ -16,6 +16,7 @@
 {.name = "ncd_net_backend_physical", .loglevel = 4},
 {.name = "ncd_net_backend_waitdevice", .loglevel = 4},
 {.name = "ncd_net_backend_badvpn", .loglevel = 4},
+{.name = "ncd_net_backend_wpa_supplicant", .loglevel = 4},
 {.name = "ncd_net_backend_rfkill", .loglevel = 4},
 {.name = "ncd_net_dns", .loglevel = 4},
 {.name = "ncd_net_iptables", .loglevel = 4},

+ 1 - 0
ncd/CMakeLists.txt

@@ -19,6 +19,7 @@ add_executable(badvpn-ncd
     modules/net_backend_physical.c
     modules/net_backend_waitdevice.c
     modules/net_backend_badvpn.c
+    modules/net_backend_wpa_supplicant.c
     modules/net_backend_rfkill.c
     modules/net_dns.c
     modules/net_iptables.c

+ 2 - 0
ncd/modules/modules.h

@@ -40,6 +40,7 @@ extern const struct NCDModuleGroup ncdmodule_run;
 extern const struct NCDModuleGroup ncdmodule_net_backend_physical;
 extern const struct NCDModuleGroup ncdmodule_net_backend_waitdevice;
 extern const struct NCDModuleGroup ncdmodule_net_backend_badvpn;
+extern const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant;
 extern const struct NCDModuleGroup ncdmodule_net_backend_rfkill;
 extern const struct NCDModuleGroup ncdmodule_net_dns;
 extern const struct NCDModuleGroup ncdmodule_net_iptables;
@@ -61,6 +62,7 @@ static const struct NCDModuleGroup *ncd_modules[] = {
     &ncdmodule_net_backend_physical,
     &ncdmodule_net_backend_waitdevice,
     &ncdmodule_net_backend_badvpn,
+    &ncdmodule_net_backend_wpa_supplicant,
     &ncdmodule_net_backend_rfkill,
     &ncdmodule_net_dns,
     &ncdmodule_net_iptables,

+ 373 - 0
ncd/modules/net_backend_wpa_supplicant.c

@@ -0,0 +1,373 @@
+/**
+ * @file net_backend_wpa_supplicant.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
+ * 
+ * Wireless interface module which runs wpa_supplicant.
+ * 
+ * Note: wpa_supplicant does not monitor the state of rfkill switches and will fail to
+ * start if the switch is of when it is started, and will stop working indefinitely if the
+ * switch is turned off while it is running. Therefore, you should put a "net.backend.rfkill"
+ * statement in front of the wpa_supplicant statement.
+ * 
+ * Synopsis: net.backend.wpa_supplicant(string ifname, string conf, string exec, list(string) args)
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <misc/cmdline.h>
+#include <misc/string_begins_with.h>
+#include <system/BSocket.h>
+#include <flow/StreamSocketSource.h>
+#include <flow/LineBuffer.h>
+#include <ncd/NCDModule.h>
+
+#include <generated/blog_channel_ncd_net_backend_wpa_supplicant.h>
+
+#define MAX_LINE_LEN 512
+#define EVENT_STRING_CONNECTED "CTRL-EVENT-CONNECTED"
+#define EVENT_STRING_DISCONNECTED "CTRL-EVENT-DISCONNECTED"
+
+#define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
+
+struct instance {
+    NCDModuleInst *i;
+    char *ifname;
+    char *conf;
+    char *exec;
+    NCDValue *args;
+    int dying;
+    int up;
+    BProcess process;
+    int pipe_fd;
+    BSocket pipe_sock;
+    FlowErrorDomain pipe_domain;
+    StreamSocketSource pipe_source;
+    LineBuffer pipe_buffer;
+    PacketPassInterface pipe_input;
+};
+
+static int build_cmdline (struct instance *o, CmdLine *c);
+static int init_pipe (struct instance *o, int pipe_fd);
+static void free_pipe (struct instance *o);
+static void process_handler (struct instance *o, int normally, uint8_t normally_exit_status);
+static void process_pipe_handler_error (struct instance *o, int component, int code);
+static void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len);
+
+int build_cmdline (struct instance *o, CmdLine *c)
+{
+    if (!CmdLine_Init(c)) {
+        goto fail0;
+    }
+    
+    // append exec
+    if (!CmdLine_Append(c, o->exec)) {
+        goto fail1;
+    }
+    
+    // append user arguments
+    NCDValue *arg = NCDValue_ListFirst(o->args);
+    while (arg) {
+        if (NCDValue_Type(arg) != NCDVALUE_STRING) {
+            ModuleLog(o->i, BLOG_ERROR, "wrong type");
+            goto fail1;
+        }
+        
+        // append argument
+        if (!CmdLine_Append(c, NCDValue_StringValue(arg))) {
+            goto fail1;
+        }
+        
+        arg = NCDValue_ListNext(o->args, arg);
+    }
+    
+    // append interface name
+    if (!CmdLine_Append(c, "-i") || !CmdLine_Append(c, o->ifname)) {
+        goto fail1;
+    }
+    
+    // append config file
+    if (!CmdLine_Append(c, "-c") || !CmdLine_Append(c, o->conf)) {
+        goto fail1;
+    }
+    
+    // terminate cmdline
+    if (!CmdLine_Finish(c)) {
+        goto fail1;
+    }
+    
+    return 1;
+    
+fail1:
+    CmdLine_Free(c);
+fail0:
+    return 0;
+}
+
+int init_pipe (struct instance *o, int pipe_fd)
+{
+    // init socket
+    if (BSocket_InitPipe(&o->pipe_sock, o->i->reactor, pipe_fd) < 0) {
+        ModuleLog(o->i, BLOG_ERROR, "BSocket_InitPipe failed");
+        goto fail0;
+    }
+    
+    // init domain
+    FlowErrorDomain_Init(&o->pipe_domain, (FlowErrorDomain_handler)process_pipe_handler_error, o);
+    
+    // init source
+    StreamSocketSource_Init(&o->pipe_source, FlowErrorReporter_Create(&o->pipe_domain, 0), &o->pipe_sock, BReactor_PendingGroup(o->i->reactor));
+    
+    // init input interface
+    PacketPassInterface_Init(&o->pipe_input, MAX_LINE_LEN, (PacketPassInterface_handler_send)process_pipe_handler_send, o, BReactor_PendingGroup(o->i->reactor));
+    
+    // init buffer
+    if (!LineBuffer_Init(&o->pipe_buffer, StreamSocketSource_GetOutput(&o->pipe_source), &o->pipe_input, MAX_LINE_LEN, '\n')) {
+        ModuleLog(o->i, BLOG_ERROR, "LineBuffer_Init failed");
+        goto fail1;
+    }
+    
+    return 1;
+    
+fail1:
+    PacketPassInterface_Free(&o->pipe_input);
+    StreamSocketSource_Free(&o->pipe_source);
+    BSocket_Free(&o->pipe_sock);
+fail0:
+    return 0;
+}
+
+void free_pipe (struct instance *o)
+{
+    // free buffer
+    LineBuffer_Free(&o->pipe_buffer);
+    
+    // free input interface
+    PacketPassInterface_Free(&o->pipe_input);
+    
+    // free source
+    StreamSocketSource_Free(&o->pipe_source);
+    
+    // free socket
+    BSocket_Free(&o->pipe_sock);
+}
+
+void process_handler (struct instance *o, int normally, uint8_t normally_exit_status)
+{
+    ModuleLog(o->i, (o->dying ? BLOG_INFO : BLOG_ERROR), "process terminated");
+    
+    int was_dying = o->dying;
+    
+    // set dying so free doesn't attempt kill
+    o->dying = 1;
+    
+    // die
+    NCDModuleInst_Backend_Died(o->i, !was_dying);
+    return;
+}
+
+void process_pipe_handler_error (struct instance *o, int component, int code)
+{
+    ASSERT(o->pipe_fd >= 0)
+    
+    if (code == STREAMSOCKETSOURCE_ERROR_CLOSED) {
+        ModuleLog(o->i, BLOG_INFO, "pipe eof");
+    } else {
+        ModuleLog(o->i, BLOG_ERROR, "pipe error");
+    }
+    
+    // free pipe reading
+    free_pipe(o);
+    
+    // close pipe read end
+    ASSERT_FORCE(close(o->pipe_fd) == 0)
+    
+    // forget pipe
+    o->pipe_fd = -1;
+}
+
+void process_pipe_handler_send (struct instance *o, uint8_t *data, int data_len)
+{
+    ASSERT(o->pipe_fd >= 0)
+    ASSERT(data_len > 0)
+    
+    // accept packet
+    PacketPassInterface_Done(&o->pipe_input);
+    
+    if (o->dying) {
+        return;
+    }
+    
+    if (data_begins_with(data, data_len, EVENT_STRING_CONNECTED)) {
+        ModuleLog(o->i, BLOG_INFO, "connected event");
+        
+        if (!o->up) {
+            o->up = 1;
+            NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
+        }
+    }
+    else if (data_begins_with(data, data_len, EVENT_STRING_DISCONNECTED)) {
+        ModuleLog(o->i, BLOG_INFO, "disconnected event");
+        
+        if (o->up) {
+            o->up = 0;
+            NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_DOWN);
+        }
+    }
+}
+
+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;
+    }
+    
+    // init arguments
+    o->i = i;
+    
+    // read arguments
+    NCDValue *ifname_arg;
+    NCDValue *conf_arg;
+    NCDValue *exec_arg;
+    NCDValue *args_arg;
+    if (!NCDValue_ListRead(o->i->args, 4, &ifname_arg, &conf_arg, &exec_arg, &args_arg)) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong arity");
+        goto fail1;
+    }
+    if (NCDValue_Type(ifname_arg) != NCDVALUE_STRING || NCDValue_Type(conf_arg) != NCDVALUE_STRING ||
+        NCDValue_Type(exec_arg) != NCDVALUE_STRING || NCDValue_Type(args_arg) != NCDVALUE_LIST) {
+        ModuleLog(o->i, BLOG_ERROR, "wrong type");
+        goto fail1;
+    }
+    o->ifname = NCDValue_StringValue(ifname_arg);
+    o->conf = NCDValue_StringValue(conf_arg);
+    o->exec = NCDValue_StringValue(exec_arg);
+    o->args = args_arg;
+    
+    // set not dying
+    o->dying = 0;
+    
+    // set not up
+    o->up = 0;
+    
+    // create pipe
+    int pipefds[2];
+    if (pipe(pipefds) < 0) {
+        ModuleLog(o->i, BLOG_ERROR, "pipe failed");
+        goto fail1;
+    }
+    
+    // init pipe reading
+    if (!init_pipe(o, pipefds[0])) {
+        goto fail2;
+    }
+    
+    // build process cmdline
+    CmdLine c;
+    if (!build_cmdline(o, &c)) {
+        ModuleLog(o->i, BLOG_ERROR, "failed to build cmdline");
+        goto fail3;
+    }
+    
+    // start process
+    int fds[] = { pipefds[1], -1 };
+    int fds_map[] = { 1 };
+    if (!BProcess_InitWithFds(&o->process, o->i->manager, (BProcess_handler)process_handler, o, ((char **)c.arr.v)[0], (char **)c.arr.v, NULL, fds, fds_map)) {
+        ModuleLog(o->i, BLOG_ERROR, "BProcess_Init failed");
+        goto fail4;
+    }
+    
+    // remember pipe read end
+    o->pipe_fd = pipefds[0];
+    
+    CmdLine_Free(&c);
+    ASSERT_FORCE(close(pipefds[1]) == 0)
+    
+    return o;
+    
+fail4:
+    CmdLine_Free(&c);
+fail3:
+    free_pipe(o);
+fail2:
+    ASSERT_FORCE(close(pipefds[0]) == 0)
+    ASSERT_FORCE(close(pipefds[1]) == 0)
+fail1:
+    free(o);
+fail0:
+    return NULL;
+}
+
+static void func_free (void *vo)
+{
+    struct instance *o = vo;
+    
+    if (!o->dying) {
+        // kill process
+        BProcess_Kill(&o->process);
+    }
+    
+    // free process
+    BProcess_Free(&o->process);
+    
+    if (o->pipe_fd >= 0) {
+        // free pipe reading
+        free_pipe(o);
+        
+        // close pipe read end
+        ASSERT_FORCE(close(o->pipe_fd) == 0)
+    }
+    
+    // free instance
+    free(o);
+}
+
+static void func_die (void *vo)
+{
+    struct instance *o = vo;
+    ASSERT(!o->dying)
+    
+    // request termination
+    BProcess_Terminate(&o->process);
+    
+    // remember dying
+    o->dying = 1;
+}
+
+static const struct NCDModule modules[] = {
+    {
+        .type = "net.backend.wpa_supplicant",
+        .func_new = func_new,
+        .func_free = func_free,
+        .func_die = func_die
+    }, {
+        .type = NULL
+    }
+};
+
+const struct NCDModuleGroup ncdmodule_net_backend_wpa_supplicant = {
+    .modules = modules
+};