浏览代码

add udpgw_client

ambrop7 14 年之前
父节点
当前提交
ada1041a31

+ 1 - 0
CMakeLists.txt

@@ -143,6 +143,7 @@ add_subdirectory(dhcpclient)
 add_subdirectory(ncdconfig)
 add_subdirectory(threadwork)
 add_subdirectory(stringmap)
+add_subdirectory(udpgw_client)
 if (NOT WIN32)
     add_subdirectory(ipc)
     add_subdirectory(process)

+ 4 - 0
generated/blog_channel_UdpGwClient.h

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

+ 2 - 1
generated/blog_channels_defines.h

@@ -83,4 +83,5 @@
 #define BLOG_CHANNEL_NCDInterfaceMonitor 82
 #define BLOG_CHANNEL_NCDRfkillMonitor 83
 #define BLOG_CHANNEL_udpgw 84
-#define BLOG_NUM_CHANNELS 85
+#define BLOG_CHANNEL_UdpGwClient 85
+#define BLOG_NUM_CHANNELS 86

+ 1 - 0
generated/blog_channels_list.h

@@ -83,3 +83,4 @@
 {.name = "NCDInterfaceMonitor", .loglevel = 4},
 {.name = "NCDRfkillMonitor", .loglevel = 4},
 {.name = "udpgw", .loglevel = 4},
+{.name = "UdpGwClient", .loglevel = 4},

+ 2 - 0
udpgw_client/CMakeLists.txt

@@ -0,0 +1,2 @@
+add_library(udpgw_client UdpGwClient.c)
+target_link_libraries(udpgw_client system flow)

+ 561 - 0
udpgw_client/UdpGwClient.c

