Przeglądaj źródła

client: implement relaying with DPRelay which makes use of DataProtoDevice

ambrop7 15 lat temu
rodzic
commit
c1d4b93336

+ 1 - 0
blog_channels.txt

@@ -42,3 +42,4 @@ PRStreamSink 4
 PRStreamSource 4
 BSocketPRFileDesc 4
 PacketProtoDecoder 4
+DPRelay 4

+ 1 - 0
client/CMakeLists.txt

@@ -6,6 +6,7 @@ add_executable(badvpn-client
     DataProto.c
     PasswordSender.c
     FrameDecider.c
+    DPRelay.c
 )
 target_link_libraries(badvpn-client system flow tuntap server_conection security ${NSPR_LIBRARIES} ${NSS_LIBRARIES})
 

+ 288 - 0
client/DPRelay.c

@@ -0,0 +1,288 @@
+/**
+ * @file DPRelay.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 <stdlib.h>
+#include <string.h>
+
+#include <misc/offset.h>
+#include <system/BLog.h>
+
+#include <client/DPRelay.h>
+
+#include <generated/blog_channel_DPRelay.h>
+
+static struct DPRelay_flow * create_flow (DPRelaySource *src, DPRelaySink *sink, int num_packets)
+{
+    ASSERT(num_packets > 0)
+    
+    // allocate structure
+    struct DPRelay_flow *flow = malloc(sizeof(*flow));
+    if (!flow) {
+        BLog(BLOG_ERROR, "relay flow %d->%d: malloc failed", (int)src->source_id, (int)sink->dest_id);
+        goto fail0;
+    }
+    
+    // set src and sink
+    flow->src = src;
+    flow->sink = sink;
+    
+    // init DataProtoLocalSource
+    if (!DataProtoLocalSource_Init(&flow->dpls, &src->device, src->source_id, sink->dest_id, num_packets)) {
+        BLog(BLOG_ERROR, "relay flow %d->%d: DataProtoLocalSource_Init failed", (int)src->source_id, (int)sink->dest_id);
+        goto fail1;
+    }
+    
+    // insert to source list
+    LinkedList1_Append(&src->flows_list, &flow->src_list_node);
+    
+    // insert to sink list
+    LinkedList1_Append(&sink->flows_list, &flow->sink_list_node);
+    
+    // attach flow if needed
+    if (sink->dest) {
+        DataProtoLocalSource_Attach(&flow->dpls, sink->dest);
+    }
+    
+    return flow;
+    
+fail1:
+    free(flow);
+fail0:
+    return NULL;
+}
+
+static void free_flow (struct DPRelay_flow *flow)
+{
+    // detach flow if needed
+    if (flow->sink->dest) {
+        DataProtoLocalSource_Detach(&flow->dpls);
+    }
+    
+    // remove from source's current flow
+    if (flow->src->current_flow == flow) {
+        flow->src->current_flow = NULL;
+    }
+    
+    // remove from sink list
+    LinkedList1_Remove(&flow->sink->flows_list, &flow->sink_list_node);
+    
+    // remove from source list
+    LinkedList1_Remove(&flow->src->flows_list, &flow->src_list_node);
+    
+    // free DataProtoLocalSource
+    DataProtoLocalSource_Free(&flow->dpls);
+    
+    // free structore
+    free(flow);
+}
+
+static struct DPRelay_flow * source_find_flow (DPRelaySource *o, DPRelaySink *sink)
+{
+    LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list);
+    while (node) {
+        struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, src_list_node);
+        ASSERT(flow->src == o)
+        if (flow->sink == sink) {
+            return flow;
+        }
+        node = LinkedList1Node_Next(node);
+    }
+    
+    return NULL;
+}
+
+static void source_device_handler (DPRelaySource *o, const uint8_t *frame, int frame_len)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    if (!o->current_flow) {
+        return;
+    }
+    
+    // route frame to current flow
+    DataProtoLocalSource_Route(&o->current_flow->dpls, 0);
+    
+    // set no current flow
+    o->current_flow = NULL;
+}
+
+int DPRelaySource_Init (DPRelaySource *o, peerid_t source_id, int frame_mtu, BReactor *reactor)
+{
+    ASSERT(frame_mtu >= 0)
+    ASSERT(frame_mtu <= INT_MAX - DATAPROTO_MAX_OVERHEAD)
+    
+    // init arguments
+    o->source_id = source_id;
+    o->frame_mtu = frame_mtu;
+    
+    // init BufferWriter
+    BufferWriter_Init(&o->writer, frame_mtu, BReactor_PendingGroup(reactor));
+    
+    // init DataProtoDevice
+    if (!DataProtoDevice_Init(&o->device, BufferWriter_GetOutput(&o->writer), (DataProtoDevice_handler)source_device_handler, o, reactor)) {
+        goto fail1;
+    }
+    
+    // init flows list
+    LinkedList1_Init(&o->flows_list);
+    
+    // have no current flow
+    o->current_flow = NULL;
+    
+    DebugObject_Init(&o->d_obj);
+    
+    return 1;
+    
+fail1:
+    BufferWriter_Free(&o->writer);
+    return 0;
+}
+
+void DPRelaySource_Free (DPRelaySource *o)
+{
+    DebugObject_Free(&o->d_obj);
+    
+    // free flows, detaching them if needed
+    LinkedList1Node *node;
+    while (node = LinkedList1_GetFirst(&o->flows_list)) {
+        struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, src_list_node);
+        free_flow(flow);
+    }
+    
+    ASSERT(!o->current_flow)
+    
+    // free DataProtoDevice
+    DataProtoDevice_Free(&o->device);
+    
+    // free BufferWriter
+    BufferWriter_Free(&o->writer);
+}
+
+void DPRelaySource_SubmitFrame (DPRelaySource *o, DPRelaySink *sink, uint8_t *data, int data_len, int num_packets)
+{
+    ASSERT(data_len >= 0)
+    ASSERT(data_len <= o->frame_mtu)
+    ASSERT(num_packets > 0)
+    ASSERT(!o->current_flow)
+    DebugObject_Access(&o->d_obj);
+    DebugObject_Access(&sink->d_obj);
+    
+    // get memory location
+    uint8_t *out;
+    if (!BufferWriter_StartPacket(&o->writer, &out)) {
+        BLog(BLOG_ERROR, "BufferWriter_StartPacket failed for frame %d->%d !?", (int)o->source_id, (int)sink->dest_id);
+        return;
+    }
+    
+    // write frame
+    memcpy(out, data, data_len);
+    
+    // submit frame
+    BufferWriter_EndPacket(&o->writer, data_len);
+    
+    // get a flow
+    // this comes _after_ writing the packet, in case flow initialization schedules jobs
+    struct DPRelay_flow *flow = source_find_flow(o, sink);
+    if (!flow) {
+        if (!(flow = create_flow(o, sink, num_packets))) {
+            return;
+        }
+    }
+    
+    // remember flow so we know where to route the frame in source_device_handler
+    o->current_flow = flow;
+}
+
+void DPRelaySource_PrepareFreeDestinations (DPRelaySource *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list);
+    while (node) {
+        struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, src_list_node);
+        if (flow->sink->dest) {
+            DataProtoDest_PrepareFree(flow->sink->dest);
+        }
+        node = LinkedList1Node_Next(node);
+    }
+}
+
+void DPRelaySink_Init (DPRelaySink *o, peerid_t dest_id, BReactor *reactor)
+{
+    // init arguments
+    o->dest_id = dest_id;
+    
+    // have no dest
+    o->dest = NULL;
+    
+    // init flows list
+    LinkedList1_Init(&o->flows_list);
+    
+    DebugObject_Init(&o->d_obj);
+}
+
+void DPRelaySink_Free (DPRelaySink *o)
+{
+    ASSERT(!o->dest)
+    DebugObject_Free(&o->d_obj);
+    
+    // free flows
+    LinkedList1Node *node;
+    while (node = LinkedList1_GetFirst(&o->flows_list)) {
+        struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node);
+        free_flow(flow);
+    }
+}
+
+void DPRelaySink_Attach (DPRelaySink *o, DataProtoDest *dest)
+{
+    ASSERT(!o->dest)
+    DebugObject_Access(&o->d_obj);
+    
+    // set dest
+    o->dest = dest;
+    
+    // attach flows
+    LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list);
+    while (node) {
+        struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node);
+        DataProtoLocalSource_Attach(&flow->dpls, o->dest);
+        node = LinkedList1Node_Next(node);
+    }
+}
+
+void DPRelaySink_Detach (DPRelaySink *o)
+{
+    ASSERT(o->dest)
+    DebugObject_Access(&o->d_obj);
+    
+    // set no dest
+    o->dest = NULL;
+    
+    // detach flows
+    LinkedList1Node *node = LinkedList1_GetFirst(&o->flows_list);
+    while (node) {
+        struct DPRelay_flow *flow = UPPER_OBJECT(node, struct DPRelay_flow, sink_list_node);
+        DataProtoLocalSource_Detach(&flow->dpls);
+        node = LinkedList1Node_Next(node);
+    }
+}

+ 74 - 0
client/DPRelay.h

@@ -0,0 +1,74 @@
+/**
+ * @file DPRelay.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_CLIENT_DPRELAY_H
+#define BADVPN_CLIENT_DPRELAY_H
+
+#include <stdint.h>
+#include <limits.h>
+
+#include <protocol/scproto.h>
+#include <protocol/dataproto.h>
+#include <misc/debug.h>
+#include <structure/LinkedList1.h>
+#include <system/DebugObject.h>
+#include <flow/BufferWriter.h>
+#include <client/DataProto.h>
+
+struct DPRelay_flow;
+
+typedef struct {
+    peerid_t source_id;
+    int frame_mtu;
+    BufferWriter writer;
+    DataProtoDevice device;
+    LinkedList1 flows_list;
+    struct DPRelay_flow *current_flow;
+    DebugObject d_obj;
+} DPRelaySource;
+
+typedef struct {
+    peerid_t dest_id;
+    DataProtoDest *dest;
+    LinkedList1 flows_list;
+    DebugObject d_obj;
+} DPRelaySink;
+
+struct DPRelay_flow {
+    DPRelaySource *src;
+    DPRelaySink *sink;
+    DataProtoLocalSource dpls;
+    LinkedList1Node src_list_node;
+    LinkedList1Node sink_list_node;
+};
+
+int DPRelaySource_Init (DPRelaySource *o, peerid_t source_id, int frame_mtu, BReactor *reactor) WARN_UNUSED;
+void DPRelaySource_Free (DPRelaySource *o);
+void DPRelaySource_SubmitFrame (DPRelaySource *o, DPRelaySink *sink, uint8_t *data, int data_len, int num_packets);
+void DPRelaySource_PrepareFreeDestinations (DPRelaySource *o);
+
+void DPRelaySink_Init (DPRelaySink *o, peerid_t dest_id, BReactor *reactor);
+void DPRelaySink_Free (DPRelaySink *o);
+void DPRelaySink_Attach (DPRelaySink *o, DataProtoDest *dest);
+void DPRelaySink_Detach (DPRelaySink *o);
+
+#endif

+ 23 - 11
client/client.c

@@ -555,7 +555,7 @@ int main (int argc, char *argv[])
         }
         
         // free relay source
-        DataProtoRelaySource_FreeRelease(&peer->relay_source);
+        DPRelaySource_PrepareFreeDestinations(&peer->relay_source);
         
         // deallocate peer
         peer_dealloc(peer);
@@ -1231,7 +1231,12 @@ int peer_add (peerid_t id, int flags, const uint8_t *cert, int cert_len)
     PacketPassInterface_Sender_Init(peer->local_recv_if, (PacketPassInterface_handler_done)local_recv_qflow_output_handler_done, peer);
     
     // init relay source
-    DataProtoRelaySource_Init(&peer->relay_source, peer->id);
+    if (!DPRelaySource_Init(&peer->relay_source, peer->id, device.mtu, &ss)) {
+        goto fail2;
+    }
+    
+    // init relay sink
+    DPRelaySink_Init(&peer->relay_sink, peer->id, &ss);
     
     // have no link
     peer->have_link = 0;
@@ -1278,7 +1283,9 @@ int peer_add (peerid_t id, int flags, const uint8_t *cert, int cert_len)
     }
     
 fail5:
-    DataProtoRelaySource_Free(&peer->relay_source);
+    DPRelaySink_Free(&peer->relay_sink);
+    DPRelaySource_Free(&peer->relay_source);
+fail2:
     PacketPassFairQueueFlow_Free(&peer->local_recv_qflow);
     DataProtoLocalSource_Free(&peer->local_dpflow);
 fail1:
@@ -1302,9 +1309,6 @@ void peer_remove (struct peer_data *peer)
         peer_dealloc_relay_provider(peer);
     }
     
-    // release relay flows
-    DataProtoRelaySource_Release(&peer->relay_source);
-    
     // release local receive flow
     if (PacketPassFairQueueFlow_IsBusy(&peer->local_recv_qflow)) {
         PacketPassFairQueueFlow_Release(&peer->local_recv_qflow);
@@ -1321,7 +1325,6 @@ void peer_dealloc (struct peer_data *peer)
 {
     ASSERT(!peer->have_relaying)
     ASSERT(!peer->is_relay)
-    DataProtoRelaySource_AssertFree(&peer->relay_source);
     PacketPassFairQueueFlow_AssertFree(&peer->local_recv_qflow);
     
     LinkedList2Iterator it;
@@ -1355,8 +1358,11 @@ void peer_dealloc (struct peer_data *peer)
     // free retry timer
     BReactor_RemoveTimer(&ss, &peer->reset_timer);
     
+    // free relay sink
+    DPRelaySink_Free(&peer->relay_sink);
+    
     // free relay source
-    DataProtoRelaySource_Free(&peer->relay_source);
+    DPRelaySource_Free(&peer->relay_source);
     
     // free local receive flow
     PacketPassFairQueueFlow_Free(&peer->local_recv_qflow);
@@ -1435,9 +1441,12 @@ int peer_init_link (struct peer_data *peer)
         goto fail2;
     }
     
-    // attach local flow to our DataProto
+    // attach local flow to our DataProtoDest
     DataProtoLocalSource_Attach(&peer->local_dpflow, &peer->send_dp);
     
+    // attach relay sink flows to our DataProtoDest
+    DPRelaySink_Attach(&peer->relay_sink, &peer->send_dp);
+    
     peer->have_link = 1;
     
     return 1;
@@ -1464,7 +1473,10 @@ void peer_free_link (struct peer_data *peer)
     // allow detaching DataProto flows
     DataProtoDest_PrepareFree(&peer->send_dp);
     
-    // detach local flow from our DataProto
+    // detach relay sink flows from our DataProtoDest
+    DPRelaySink_Detach(&peer->relay_sink);
+    
+    // detach local flow from our DataProtoDest
     DataProtoLocalSource_Detach(&peer->local_dpflow);
     
     // free sending
@@ -2130,7 +2142,7 @@ out:
     
     // relay frame
     if (relay_dest) {
-        DataProtoDest_SubmitRelayFrame(&relay_dest->send_dp, &src_peer->relay_source, data, data_len, options.send_buffer_relay_size);
+        DPRelaySource_SubmitFrame(&src_peer->relay_source, &relay_dest->relay_sink, data, data_len, options.send_buffer_relay_size);
     }
     
     // inform DataProto of received packet

+ 5 - 1
client/client.h

@@ -32,6 +32,7 @@
 #include <client/DatagramPeerIO.h>
 #include <client/StreamPeerIO.h>
 #include <client/DataProto.h>
+#include <client/DPRelay.h>
 #include <client/FrameDecider.h>
 
 // NOTE: all time values are in milliseconds
@@ -112,7 +113,10 @@ struct peer_data {
     PacketPassFairQueueFlow local_recv_qflow;
     
     // relay source
-    DataProtoRelaySource relay_source;
+    DPRelaySource relay_source;
+    
+    // relay sink
+    DPRelaySink relay_sink;
     
     // flag if link objects are initialized
     int have_link;

+ 4 - 0
generated/blog_channel_DPRelay.h

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

+ 2 - 1
generated/blog_channels_defines.h

@@ -42,4 +42,5 @@
 #define BLOG_CHANNEL_PRStreamSource 41
 #define BLOG_CHANNEL_BSocketPRFileDesc 42
 #define BLOG_CHANNEL_PacketProtoDecoder 43
-#define BLOG_NUM_CHANNELS 44
+#define BLOG_CHANNEL_DPRelay 44
+#define BLOG_NUM_CHANNELS 45

+ 1 - 0
generated/blog_channels_list.h

@@ -42,3 +42,4 @@
 {.name = "PRStreamSource", .loglevel = 4},
 {.name = "BSocketPRFileDesc", .loglevel = 4},
 {.name = "PacketProtoDecoder", .loglevel = 4},
+{.name = "DPRelay", .loglevel = 4},