@@ -0,0 +1,561 @@
+/**
+ * @file UdpGwClient.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 <misc/byteorder.h>
+#include <system/BLog.h>
+
+#include <udpgw_client/UdpGwClient.h>
+
+#include <generated/blog_channel_UdpGwClient.h>
+
+static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2);
+static int compare_addresses (BAddr v1, BAddr v2);
+static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2);
+static void free_server (UdpGwClient *o);
+static void server_error_handler (UdpGwClient *o, int component, int code);
+static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len);
+static void send_monitor_handler (UdpGwClient *o);
+static void keepalive_if_handler_done (UdpGwClient *o);
+static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr);
+static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid);
+static uint16_t find_unused_conid (UdpGwClient *o);
+static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, const uint8_t *data, int data_len);
+static void connection_free (struct UdpGwClient_connection *con);
+static void connection_first_job_handler (struct UdpGwClient_connection *con);
+static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len);
+static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr);
+
+static int uint16_comparator (void *unused, uint16_t *v1, uint16_t *v2)
+{
+    if (*v1 < *v2) {
+        return -1;
+    }
+    if (*v1 > *v2) {
+        return 1;
+    }
+    return 0;
+}
+
+static int compare_addresses (BAddr v1, BAddr v2)
+{
+    ASSERT(v1.type == BADDR_TYPE_IPV4)
+    ASSERT(v2.type == BADDR_TYPE_IPV4)
+    
+    if (v1.ipv4.port < v2.ipv4.port) {
+        return -1;
+    }
+    if (v1.ipv4.port > v2.ipv4.port) {
+        return 1;
+    }
+    if (v1.ipv4.ip < v2.ipv4.ip) {
+        return -1;
+    }
+    if (v1.ipv4.ip > v2.ipv4.ip) {
+        return 1;
+    }
+    return 0;
+}
+
+static int conaddr_comparator (void *unused, struct UdpGwClient_conaddr *v1, struct UdpGwClient_conaddr *v2)
+{
+    ASSERT(v1->local_addr.type == BADDR_TYPE_IPV4)
+    ASSERT(v1->remote_addr.type == BADDR_TYPE_IPV4)
+    ASSERT(v2->local_addr.type == BADDR_TYPE_IPV4)
+    ASSERT(v2->remote_addr.type == BADDR_TYPE_IPV4)
+    
+    int r = compare_addresses(v1->remote_addr, v2->remote_addr);
+    if (r) {
+        return r;
+    }
+    return compare_addresses(v1->local_addr, v2->local_addr);
+}
+
+static void free_server (UdpGwClient *o)
+{
+    // disconnect send connector
+    PacketPassConnector_DisconnectOutput(&o->send_connector);
+    
+    // free send sender
+    PacketStreamSender_Free(&o->send_sender);
+    
+    // free receive decoder
+    PacketProtoDecoder_Free(&o->recv_decoder);
+    
+    // free receive interface
+    PacketPassInterface_Free(&o->recv_if);
+}
+
+static void server_error_handler (UdpGwClient *o, int component, int code)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->have_server)
+    
+    BLog(BLOG_ERROR, "decoder error");
+    
+    // report error
+    o->handler_servererror(o->user);
+    return;
+}
+
+static void recv_interface_handler_send (UdpGwClient *o, uint8_t *data, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->have_server)
+    ASSERT(data_len >= 0)
+    ASSERT(data_len <= o->udpgw_mtu)
+    
+    // accept packet
+    PacketPassInterface_Done(&o->recv_if);
+    
+    // check header
+    if (data_len < sizeof(struct udpgw_header)) {
+        BLog(BLOG_ERROR, "missing header");
+        return;
+    }
+    struct udpgw_header *header = (struct udpgw_header *)data;
+    data += sizeof(*header);
+    data_len -= sizeof(*header);
+    uint8_t flags = ltoh8(header->flags);
+    uint16_t conid = ltoh16(header->conid);
+    
+    // check remaining data
+    if (data_len > o->udp_mtu) {
+        BLog(BLOG_ERROR, "too much data");
+        return;
+    }
+    
+    // find connection
+    struct UdpGwClient_connection *con = find_connection_by_conid(o, conid);
+    if (!con) {
+        BLog(BLOG_ERROR, "unknown conid");
+        return;
+    }
+    
+    // check remote address
+    if (con->conaddr.remote_addr.ipv4.port != header->addr_port || con->conaddr.remote_addr.ipv4.ip != header->addr_ip) {
+        BLog(BLOG_ERROR, "wrong remote address");
+        return;
+    }
+    
+    // move connection to front of the list
+    LinkedList1_Remove(&o->connections_list, &con->connections_list_node);
+    LinkedList1_Append(&o->connections_list, &con->connections_list_node);
+    
+    // pass packet to user
+    o->handler_received(o->user, con->conaddr.local_addr, con->conaddr.remote_addr, data, data_len);
+    return;
+}
+
+static void send_monitor_handler (UdpGwClient *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    if (o->keepalive_sending) {
+        return;
+    }
+    
+    BLog(BLOG_INFO, "keepalive");
+    
+    // send keepalive
+    PacketPassInterface_Sender_Send(o->keepalive_if, (uint8_t *)&o->keepalive_packet, sizeof(o->keepalive_packet));
+    
+    // set sending keep-alive
+    o->keepalive_sending = 1;
+}
+
+static void keepalive_if_handler_done (UdpGwClient *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->keepalive_sending)
+    
+    // set not sending keepalive
+    o->keepalive_sending = 0;
+}
+
+static struct UdpGwClient_connection * find_connection_by_conaddr (UdpGwClient *o, struct UdpGwClient_conaddr conaddr)
+{
+    BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conaddr, &conaddr);
+    if (!tree_node) {
+        return NULL;
+    }
+    
+    return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conaddr_node);
+}
+
+static struct UdpGwClient_connection * find_connection_by_conid (UdpGwClient *o, uint16_t conid)
+{
+    BAVLNode *tree_node = BAVL_LookupExact(&o->connections_tree_by_conid, &conid);
+    if (!tree_node) {
+        return NULL;
+    }
+    
+    return UPPER_OBJECT(tree_node, struct UdpGwClient_connection, connections_tree_by_conid_node);
+}
+
+static uint16_t find_unused_conid (UdpGwClient *o)
+{
+    ASSERT(o->num_connections < o->max_connections)
+    
+    while (1) {
+        if (!find_connection_by_conid(o, o->next_conid)) {
+            return o->next_conid;
+        }
+        
+        if (o->next_conid == o->max_connections - 1) {
+            o->next_conid = 0;
+        } else {
+            o->next_conid++;
+        }
+    }
+}
+
+static void connection_init (UdpGwClient *o, struct UdpGwClient_conaddr conaddr, const uint8_t *data, int data_len)
+{
+    ASSERT(o->num_connections < o->max_connections)
+    ASSERT(!find_connection_by_conaddr(o, conaddr))
+    ASSERT(data_len >= 0)
+    ASSERT(data_len <= o->udp_mtu)
+    
+    // allocate structure
+    struct UdpGwClient_connection *con = malloc(sizeof(*con));
+    if (!con) {
+        BLog(BLOG_ERROR, "malloc failed");
+        goto fail0;
+    }
+    
+    // init arguments
+    con->client = o;
+    con->conaddr = conaddr;
+    con->first_data = data;
+    con->first_data_len = data_len;
+    
+    // allocate conid
+    con->conid = find_unused_conid(o);
+    
+    // init first job
+    BPending_Init(&con->first_job, BReactor_PendingGroup(o->reactor), (BPending_handler)connection_first_job_handler, con);
+    BPending_Set(&con->first_job);
+    
+    // init queue flow
+    PacketPassFairQueueFlow_Init(&con->send_qflow, &o->send_queue);
+    
+    // init PacketProtoFlow
+    if (!PacketProtoFlow_Init(&con->send_ppflow, o->udpgw_mtu, o->send_buffer_size, PacketPassFairQueueFlow_GetInput(&con->send_qflow), BReactor_PendingGroup(o->reactor))) {
+        BLog(BLOG_ERROR, "PacketProtoFlow_Init failed");
+        goto fail1;
+    }
+    con->send_if = PacketProtoFlow_GetInput(&con->send_ppflow);
+    
+    // insert to connections tree by conaddr
+    ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL))
+    
+    // insert to connections tree by conid
+    ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node, NULL))
+    
+    // insert to connections list
+    LinkedList1_Append(&o->connections_list, &con->connections_list_node);
+    
+    // increment number of connections
+    o->num_connections++;
+    
+    return;
+    
+fail1:
+    PacketPassFairQueueFlow_Free(&con->send_qflow);
+    BPending_Free(&con->first_job);
+    free(con);
+fail0:
+    return;
+}
+
+static void connection_free (struct UdpGwClient_connection *con)
+{
+    UdpGwClient *o = con->client;
+    PacketPassFairQueueFlow_AssertFree(&con->send_qflow);
+    
+    // decrement number of connections
+    o->num_connections--;
+    
+    // remove from connections list
+    LinkedList1_Remove(&o->connections_list, &con->connections_list_node);
+    
+    // remove from connections tree by conid
+    BAVL_Remove(&o->connections_tree_by_conid, &con->connections_tree_by_conid_node);
+    
+    // remove from connections tree by conaddr
+    BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node);
+    
+    // free PacketProtoFlow
+    PacketProtoFlow_Free(&con->send_ppflow);
+    
+    // free queue flow
+    PacketPassFairQueueFlow_Free(&con->send_qflow);
+    
+    // free first job
+    BPending_Free(&con->first_job);
+    
+    // free structure
+    free(con);
+}
+
+static void connection_first_job_handler (struct UdpGwClient_connection *con)
+{
+    connection_send(con, UDPGW_CLIENT_FLAG_REBIND, con->first_data, con->first_data_len);
+}
+
+static void connection_send (struct UdpGwClient_connection *con, uint8_t flags, const uint8_t *data, int data_len)
+{
+    UdpGwClient *o = con->client;
+    ASSERT(data_len >= 0)
+    ASSERT(data_len <= o->udp_mtu)
+    
+    // get buffer location
+    uint8_t *out;
+    if (!BufferWriter_StartPacket(con->send_if, &out)) {
+        BLog(BLOG_ERROR, "out of buffer");
+        return;
+    }
+    
+    // write header
+    struct udpgw_header *header = (struct udpgw_header *)out;
+    header->flags = ltoh8(flags);
+    header->conid = ltoh16(con->conid);
+    header->addr_ip = con->conaddr.remote_addr.ipv4.ip;
+    header->addr_port = con->conaddr.remote_addr.ipv4.port;
+    
+    // write packet to buffer
+    memcpy(out + sizeof(*header), data, data_len);
+    
+    // submit packet to buffer
+    BufferWriter_EndPacket(con->send_if, sizeof(*header) + data_len);
+}
+
+static struct UdpGwClient_connection * reuse_connection (UdpGwClient *o, struct UdpGwClient_conaddr conaddr)
+{
+    ASSERT(!find_connection_by_conaddr(o, conaddr))
+    ASSERT(o->num_connections > 0)
+    
+    // get least recently used connection
+    struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node);
+    
+    // remove from connections tree by conaddr
+    BAVL_Remove(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node);
+    
+    // set new conaddr
+    con->conaddr = conaddr;
+    
+    // insert to connections tree by conaddr
+    ASSERT_EXECUTE(BAVL_Insert(&o->connections_tree_by_conaddr, &con->connections_tree_by_conaddr_node, NULL))
+    
+    return con;
+}
+
+void UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user,
+                       UdpGwClient_handler_servererror handler_servererror,
+                       UdpGwClient_handler_received handler_received)
+{
+    ASSERT(udp_mtu >= 0)
+    ASSERT(udpgw_compute_mtu(udp_mtu) >= 0)
+    ASSERT(udpgw_compute_mtu(udp_mtu) <= PACKETPROTO_MAXPAYLOAD)
+    ASSERT(max_connections > 0)
+    ASSERT(send_buffer_size > 0)
+    
+    // init arguments
+    o->udp_mtu = udp_mtu;
+    o->max_connections = max_connections;
+    o->send_buffer_size = send_buffer_size;
+    o->keepalive_time = keepalive_time;
+    o->reactor = reactor;
+    o->user = user;
+    o->handler_servererror = handler_servererror;
+    o->handler_received = handler_received;
+    
+    // limit max connections to number of conid's
+    if (o->max_connections > UINT16_MAX + 1) {
+        o->max_connections = UINT16_MAX + 1;
+    }
+    
+    // compute MTUs
+    o->udpgw_mtu = udpgw_compute_mtu(o->udp_mtu);
+    o->pp_mtu = o->udpgw_mtu + sizeof(struct packetproto_header);
+    
+    // init connections tree by conaddr
+    BAVL_Init(&o->connections_tree_by_conaddr, OFFSET_DIFF(struct UdpGwClient_connection, conaddr, connections_tree_by_conaddr_node), (BAVL_comparator)conaddr_comparator, NULL);
+    
+    // init connections tree by conid
+    BAVL_Init(&o->connections_tree_by_conid, OFFSET_DIFF(struct UdpGwClient_connection, conid, connections_tree_by_conid_node), (BAVL_comparator)uint16_comparator, NULL);
+    
+    // init connections list
+    LinkedList1_Init(&o->connections_list);
+    
+    // set zero connections
+    o->num_connections = 0;
+    
+    // set next conid
+    o->next_conid = 0;
+    
+    // init send connector
+    PacketPassConnector_Init(&o->send_connector, o->pp_mtu, BReactor_PendingGroup(o->reactor));
+    
+    // init send monitor
+    PacketPassInactivityMonitor_Init(&o->send_monitor, PacketPassConnector_GetInput(&o->send_connector), o->reactor, o->keepalive_time, (PacketPassInactivityMonitor_handler)send_monitor_handler, o);
+    
+    // init send queue
+    PacketPassFairQueue_Init(&o->send_queue, PacketPassInactivityMonitor_GetInput(&o->send_monitor), BReactor_PendingGroup(o->reactor), 0, 1);
+    
+    // construct keepalive packet
+    o->keepalive_packet.pp.len = sizeof(o->keepalive_packet.udpgw);
+    memset(&o->keepalive_packet.udpgw, 0, sizeof(o->keepalive_packet.udpgw));
+    o->keepalive_packet.udpgw.flags = UDPGW_CLIENT_FLAG_KEEPALIVE;
+    
+    // init keepalive queue flow
+    PacketPassFairQueueFlow_Init(&o->keepalive_qflow, &o->send_queue);
+    o->keepalive_if = PacketPassFairQueueFlow_GetInput(&o->keepalive_qflow);
+    
+    // init keepalive output
+    PacketPassInterface_Sender_Init(o->keepalive_if, (PacketPassInterface_handler_done)keepalive_if_handler_done, o);
+    
+    // set not sending keepalive
+    o->keepalive_sending = 0;
+    
+    // set have no server
+    o->have_server = 0;
+    
+    DebugObject_Init(&o->d_obj);
+}
+
+void UdpGwClient_Free (UdpGwClient *o)
+{
+    DebugObject_Free(&o->d_obj);
+    
+    // allow freeing send queue flows
+    PacketPassFairQueue_PrepareFree(&o->send_queue);
+    
+    // free connections
+    while (!LinkedList1_IsEmpty(&o->connections_list)) {
+        struct UdpGwClient_connection *con = UPPER_OBJECT(LinkedList1_GetFirst(&o->connections_list), struct UdpGwClient_connection, connections_list_node);
+        connection_free(con);
+    }
+    
+    // free server
+    if (o->have_server) {
+        free_server(o);
+    }
+    
+    // free keepalive queue flow
+    PacketPassFairQueueFlow_Free(&o->keepalive_qflow);
+    
+    // free send queue
+    PacketPassFairQueue_Free(&o->send_queue);
+    
+    // free send
+    PacketPassInactivityMonitor_Free(&o->send_monitor);
+    
+    // free send connector
+    PacketPassConnector_Free(&o->send_connector);
+}
+
+void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(local_addr.type == BADDR_TYPE_IPV4)
+    ASSERT(remote_addr.type == BADDR_TYPE_IPV4)
+    ASSERT(data_len >= 0)
+    ASSERT(data_len <= o->udp_mtu)
+    
+    // build conaddr
+    struct UdpGwClient_conaddr conaddr;
+    conaddr.local_addr = local_addr;
+    conaddr.remote_addr = remote_addr;
+    
+    // lookup connection
+    struct UdpGwClient_connection *con = find_connection_by_conaddr(o, conaddr);
+    
+    uint8_t flags = 0;
+    
+    // if no connection and can't create a new one, reuse the least recently used une
+    if (!con && o->num_connections == o->max_connections) {
+        con = reuse_connection(o, conaddr);
+        flags |= UDPGW_CLIENT_FLAG_REBIND;
+    }
+    
+    if (!con) {
+        // create new connection
+        connection_init(o, conaddr, data, data_len);
+    } else {
+        // move connection to front of the list
+        LinkedList1_Remove(&o->connections_list, &con->connections_list_node);
+        LinkedList1_Append(&o->connections_list, &con->connections_list_node);
+        
+        // send packet to existing connection
+        connection_send(con, flags, data, data_len);
+    }
+}
+
+int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(!o->have_server)
+    
+    // init error domain
+    FlowErrorDomain_Init(&o->domain, (FlowErrorDomain_handler)server_error_handler, o);
+    
+    // init receive interface
+    PacketPassInterface_Init(&o->recv_if, o->udpgw_mtu, (PacketPassInterface_handler_send)recv_interface_handler_send, o, BReactor_PendingGroup(o->reactor));
+    
+    // init receive decoder
+    if (!PacketProtoDecoder_Init(&o->recv_decoder, FlowErrorReporter_Create(&o->domain, 0), recv_if, &o->recv_if, BReactor_PendingGroup(o->reactor))) {
+        BLog(BLOG_ERROR, "PacketProtoDecoder_Init failed");
+        goto fail1;
+    }
+    
+    // init send sender
+    PacketStreamSender_Init(&o->send_sender, send_if, o->pp_mtu, BReactor_PendingGroup(o->reactor));
+    
+    // connect send connector
+    PacketPassConnector_ConnectOutput(&o->send_connector, PacketStreamSender_GetInput(&o->send_sender));
+    
+    // set have server
+    o->have_server = 1;
+    
+    return 1;
+    
+fail1:
+    PacketPassInterface_Free(&o->recv_if);
+    return 0;
+}
+
+void UdpGwClient_DisconnectServer (UdpGwClient *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->have_server)
+    
+    // free server
+    free_server(o);
+    
+    // set have no server
+    o->have_server = 0;
+}

+ 107 - 0
udpgw_client/UdpGwClient.h

@@ -0,0 +1,107 @@
+/**
+ * @file UdpGwClient.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_UDPGW_CLIENT_UDPGWCLIENT_H
+#define BADVPN_UDPGW_CLIENT_UDPGWCLIENT_H
+
+#include <stdint.h>
+
+#include <protocol/udpgw_proto.h>
+#include <misc/debug.h>
+#include <structure/BAVL.h>
+#include <structure/LinkedList1.h>
+#include <system/DebugObject.h>
+#include <system/BAddr.h>
+#include <system/BPending.h>
+#include <flow/PacketPassFairQueue.h>
+#include <flow/PacketStreamSender.h>
+#include <flow/PacketProtoFlow.h>
+#include <flow/PacketProtoDecoder.h>
+#include <flow/PacketPassConnector.h>
+#include <flow/PacketPassInactivityMonitor.h>
+
+typedef void (*UdpGwClient_handler_servererror) (void *user);
+typedef void (*UdpGwClient_handler_received) (void *user, BAddr local_addr, BAddr remote_addr, const char *data, int data_len);
+
+typedef struct {
+    int udp_mtu;
+    int max_connections;
+    int send_buffer_size;
+    btime_t keepalive_time;
+    BReactor *reactor;
+    void *user;
+    UdpGwClient_handler_servererror handler_servererror;
+    UdpGwClient_handler_received handler_received;
+    int udpgw_mtu;
+    int pp_mtu;
+    BAVL connections_tree_by_conaddr;
+    BAVL connections_tree_by_conid;
+    LinkedList1 connections_list;
+    int num_connections;
+    int next_conid;
+    PacketPassFairQueue send_queue;
+    PacketPassInactivityMonitor send_monitor;
+    PacketPassConnector send_connector;
+    struct {
+        struct packetproto_header pp;
+        struct udpgw_header udpgw;
+    } __attribute__((packed)) keepalive_packet;
+    PacketPassInterface *keepalive_if;
+    PacketPassFairQueueFlow keepalive_qflow;
+    int keepalive_sending;
+    int have_server;
+    FlowErrorDomain domain;
+    PacketStreamSender send_sender;
+    PacketProtoDecoder recv_decoder;
+    PacketPassInterface recv_if;
+    DebugObject d_obj;
+} UdpGwClient;
+
+struct UdpGwClient_conaddr {
+    BAddr local_addr;
+    BAddr remote_addr;
+};
+
+struct UdpGwClient_connection {
+    UdpGwClient *client;
+    struct UdpGwClient_conaddr conaddr;
+    const uint8_t *first_data;
+    int first_data_len;
+    uint16_t conid;
+    BPending first_job;
+    BufferWriter *send_if;
+    PacketProtoFlow send_ppflow;
+    PacketPassFairQueueFlow send_qflow;
+    BAVLNode connections_tree_by_conaddr_node;
+    BAVLNode connections_tree_by_conid_node;
+    LinkedList1Node connections_list_node;
+};
+
+void UdpGwClient_Init (UdpGwClient *o, int udp_mtu, int max_connections, int send_buffer_size, btime_t keepalive_time, BReactor *reactor, void *user,
+                       UdpGwClient_handler_servererror handler_servererror,
+                       UdpGwClient_handler_received handler_received);
+void UdpGwClient_Free (UdpGwClient *o);
+void UdpGwClient_SubmitPacket (UdpGwClient *o, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len);
+int UdpGwClient_ConnectServer (UdpGwClient *o, StreamPassInterface *send_if, StreamRecvInterface *recv_if) WARN_UNUSED;
+void UdpGwClient_DisconnectServer (UdpGwClient *o);
+
+#endif