From f3014d25c8b6f8cc1cf8ab48cabf6fbc8e8311d0 Mon Sep 17 00:00:00 2001 From: Guus Sliepen Date: Mon, 21 Jun 2021 23:13:50 +0200 Subject: [PATCH] Remove all support for channels. Also, only allow communication with the peer we have a connection to. Instead of using an inner SPTPS session, just send raw packets directly over the meta-connection. --- src/Makefile.am | 17 +- src/connection.h | 2 + src/devtools.c | 2 - src/meshlink-tiny++.h | 314 ------- src/meshlink-tiny.h | 287 +------ src/meshlink.c | 445 +--------- src/meshlink_internal.h | 18 - src/meta.c | 14 +- src/net.c | 21 - src/net.h | 17 - src/net_packet.c | 189 ----- src/net_setup.c | 1 - src/node.c | 3 - src/node.h | 7 - src/protocol.c | 2 + src/protocol.h | 7 +- src/protocol_auth.c | 17 +- src/protocol_key.c | 379 +-------- src/protocol_misc.c | 11 + src/route.c | 92 --- src/route.h | 28 - src/utcp-test.c | 410 ---------- src/utcp.c | 1717 --------------------------------------- src/utcp.h | 110 --- src/utcp_priv.h | 172 ---- 25 files changed, 54 insertions(+), 4228 deletions(-) delete mode 100644 src/net_packet.c delete mode 100644 src/route.c delete mode 100644 src/route.h delete mode 100644 src/utcp-test.c delete mode 100644 src/utcp.c delete mode 100644 src/utcp.h delete mode 100644 src/utcp_priv.h diff --git a/src/Makefile.am b/src/Makefile.am index af1d51c..bcd138e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,12 +25,7 @@ chacha_poly1305_SOURCES = \ chacha-poly1305/chacha-poly1305.c chacha-poly1305/chacha-poly1305.h \ chacha-poly1305/poly1305.c chacha-poly1305/poly1305.h -utcp_SOURCES = \ - utcp.c utcp.h \ - utcp_priv.h - lib_LTLIBRARIES = libmeshlink-tiny.la -EXTRA_PROGRAMS = utcp-test pkginclude_HEADERS = meshlink-tiny++.h meshlink-tiny.h @@ -55,7 +50,6 @@ libmeshlink_tiny_la_SOURCES = \ meshlink_queue.h \ meta.c meta.h \ net.c net.h \ - net_packet.c \ net_setup.c \ net_socket.c \ netutl.c netutl.h \ @@ -67,7 +61,6 @@ libmeshlink_tiny_la_SOURCES = \ protocol_edge.c \ protocol_key.c \ protocol_misc.c \ - route.c route.h \ sockaddr.h \ splay_tree.c splay_tree.h \ sptps.c sptps.h \ @@ -77,17 +70,9 @@ libmeshlink_tiny_la_SOURCES = \ xoshiro.c xoshiro.h \ devtools.c devtools.h \ $(ed25519_SOURCES) \ - $(chacha_poly1305_SOURCES) \ - $(utcp_SOURCES) - -utcp_test_SOURCES = \ - utcp-test.c \ - $(utcp_SOURCES) + $(chacha_poly1305_SOURCES) EXTRA_libmeshlink_tiny_la_DEPENDENCIES = $(srcdir)/meshlink.sym libmeshlink_tiny_la_CFLAGS = $(PTHREAD_CFLAGS) -fPIC -iquote. libmeshlink_tiny_la_LDFLAGS += $(PTHREAD_LIBS) - -utcp_test_CFLAGS = $(PTHREAD_CFLAGS) -iquote. -utcp_test_LDFLAGS = $(PTHREAD_LIBS) diff --git a/src/connection.h b/src/connection.h index d414e83..b191896 100644 --- a/src/connection.h +++ b/src/connection.h @@ -41,6 +41,7 @@ typedef struct connection_status_t { uint16_t invitation: 1; /* 1 if this is an invitation */ uint16_t invitation_used: 1; /* 1 if the invitation has been consumed */ uint16_t initiator: 1; /* 1 if we initiated this connection */ + uint16_t raw_packet: 1; /* 1 if we are expecting a raw packet next */ } connection_status_t; #include "ecdsa.h" @@ -63,6 +64,7 @@ typedef struct connection_t { struct buffer_t outbuf; io_t io; /* input/output event on this metadata connection */ int allow_request; /* defined if there's only one request possible */ + uint16_t packet_len; /* length of a raw packet being received */ time_t last_ping_time; /* last time we saw some activity from the other end or pinged them */ time_t last_key_renewal; /* last time we renewed the SPTPS key */ diff --git a/src/devtools.c b/src/devtools.c index 1b08d42..1a64721 100644 --- a/src/devtools.c +++ b/src/devtools.c @@ -79,8 +79,6 @@ void devtool_force_sptps_renewal(meshlink_handle_t *mesh, meshlink_node_t *node) node_t *n = (node_t *)node; connection_t *c = n->connection; - n->last_req_key = -3600; - if(c) { c->last_key_renewal = -3600; } diff --git a/src/meshlink-tiny++.h b/src/meshlink-tiny++.h index cfcf2b9..4793ea0 100644 --- a/src/meshlink-tiny++.h +++ b/src/meshlink-tiny++.h @@ -26,7 +26,6 @@ namespace meshlink { class mesh; class node; -class channel; /// Severity of log messages generated by MeshLink. typedef meshlink_log_level_t log_level_t; @@ -70,58 +69,10 @@ typedef void (*duplicate_cb_t)(mesh *mesh, node *node); */ typedef void (*log_cb_t)(mesh *mesh, log_level_t level, const char *text); -/// A callback for listening for incoming channels. -/** @param mesh A handle which represents an instance of MeshLink. - * @param node A handle for the node that wants to open a channel. - * @param port The port number the peer wishes to connect to. - * - * @return This function should return true if the application listens for the incoming channel, false otherwise. - */ -typedef bool (*meshlink_channel_listen_cb_t)(struct meshlink_handle *mesh, struct meshlink_node *node, uint16_t port); - -/// A callback for accepting incoming channels. -/** @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the incoming channel. - * @param port The port number the peer wishes to connect to. - * @param data A pointer to a buffer containing data already received. (Not yet used.) - * @param len The length of the data. (Not yet used.) - * - * @return This function should return true if the application accepts the incoming channel, false otherwise. - * If returning false, the channel is invalid and may not be used anymore. - */ -typedef bool (*channel_accept_cb_t)(mesh *mesh, channel *channel, uint16_t port, const void *data, size_t len); - -/// A callback for receiving data from a channel. -/** @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - * @param data A pointer to a buffer containing data sent by the source. - * @param len The length of the data. - */ -typedef void (*channel_receive_cb_t)(mesh *mesh, channel *channel, const void *data, size_t len); - -/// A callback that is called when data can be send on a channel. -/** @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - * @param len The maximum length of data that is guaranteed to be accepted by a call to channel_send(). - */ -typedef void (*channel_poll_cb_t)(mesh *mesh, channel *channel, size_t len); - /// A class describing a MeshLink node. class node: public meshlink_node_t { }; -/// A class describing a MeshLink channel. -class channel: public meshlink_channel_t { -public: - static const uint32_t RELIABLE = MESHLINK_CHANNEL_RELIABLE; - static const uint32_t ORDERED = MESHLINK_CHANNEL_ORDERED; - static const uint32_t FRAMED = MESHLINK_CHANNEL_FRAMED; - static const uint32_t DROP_LATE = MESHLINK_CHANNEL_DROP_LATE; - static const uint32_t NO_PARTIAL = MESHLINK_CHANNEL_NO_PARTIAL; - static const uint32_t TCP = MESHLINK_CHANNEL_TCP; - static const uint32_t UDP = MESHLINK_CHANNEL_UDP; -}; - /// A class describing a MeshLink mesh. class mesh { public: @@ -237,87 +188,6 @@ public: (void)peer; } - /// This functions is called to determine if we are listening for incoming channels. - /** - * The function is run in MeshLink's own thread. - * It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.) - * to pass data to or from the application's thread. - * The callback should also not block itself and return as quickly as possible. - * - * @param node A handle for the node that wants to open a channel. - * @param port The port number the peer wishes to connect to. - * - * @return This function should return true if the application accepts the incoming channel, false otherwise. - */ - virtual bool channel_listen(node *node, uint16_t port) { - /* by default accept all channels */ - (void)node; - (void)port; - return true; - } - - /// This functions is called whenever another node attempts to open a channel to the local node. - /** - * If the channel is accepted, the poll_callback will be set to channel_poll and can be - * changed using set_channel_poll_cb(). Likewise, the receive callback is set to - * channel_receive(). - * - * The function is run in MeshLink's own thread. - * It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.) - * to pass data to or from the application's thread. - * The callback should also not block itself and return as quickly as possible. - * - * @param channel A handle for the incoming channel. - * @param port The port number the peer wishes to connect to. - * @param data A pointer to a buffer containing data already received. (Not yet used.) - * @param len The length of the data. (Not yet used.) - * - * @return This function should return true if the application accepts the incoming channel, false otherwise. - * If returning false, the channel is invalid and may not be used anymore. - */ - virtual bool channel_accept(channel *channel, uint16_t port, const void *data, size_t len) { - /* by default reject all channels */ - (void)channel; - (void)port; - (void)data; - (void)len; - return false; - } - - /// This function is called by Meshlink for receiving data from a channel. - /** - * The function is run in MeshLink's own thread. - * It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.) - * to pass data to or from the application's thread. - * The callback should also not block itself and return as quickly as possible. - * - * @param channel A handle for the channel. - * @param data A pointer to a buffer containing data sent by the source. - * @param len The length of the data. - */ - virtual void channel_receive(channel *channel, const void *data, size_t len) { - /* do nothing */ - (void)channel; - (void)data; - (void)len; - } - - /// This function is called by Meshlink when data can be send on a channel. - /** - * The function is run in MeshLink's own thread. - * It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.) - * to pass data to or from the application's thread. - * - * The callback should also not block itself and return as quickly as possible. - * @param channel A handle for the channel. - * @param len The maximum length of data that is guaranteed to be accepted by a call to channel_send(). - */ - virtual void channel_poll(channel *channel, size_t len) { - /* do nothing */ - (void)channel; - (void)len; - } - /// Start MeshLink. /** This function causes MeshLink to open network sockets, make outgoing connections, and * create a new thread, which will handle all network I/O. @@ -330,8 +200,6 @@ public: meshlink_set_node_duplicate_cb(handle, &node_duplicate_trampoline); meshlink_set_log_cb(handle, MESHLINK_DEBUG, &log_trampoline); meshlink_set_error_cb(handle, &error_trampoline); - meshlink_set_channel_listen_cb(handle, &channel_listen_trampoline); - meshlink_set_channel_accept_cb(handle, &channel_accept_trampoline); meshlink_set_connection_try_cb(handle, &connection_try_trampoline); return meshlink_start(handle); } @@ -542,8 +410,6 @@ public: /** This function allows the local node to forget any information it has about a node, * and if possible will remove any data it has stored on disk about the node. * - * Any open channels to this node must be closed before calling this function. - * * After this call returns, the node handle is invalid and may no longer be used, regardless * of the return value of this call. * @@ -561,144 +427,6 @@ public: return meshlink_forget_node(handle, node); } - /// Set the flags of a channel. - /** This function allows changing some of the channel flags. - * Currently only MESHLINK_CHANNEL_NO_PARTIAL and MESHLINK_CHANNEL_DROP_LATE are supported, other flags are ignored. - * These flags only affect the local side of the channel with the peer. - * The changes take effect immediately. - * - * @param channel A handle for the channel. - * @param flags A bitwise-or'd combination of flags that set the semantics for this channel. - */ - void set_channel_flags(channel *channel, uint32_t flags) { - meshlink_set_channel_flags(handle, channel, flags); - } - - /// Set the connection timeout used for channels to the given node. - /** This sets the timeout after which unresponsive channels will be reported as closed. - * The timeout is set for all current and future channels to the given node. - * - * @param node The node to set the channel timeout for. - * @param timeout The timeout in seconds after which unresponsive channels will be reported as closed. - * The default is 60 seconds. - */ - void set_node_channel_timeout(node *node, int timeout) { - meshlink_set_node_channel_timeout(handle, node, timeout); - } - - /// Open a reliable stream channel to another node. - /** This function is called whenever a remote node wants to open a channel to the local node. - * The application then has to decide whether to accept or reject this channel. - * - * This function sets the channel poll callback to channel_poll_trampoline, which in turn - * calls channel_poll. To set a different, channel-specific poll callback, use set_channel_poll_cb. - * - * @param node The node to which this channel is being initiated. - * @param port The port number the peer wishes to connect to. - * @param cb A pointer to the function which will be called when the remote node sends data to the local node. - * @param data A pointer to a buffer containing data to already queue for sending. - * @param len The length of the data. - * If len is 0, the data pointer is copied into the channel's priv member. - * @param flags A bitwise-or'd combination of flags that set the semantics for this channel. - * - * @return A handle for the channel, or NULL in case of an error. - */ - channel *channel_open(node *node, uint16_t port, channel_receive_cb_t cb, const void *data, size_t len, uint32_t flags = channel::TCP) { - channel *ch = (channel *)meshlink_channel_open_ex(handle, node, port, (meshlink_channel_receive_cb_t)cb, data, len, flags); - return ch; - } - - /// Open a reliable stream channel to another node. - /** This function is called whenever a remote node wants to open a channel to the local node. - * The application then has to decide whether to accept or reject this channel. - * - * This function sets the channel receive callback to channel_receive_trampoline, - * which in turn calls channel_receive. - * - * This function sets the channel poll callback to channel_poll_trampoline, which in turn - * calls channel_poll. To set a different, channel-specific poll callback, use set_channel_poll_cb. - * - * @param node The node to which this channel is being initiated. - * @param port The port number the peer wishes to connect to. - * @param data A pointer to a buffer containing data to already queue for sending. - * @param len The length of the data. - * If len is 0, the data pointer is copied into the channel's priv member. - * @param flags A bitwise-or'd combination of flags that set the semantics for this channel. - * - * @return A handle for the channel, or NULL in case of an error. - */ - channel *channel_open(node *node, uint16_t port, const void *data, size_t len, uint32_t flags = channel::TCP) { - channel *ch = (channel *)meshlink_channel_open_ex(handle, node, port, &channel_receive_trampoline, data, len, flags); - return ch; - } - - /// Partially close a reliable stream channel. - /** This shuts down the read or write side of a channel, or both, without closing the handle. - * It can be used to inform the remote node that the local node has finished sending all data on the channel, - * but still allows waiting for incoming data from the remote node. - * - * @param channel A handle for the channel. - * @param direction Must be one of SHUT_RD, SHUT_WR or SHUT_RDWR. - */ - void channel_shutdown(channel *channel, int direction) { - return meshlink_channel_shutdown(handle, channel, direction); - } - - /// Close a reliable stream channel. - /** This informs the remote node that the local node has finished sending all data on the channel. - * It also causes the local node to stop accepting incoming data from the remote node. - * Afterwards, the channel handle is invalid and must not be used any more. - * - * It is allowed to call this function at any time on a valid handle, even inside callback functions. - * If called with a valid handle, this function always succeeds, otherwise the result is undefined. - * - * @param channel A handle for the channel. - */ - void channel_close(meshlink_channel_t *channel) { - return meshlink_channel_close(handle, channel); - } - - /// Abort a reliable stream channel. - /** This aborts a channel. - * Data that was in the send and receive buffers is dropped, so potentially there is some data that - * was sent on this channel that will not be received by the peer. - * Afterwards, the channel handle is invalid and must not be used any more. - * - * It is allowed to call this function at any time on a valid handle, even inside callback functions. - * If called with a valid handle, this function always succeeds, otherwise the result is undefined. - * - * @param channel A handle for the channel. - */ - void channel_abort(meshlink_channel_t *channel) { - return meshlink_channel_abort(handle, channel); - } - - /// Transmit data on a channel - /** This queues data to send to the remote node. - * - * @param channel A handle for the channel. - * @param data A pointer to a buffer containing data sent by the source. - * @param len The length of the data. - * - * @return The amount of data that was queued, which can be less than len, or a negative value in case of an error. - * If MESHLINK_CHANNEL_NO_PARTIAL is set, then the result will either be len, - * 0 if the buffer is currently too full, or -1 if len is too big even for an empty buffer. - */ - ssize_t channel_send(channel *channel, void *data, size_t len) { - return meshlink_channel_send(handle, channel, data, len); - } - - /// Get the maximum segment size of a channel. - /** This returns the amount of bytes that can be sent at once for channels with UDP semantics. - * - * @param channel A handle for the channel. - * - * @return The amount of bytes in the receive buffer. - */ - size_t channel_get_mss(channel *channel) { - return meshlink_channel_get_mss(handle, channel); - }; - /// Inform MeshLink that the local network configuration might have changed /** This is intended to be used when there is no way for MeshLink to get notifications of local network changes. * It forces MeshLink to scan all network interfaces for changes in up/down status and new/removed addresses, @@ -812,48 +540,6 @@ private: that->connection_try(static_cast(peer)); } - static bool channel_listen_trampoline(meshlink_handle_t *handle, meshlink_node_t *node, uint16_t port) { - if(!(handle->priv)) { - return false; - } - - meshlink::mesh *that = static_cast(handle->priv); - return that->channel_listen(static_cast(node), port); - } - - static bool channel_accept_trampoline(meshlink_handle_t *handle, meshlink_channel *channel, uint16_t port, const void *data, size_t len) { - if(!(handle->priv)) { - return false; - } - - meshlink::mesh *that = static_cast(handle->priv); - bool accepted = that->channel_accept(static_cast(channel), port, data, len); - - if(accepted) { - meshlink_set_channel_receive_cb(handle, channel, &channel_receive_trampoline); - } - - return accepted; - } - - static void channel_receive_trampoline(meshlink_handle_t *handle, meshlink_channel *channel, const void *data, size_t len) { - if(!(handle->priv)) { - return; - } - - meshlink::mesh *that = static_cast(handle->priv); - that->channel_receive(static_cast(channel), data, len); - } - - static void channel_poll_trampoline(meshlink_handle_t *handle, meshlink_channel *channel, size_t len) { - if(!(handle->priv)) { - return; - } - - meshlink::mesh *that = static_cast(handle->priv); - that->channel_poll(static_cast(channel), len); - } - meshlink_handle_t *handle; }; diff --git a/src/meshlink-tiny.h b/src/meshlink-tiny.h index 8ba8add..5cb9c5e 100644 --- a/src/meshlink-tiny.h +++ b/src/meshlink-tiny.h @@ -48,9 +48,6 @@ typedef struct meshlink_handle meshlink_handle_t; /// A handle for a MeshLink node. typedef struct meshlink_node meshlink_node_t; -/// A handle for a MeshLink channel. -typedef struct meshlink_channel meshlink_channel_t; - /// A struct containing all parameters used for opening a mesh. typedef struct meshlink_open_params meshlink_open_params_t; @@ -94,15 +91,6 @@ static const uint32_t MESHLINK_INVITE_IPV4 = 4; // Only use IPv4 addresses i static const uint32_t MESHLINK_INVITE_IPV6 = 8; // Only use IPv6 addresses in the URL static const uint32_t MESHLINK_INVITE_NUMERIC = 16; // Don't look up hostnames -/// Channel flags -static const uint32_t MESHLINK_CHANNEL_RELIABLE = 1; // Data is retransmitted when packets are lost. -static const uint32_t MESHLINK_CHANNEL_ORDERED = 2; // Data is delivered in-order to the application. -static const uint32_t MESHLINK_CHANNEL_FRAMED = 4; // Data is delivered in chunks of the same length as data was originally sent. -static const uint32_t MESHLINK_CHANNEL_DROP_LATE = 8; // When packets are reordered, late packets are ignored. -static const uint32_t MESHLINK_CHANNEL_NO_PARTIAL = 16; // Calls to meshlink_channel_send() will either send all data or nothing. -static const uint32_t MESHLINK_CHANNEL_TCP = 3; // Select TCP semantics. -static const uint32_t MESHLINK_CHANNEL_UDP = 0; // Select UDP semantics. - /// A variable holding the last encountered error from MeshLink. /** This is a thread local variable that contains the error code of the most recent error * encountered by a MeshLink API function called in the current thread. @@ -123,11 +111,6 @@ struct meshlink_node { void *priv; ///< Private pointer which may be set freely by the application, and is never used or modified by MeshLink. }; -struct meshlink_channel { - struct meshlink_node *const node; ///< Pointer to the peer of this channel. - void *priv; ///< Private pointer which may be set freely by the application, and is never used or modified by MeshLink. -}; - #endif // MESHLINK_INTERNAL_H /// Get the text for the given MeshLink error code. @@ -333,7 +316,6 @@ bool meshlink_start(struct meshlink_handle *mesh) __attribute__((__warn_unused_r * close all sockets, and shut down its own thread. * * This function always succeeds. It is allowed to call meshlink_stop() even if MeshLink is already stopped or has never been started. - * Channels that are still open will remain valid, but any communication via channels will stop as well. * * \memberof meshlink_handle * @param mesh A handle which represents an instance of MeshLink. @@ -342,8 +324,8 @@ void meshlink_stop(struct meshlink_handle *mesh); /// Close the MeshLink handle. /** This function calls meshlink_stop() if necessary, - * and frees the struct meshlink_handle and all associacted memory allocated by MeshLink, including all channels. - * Afterwards, the handle and any pointers to a struct meshlink_node or struct meshlink_channel are invalid. + * and frees the struct meshlink_handle and all associacted memory allocated by MeshLink. + * Afterwards, the handle and any pointers to a struct meshlink_node are invalid. * * It is allowed to call this function at any time on a valid handle, except inside callback functions. * If called at a proper time with a valid handle, this function always succeeds. @@ -816,8 +798,6 @@ bool meshlink_import(struct meshlink_handle *mesh, const char *data) __attribute /** This function allows the local node to forget any information it has about a node, * and if possible will remove any data it has stored on disk about the node. * - * Any open channels to this node must be closed before calling this function. - * * After this call returns, the node handle is invalid and may no longer be used, regardless * of the return value of this call. * @@ -834,269 +814,6 @@ bool meshlink_import(struct meshlink_handle *mesh, const char *data) __attribute */ bool meshlink_forget_node(struct meshlink_handle *mesh, struct meshlink_node *node); -/// A callback for listening for incoming channels. -/** This function is called whenever a remote node wants to open a channel to the local node. - * This callback should only make a decision whether to accept or reject this channel. - * The accept callback should be set to get a handle to the actual channel. - * - * The callback is run in MeshLink's own thread. - * It is therefore important that the callback return quickly and uses apprioriate methods (queues, pipes, locking, etc.) - * to hand any data over to the application's thread. - * - * @param mesh A handle which represents an instance of MeshLink. - * @param node A handle for the node that wants to open a channel. - * @param port The port number the peer wishes to connect to. - * - * @return This function should return true if the application accepts the incoming channel, false otherwise. - */ -typedef bool (*meshlink_channel_listen_cb_t)(struct meshlink_handle *mesh, struct meshlink_node *node, uint16_t port); - -/// A callback for accepting incoming channels. -/** This function is called whenever a remote node has opened a channel to the local node. - * - * The callback is run in MeshLink's own thread. - * It is therefore important that the callback return quickly and uses apprioriate methods (queues, pipes, locking, etc.) - * to hand any data over to the application's thread. - * - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the incoming channel. - * If the application accepts the incoming channel by returning true, - * then this handle is valid until meshlink_channel_close() is called on it. - * If the application rejects the incoming channel by returning false, - * then this handle is invalid after the callback returns - * (the callback does not need to call meshlink_channel_close() itself in this case). - * @param port The port number the peer wishes to connect to. - * @param data A pointer to a buffer containing data already received, or NULL in case no data has been received yet. (Not yet used.) - * The pointer is only valid during the lifetime of the callback. - * The callback should mempcy() the data if it needs to be available outside the callback. - * @param len The length of the data, or 0 in case no data has been received yet. (Not yet used.) - * - * @return This function should return true if the application accepts the incoming channel, false otherwise. - * If returning false, the channel is invalid and may not be used anymore. - */ -typedef bool (*meshlink_channel_accept_cb_t)(struct meshlink_handle *mesh, struct meshlink_channel *channel, uint16_t port, const void *data, size_t len); - -/// A callback for receiving data from a channel. -/** This function is called whenever data is received from a remote node on a channel. - * - * This function is also called in case the channel has been closed by the remote node, or when the channel is terminated abnormally. - * In both cases, @a data will be NULL and @a len will be 0, and meshlink_errno will be set. - * In any case, the @a channel handle will still be valid until the application calls meshlink_close(). - * - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - * @param data A pointer to a buffer containing data sent by the source, or NULL in case of an error. - * The pointer is only valid during the lifetime of the callback. - * The callback should mempcy() the data if it needs to be available outside the callback. - * @param len The length of the data, or 0 in case of an error. - */ -typedef void (*meshlink_channel_receive_cb_t)(struct meshlink_handle *mesh, struct meshlink_channel *channel, const void *data, size_t len); - -/// Set the listen callback. -/** This functions sets the callback that is called whenever another node wants to open a channel to the local node. - * The callback is run in MeshLink's own thread. - * It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.) - * to hand the data over to the application's thread. - * The callback should also not block itself and return as quickly as possible. - * - * If no listen or accept callbacks are set, incoming channels are rejected. - * - * \memberof meshlink_handle - * @param mesh A handle which represents an instance of MeshLink. - * @param cb A pointer to the function which will be called when another node want to open a channel. - * If a NULL pointer is given, the callback will be disabled. - */ -void meshlink_set_channel_listen_cb(struct meshlink_handle *mesh, meshlink_channel_listen_cb_t cb); - -/// Set the accept callback. -/** This functions sets the callback that is called whenever a remote node has opened a channel to the local node. - * The callback is run in MeshLink's own thread. - * It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.) - * to hand the data over to the application's thread. - * The callback should also not block itself and return as quickly as possible. - * - * If no listen or accept callbacks are set, incoming channels are rejected. - * - * \memberof meshlink_handle - * @param mesh A handle which represents an instance of MeshLink. - * @param cb A pointer to the function which will be called when a new channel has been opened by a remote node. - * If a NULL pointer is given, the callback will be disabled. - */ -void meshlink_set_channel_accept_cb(struct meshlink_handle *mesh, meshlink_channel_accept_cb_t cb); - -/// Set the receive callback. -/** This functions sets the callback that is called whenever another node sends data to the local node. - * The callback is run in MeshLink's own thread. - * It is therefore important that the callback uses apprioriate methods (queues, pipes, locking, etc.) - * to hand the data over to the application's thread. - * The callback should also not block itself and return as quickly as possible. - * - * \memberof meshlink_channel - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - * @param cb A pointer to the function which will be called when another node sends data to the local node. - * If a NULL pointer is given, the callback will be disabled and incoming data is ignored. - */ -void meshlink_set_channel_receive_cb(struct meshlink_handle *mesh, struct meshlink_channel *channel, meshlink_channel_receive_cb_t cb); - -/// Set the flags of a channel. -/** This function allows changing some of the channel flags. - * Currently only MESHLINK_CHANNEL_NO_PARTIAL and MESHLINK_CHANNEL_DROP_LATE are supported, other flags are ignored. - * These flags only affect the local side of the channel with the peer. - * The changes take effect immediately. - * - * \memberof meshlink_channel - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - * @param flags A bitwise-or'd combination of flags that set the semantics for this channel. - */ -void meshlink_set_channel_flags(struct meshlink_handle *mesh, struct meshlink_channel *channel, uint32_t flags); - -/// Open a reliable stream channel to another node. -/** This function is called whenever a remote node wants to open a channel to the local node. - * The application then has to decide whether to accept or reject this channel. - * - * This function returns a pointer to a struct meshlink_channel that will be allocated by MeshLink. - * When the application does no longer need to use this channel, it must call meshlink_close() - * to free its resources. - * - * \memberof meshlink_node - * @param mesh A handle which represents an instance of MeshLink. - * @param node The node to which this channel is being initiated. - * @param port The port number the peer wishes to connect to. - * @param cb A pointer to the function which will be called when the remote node sends data to the local node. - * The pointer may be NULL, in which case incoming data is ignored. - * @param data A pointer to a buffer containing data to already queue for sending, or NULL if there is no data to send. - * After meshlink_send() returns, the application is free to overwrite or free this buffer. - * If len is 0, the data pointer is copied into the channel's priv member. - * @param len The length of the data, or 0 if there is no data to send. - * @param flags A bitwise-or'd combination of flags that set the semantics for this channel. - * - * @return A handle for the channel, or NULL in case of an error. - * The handle is valid until meshlink_channel_close() is called. - */ -struct meshlink_channel *meshlink_channel_open_ex(struct meshlink_handle *mesh, struct meshlink_node *node, uint16_t port, meshlink_channel_receive_cb_t cb, const void *data, size_t len, uint32_t flags) __attribute__((__warn_unused_result__)); - -/// Open a reliable stream channel to another node. -/** This function is called whenever a remote node wants to open a channel to the local node. - * The application then has to decide whether to accept or reject this channel. - * - * This function returns a pointer to a struct meshlink_channel that will be allocated by MeshLink. - * When the application does no longer need to use this channel, it must call meshlink_close() - * to free its resources. - * - * Calling this function is equivalent to calling meshlink_channel_open_ex() - * with the flags set to MESHLINK_CHANNEL_TCP. - * - * \memberof meshlink_node - * @param mesh A handle which represents an instance of MeshLink. - * @param node The node to which this channel is being initiated. - * @param port The port number the peer wishes to connect to. - * @param cb A pointer to the function which will be called when the remote node sends data to the local node. - * The pointer may be NULL, in which case incoming data is ignored. - * @param data A pointer to a buffer containing data to already queue for sending, or NULL if there is no data to send. - * After meshlink_send() returns, the application is free to overwrite or free this buffer. - * @param len The length of the data, or 0 if there is no data to send. - * If len is 0, the data pointer is copied into the channel's priv member. - * - * @return A handle for the channel, or NULL in case of an error. - * The handle is valid until meshlink_channel_close() is called. - */ -struct meshlink_channel *meshlink_channel_open(struct meshlink_handle *mesh, struct meshlink_node *node, uint16_t port, meshlink_channel_receive_cb_t cb, const void *data, size_t len) __attribute__((__warn_unused_result__)); - -/// Partially close a reliable stream channel. -/** This shuts down the read or write side of a channel, or both, without closing the handle. - * It can be used to inform the remote node that the local node has finished sending all data on the channel, - * but still allows waiting for incoming data from the remote node. - * - * Shutting down the receive direction is also possible, and is equivalent to setting the receive callback to NULL. - * - * \memberof meshlink_channel - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - * @param direction Must be one of SHUT_RD, SHUT_WR or SHUT_RDWR, otherwise this call will not have any affect. - */ -void meshlink_channel_shutdown(struct meshlink_handle *mesh, struct meshlink_channel *channel, int direction); - -/// Close a reliable stream channel. -/** This informs the remote node that the local node has finished sending all data on the channel. - * It also causes the local node to stop accepting incoming data from the remote node. - * Afterwards, the channel handle is invalid and must not be used any more. - * - * It is allowed to call this function at any time on a valid handle, even inside callback functions. - * If called with a valid handle, this function always succeeds, otherwise the result is undefined. - * - * \memberof meshlink_channel - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - */ -void meshlink_channel_close(struct meshlink_handle *mesh, struct meshlink_channel *channel); - -/// Abort a reliable stream channel. -/** This aborts a channel. - * Data that was in the send and receive buffers is dropped, so potentially there is some data that - * was sent on this channel that will not be received by the peer. - * Afterwards, the channel handle is invalid and must not be used any more. - * - * It is allowed to call this function at any time on a valid handle, even inside callback functions. - * If called with a valid handle, this function always succeeds, otherwise the result is undefined. - * - * \memberof meshlink_channel - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - */ -void meshlink_channel_abort(struct meshlink_handle *mesh, struct meshlink_channel *channel); - -/// Transmit data on a channel -/** This queues data to send to the remote node. - * - * \memberof meshlink_channel - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - * @param data A pointer to a buffer containing data sent by the source, or NULL if there is no data to send. - * After meshlink_send() returns, the application is free to overwrite or free this buffer. - * @param len The length of the data, or 0 if there is no data to send. - * - * @return The amount of data that was queued, which can be less than len, or a negative value in case of an error. - * If MESHLINK_CHANNEL_NO_PARTIAL is set, then the result will either be len, - * 0 if the buffer is currently too full, or -1 if len is too big even for an empty buffer. - */ -ssize_t meshlink_channel_send(struct meshlink_handle *mesh, struct meshlink_channel *channel, const void *data, size_t len) __attribute__((__warn_unused_result__)); - -/// Get channel flags. -/** This returns the flags used when opening this channel. - * - * \memberof meshlink_channel - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - * - * @return The flags set for this channel. - */ -uint32_t meshlink_channel_get_flags(struct meshlink_handle *mesh, struct meshlink_channel *channel) __attribute__((__warn_unused_result__)); - -/// Get the maximum segment size of a channel. -/** This returns the amount of bytes that can be sent at once for channels with UDP semantics. - * - * \memberof meshlink_channel - * @param mesh A handle which represents an instance of MeshLink. - * @param channel A handle for the channel. - * - * @return The amount of bytes in the receive buffer. - */ -size_t meshlink_channel_get_mss(struct meshlink_handle *mesh, struct meshlink_channel *channel) __attribute__((__warn_unused_result__)); - -/// Set the connection timeout used for channels to the given node. -/** This sets the timeout after which unresponsive channels will be reported as closed. - * The timeout is set for all current and future channels to the given node. - * - * \memberof meshlink_node - * @param mesh A handle which represents an instance of MeshLink. - * @param node A pointer to a struct meshlink_node describing the node to set the channel connection timeout for. - * @param timeout The timeout in seconds after which unresponsive channels will be reported as closed. - * The default is 60 seconds. - */ -void meshlink_set_node_channel_timeout(struct meshlink_handle *mesh, struct meshlink_node *node, int timeout); - /// Hint that a hostname may be found at an address /** This function indicates to meshlink that the given hostname is likely found * at the given IP address and port. diff --git a/src/meshlink.c b/src/meshlink.c index c27afc8..b69c161 100644 --- a/src/meshlink.c +++ b/src/meshlink.c @@ -30,7 +30,6 @@ #include "packmsg.h" #include "prf.h" #include "protocol.h" -#include "route.h" #include "sockaddr.h" #include "utils.h" #include "xalloc.h" @@ -490,15 +489,11 @@ static bool ecdsa_keygen(meshlink_handle_t *mesh) { static struct timespec idle(event_loop_t *loop, void *data) { (void)loop; - meshlink_handle_t *mesh = data; + (void)data; - if(mesh->peer && mesh->peer->utcp) { - return utcp_timeout(mesh->peer->utcp); - } else { - return (struct timespec) { - 3600, 0 - }; - } + return (struct timespec) { + 3600, 0 + }; } static bool meshlink_setup(meshlink_handle_t *mesh) { @@ -1468,56 +1463,6 @@ void meshlink_set_error_cb(struct meshlink_handle *mesh, meshlink_error_cb_t cb) pthread_mutex_unlock(&mesh->mutex); } -static bool prepare_packet(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len, vpn_packet_t *packet) { - meshlink_packethdr_t *hdr; - - if(len > MAXSIZE - sizeof(*hdr)) { - meshlink_errno = MESHLINK_EINVAL; - return false; - } - - node_t *n = (node_t *)destination; - - if(n->status.blacklisted) { - logger(mesh, MESHLINK_ERROR, "Node %s blacklisted, dropping packet\n", n->name); - meshlink_errno = MESHLINK_EBLACKLISTED; - return false; - } - - // Prepare the packet - packet->probe = false; - packet->tcp = false; - packet->len = len + sizeof(*hdr); - - hdr = (meshlink_packethdr_t *)packet->data; - memset(hdr, 0, sizeof(*hdr)); - // leave the last byte as 0 to make sure strings are always - // null-terminated if they are longer than the buffer - strncpy((char *)hdr->destination, destination->name, sizeof(hdr->destination) - 1); - strncpy((char *)hdr->source, mesh->self->name, sizeof(hdr->source) - 1); - - memcpy(packet->data + sizeof(*hdr), data, len); - - return true; -} - -static bool meshlink_send_immediate(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { - assert(mesh); - assert(destination); - assert(data); - assert(len); - - // Prepare the packet - if(!prepare_packet(mesh, destination, data, len, mesh->packet)) { - return false; - } - - // Send it immediately - route(mesh, mesh->self, mesh->packet); - - return true; -} - bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const void *data, size_t len) { logger(mesh, MESHLINK_DEBUG, "meshlink_send(%s, %p, %zu)", destination ? destination->name : "(null)", data, len); @@ -1531,7 +1476,7 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const return true; } - if(!data) { + if(!data || len > MTU) { meshlink_errno = MESHLINK_EINVAL; return false; } @@ -1544,10 +1489,8 @@ bool meshlink_send(meshlink_handle_t *mesh, meshlink_node_t *destination, const return false; } - if(!prepare_packet(mesh, destination, data, len, packet)) { - free(packet); - return false; - } + packet->len = len; + memcpy(packet->data, data, len); // Queue it if(!meshlink_queue_push(&mesh->outpacketqueue, packet)) { @@ -1572,7 +1515,7 @@ void meshlink_send_from_queue(event_loop_t *loop, void *data) { for(vpn_packet_t *packet; (packet = meshlink_queue_pop(&mesh->outpacketqueue));) { logger(mesh, MESHLINK_DEBUG, "Removing packet of %d bytes from packet queue", packet->len); - route(mesh, mesh->self, packet); + send_raw_packet(mesh, mesh->peer->connection, packet); free(packet); } } @@ -2246,367 +2189,7 @@ void meshlink_hint_address(meshlink_handle_t *mesh, meshlink_node_t *node, const // @TODO do we want to fire off a connection attempt right away? } -static bool channel_pre_accept(struct utcp *utcp, uint16_t port) { - (void)port; - node_t *n = utcp->priv; - meshlink_handle_t *mesh = n->mesh; - - if(mesh->channel_accept_cb && mesh->channel_listen_cb) { - return mesh->channel_listen_cb(mesh, (meshlink_node_t *)n, port); - } else { - return mesh->channel_accept_cb; - } -} - -static ssize_t channel_recv(struct utcp_connection *connection, const void *data, size_t len) { - meshlink_channel_t *channel = connection->priv; - - if(!channel) { - abort(); - } - - node_t *n = channel->node; - meshlink_handle_t *mesh = n->mesh; - - if(n->status.destroyed) { - meshlink_channel_close(mesh, channel); - return len; - } - - const char *p = data; - size_t left = len; - - if(channel->receive_cb) { - channel->receive_cb(mesh, channel, p, left); - } - - return len; -} - -static void channel_accept(struct utcp_connection *utcp_connection, uint16_t port) { - node_t *n = utcp_connection->utcp->priv; - - if(!n) { - abort(); - } - - meshlink_handle_t *mesh = n->mesh; - - if(!mesh->channel_accept_cb) { - return; - } - - meshlink_channel_t *channel = xzalloc(sizeof(*channel)); - channel->node = n; - channel->c = utcp_connection; - - if(mesh->channel_accept_cb(mesh, channel, port, NULL, 0)) { - utcp_accept(utcp_connection, channel_recv, channel); - } else { - free(channel); - } -} - -static ssize_t channel_send(struct utcp *utcp, const void *data, size_t len) { - node_t *n = utcp->priv; - - if(n->status.destroyed) { - return -1; - } - - meshlink_handle_t *mesh = n->mesh; - return meshlink_send_immediate(mesh, (meshlink_node_t *)n, data, len) ? (ssize_t)len : -1; -} - -void meshlink_set_channel_receive_cb(meshlink_handle_t *mesh, meshlink_channel_t *channel, meshlink_channel_receive_cb_t cb) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_receive_cb(%p, %p)", (void *)channel, (void *)(intptr_t)cb); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - channel->receive_cb = cb; -} - -static void channel_receive(meshlink_handle_t *mesh, meshlink_node_t *source, const void *data, size_t len) { - (void)mesh; - node_t *n = (node_t *)source; - - if(!n->utcp) { - abort(); - } - - utcp_recv(n->utcp, data, len); -} - -void meshlink_set_channel_listen_cb(meshlink_handle_t *mesh, meshlink_channel_listen_cb_t cb) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_listen_cb(%p)", (void *)(intptr_t)cb); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->channel_listen_cb = cb; - - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_channel_accept_cb(meshlink_handle_t *mesh, meshlink_channel_accept_cb_t cb) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_accept_cb(%p)", (void *)(intptr_t)cb); - - if(!mesh) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - mesh->channel_accept_cb = cb; - mesh->receive_cb = channel_receive; - - if(mesh->peer) { - mesh->peer->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, mesh->peer); - } - - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_set_channel_flags(meshlink_handle_t *mesh, meshlink_channel_t *channel, uint32_t flags) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_channel_flags(%p, %u)", (void *)channel, flags); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - utcp_set_flags(channel->c, flags); - pthread_mutex_unlock(&mesh->mutex); -} - -meshlink_channel_t *meshlink_channel_open_ex(meshlink_handle_t *mesh, meshlink_node_t *node, uint16_t port, meshlink_channel_receive_cb_t cb, const void *data, size_t len, uint32_t flags) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_open_ex(%s, %u, %p, %p, %zu, %u)", node ? node->name : "(null)", port, (void *)(intptr_t)cb, data, len, flags); - - if(data && len) { - abort(); // TODO: handle non-NULL data - } - - if(!mesh || !node) { - meshlink_errno = MESHLINK_EINVAL; - return NULL; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - node_t *n = (node_t *)node; - - if(!n->utcp) { - n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n); - mesh->receive_cb = channel_receive; - - if(!n->utcp) { - meshlink_errno = errno == ENOMEM ? MESHLINK_ENOMEM : MESHLINK_EINTERNAL; - pthread_mutex_unlock(&mesh->mutex); - return NULL; - } - } - - if(n->status.blacklisted) { - logger(mesh, MESHLINK_ERROR, "Cannot open a channel with blacklisted node\n"); - meshlink_errno = MESHLINK_EBLACKLISTED; - pthread_mutex_unlock(&mesh->mutex); - return NULL; - } - - meshlink_channel_t *channel = xzalloc(sizeof(*channel)); - channel->node = n; - channel->receive_cb = cb; - - if(data && !len) { - channel->priv = (void *)data; - } - - channel->c = utcp_connect_ex(n->utcp, port, channel_recv, channel, flags); - - pthread_mutex_unlock(&mesh->mutex); - - if(!channel->c) { - meshlink_errno = errno == ENOMEM ? MESHLINK_ENOMEM : MESHLINK_EINTERNAL; - free(channel); - return NULL; - } - - return channel; -} - -meshlink_channel_t *meshlink_channel_open(meshlink_handle_t *mesh, meshlink_node_t *node, uint16_t port, meshlink_channel_receive_cb_t cb, const void *data, size_t len) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_open_ex(%s, %u, %p, %p, %zu)", node ? node->name : "(null)", port, (void *)(intptr_t)cb, data, len); - - return meshlink_channel_open_ex(mesh, node, port, cb, data, len, MESHLINK_CHANNEL_TCP); -} - -void meshlink_channel_shutdown(meshlink_handle_t *mesh, meshlink_channel_t *channel, int direction) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_shutdown(%p, %d)", (void *)channel, direction); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - utcp_shutdown(channel->c, direction); - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_channel_close(meshlink_handle_t *mesh, meshlink_channel_t *channel) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_close(%p)", (void *)channel); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - if(channel->c) { - utcp_close(channel->c); - channel->c = NULL; - } - - if(!channel->in_callback) { - free(channel); - } - - pthread_mutex_unlock(&mesh->mutex); -} - -void meshlink_channel_abort(meshlink_handle_t *mesh, meshlink_channel_t *channel) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_abort(%p)", (void *)channel); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - if(channel->c) { - utcp_abort(channel->c); - channel->c = NULL; - } - - if(!channel->in_callback) { - free(channel); - } - - pthread_mutex_unlock(&mesh->mutex); -} - -ssize_t meshlink_channel_send(meshlink_handle_t *mesh, meshlink_channel_t *channel, const void *data, size_t len) { - logger(mesh, MESHLINK_DEBUG, "meshlink_channel_send(%p, %p, %zu)", (void *)channel, data, len); - - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - if(!len) { - return 0; - } - - if(!data) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - // TODO: more finegrained locking. - // Ideally we want to put the data into the UTCP connection's send buffer. - // Then, preferably only if there is room in the receiver window, - // kick the meshlink thread to go send packets. - - ssize_t retval; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - retval = utcp_send(channel->c, data, len); - - pthread_mutex_unlock(&mesh->mutex); - - if(retval < 0) { - meshlink_errno = MESHLINK_ENETWORK; - } - - return retval; -} - -uint32_t meshlink_channel_get_flags(meshlink_handle_t *mesh, meshlink_channel_t *channel) { - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - return channel->c->flags; -} - -size_t meshlink_channel_get_mss(meshlink_handle_t *mesh, meshlink_channel_t *channel) { - if(!mesh || !channel) { - meshlink_errno = MESHLINK_EINVAL; - return -1; - } - - return utcp_get_mss(channel->node->utcp); -} - -void meshlink_set_node_channel_timeout(meshlink_handle_t *mesh, meshlink_node_t *node, int timeout) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_node_channel_timeout(%s, %d)", node ? node->name : "(null)", timeout); - - if(!mesh || !node) { - meshlink_errno = MESHLINK_EINVAL; - return; - } - - node_t *n = (node_t *)node; - - if(pthread_mutex_lock(&mesh->mutex) != 0) { - abort(); - } - - if(!n->utcp) { - n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n); - } - - utcp_set_user_timeout(n->utcp, timeout); - - pthread_mutex_unlock(&mesh->mutex); -} - void update_node_status(meshlink_handle_t *mesh, node_t *n) { - if(n->status.reachable && mesh->channel_accept_cb && !n->utcp) { - n->utcp = utcp_init(channel_accept, channel_pre_accept, channel_send, n); - } - if(mesh->node_status_cb) { mesh->node_status_cb(mesh, (meshlink_node_t *)n, n->status.reachable && !n->status.blacklisted); } @@ -2732,17 +2315,6 @@ void meshlink_set_inviter_commits_first(struct meshlink_handle *mesh, bool invit pthread_mutex_unlock(&mesh->mutex); } -void meshlink_set_scheduling_granularity(struct meshlink_handle *mesh, long granularity) { - logger(mesh, MESHLINK_DEBUG, "meshlink_set_scheduling_granularity(%ld)", granularity); - - if(!mesh || granularity < 0) { - meshlink_errno = EINVAL; - return; - } - - utcp_set_clock_granularity(granularity); -} - void meshlink_set_storage_policy(struct meshlink_handle *mesh, meshlink_storage_policy_t policy) { logger(mesh, MESHLINK_DEBUG, "meshlink_set_storage_policy(%d)", policy); @@ -2787,7 +2359,6 @@ void call_error_cb(meshlink_handle_t *mesh, meshlink_errno_t cb_errno) { static void __attribute__((constructor)) meshlink_init(void) { crypto_init(); - utcp_set_clock_granularity(10000); } static void __attribute__((destructor)) meshlink_exit(void) { diff --git a/src/meshlink_internal.h b/src/meshlink_internal.h index 56c810f..f77d700 100644 --- a/src/meshlink_internal.h +++ b/src/meshlink_internal.h @@ -111,8 +111,6 @@ struct meshlink_handle { // Infrequently used callbacks meshlink_node_status_cb_t node_status_cb; meshlink_node_status_cb_t meta_status_cb; - meshlink_channel_listen_cb_t channel_listen_cb; - meshlink_channel_accept_cb_t channel_accept_cb; meshlink_node_duplicate_cb_t node_duplicate_cb; meshlink_connection_try_cb_t connection_try_cb; meshlink_error_cb_t error_cb; @@ -150,22 +148,6 @@ struct meshlink_node { void *priv; }; -/// A channel. -struct meshlink_channel { - struct node_t *node; - void *priv; - bool in_callback; - - struct utcp_connection *c; - meshlink_channel_receive_cb_t receive_cb; -}; - -/// Header for data packets routed between nodes -typedef struct meshlink_packethdr { - uint8_t destination[16]; - uint8_t source[16]; -} __attribute__((__packed__)) meshlink_packethdr_t; - void meshlink_send_from_queue(event_loop_t *loop, void *mesh); void update_node_status(meshlink_handle_t *mesh, struct node_t *n); extern meshlink_log_level_t global_log_level; diff --git a/src/meta.c b/src/meta.c index 09edf79..12241f8 100644 --- a/src/meta.c +++ b/src/meta.c @@ -47,7 +47,7 @@ bool send_meta_sptps(void *handle, uint8_t type, const void *buffer, size_t leng bool send_meta(meshlink_handle_t *mesh, connection_t *c, const char *buffer, int length) { assert(c); assert(buffer); - assert(length); + assert(length >= 0); logger(mesh, MESHLINK_DEBUG, "Sending %d bytes of metadata to %s", length, c->name); @@ -95,6 +95,18 @@ bool receive_meta_sptps(void *handle, uint8_t type, const void *data, uint16_t l return true; } + /* Are we receiving a raw packet? */ + + if(c->status.raw_packet) { + c->status.raw_packet = false; + + if(mesh->receive_cb) { + mesh->receive_cb(mesh, (meshlink_node_t *)c->node, data, length); + } + + return true; + } + /* Change newline to null byte, just like non-SPTPS requests */ if(request[length - 1] == '\n') { diff --git a/src/net.c b/src/net.c index 101c957..d930d1f 100644 --- a/src/net.c +++ b/src/net.c @@ -101,12 +101,6 @@ static void timeout_handler(event_loop_t *loop, void *data) { } // Also make sure that if outstanding key requests for the UDP counterpart of a connection has timed out, we restart it. - if(c->node) { - if(c->node->status.waitingforkey && c->node->last_req_key + pingtimeout < mesh->loop.now.tv_sec) { - send_req_key(mesh, c->node); - } - } - if(c->status.active && c->last_key_renewal + 3600 < mesh->loop.now.tv_sec) { devtool_sptps_renewal_probe((meshlink_node_t *)c->node); @@ -191,21 +185,6 @@ static void periodic_handler(event_loop_t *loop, void *data) { logger(mesh, MESHLINK_DEBUG, "Could not update %s", n->name); } } - - if(n->status.reachable && n->status.validkey && n->last_req_key + 3600 < mesh->loop.now.tv_sec) { - logger(mesh, MESHLINK_DEBUG, "SPTPS key renewal for node %s", n->name); - devtool_sptps_renewal_probe((meshlink_node_t *)n); - - if(!sptps_force_kex(&n->sptps)) { - logger(mesh, MESHLINK_ERROR, "SPTPS key renewal for node %s failed", n->name); - n->status.validkey = false; - sptps_stop(&n->sptps); - n->status.waitingforkey = false; - n->last_req_key = -3600; - } else { - n->last_req_key = mesh->loop.now.tv_sec; - } - } } timeout_set(&mesh->loop, data, &(struct timespec) { diff --git a/src/net.h b/src/net.h index 91b9ad2..ec37eaa 100644 --- a/src/net.h +++ b/src/net.h @@ -39,23 +39,10 @@ #define MAXBUFSIZE ((MAXSIZE * 8) / 6 + 128) typedef struct vpn_packet_t { - uint16_t probe: 1; - int16_t tcp: 1; uint16_t len; /* the actual number of bytes in the `data' field */ uint8_t data[MAXSIZE]; } vpn_packet_t; -/* Packet types when using SPTPS */ - -#define PKT_COMPRESSED 1 -#define PKT_PROBE 4 - -typedef enum packet_type_t { - PACKET_NORMAL, - PACKET_COMPRESSED, - PACKET_PROBE -} packet_type_t; - #include "conf.h" #include "list.h" @@ -88,10 +75,6 @@ void handle_incoming_vpn_data(struct event_loop_t *loop, void *, int); void finish_connecting(struct meshlink_handle *mesh, struct connection_t *); void do_outgoing_connection(struct meshlink_handle *mesh, struct outgoing_t *); void handle_new_meta_connection(struct event_loop_t *loop, void *, int); -bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len); -bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) __attribute__((__warn_unused_result__)); -void send_packet(struct meshlink_handle *mesh, struct node_t *, struct vpn_packet_t *); -char *get_name(struct meshlink_handle *mesh) __attribute__((__warn_unused_result__)); void load_all_nodes(struct meshlink_handle *mesh); bool setup_myself_reloadable(struct meshlink_handle *mesh) __attribute__((__warn_unused_result__)); bool setup_network(struct meshlink_handle *mesh) __attribute__((__warn_unused_result__)); diff --git a/src/net_packet.c b/src/net_packet.c deleted file mode 100644 index 21e164a..0000000 --- a/src/net_packet.c +++ /dev/null @@ -1,189 +0,0 @@ -/* - net_packet.c -- Handles in- and outgoing VPN packets - Copyright (C) 2014-2017 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 "system.h" - -#include "conf.h" -#include "connection.h" -#include "crypto.h" -#include "logger.h" -#include "meshlink_internal.h" -#include "net.h" -#include "netutl.h" -#include "protocol.h" -#include "route.h" -#include "utils.h" -#include "xalloc.h" - -int keylifetime = 0; - -/* VPN packet I/O */ - -static void receive_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet) { - logger(mesh, MESHLINK_DEBUG, "Received packet of %d bytes from %s", packet->len, n->name); - - if(n->status.blacklisted) { - logger(mesh, MESHLINK_WARNING, "Dropping packet from blacklisted node %s", n->name); - } else { - route(mesh, n, packet); - } -} - -static void send_sptps_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *origpkt) { - if(!n->status.reachable) { - logger(mesh, MESHLINK_ERROR, "Trying to send SPTPS data to unreachable node %s", n->name); - return; - } - - if(!n->status.validkey) { - logger(mesh, MESHLINK_INFO, "No valid key known yet for %s", n->name); - - if(!n->status.waitingforkey) { - send_req_key(mesh, n); - } else if(n->last_req_key + 10 < mesh->loop.now.tv_sec) { - logger(mesh, MESHLINK_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name); - sptps_stop(&n->sptps); - n->status.waitingforkey = false; - send_req_key(mesh, n); - } - - return; - } - - uint8_t type = 0; - - // If it's a probe, send it immediately without trying to compress it. - if(origpkt->probe) { - sptps_send_record(&n->sptps, PKT_PROBE, origpkt->data, origpkt->len); - return; - } - - sptps_send_record(&n->sptps, type, origpkt->data, origpkt->len); - return; -} - -bool send_sptps_data(void *handle, uint8_t type, const void *data, size_t len) { - assert(handle); - assert(data); - assert(len); - - node_t *to = handle; - meshlink_handle_t *mesh = to->mesh; - - if(!to->status.reachable) { - logger(mesh, MESHLINK_ERROR, "Trying to send SPTPS data to unreachable node %s", to->name); - return false; - } - - - if(type == PKT_PROBE) { - /* Probe packets are not supported. */ - return false; - } - - /* Send it via TCP. */ - - char buf[len * 4 / 3 + 5]; - b64encode(data, buf, len); - - if(!to->nexthop || !to->nexthop->connection) { - logger(mesh, MESHLINK_WARNING, "Unable to forward SPTPS packet to %s via %s", to->name, to->nexthop ? to->nexthop->name : to->name); - return false; - } - - /* If no valid key is known yet, send the packets using ANS_KEY requests, - to ensure we get to learn the reflexive UDP address. */ - if(!to->status.validkey) { - return send_request(mesh, to->nexthop->connection, "%d %s %s %s -1 -1 -1 %d", ANS_KEY, mesh->self->name, to->name, buf, 0); - } else { - return send_request(mesh, to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, mesh->self->name, to->name, REQ_SPTPS, buf); - } -} - -bool receive_sptps_record(void *handle, uint8_t type, const void *data, uint16_t len) { - assert(handle); - assert(!data || len); - - node_t *from = handle; - meshlink_handle_t *mesh = from->mesh; - - if(type == SPTPS_HANDSHAKE) { - if(!from->status.validkey) { - logger(mesh, MESHLINK_INFO, "SPTPS key exchange with %s successful", from->name); - from->status.validkey = true; - from->status.waitingforkey = false; - - if(from->utcp) { - utcp_reset_timers(from->utcp); - } - } - - return true; - } - - if(len > MAXSIZE) { - logger(mesh, MESHLINK_ERROR, "Packet from %s larger than maximum supported size (%d > %d)", from->name, len, MAXSIZE); - return false; - } - - vpn_packet_t inpkt; - - if(type == PKT_PROBE) { - /* We shouldn't receive any UDP probe packets. */ - return false; - } else { - inpkt.probe = false; - } - - if(type & ~(PKT_COMPRESSED)) { - logger(mesh, MESHLINK_ERROR, "Unexpected SPTPS record type %d len %d from %s", type, len, from->name); - return false; - } - - if(type & PKT_COMPRESSED) { - logger(mesh, MESHLINK_ERROR, "Error while decompressing packet from %s", from->name); - return false; - } - - memcpy(inpkt.data, data, len); // TODO: get rid of memcpy - inpkt.len = len; - - receive_packet(mesh, from, &inpkt); - return true; -} - -/* - send a packet to the given vpn ip. -*/ -void send_packet(meshlink_handle_t *mesh, node_t *n, vpn_packet_t *packet) { - if(n == mesh->self) { - // TODO: send to application - return; - } - - logger(mesh, MESHLINK_DEBUG, "Sending packet of %d bytes to %s", packet->len, n->name); - - if(!n->status.reachable) { - logger(mesh, MESHLINK_WARNING, "Node %s is not reachable", n->name); - return; - } - - send_sptps_packet(mesh, n, packet); - return; -} diff --git a/src/net_setup.c b/src/net_setup.c index 8850f2e..7f8804b 100644 --- a/src/net_setup.c +++ b/src/net_setup.c @@ -28,7 +28,6 @@ #include "netutl.h" #include "packmsg.h" #include "protocol.h" -#include "route.h" #include "utils.h" #include "xalloc.h" diff --git a/src/node.c b/src/node.c index 01173f8..c02644d 100644 --- a/src/node.c +++ b/src/node.c @@ -51,10 +51,7 @@ node_t *new_node(void) { void free_node(node_t *n) { n->status.destroyed = true; - utcp_exit(n->utcp); - ecdsa_free(n->ecdsa); - sptps_stop(&n->sptps); free(n->name); free(n->canonical_address); diff --git a/src/node.h b/src/node.h index 7384f17..90e0c29 100644 --- a/src/node.h +++ b/src/node.h @@ -24,7 +24,6 @@ #include "meshlink_internal.h" #include "sockaddr.h" #include "sptps.h" -#include "utcp.h" typedef struct node_status_t { uint16_t validkey: 1; /* 1 if we currently have a valid key for him */ @@ -51,17 +50,11 @@ typedef struct node_t { node_status_t status; dev_class_t devclass; - // Used for packet I/O uint32_t session_id; /* Unique ID for this node's currently running process */ - sptps_t sptps; - - struct utcp *utcp; // Used for meta-connection I/O, timeouts struct meshlink_handle *mesh; /* The mesh this node belongs to */ - time_t last_req_key; - struct ecdsa *ecdsa; /* His public ECDSA key */ struct connection_t *connection; /* Connection associated with this node (if a direct connection exists) */ diff --git a/src/protocol.c b/src/protocol.c index 25419c0..61a7608 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -43,6 +43,7 @@ static bool (*request_handlers[NUM_REQUESTS])(meshlink_handle_t *, connection_t [KEY_CHANGED] = key_changed_h, [REQ_KEY] = req_key_h, [ANS_KEY] = ans_key_h, + [PACKET] = raw_packet_h, }; /* Request names */ @@ -60,6 +61,7 @@ static const char *request_name[NUM_REQUESTS] __attribute__((unused)) = { [KEY_CHANGED] = "KEY_CHANGED", [REQ_KEY] = "REQ_KEY", [ANS_KEY] = "ANS_KEY", + [PACKET] = "PACKET", }; bool check_id(const char *id) { diff --git a/src/protocol.h b/src/protocol.h index ed7e5df..02c2266 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -57,6 +57,10 @@ typedef enum request_error_t { BLACKLISTED = 1, } request_error_t; +/* Protocol support flags */ + +static const uint32_t PROTOCOL_TINY = 1; // Peer is using meshlink-tiny + /* Maximum size of strings in a request. * scanf terminates %2048s with a NUL character, * but the NUL character can be written after the 2048th non-NUL character. @@ -82,8 +86,8 @@ bool send_error(struct meshlink_handle *mesh, struct connection_t *, request_err bool send_ping(struct meshlink_handle *mesh, struct connection_t *); bool send_pong(struct meshlink_handle *mesh, struct connection_t *); bool send_add_edge(struct meshlink_handle *mesh, struct connection_t *, int); -bool send_req_key(struct meshlink_handle *mesh, struct node_t *); bool send_canonical_address(struct meshlink_handle *mesh, struct node_t *); +bool send_raw_packet(struct meshlink_handle *mesh, struct connection_t *, const vpn_packet_t *); /* Request handlers */ @@ -99,5 +103,6 @@ bool del_edge_h(struct meshlink_handle *mesh, struct connection_t *, const char bool key_changed_h(struct meshlink_handle *mesh, struct connection_t *, const char *); bool req_key_h(struct meshlink_handle *mesh, struct connection_t *, const char *); bool ans_key_h(struct meshlink_handle *mesh, struct connection_t *, const char *); +bool raw_packet_h(struct meshlink_handle *mesh, struct connection_t *, const char *); #endif diff --git a/src/protocol_auth.c b/src/protocol_auth.c index 573da14..9ffd9de 100644 --- a/src/protocol_auth.c +++ b/src/protocol_auth.c @@ -42,7 +42,7 @@ extern bool node_write_devclass(meshlink_handle_t *mesh, node_t *n); bool send_id(meshlink_handle_t *mesh, connection_t *c) { - return send_request(mesh, c, "%d %s %d.%d %s", ID, mesh->self->name, PROT_MAJOR, PROT_MINOR, mesh->appname); + return send_request(mesh, c, "%d %s %d.%d %s %u", ID, mesh->self->name, PROT_MAJOR, PROT_MINOR, mesh->appname, PROTOCOL_TINY); } bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { @@ -50,8 +50,9 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { assert(*request); char name[MAX_STRING_SIZE]; + uint32_t flags; - if(sscanf(request, "%*d " MAX_STRING " %d.%d", name, &c->protocol_major, &c->protocol_minor) < 2) { + if(sscanf(request, "%*d " MAX_STRING " %d.%d %*s %u", name, &c->protocol_major, &c->protocol_minor, &flags) < 4) { logger(mesh, MESHLINK_ERROR, "Got bad %s from %s", "ID", c->name); return false; } @@ -97,12 +98,6 @@ bool id_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { if(!node_read_public_key(mesh, n)) { logger(mesh, MESHLINK_ERROR, "No key known for peer %s", c->name); - - if(n->status.reachable && !n->status.waitingforkey) { - logger(mesh, MESHLINK_INFO, "Requesting key from peer %s", c->name); - send_req_key(mesh, n); - } - return false; } @@ -200,11 +195,5 @@ bool ack_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { n->status.reachable = true; update_node_status(mesh, c->node); - /* Request a session key to jump start UDP traffic */ - - if(c->status.initiator) { - send_req_key(mesh, n); - } - return true; } diff --git a/src/protocol_key.c b/src/protocol_key.c index 6ee2875..c278531 100644 --- a/src/protocol_key.c +++ b/src/protocol_key.c @@ -31,8 +31,6 @@ #include "utils.h" #include "xalloc.h" -static const int req_key_timeout = 2; - bool key_changed_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { (void)mesh; (void)c; @@ -40,381 +38,16 @@ bool key_changed_h(meshlink_handle_t *mesh, connection_t *c, const char *request return true; } -static bool send_initial_sptps_data(void *handle, uint8_t type, const void *data, size_t len) { - (void)type; - - assert(data); - assert(len); - - node_t *to = handle; - meshlink_handle_t *mesh = to->mesh; - - if(!to->nexthop || !to->nexthop->connection) { - logger(mesh, MESHLINK_WARNING, "Cannot send SPTPS data to %s via %s", to->name, to->nexthop ? to->nexthop->name : to->name); - return false; - } - - to->sptps.send_data = send_sptps_data; - char buf[len * 4 / 3 + 5]; - b64encode(data, buf, len); - return send_request(mesh, to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, mesh->self->name, to->name, REQ_KEY, buf); -} - -bool send_canonical_address(meshlink_handle_t *mesh, node_t *to) { - if(!mesh->self->canonical_address) { - return true; - } - - return send_request(mesh, to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, mesh->self->name, to->name, REQ_CANONICAL, mesh->self->canonical_address); -} - -bool send_req_key(meshlink_handle_t *mesh, node_t *to) { - if(!node_read_public_key(mesh, to)) { - logger(mesh, MESHLINK_DEBUG, "No ECDSA key known for %s", to->name); - - if(!to->nexthop || !to->nexthop->connection) { - logger(mesh, MESHLINK_WARNING, "Cannot send REQ_PUBKEY to %s via %s", to->name, to->nexthop ? to->nexthop->name : to->name); - return true; - } - - char *pubkey = ecdsa_get_base64_public_key(mesh->private_key); - send_request(mesh, to->nexthop->connection, "%d %s %s %d %s", REQ_KEY, mesh->self->name, to->name, REQ_PUBKEY, pubkey); - free(pubkey); - return true; - } - - if(to->sptps.label) { - logger(mesh, MESHLINK_DEBUG, "send_req_key(%s) called while sptps->label != NULL!", to->name); - } - - /* Send our canonical address to help with UDP hole punching */ - send_canonical_address(mesh, to); - - char label[sizeof(meshlink_udp_label) + strlen(mesh->self->name) + strlen(to->name) + 2]; - snprintf(label, sizeof(label), "%s %s %s", meshlink_udp_label, mesh->self->name, to->name); - sptps_stop(&to->sptps); - to->status.validkey = false; - to->status.waitingforkey = true; - to->last_req_key = mesh->loop.now.tv_sec; - return sptps_start(&to->sptps, to, true, true, mesh->private_key, to->ecdsa, label, sizeof(label) - 1, send_initial_sptps_data, receive_sptps_record); -} - -/* REQ_KEY is overloaded to allow arbitrary requests to be routed between two nodes. */ - -static bool req_key_ext_h(meshlink_handle_t *mesh, connection_t *c, const char *request, node_t *from, int reqno) { - (void)c; - - if(!from->nexthop || !from->nexthop->connection) { - logger(mesh, MESHLINK_WARNING, "Cannot answer REQ_KEY from %s via %s", from->name, from->nexthop ? from->nexthop->name : from->name); - return true; - } - - switch(reqno) { - case REQ_PUBKEY: { - char *pubkey = ecdsa_get_base64_public_key(mesh->private_key); - - if(!node_read_public_key(mesh, from)) { - char hiskey[MAX_STRING_SIZE]; - - if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, hiskey) == 1) { - from->ecdsa = ecdsa_set_base64_public_key(hiskey); - - if(!from->ecdsa) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s: %s", "REQ_PUBKEY", from->name, "invalid pubkey"); - return true; - } - - logger(mesh, MESHLINK_INFO, "Learned ECDSA public key from %s", from->name); - from->status.dirty = true; - - if(!node_write_config(mesh, from, true)) { - // ignore - } - } - } - - send_request(mesh, from->nexthop->connection, "%d %s %s %d %s", REQ_KEY, mesh->self->name, from->name, ANS_PUBKEY, pubkey); - free(pubkey); - return true; - } - - case ANS_PUBKEY: { - if(node_read_public_key(mesh, from)) { - logger(mesh, MESHLINK_WARNING, "Got ANS_PUBKEY from %s even though we already have his pubkey", from->name); - return true; - } - - char pubkey[MAX_STRING_SIZE]; - - if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, pubkey) != 1 || !(from->ecdsa = ecdsa_set_base64_public_key(pubkey))) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s: %s", "ANS_PUBKEY", from->name, "invalid pubkey"); - return true; - } - - logger(mesh, MESHLINK_INFO, "Learned ECDSA public key from %s", from->name); - from->status.dirty = true; - - if(!node_write_config(mesh, from, true)) { - // ignore - } - - /* If we are trying to form an outgoing connection to this node, retry immediately */ - if(mesh->outgoing) { - if(mesh->outgoing->node == from && mesh->outgoing->ev.cb) { - mesh->outgoing->timeout = 0; - timeout_set(&mesh->loop, &mesh->outgoing->ev, &(struct timespec) { - 0, 0 - }); - } - } - - /* Also reset any UTCP timers */ - utcp_reset_timers(from->utcp); - - return true; - } - - case REQ_KEY: { - if(!node_read_public_key(mesh, from)) { - logger(mesh, MESHLINK_DEBUG, "No ECDSA key known for %s", from->name); - send_request(mesh, from->nexthop->connection, "%d %s %s %d", REQ_KEY, mesh->self->name, from->name, REQ_PUBKEY); - return true; - } - - if(from->sptps.label) { - logger(mesh, MESHLINK_DEBUG, "Got REQ_KEY from %s while we already started a SPTPS session!", from->name); - - if(mesh->loop.now.tv_sec < from->last_req_key + req_key_timeout && strcmp(mesh->self->name, from->name) < 0) { - logger(mesh, MESHLINK_DEBUG, "Ignoring REQ_KEY from %s.", from->name); - return true; - } - } - - char buf[MAX_STRING_SIZE]; - int len; - - if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1 || !(len = b64decode(buf, buf, strlen(buf)))) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s: %s", "REQ_SPTPS_START", from->name, "invalid SPTPS data"); - return true; - } - - char label[sizeof(meshlink_udp_label) + strlen(from->name) + strlen(mesh->self->name) + 2]; - snprintf(label, sizeof(label), "%s %s %s", meshlink_udp_label, from->name, mesh->self->name); - sptps_stop(&from->sptps); - from->status.validkey = false; - from->status.waitingforkey = true; - from->last_req_key = mesh->loop.now.tv_sec; - - /* Send our canonical address to help with UDP hole punching */ - send_canonical_address(mesh, from); - - if(!sptps_start(&from->sptps, from, false, true, mesh->private_key, from->ecdsa, label, sizeof(label) - 1, send_sptps_data, receive_sptps_record)) { - logger(mesh, MESHLINK_ERROR, "Could not start SPTPS session with %s: %s", from->name, strerror(errno)); - return true; - } - - if(!sptps_receive_data(&from->sptps, buf, len)) { - logger(mesh, MESHLINK_ERROR, "Could not process SPTPS data from %s: %s", from->name, strerror(errno)); - return true; - } - - return true; - } - - case REQ_SPTPS: { - if(!from->status.validkey) { - logger(mesh, MESHLINK_ERROR, "Got REQ_SPTPS from %s but we don't have a valid key yet", from->name); - return true; - } - - char buf[MAX_STRING_SIZE]; - int len; - - if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING, buf) != 1 || !(len = b64decode(buf, buf, strlen(buf)))) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s: %s", "REQ_SPTPS", from->name, "invalid SPTPS data"); - return true; - } - - if(!sptps_receive_data(&from->sptps, buf, len)) { - logger(mesh, MESHLINK_ERROR, "Could not process SPTPS data from %s: %s", from->name, strerror(errno)); - return true; - } - - return true; - } - - case REQ_CANONICAL: { - char host[MAX_STRING_SIZE]; - char port[MAX_STRING_SIZE]; - - if(sscanf(request, "%*d %*s %*s %*d " MAX_STRING " " MAX_STRING, host, port) != 2) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s: %s", "REQ_CANONICAL", from->name, "invalid canonical address"); - return true; - } - - char *canonical_address; - xasprintf(&canonical_address, "%s %s", host, port); - - if(mesh->log_level <= MESHLINK_DEBUG && (!from->canonical_address || strcmp(from->canonical_address, canonical_address))) { - logger(mesh, MESHLINK_DEBUG, "Updating canonical address of %s to %s", from->name, canonical_address); - } - - free(from->canonical_address); - from->canonical_address = canonical_address; - return true; - } - - default: - logger(mesh, MESHLINK_ERROR, "Unknown extended REQ_KEY request from %s: %s", from->name, request); - return true; - } -} - bool req_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { - assert(request); - assert(*request); - - char from_name[MAX_STRING_SIZE]; - char to_name[MAX_STRING_SIZE]; - node_t *from, *to; - int reqno = 0; - - if(sscanf(request, "%*d " MAX_STRING " " MAX_STRING " %d", from_name, to_name, &reqno) < 2) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s", "REQ_KEY", c->name); - return false; - } - - if(!check_id(from_name) || !check_id(to_name)) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s: %s", "REQ_KEY", c->name, "invalid name"); - return false; - } - - from = lookup_node(mesh, from_name); - - if(!from) { - logger(mesh, MESHLINK_ERROR, "Got %s from %s origin %s which does not exist in our connection list", - "REQ_KEY", c->name, from_name); - return true; - } - - to = lookup_node(mesh, to_name); - - if(!to) { - logger(mesh, MESHLINK_ERROR, "Got %s from %s destination %s which does not exist in our connection list", - "REQ_KEY", c->name, to_name); - return true; - } - - /* Check if this key request is for us */ - - if(to == mesh->self) { /* Yes */ - /* Is this an extended REQ_KEY message? */ - if(reqno) { - return req_key_ext_h(mesh, c, request, from, reqno); - } - - /* This should never happen. Ignore it, unless it came directly from the connected peer, in which case we disconnect. */ - return from->connection != c; - } else { - if(!to->status.reachable || !to->nexthop || !to->nexthop->connection) { - logger(mesh, MESHLINK_WARNING, "Got %s from %s destination %s which is not reachable", - "REQ_KEY", c->name, to_name); - return true; - } - - send_request(mesh, to->nexthop->connection, "%s", request); - } - + (void)mesh; + (void)c; + (void)request; return true; } bool ans_key_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { - assert(request); - assert(*request); + (void)mesh; (void)c; - - char from_name[MAX_STRING_SIZE]; - char to_name[MAX_STRING_SIZE]; - char key[MAX_STRING_SIZE]; - char address[MAX_STRING_SIZE] = ""; - char port[MAX_STRING_SIZE] = ""; - int cipher, digest, maclength, compression; - node_t *from, *to; - - if(sscanf(request, "%*d "MAX_STRING" "MAX_STRING" "MAX_STRING" %d %d %d %d "MAX_STRING" "MAX_STRING, - from_name, to_name, key, &cipher, &digest, &maclength, - &compression, address, port) < 7) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s", "ANS_KEY", c->name); - return false; - } - - if(!check_id(from_name) || !check_id(to_name)) { - logger(mesh, MESHLINK_ERROR, "Got bad %s from %s: %s", "ANS_KEY", c->name, "invalid name"); - return false; - } - - from = lookup_node(mesh, from_name); - - if(!from) { - logger(mesh, MESHLINK_ERROR, "Got %s from %s origin %s which does not exist in our connection list", - "ANS_KEY", c->name, from_name); - return true; - } - - to = lookup_node(mesh, to_name); - - if(!to) { - logger(mesh, MESHLINK_ERROR, "Got %s from %s destination %s which does not exist in our connection list", - "ANS_KEY", c->name, to_name); - return true; - } - - /* Forward it if necessary */ - - if(to != mesh->self) { - logger(mesh, MESHLINK_WARNING, "Got %s from %s destination %s which is not for us", - "ANS_KEY", c->name, to_name); - return true; - } - - /* Is this an ANS_KEY informing us of our own reflexive UDP address? */ - - if(from == mesh->self) { - if(*key == '.' && *address && *port) { - /* Ignore reflexive UDP address */ - } else { - logger(mesh, MESHLINK_WARNING, "Got %s from %s from %s to %s", - "ANS_KEY", c->name, from_name, to_name); - } - - return true; - } - - /* Process SPTPS data if present */ - - if(*key != '.') { - /* Don't use key material until every check has passed. */ - from->status.validkey = false; - - /* Compression is not supported. */ - if(compression != 0) { - logger(mesh, MESHLINK_ERROR, "Node %s uses bogus compression level!", from->name); - return true; - } - - char buf[strlen(key)]; - int len = b64decode(key, buf, strlen(key)); - - if(!len || !sptps_receive_data(&from->sptps, buf, len)) { - logger(mesh, MESHLINK_ERROR, "Error processing SPTPS data from %s", from->name); - } - } - - if(from->status.validkey) { - if(*address && *port) { - /* Ignore reflexive UDP address */ - } - } - + (void)request; return true; -} +} \ No newline at end of file diff --git a/src/protocol_misc.c b/src/protocol_misc.c index 9218f96..d9466f0 100644 --- a/src/protocol_misc.c +++ b/src/protocol_misc.c @@ -100,3 +100,14 @@ bool pong_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { return true; } + +bool send_raw_packet(meshlink_handle_t *mesh, connection_t *c, const vpn_packet_t *packet) { + return send_request(mesh, c, "%d", PACKET) && send_meta(mesh, c, (const char *)packet->data, packet->len); +} + +bool raw_packet_h(meshlink_handle_t *mesh, connection_t *c, const char *request) { + (void)mesh; + (void)request; + c->status.raw_packet = true; + return true; +} diff --git a/src/route.c b/src/route.c deleted file mode 100644 index ee36a9b..0000000 --- a/src/route.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - route.c -- routing - Copyright (C) 2014 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 "system.h" - -#include "logger.h" -#include "meshlink_internal.h" -#include "net.h" -#include "route.h" -#include "utils.h" - -static bool checklength(node_t *source, vpn_packet_t *packet, uint16_t length) { - assert(length); - - if(packet->len < length) { - logger(source->mesh, MESHLINK_WARNING, "Got too short packet from %s", source->name); - return false; - } else { - return true; - } -} - -void route(meshlink_handle_t *mesh, node_t *source, vpn_packet_t *packet) { - assert(source); - - // TODO: route on name or key - - meshlink_packethdr_t *hdr = (meshlink_packethdr_t *) packet->data; - node_t *dest = lookup_node(mesh, (char *)hdr->destination); - logger(mesh, MESHLINK_DEBUG, "Routing packet from \"%s\" to \"%s\"\n", hdr->source, hdr->destination); - - //Check Length - if(!checklength(source, packet, sizeof(*hdr))) { - return; - } - - if(dest == NULL) { - //Lookup failed - logger(mesh, MESHLINK_WARNING, "Can't lookup the destination of a packet in the route() function. This should never happen!\n"); - logger(mesh, MESHLINK_WARNING, "Destination was: %s\n", hdr->destination); - return; - } - - if(dest == mesh->self) { - const void *payload = packet->data + sizeof(*hdr); - size_t len = packet->len - sizeof(*hdr); - - char hex[len * 2 + 1]; - - if(mesh->log_level <= MESHLINK_DEBUG) { - bin2hex(payload, hex, len); // don't do this unless it's going to be logged - } - - logger(mesh, MESHLINK_DEBUG, "I received a packet for me with payload: %s\n", hex); - - if(mesh->receive_cb) { - mesh->receive_cb(mesh, (meshlink_node_t *)source, payload, len); - } - - return; - } - - if(!dest->status.reachable) { - //TODO: check what to do here, not just print a warning - logger(mesh, MESHLINK_WARNING, "The destination of a packet in the route() function is unreachable. Dropping packet.\n"); - return; - } - - if(dest == source) { - logger(mesh, MESHLINK_ERROR, "Routing loop for packet from %s!", source->name); - return; - } - - send_packet(mesh, dest, packet); - return; -} diff --git a/src/route.h b/src/route.h deleted file mode 100644 index 92e9fe0..0000000 --- a/src/route.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef MESHLINK_ROUTE_H -#define MESHLINK_ROUTE_H - -/* - route.h -- header file for route.c - Copyright (C) 2014, 2017 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 "net.h" -#include "node.h" - -void route(struct meshlink_handle *mesh, struct node_t *, struct vpn_packet_t *); - -#endif diff --git a/src/utcp-test.c b/src/utcp-test.c deleted file mode 100644 index a7c0534..0000000 --- a/src/utcp-test.c +++ /dev/null @@ -1,410 +0,0 @@ -/* - utcp-test.c -- Test application for UTCP - Copyright (C) 2014-2020 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 "system.h" -#include -#include - -#include "utcp.h" - -#define DIR_READ 1 -#define DIR_WRITE 2 - -static struct utcp_connection *c; -static int dir = DIR_READ | DIR_WRITE; -static long inpktno; -static long outpktno; -static long dropfrom; -static long dropto; -static double reorder; -static long reorder_dist = 10; -static double dropin; -static double dropout; -static long total_out; -static long total_in; -static FILE *reference; -static long mtu; -static long bufsize; - -static char *reorder_data; -static size_t reorder_len; -static int reorder_countdown; - -#if UTCP_DEBUG -static void debug(const char *format, ...) { - struct timespec tv; - char buf[1024]; - int len; - - clock_gettime(CLOCK_REALTIME, &tv); - len = snprintf(buf, sizeof(buf), "%ld.%06lu ", (long)tv.tv_sec, tv.tv_nsec / 1000); - va_list ap; - va_start(ap, format); - len += vsnprintf(buf + len, sizeof(buf) - len, format, ap); - va_end(ap); - - if(len > 0 && (size_t)len < sizeof(buf)) { - fwrite(buf, len, 1, stderr); - } -} -#else -#define debug(...) do {} while(0) -#endif - -static ssize_t do_recv(struct utcp_connection *rc, const void *data, size_t len) { - (void)rc; - - if(!data || !len) { - if(errno) { - debug("Error: %s\n", strerror(errno)); - dir = 0; - } else { - dir &= ~DIR_WRITE; - debug("Connection closed by peer\n"); - } - - return -1; - } - - if(reference) { - char buf[len]; - - if(fread(buf, len, 1, reference) != 1) { - debug("Error reading reference\n"); - abort(); - } - - if(memcmp(buf, data, len)) { - debug("Received data differs from reference\n"); - abort(); - } - } - - return write(1, data, len); -} - -static void do_accept(struct utcp_connection *nc, uint16_t port) { - (void)port; - utcp_accept(nc, do_recv, NULL); - c = nc; - - if(bufsize) { - utcp_set_sndbuf(c, NULL, bufsize); - utcp_set_rcvbuf(c, NULL, bufsize); - } - - utcp_set_accept_cb(c->utcp, NULL, NULL); -} - -static ssize_t do_send(struct utcp *utcp, const void *data, size_t len) { - int s = *(int *)utcp->priv; - outpktno++; - - if(outpktno >= dropfrom && outpktno < dropto) { - if(drand48() < dropout) { - debug("Dropped outgoing packet\n"); - return len; - } - - if(!reorder_data && drand48() < reorder) { - reorder_data = malloc(len); - - if(!reorder_data) { - debug("Out of memory\n"); - return len; - } - - reorder_len = len; - memcpy(reorder_data, data, len); - reorder_countdown = 1 + drand48() * reorder_dist; - return len; - } - } - - if(reorder_data) { - if(--reorder_countdown < 0) { - total_out += reorder_len; - send(s, reorder_data, reorder_len, MSG_DONTWAIT); - free(reorder_data); - reorder_data = NULL; - } - } - - total_out += len; - ssize_t result = send(s, data, len, MSG_DONTWAIT); - - if(result <= 0) { - debug("Error sending UDP packet: %s\n", strerror(errno)); - } - - return result; -} - -static void set_mtu(struct utcp *u, int s) { -#ifdef IP_MTU - - if(!mtu) { - socklen_t optlen = sizeof(mtu); - getsockopt(s, IPPROTO_IP, IP_MTU, &mtu, &optlen); - } - -#else - (void)s; -#endif - - if(!mtu || mtu == 65535) { - mtu = 1500; - } - - debug("Using MTU %lu\n", mtu); - - utcp_set_mtu(u, mtu ? mtu - 28 : 1300); -} - -int main(int argc, char *argv[]) { - srand(time(NULL)); - srand48(time(NULL)); - - if(argc < 2 || argc > 3) { - return 1; - } - - bool server = argc == 2; - bool connected = false; - uint32_t flags = UTCP_TCP; - size_t read_size = 102400; - - if(getenv("DROPIN")) { - dropin = atof(getenv("DROPIN")); - } - - if(getenv("DROPOUT")) { - dropout = atof(getenv("DROPOUT")); - } - - if(getenv("DROPFROM")) { - dropfrom = atoi(getenv("DROPFROM")); - } - - if(getenv("DROPTO")) { - dropto = atoi(getenv("DROPTO")); - } - - if(getenv("REORDER")) { - reorder = atof(getenv("REORDER")); - } - - if(getenv("REORDER_DIST")) { - reorder_dist = atoi(getenv("REORDER_DIST")); - } - - if(getenv("FLAGS")) { - flags = atoi(getenv("FLAGS")); - } - - if(getenv("READ_SIZE")) { - read_size = atoi(getenv("READ_SIZE")); - } - - if(getenv("MTU")) { - mtu = atoi(getenv("MTU")); - } - - if(getenv("BUFSIZE")) { - bufsize = atoi(getenv("BUFSIZE")); - } - - char *reference_filename = getenv("REFERENCE"); - - if(reference_filename) { - reference = fopen(reference_filename, "r"); - } - - if(dropto < dropfrom) { - dropto = 1 << 30; - } - - struct addrinfo *ai; - - struct addrinfo hint = { - .ai_flags = server ? AI_PASSIVE : 0, - .ai_socktype = SOCK_DGRAM, - }; - - getaddrinfo(server ? NULL : argv[1], server ? argv[1] : argv[2], &hint, &ai); - - if(!ai) { - debug("Could not lookup address: %s\n", strerror(errno)); - return 1; - } - - int s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - - if(s == -1) { - debug("Could not create socket: %s\n", strerror(errno)); - return 1; - } - - static const int one = 1; - setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); - - if(server) { - if(bind(s, ai->ai_addr, ai->ai_addrlen)) { - debug("Could not bind: %s\n", strerror(errno)); - return 1; - } - } else { -#ifdef SO_NOSIGPIPE - int nosigpipe = 1; - setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe, sizeof(nosigpipe)); -#endif - - if(connect(s, ai->ai_addr, ai->ai_addrlen)) { - debug("Could not connect: %s\n", strerror(errno)); - return 1; - } - - connected = true; - } - - freeaddrinfo(ai); - - struct utcp *u = utcp_init(server ? do_accept : NULL, NULL, do_send, &s); - - if(!u) { - debug("Could not initialize UTCP\n"); - return 1; - } - - utcp_set_user_timeout(u, 10); - - if(!server) { - set_mtu(u, s); - c = utcp_connect_ex(u, 1, do_recv, NULL, flags); - - if(bufsize) { - utcp_set_sndbuf(c, NULL, bufsize); - utcp_set_rcvbuf(c, NULL, bufsize); - } - } - - struct pollfd fds[2] = { - {.fd = 0, .events = POLLIN | POLLERR | POLLHUP}, - {.fd = s, .events = POLLIN | POLLERR | POLLHUP}, - }; - - char buf[102400]; - - struct timespec timeout = utcp_timeout(u); - - while(!connected || utcp_is_active(u)) { - size_t max = c ? utcp_get_sndbuf_free(c) : 0; - - if(max > sizeof(buf)) { - max = sizeof(buf); - } - - if(max > read_size) { - max = read_size; - } - - int timeout_ms = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000 + 1; - - debug("polling, dir = %d, timeout = %d\n", dir, timeout_ms); - - if((dir & DIR_READ) && max) { - poll(fds, 2, timeout_ms); - } else { - poll(fds + 1, 1, timeout_ms); - } - - if(fds[0].revents) { - fds[0].revents = 0; - ssize_t len = read(0, buf, max); - debug("stdin %zd\n", len); - - if(len <= 0) { - fds[0].fd = -1; - dir &= ~DIR_READ; - - if(c) { - utcp_shutdown(c, SHUT_WR); - } - - if(len == -1) { - break; - } else { - continue; - } - } - - if(c) { - ssize_t sent = utcp_send(c, buf, len); - - if(sent != len) { - debug("Short send: %zd != %zd\n", sent, len); - } - } - } - - if(fds[1].revents) { - fds[1].revents = 0; - struct sockaddr_storage ss; - socklen_t sl = sizeof(ss); - int len = recvfrom(s, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&ss, &sl); - debug("netin %zu\n", len); - - if(len <= 0) { - debug("Error receiving UDP packet: %s\n", strerror(errno)); - break; - } - - if(!connected) { - if(!connect(s, (struct sockaddr *)&ss, sl)) { - connected = true; - set_mtu(u, s); - } - } - - inpktno++; - - if(inpktno >= dropto || inpktno < dropfrom || drand48() >= dropin) { - total_in += len; - - if(utcp_recv(u, buf, len) == -1) { - debug("Error receiving UTCP packet: %s\n", strerror(errno)); - } - } else { - debug("Dropped incoming packet\n"); - } - } - - timeout = utcp_timeout(u); - }; - - utcp_close(c); - - utcp_exit(u); - - free(reorder_data); - - debug("Total bytes in: %ld, out: %ld\n", total_in, total_out); - - return 0; -} diff --git a/src/utcp.c b/src/utcp.c deleted file mode 100644 index 0909a01..0000000 --- a/src/utcp.c +++ /dev/null @@ -1,1717 +0,0 @@ -/* - utcp.c -- Userspace TCP - Copyright (C) 2014-2017 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 "system.h" -#include - -#include "utcp_priv.h" - -#ifndef EBADMSG -#define EBADMSG 104 -#endif - -#ifndef SHUT_RDWR -#define SHUT_RDWR 2 -#endif - -#ifdef poll -#undef poll -#endif - -#ifndef UTCP_CLOCK -#if defined(CLOCK_MONOTONIC_RAW) && defined(__x86_64__) -#define UTCP_CLOCK CLOCK_MONOTONIC_RAW -#else -#define UTCP_CLOCK CLOCK_MONOTONIC -#endif -#endif - -static void timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *r) { - r->tv_sec = a->tv_sec - b->tv_sec; - r->tv_nsec = a->tv_nsec - b->tv_nsec; - - if(r->tv_nsec < 0) { - r->tv_sec--, r->tv_nsec += NSEC_PER_SEC; - } -} - -static int32_t timespec_diff_usec(const struct timespec *a, const struct timespec *b) { - return (a->tv_sec - b->tv_sec) * 1000000 + (a->tv_nsec - b->tv_nsec) / 1000; -} - -static bool timespec_lt(const struct timespec *a, const struct timespec *b) { - if(a->tv_sec == b->tv_sec) { - return a->tv_nsec < b->tv_nsec; - } else { - return a->tv_sec < b->tv_sec; - } -} - -static void timespec_clear(struct timespec *a) { - a->tv_sec = 0; - a->tv_nsec = 0; -} - -static bool timespec_isset(const struct timespec *a) { - return a->tv_sec; -} - -static long CLOCK_GRANULARITY; // usec - -static inline size_t min(size_t a, size_t b) { - return a < b ? a : b; -} - -static inline size_t max(size_t a, size_t b) { - return a > b ? a : b; -} - -#ifdef UTCP_DEBUG -#include - -#ifndef UTCP_DEBUG_DATALEN -#define UTCP_DEBUG_DATALEN 20 -#endif - -static void debug(struct utcp_connection *c, const char *format, ...) { - struct timespec tv; - char buf[1024]; - int len; - - clock_gettime(CLOCK_REALTIME, &tv); - len = snprintf(buf, sizeof(buf), "%ld.%06lu %u:%u ", (long)tv.tv_sec, tv.tv_nsec / 1000, c ? c->src : 0, c ? c->dst : 0); - va_list ap; - va_start(ap, format); - len += vsnprintf(buf + len, sizeof(buf) - len, format, ap); - va_end(ap); - - if(len > 0 && (size_t)len < sizeof(buf)) { - fwrite(buf, len, 1, stderr); - } -} - -static void print_packet(struct utcp_connection *c, const char *dir, const void *pkt, size_t len) { - struct hdr hdr; - - if(len < sizeof(hdr)) { - debug(c, "%s: short packet (%lu bytes)\n", dir, (unsigned long)len); - return; - } - - memcpy(&hdr, pkt, sizeof(hdr)); - - uint32_t datalen; - - if(len > sizeof(hdr)) { - datalen = min(len - sizeof(hdr), UTCP_DEBUG_DATALEN); - } else { - datalen = 0; - } - - - const uint8_t *data = (uint8_t *)pkt + sizeof(hdr); - char str[datalen * 2 + 1]; - char *p = str; - - for(uint32_t i = 0; i < datalen; i++) { - *p++ = "0123456789ABCDEF"[data[i] >> 4]; - *p++ = "0123456789ABCDEF"[data[i] & 15]; - } - - *p = 0; - - debug(c, "%s: len %lu src %u dst %u seq %u ack %u wnd %u aux %x ctl %s%s%s%s%s data %s\n", - dir, (unsigned long)len, hdr.src, hdr.dst, hdr.seq, hdr.ack, hdr.wnd, hdr.aux, - hdr.ctl & SYN ? "SYN" : "", - hdr.ctl & RST ? "RST" : "", - hdr.ctl & FIN ? "FIN" : "", - hdr.ctl & ACK ? "ACK" : "", - hdr.ctl & MF ? "MF" : "", - str - ); -} - -static void debug_cwnd(struct utcp_connection *c) { - debug(c, "snd.cwnd %u snd.ssthresh %u\n", c->snd.cwnd, ~c->snd.ssthresh ? c->snd.ssthresh : 0); -} -#else -#define debug(...) do {} while(0) -#define print_packet(...) do {} while(0) -#define debug_cwnd(...) do {} while(0) -#endif - -static void set_state(struct utcp_connection *c, enum state state) { - c->state = state; - - if(state == ESTABLISHED) { - timespec_clear(&c->conn_timeout); - } - - debug(c, "state %s\n", strstate[state]); -} - -static bool fin_wanted(struct utcp_connection *c, uint32_t seq) { - if(seq != c->snd.last) { - return false; - } - - switch(c->state) { - case FIN_WAIT_1: - case CLOSING: - case LAST_ACK: - return true; - - default: - return false; - } -} - -static int32_t seqdiff(uint32_t a, uint32_t b) { - return a - b; -} - -// Connections are stored in a sorted list. -// This gives O(log(N)) lookup time, O(N log(N)) insertion time and O(N) deletion time. - -static int compare(const void *va, const void *vb) { - assert(va && vb); - - const struct utcp_connection *a = *(struct utcp_connection **)va; - const struct utcp_connection *b = *(struct utcp_connection **)vb; - - assert(a && b); - - int c = (int)a->src - (int)b->src; - - if(c) { - return c; - } - - c = (int)a->dst - (int)b->dst; - return c; -} - -static struct utcp_connection *find_connection(const struct utcp *utcp, uint16_t src, uint16_t dst) { - if(!utcp->nconnections) { - return NULL; - } - - struct utcp_connection key = { - .src = src, - .dst = dst, - }, *keyp = &key; - struct utcp_connection **match = bsearch(&keyp, utcp->connections, utcp->nconnections, sizeof(*utcp->connections), compare); - return match ? *match : NULL; -} - -static void free_connection(struct utcp_connection *c) { - struct utcp *utcp = c->utcp; - struct utcp_connection **cp = bsearch(&c, utcp->connections, utcp->nconnections, sizeof(*utcp->connections), compare); - - assert(cp); - - int i = cp - utcp->connections; - memmove(cp, cp + 1, (utcp->nconnections - i - 1) * sizeof(*cp)); - utcp->nconnections--; - - free(c); -} - -static struct utcp_connection *allocate_connection(struct utcp *utcp, uint16_t src, uint16_t dst) { - // Check whether this combination of src and dst is free - - if(src) { - if(find_connection(utcp, src, dst)) { - errno = EADDRINUSE; - return NULL; - } - } else { // If src == 0, generate a random port number with the high bit set - if(utcp->nconnections >= 32767) { - errno = ENOMEM; - return NULL; - } - - src = rand() | 0x8000; - - while(find_connection(utcp, src, dst)) { - src++; - } - } - - // Allocate memory for the new connection - - if(utcp->nconnections >= utcp->nallocated) { - if(!utcp->nallocated) { - utcp->nallocated = 4; - } else { - utcp->nallocated *= 2; - } - - struct utcp_connection **new_array = realloc(utcp->connections, utcp->nallocated * sizeof(*utcp->connections)); - - if(!new_array) { - return NULL; - } - - utcp->connections = new_array; - } - - struct utcp_connection *c = calloc(1, sizeof(*c)); - - if(!c) { - return NULL; - } - - // Fill in the details - - c->src = src; - c->dst = dst; -#ifdef UTCP_DEBUG - c->snd.iss = 0; -#else - c->snd.iss = rand(); -#endif - c->snd.una = c->snd.iss; - c->snd.nxt = c->snd.iss + 1; - c->snd.last = c->snd.nxt; - c->snd.cwnd = (utcp->mss > 2190 ? 2 : utcp->mss > 1095 ? 3 : 4) * utcp->mss; - c->snd.ssthresh = ~0; - debug_cwnd(c); - c->srtt = 0; - c->rttvar = 0; - c->rto = START_RTO; - c->utcp = utcp; - - // Add it to the sorted list of connections - - utcp->connections[utcp->nconnections++] = c; - qsort(utcp->connections, utcp->nconnections, sizeof(*utcp->connections), compare); - - return c; -} - -static inline uint32_t absdiff(uint32_t a, uint32_t b) { - if(a > b) { - return a - b; - } else { - return b - a; - } -} - -// Update RTT variables. See RFC 6298. -static void update_rtt(struct utcp_connection *c, uint32_t rtt) { - if(!rtt) { - debug(c, "invalid rtt\n"); - return; - } - - if(!c->srtt) { - c->srtt = rtt; - c->rttvar = rtt / 2; - } else { - c->rttvar = (c->rttvar * 3 + absdiff(c->srtt, rtt)) / 4; - c->srtt = (c->srtt * 7 + rtt) / 8; - } - - c->rto = c->srtt + max(4 * c->rttvar, CLOCK_GRANULARITY); - - if(c->rto > MAX_RTO) { - c->rto = MAX_RTO; - } - - debug(c, "rtt %u srtt %u rttvar %u rto %u\n", rtt, c->srtt, c->rttvar, c->rto); -} - -static void start_retransmit_timer(struct utcp_connection *c) { - clock_gettime(UTCP_CLOCK, &c->rtrx_timeout); - - uint32_t rto = c->rto; - - while(rto > USEC_PER_SEC) { - c->rtrx_timeout.tv_sec++; - rto -= USEC_PER_SEC; - } - - c->rtrx_timeout.tv_nsec += rto * 1000; - - if(c->rtrx_timeout.tv_nsec >= NSEC_PER_SEC) { - c->rtrx_timeout.tv_nsec -= NSEC_PER_SEC; - c->rtrx_timeout.tv_sec++; - } - - debug(c, "rtrx_timeout %ld.%06lu\n", c->rtrx_timeout.tv_sec, c->rtrx_timeout.tv_nsec); -} - -static void stop_retransmit_timer(struct utcp_connection *c) { - timespec_clear(&c->rtrx_timeout); - debug(c, "rtrx_timeout cleared\n"); -} - -struct utcp_connection *utcp_connect_ex(struct utcp *utcp, uint16_t dst, utcp_recv_t recv, void *priv, uint32_t flags) { - struct utcp_connection *c = allocate_connection(utcp, 0, dst); - - if(!c) { - return NULL; - } - - assert(flags == 0); // UDP only - - c->flags = flags; - c->recv = recv; - c->priv = priv; - - struct { - struct hdr hdr; - uint8_t init[4]; - } pkt; - - pkt.hdr.src = c->src; - pkt.hdr.dst = c->dst; - pkt.hdr.seq = c->snd.iss; - pkt.hdr.ack = 0; - pkt.hdr.wnd = c->utcp->mtu; - pkt.hdr.ctl = SYN; - pkt.hdr.aux = 0x0101; - pkt.init[0] = 1; - pkt.init[1] = 0; - pkt.init[2] = 0; - pkt.init[3] = flags & 0x7; - - set_state(c, SYN_SENT); - - print_packet(c, "send", &pkt, sizeof(pkt)); - utcp->send(utcp, &pkt, sizeof(pkt)); - - clock_gettime(UTCP_CLOCK, &c->conn_timeout); - c->conn_timeout.tv_sec += utcp->timeout; - - start_retransmit_timer(c); - - return c; -} - -void utcp_accept(struct utcp_connection *c, utcp_recv_t recv, void *priv) { - if(c->reapable || c->state != SYN_RECEIVED) { - debug(c, "accept() called on invalid connection in state %s\n", c, strstate[c->state]); - return; - } - - debug(c, "accepted %p %p\n", c, recv, priv); - c->recv = recv; - c->priv = priv; - set_state(c, ESTABLISHED); -} - -static void ack(struct utcp_connection *c, const void *data, size_t len) { - struct { - struct hdr hdr; - uint8_t data[]; - } *pkt = c->utcp->pkt; - - pkt->hdr.src = c->src; - pkt->hdr.dst = c->dst; - pkt->hdr.ack = c->rcv.nxt; - pkt->hdr.wnd = 0; - pkt->hdr.ctl = ACK; - pkt->hdr.aux = 0; - - uint32_t seglen = len; - pkt->hdr.seq = c->snd.nxt; - - c->snd.nxt += seglen; - - if(fin_wanted(c, c->snd.nxt)) { - pkt->hdr.ctl |= FIN; - } - - if(data && len) { - assert(len <= c->utcp->mtu); - memcpy(pkt->data, data, len); - } else { - assert(!data && !len); - } - - if(!c->rtt_start.tv_sec) { - // Start RTT measurement - clock_gettime(UTCP_CLOCK, &c->rtt_start); - c->rtt_seq = pkt->hdr.seq + seglen; - debug(c, "starting RTT measurement, expecting ack %u\n", c->rtt_seq); - } - - print_packet(c, "send", pkt, sizeof(pkt->hdr) + seglen); - c->utcp->send(c->utcp, pkt, sizeof(pkt->hdr) + seglen); -} - -ssize_t utcp_send(struct utcp_connection *c, const void *data, size_t len) { - if(c->reapable) { - debug(c, "send() called on closed connection\n"); - errno = EBADF; - return -1; - } - - switch(c->state) { - case CLOSED: - case LISTEN: - debug(c, "send() called on unconnected connection\n"); - errno = ENOTCONN; - return -1; - - case SYN_SENT: - case SYN_RECEIVED: - case ESTABLISHED: - case CLOSE_WAIT: - break; - - case FIN_WAIT_1: - case FIN_WAIT_2: - case CLOSING: - case LAST_ACK: - case TIME_WAIT: - debug(c, "send() called on closed connection\n"); - errno = EPIPE; - return -1; - } - - // Exit early if we have nothing to send. - - if(!len) { - return 0; - } - - if(!data) { - errno = EFAULT; - return -1; - } - - if(len > MAX_UNRELIABLE_SIZE || len > c->utcp->mtu) { - errno = EMSGSIZE; - return -1; - } - - if(len <= 0) { - return len; - } - - c->snd.last += len; - - // Don't send anything yet if the connection has not fully established yet - - if(c->state == SYN_SENT || c->state == SYN_RECEIVED) { - return len; - } - - ack(c, data, len); - - c->snd.una = c->snd.nxt = c->snd.last; - - return len; -} - -static void swap_ports(struct hdr *hdr) { - uint16_t tmp = hdr->src; - hdr->src = hdr->dst; - hdr->dst = tmp; -} - -static void retransmit(struct utcp_connection *c) { - if(c->state == CLOSED || c->snd.last == c->snd.una) { - debug(c, "retransmit() called but nothing to retransmit!\n"); - stop_retransmit_timer(c); - return; - } - - struct utcp *utcp = c->utcp; - - struct { - struct hdr hdr; - uint8_t data[]; - } *pkt = c->utcp->pkt; - - pkt->hdr.src = c->src; - pkt->hdr.dst = c->dst; - pkt->hdr.wnd = c->utcp->mtu; - pkt->hdr.aux = 0; - - switch(c->state) { - case SYN_SENT: - // Send our SYN again - pkt->hdr.seq = c->snd.iss; - pkt->hdr.ack = 0; - pkt->hdr.ctl = SYN; - pkt->hdr.aux = 0x0101; - pkt->data[0] = 1; - pkt->data[1] = 0; - pkt->data[2] = 0; - pkt->data[3] = c->flags & 0x7; - print_packet(c, "rtrx", pkt, sizeof(pkt->hdr) + 4); - utcp->send(utcp, pkt, sizeof(pkt->hdr) + 4); - break; - - case SYN_RECEIVED: - // Send SYNACK again - pkt->hdr.seq = c->snd.nxt; - pkt->hdr.ack = c->rcv.nxt; - pkt->hdr.ctl = SYN | ACK; - print_packet(c, "rtrx", pkt, sizeof(pkt->hdr)); - utcp->send(utcp, pkt, sizeof(pkt->hdr)); - break; - - case ESTABLISHED: - break; - - case FIN_WAIT_1: - case CLOSE_WAIT: - case CLOSING: - case LAST_ACK: - // Send unacked data again. - pkt->hdr.seq = c->snd.una; - pkt->hdr.ack = c->rcv.nxt; - pkt->hdr.ctl = ACK; - uint32_t len = min(seqdiff(c->snd.last, c->snd.una), utcp->mss); - - if(fin_wanted(c, c->snd.una + len)) { - len--; - pkt->hdr.ctl |= FIN; - } else { - break; - } - - assert(len == 0); - - print_packet(c, "rtrx", pkt, sizeof(pkt->hdr) + len); - utcp->send(utcp, pkt, sizeof(pkt->hdr) + len); - break; - - case CLOSED: - case LISTEN: - case TIME_WAIT: - case FIN_WAIT_2: - // We shouldn't need to retransmit anything in this state. -#ifdef UTCP_DEBUG - abort(); -#endif - stop_retransmit_timer(c); - goto cleanup; - } - - start_retransmit_timer(c); - c->rto *= 2; - - if(c->rto > MAX_RTO) { - c->rto = MAX_RTO; - } - - c->rtt_start.tv_sec = 0; // invalidate RTT timer - c->dupack = 0; // cancel any ongoing fast recovery - -cleanup: - return; -} - -static void handle_unreliable(struct utcp_connection *c, const struct hdr *hdr, const void *data, size_t len) { - // Fast path for unfragmented packets - if(!hdr->wnd && !(hdr->ctl & MF)) { - if(c->recv) { - c->recv(c, data, len); - } - - c->rcv.nxt = hdr->seq + len; - return; - } -} - -static void handle_incoming_data(struct utcp_connection *c, const struct hdr *hdr, const void *data, size_t len) { - handle_unreliable(c, hdr, data, len); -} - -ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len) { - const uint8_t *ptr = data; - - if(!utcp) { - errno = EFAULT; - return -1; - } - - if(!len) { - return 0; - } - - if(!data) { - errno = EFAULT; - return -1; - } - - // Drop packets smaller than the header - - struct hdr hdr; - - if(len < sizeof(hdr)) { - print_packet(NULL, "recv", data, len); - errno = EBADMSG; - return -1; - } - - // Make a copy from the potentially unaligned data to a struct hdr - - memcpy(&hdr, ptr, sizeof(hdr)); - - // Try to match the packet to an existing connection - - struct utcp_connection *c = find_connection(utcp, hdr.dst, hdr.src); - print_packet(c, "recv", data, len); - - // Process the header - - ptr += sizeof(hdr); - len -= sizeof(hdr); - - // Drop packets with an unknown CTL flag - - if(hdr.ctl & ~(SYN | ACK | RST | FIN | MF)) { - print_packet(NULL, "recv", data, len); - errno = EBADMSG; - return -1; - } - - // Check for auxiliary headers - - const uint8_t *init = NULL; - - uint16_t aux = hdr.aux; - - while(aux) { - size_t auxlen = 4 * (aux >> 8) & 0xf; - uint8_t auxtype = aux & 0xff; - - if(len < auxlen) { - errno = EBADMSG; - return -1; - } - - switch(auxtype) { - case AUX_INIT: - if(!(hdr.ctl & SYN) || auxlen != 4) { - errno = EBADMSG; - return -1; - } - - init = ptr; - break; - - default: - errno = EBADMSG; - return -1; - } - - len -= auxlen; - ptr += auxlen; - - if(!(aux & 0x800)) { - break; - } - - if(len < 2) { - errno = EBADMSG; - return -1; - } - - memcpy(&aux, ptr, 2); - len -= 2; - ptr += 2; - } - - // Is it for a new connection? - - if(!c) { - // Ignore RST packets - - if(hdr.ctl & RST) { - return 0; - } - - // Is it a SYN packet and are we LISTENing? - - if(hdr.ctl & SYN && !(hdr.ctl & ACK) && utcp->accept) { - // If we don't want to accept it, send a RST back - if((utcp->listen && !utcp->listen(utcp, hdr.dst))) { - len = 1; - goto reset; - } - - // Try to allocate memory, otherwise send a RST back - c = allocate_connection(utcp, hdr.dst, hdr.src); - - if(!c) { - len = 1; - goto reset; - } - - // Parse auxilliary information - if(init) { - if(init[0] < 1) { - len = 1; - goto reset; - } - - c->flags = init[3] & 0x7; - } else { - c->flags = UTCP_UDP; - } - -synack: - // Return SYN+ACK, go to SYN_RECEIVED state - c->snd.wnd = hdr.wnd; - c->rcv.irs = hdr.seq; - c->rcv.nxt = c->rcv.irs + 1; - set_state(c, SYN_RECEIVED); - - struct { - struct hdr hdr; - uint8_t data[4]; - } pkt; - - pkt.hdr.src = c->src; - pkt.hdr.dst = c->dst; - pkt.hdr.ack = c->rcv.irs + 1; - pkt.hdr.seq = c->snd.iss; - pkt.hdr.wnd = c->utcp->mtu; - pkt.hdr.ctl = SYN | ACK; - - if(init) { - pkt.hdr.aux = 0x0101; - pkt.data[0] = 1; - pkt.data[1] = 0; - pkt.data[2] = 0; - pkt.data[3] = c->flags & 0x7; - print_packet(c, "send", &pkt, sizeof(hdr) + 4); - utcp->send(utcp, &pkt, sizeof(hdr) + 4); - } else { - pkt.hdr.aux = 0; - print_packet(c, "send", &pkt, sizeof(hdr)); - utcp->send(utcp, &pkt, sizeof(hdr)); - } - - start_retransmit_timer(c); - } else { - // No, we don't want your packets, send a RST back - len = 1; - goto reset; - } - - return 0; - } - - debug(c, "state %s\n", strstate[c->state]); - - // In case this is for a CLOSED connection, ignore the packet. - // TODO: make it so incoming packets can never match a CLOSED connection. - - if(c->state == CLOSED) { - debug(c, "got packet for closed connection\n"); - goto reset; - } - - // It is for an existing connection. - - // 1. Drop invalid packets. - - // 1a. Drop packets that should not happen in our current state. - - switch(c->state) { - case SYN_SENT: - case SYN_RECEIVED: - case ESTABLISHED: - case FIN_WAIT_1: - case FIN_WAIT_2: - case CLOSE_WAIT: - case CLOSING: - case LAST_ACK: - case TIME_WAIT: - break; - - default: -#ifdef UTCP_DEBUG - abort(); -#endif - break; - } - -#if UTCP_DEBUG - int32_t rcv_offset = seqdiff(hdr.seq, c->rcv.nxt); - - if(rcv_offset) { - debug(c, "packet out of order, offset %u bytes", rcv_offset); - } - -#endif - - c->snd.wnd = hdr.wnd; // TODO: move below - - // 1c. Drop packets with an invalid ACK. - // ackno should not roll back, and it should also not be bigger than what we ever could have sent - // (= snd.una + c->sndbuf.used). - - if(hdr.ack != c->snd.last && c->state >= ESTABLISHED) { - hdr.ack = c->snd.una; - } - - // 2. Handle RST packets - - if(hdr.ctl & RST) { - switch(c->state) { - case SYN_SENT: - if(!(hdr.ctl & ACK)) { - return 0; - } - - // The peer has refused our connection. - set_state(c, CLOSED); - errno = ECONNREFUSED; - - if(c->recv) { - c->recv(c, NULL, 0); - } - - return 0; - - case SYN_RECEIVED: - if(hdr.ctl & ACK) { - return 0; - } - - // We haven't told the application about this connection yet. Silently delete. - free_connection(c); - return 0; - - case ESTABLISHED: - case FIN_WAIT_1: - case FIN_WAIT_2: - case CLOSE_WAIT: - if(hdr.ctl & ACK) { - return 0; - } - - // The peer has aborted our connection. - set_state(c, CLOSED); - errno = ECONNRESET; - - if(c->recv) { - c->recv(c, NULL, 0); - } - - return 0; - - case CLOSING: - case LAST_ACK: - case TIME_WAIT: - if(hdr.ctl & ACK) { - return 0; - } - - // As far as the application is concerned, the connection has already been closed. - // If it has called utcp_close() already, we can immediately free this connection. - if(c->reapable) { - free_connection(c); - return 0; - } - - // Otherwise, immediately move to the CLOSED state. - set_state(c, CLOSED); - return 0; - - default: -#ifdef UTCP_DEBUG - abort(); -#endif - break; - } - } - - uint32_t advanced; - - if(!(hdr.ctl & ACK)) { - advanced = 0; - goto skip_ack; - } - - // 3. Advance snd.una - - if(seqdiff(hdr.ack, c->snd.last) > 0 || seqdiff(hdr.ack, c->snd.una) < 0) { - debug(c, "packet ack seqno out of range, %u <= %u < %u\n", c->snd.una, hdr.ack, c->snd.una + c->sndbuf.used); - goto reset; - } - - advanced = seqdiff(hdr.ack, c->snd.una); - - if(advanced) { - // RTT measurement - if(c->rtt_start.tv_sec) { - if(c->rtt_seq == hdr.ack) { - struct timespec now; - clock_gettime(UTCP_CLOCK, &now); - int32_t diff = timespec_diff_usec(&now, &c->rtt_start); - update_rtt(c, diff); - c->rtt_start.tv_sec = 0; - } else if(c->rtt_seq < hdr.ack) { - debug(c, "cancelling RTT measurement: %u < %u\n", c->rtt_seq, hdr.ack); - c->rtt_start.tv_sec = 0; - } - } - - int32_t data_acked = advanced; - - switch(c->state) { - case SYN_SENT: - case SYN_RECEIVED: - data_acked--; - break; - - // TODO: handle FIN as well. - default: - break; - } - - assert(data_acked >= 0); - -#ifndef NDEBUG - int32_t bufused = seqdiff(c->snd.last, c->snd.una); - assert(data_acked <= bufused); -#endif - - // Also advance snd.nxt if possible - if(seqdiff(c->snd.nxt, hdr.ack) < 0) { - c->snd.nxt = hdr.ack; - } - - c->snd.una = hdr.ack; - - if(c->dupack) { - if(c->dupack >= 3) { - debug(c, "fast recovery ended\n"); - c->snd.cwnd = c->snd.ssthresh; - } - - c->dupack = 0; - } - - // Increase the congestion window according to RFC 5681 - if(c->snd.cwnd < c->snd.ssthresh) { - c->snd.cwnd += min(advanced, utcp->mss); // eq. 2 - } else { - c->snd.cwnd += max(1, (utcp->mss * utcp->mss) / c->snd.cwnd); // eq. 3 - } - - if(c->snd.cwnd > c->utcp->mtu) { - c->snd.cwnd = c->utcp->mtu; - } - - debug_cwnd(c); - - // Check if we have sent a FIN that is now ACKed. - switch(c->state) { - case FIN_WAIT_1: - if(c->snd.una == c->snd.last) { - set_state(c, FIN_WAIT_2); - } - - break; - - case CLOSING: - if(c->snd.una == c->snd.last) { - clock_gettime(UTCP_CLOCK, &c->conn_timeout); - c->conn_timeout.tv_sec += utcp->timeout; - set_state(c, TIME_WAIT); - } - - break; - - default: - break; - } - } - - // 4. Update timers - - if(advanced) { - if(c->snd.una == c->snd.last) { - stop_retransmit_timer(c); - timespec_clear(&c->conn_timeout); - } - } - -skip_ack: - // 5. Process SYN stuff - - if(hdr.ctl & SYN) { - switch(c->state) { - case SYN_SENT: - - // This is a SYNACK. It should always have ACKed the SYN. - if(!advanced) { - goto reset; - } - - c->rcv.irs = hdr.seq; - c->rcv.nxt = hdr.seq + 1; - - if(c->shut_wr) { - c->snd.last++; - set_state(c, FIN_WAIT_1); - } else { - set_state(c, ESTABLISHED); - } - - break; - - case SYN_RECEIVED: - // This is a retransmit of a SYN, send back the SYNACK. - goto synack; - - case ESTABLISHED: - case FIN_WAIT_1: - case FIN_WAIT_2: - case CLOSE_WAIT: - case CLOSING: - case LAST_ACK: - case TIME_WAIT: - // This could be a retransmission. Ignore the SYN flag, but send an ACK back. - break; - - default: -#ifdef UTCP_DEBUG - abort(); -#endif - return 0; - } - } - - // 6. Process new data - - if(c->state == SYN_RECEIVED) { - // This is the ACK after the SYNACK. It should always have ACKed the SYNACK. - if(!advanced) { - goto reset; - } - - // Are we still LISTENing? - if(utcp->accept) { - utcp->accept(c, c->src); - } - - if(c->state != ESTABLISHED) { - set_state(c, CLOSED); - c->reapable = true; - goto reset; - } - } - - if(len) { - switch(c->state) { - case SYN_SENT: - case SYN_RECEIVED: - // This should never happen. -#ifdef UTCP_DEBUG - abort(); -#endif - return 0; - - case ESTABLISHED: - break; - - case FIN_WAIT_1: - case FIN_WAIT_2: - if(c->reapable) { - // We already closed the connection and are not interested in more data. - goto reset; - } - - break; - - case CLOSE_WAIT: - case CLOSING: - case LAST_ACK: - case TIME_WAIT: - // Ehm no, We should never receive more data after a FIN. - goto reset; - - default: -#ifdef UTCP_DEBUG - abort(); -#endif - return 0; - } - - handle_incoming_data(c, &hdr, ptr, len); - } - - // 7. Process FIN stuff - - if(hdr.ctl & FIN) { - switch(c->state) { - case SYN_SENT: - case SYN_RECEIVED: - // This should never happen. -#ifdef UTCP_DEBUG - abort(); -#endif - break; - - case ESTABLISHED: - set_state(c, CLOSE_WAIT); - break; - - case FIN_WAIT_1: - set_state(c, CLOSING); - break; - - case FIN_WAIT_2: - clock_gettime(UTCP_CLOCK, &c->conn_timeout); - c->conn_timeout.tv_sec += utcp->timeout; - set_state(c, TIME_WAIT); - break; - - case CLOSE_WAIT: - case CLOSING: - case LAST_ACK: - case TIME_WAIT: - // Ehm, no. We should never receive a second FIN. - goto reset; - - default: -#ifdef UTCP_DEBUG - abort(); -#endif - break; - } - - // FIN counts as one sequence number - c->rcv.nxt++; - len++; - - // Inform the application that the peer closed its end of the connection. - if(c->recv) { - errno = 0; - c->recv(c, NULL, 0); - } - } - - // Now we send something back if: - // - we received data, so we have to send back an ACK - // -> sendatleastone = true - // - or we got an ack, so we should maybe send a bit more data - // -> sendatleastone = false - - if(hdr.ctl & SYN || hdr.ctl & FIN) { - ack(c, NULL, 0); - } - - return 0; - -reset: - swap_ports(&hdr); - hdr.wnd = 0; - hdr.aux = 0; - - if(hdr.ctl & ACK) { - hdr.seq = hdr.ack; - hdr.ctl = RST; - } else { - hdr.ack = hdr.seq + len; - hdr.seq = 0; - hdr.ctl = RST | ACK; - } - - print_packet(c, "send", &hdr, sizeof(hdr)); - utcp->send(utcp, &hdr, sizeof(hdr)); - return 0; - -} - -int utcp_shutdown(struct utcp_connection *c, int dir) { - debug(c, "shutdown %d at %u\n", dir, c ? c->snd.last : 0); - - if(!c) { - errno = EFAULT; - return -1; - } - - if(c->reapable) { - debug(c, "shutdown() called on closed connection\n"); - errno = EBADF; - return -1; - } - - if(!(dir == UTCP_SHUT_RD || dir == UTCP_SHUT_WR || dir == UTCP_SHUT_RDWR)) { - errno = EINVAL; - return -1; - } - - // TCP does not have a provision for stopping incoming packets. - // The best we can do is to just ignore them. - if(dir == UTCP_SHUT_RD || dir == UTCP_SHUT_RDWR) { - c->recv = NULL; - } - - // The rest of the code deals with shutting down writes. - if(dir == UTCP_SHUT_RD) { - return 0; - } - - // Only process shutting down writes once. - if(c->shut_wr) { - return 0; - } - - c->shut_wr = true; - - switch(c->state) { - case CLOSED: - case LISTEN: - errno = ENOTCONN; - return -1; - - case SYN_SENT: - return 0; - - case SYN_RECEIVED: - case ESTABLISHED: - set_state(c, FIN_WAIT_1); - break; - - case FIN_WAIT_1: - case FIN_WAIT_2: - return 0; - - case CLOSE_WAIT: - set_state(c, CLOSING); - break; - - case CLOSING: - case LAST_ACK: - case TIME_WAIT: - return 0; - } - - c->snd.last++; - - ack(c, NULL, 0); - - if(!timespec_isset(&c->rtrx_timeout)) { - start_retransmit_timer(c); - } - - return 0; -} - -static bool reset_connection(struct utcp_connection *c) { - if(!c) { - errno = EFAULT; - return false; - } - - if(c->reapable) { - debug(c, "abort() called on closed connection\n"); - errno = EBADF; - return false; - } - - switch(c->state) { - case CLOSED: - return true; - - case LISTEN: - case SYN_SENT: - case CLOSING: - case LAST_ACK: - case TIME_WAIT: - set_state(c, CLOSED); - return true; - - case SYN_RECEIVED: - case ESTABLISHED: - case FIN_WAIT_1: - case FIN_WAIT_2: - case CLOSE_WAIT: - set_state(c, CLOSED); - break; - } - - // Send RST - - struct hdr hdr; - - hdr.src = c->src; - hdr.dst = c->dst; - hdr.seq = c->snd.nxt; - hdr.ack = c->rcv.nxt; - hdr.wnd = 0; - hdr.ctl = RST; - hdr.aux = 0; - - print_packet(c, "send", &hdr, sizeof(hdr)); - c->utcp->send(c->utcp, &hdr, sizeof(hdr)); - return true; -} - -static void set_reapable(struct utcp_connection *c) { - c->recv = NULL; - c->reapable = true; -} - -// Resets all connections, but does not invalidate connection handles -void utcp_reset_all_connections(struct utcp *utcp) { - if(!utcp) { - errno = EINVAL; - return; - } - - for(int i = 0; i < utcp->nconnections; i++) { - struct utcp_connection *c = utcp->connections[i]; - - if(c->reapable || c->state == CLOSED) { - continue; - } - - reset_connection(c); - - if(c->recv) { - errno = 0; - c->recv(c, NULL, 0); - } - } - - return; -} - -int utcp_close(struct utcp_connection *c) { - if(utcp_shutdown(c, SHUT_RDWR) && errno != ENOTCONN) { - return -1; - } - - set_reapable(c); - return 0; -} - -int utcp_abort(struct utcp_connection *c) { - if(!reset_connection(c)) { - return -1; - } - - set_reapable(c); - return 0; -} - -/* Handle timeouts. - * One call to this function will loop through all connections, - * checking if something needs to be resent or not. - * The return value is the time to the next timeout in milliseconds, - * or maybe a negative value if the timeout is infinite. - */ -struct timespec utcp_timeout(struct utcp *utcp) { - struct timespec now; - clock_gettime(UTCP_CLOCK, &now); - struct timespec next = {now.tv_sec + 3600, now.tv_nsec}; - - for(int i = 0; i < utcp->nconnections; i++) { - struct utcp_connection *c = utcp->connections[i]; - - if(!c) { - continue; - } - - // delete connections that have been utcp_close()d. - if(c->state == CLOSED) { - if(c->reapable) { - debug(c, "reaping\n"); - free_connection(c); - i--; - } - - continue; - } - - if(timespec_isset(&c->conn_timeout) && timespec_lt(&c->conn_timeout, &now)) { - errno = ETIMEDOUT; - c->state = CLOSED; - - if(c->recv) { - c->recv(c, NULL, 0); - } - - continue; - } - - if(timespec_isset(&c->rtrx_timeout) && timespec_lt(&c->rtrx_timeout, &now)) { - debug(c, "retransmitting after timeout\n"); - retransmit(c); - } - - if(timespec_isset(&c->conn_timeout) && timespec_lt(&c->conn_timeout, &next)) { - next = c->conn_timeout; - } - - if(timespec_isset(&c->rtrx_timeout) && timespec_lt(&c->rtrx_timeout, &next)) { - next = c->rtrx_timeout; - } - } - - struct timespec diff; - - timespec_sub(&next, &now, &diff); - - return diff; -} - -bool utcp_is_active(struct utcp *utcp) { - if(!utcp) { - return false; - } - - for(int i = 0; i < utcp->nconnections; i++) - if(utcp->connections[i]->state != CLOSED && utcp->connections[i]->state != TIME_WAIT) { - return true; - } - - return false; -} - -struct utcp *utcp_init(utcp_accept_t accept, utcp_listen_t listen, utcp_send_t send, void *priv) { - if(!send) { - errno = EFAULT; - return NULL; - } - - struct utcp *utcp = calloc(1, sizeof(*utcp)); - - if(!utcp) { - return NULL; - } - - utcp_set_mtu(utcp, DEFAULT_MTU); - - if(!utcp->pkt) { - free(utcp); - return NULL; - } - - if(!CLOCK_GRANULARITY) { - struct timespec res; - clock_getres(UTCP_CLOCK, &res); - CLOCK_GRANULARITY = res.tv_sec * USEC_PER_SEC + res.tv_nsec / 1000; - } - - utcp->accept = accept; - utcp->listen = listen; - utcp->send = send; - utcp->priv = priv; - utcp->timeout = DEFAULT_USER_TIMEOUT; // sec - - return utcp; -} - -void utcp_exit(struct utcp *utcp) { - if(!utcp) { - return; - } - - for(int i = 0; i < utcp->nconnections; i++) { - struct utcp_connection *c = utcp->connections[i]; - - if(!c->reapable) { - if(c->recv) { - c->recv(c, NULL, 0); - } - } - - free(c); - } - - free(utcp->connections); - free(utcp->pkt); - free(utcp); -} - -uint16_t utcp_get_mtu(struct utcp *utcp) { - return utcp ? utcp->mtu : 0; -} - -uint16_t utcp_get_mss(struct utcp *utcp) { - return utcp ? utcp->mss : 0; -} - -void utcp_set_mtu(struct utcp *utcp, uint16_t mtu) { - if(!utcp) { - return; - } - - if(mtu <= sizeof(struct hdr)) { - return; - } - - if(mtu > utcp->mtu) { - char *new = realloc(utcp->pkt, mtu + sizeof(struct hdr)); - - if(!new) { - return; - } - - utcp->pkt = new; - } - - utcp->mtu = mtu; - utcp->mss = mtu - sizeof(struct hdr); -} - -void utcp_reset_timers(struct utcp *utcp) { - if(!utcp) { - return; - } - - struct timespec now, then; - - clock_gettime(UTCP_CLOCK, &now); - - then = now; - - then.tv_sec += utcp->timeout; - - for(int i = 0; i < utcp->nconnections; i++) { - struct utcp_connection *c = utcp->connections[i]; - - if(c->reapable) { - continue; - } - - if(timespec_isset(&c->rtrx_timeout)) { - c->rtrx_timeout = now; - } - - if(timespec_isset(&c->conn_timeout)) { - c->conn_timeout = then; - } - - c->rtt_start.tv_sec = 0; - - if(c->rto > START_RTO) { - c->rto = START_RTO; - } - } -} - -int utcp_get_user_timeout(struct utcp *u) { - return u ? u->timeout : 0; -} - -void utcp_set_user_timeout(struct utcp *u, int timeout) { - if(u) { - u->timeout = timeout; - } -} - -bool utcp_get_nodelay(struct utcp_connection *c) { - return c ? c->nodelay : false; -} - -void utcp_set_nodelay(struct utcp_connection *c, bool nodelay) { - if(c) { - c->nodelay = nodelay; - } -} - -bool utcp_get_keepalive(struct utcp_connection *c) { - return c ? c->keepalive : false; -} - -void utcp_set_keepalive(struct utcp_connection *c, bool keepalive) { - if(c) { - c->keepalive = keepalive; - } -} - -void utcp_set_recv_cb(struct utcp_connection *c, utcp_recv_t recv) { - if(c) { - c->recv = recv; - } -} - -void utcp_set_accept_cb(struct utcp *utcp, utcp_accept_t accept, utcp_listen_t listen) { - if(utcp) { - utcp->accept = accept; - utcp->listen = listen; - } -} - -void utcp_expect_data(struct utcp_connection *c, bool expect) { - if(!c || c->reapable) { - return; - } - - if(!(c->state == ESTABLISHED || c->state == FIN_WAIT_1 || c->state == FIN_WAIT_2)) { - return; - } - - if(expect) { - // If we expect data, start the connection timer. - if(!timespec_isset(&c->conn_timeout)) { - clock_gettime(UTCP_CLOCK, &c->conn_timeout); - c->conn_timeout.tv_sec += c->utcp->timeout; - } - } else { - // If we want to cancel expecting data, only clear the timer when there is no unACKed data. - if(c->snd.una == c->snd.last) { - timespec_clear(&c->conn_timeout); - } - } -} - -void utcp_set_flags(struct utcp_connection *c, uint32_t flags) { - c->flags &= ~UTCP_CHANGEABLE_FLAGS; - c->flags |= flags & UTCP_CHANGEABLE_FLAGS; -} - -void utcp_offline(struct utcp *utcp, bool offline) { - struct timespec now; - clock_gettime(UTCP_CLOCK, &now); - - for(int i = 0; i < utcp->nconnections; i++) { - struct utcp_connection *c = utcp->connections[i]; - - if(c->reapable) { - continue; - } - - utcp_expect_data(c, offline); - - if(!offline) { - if(timespec_isset(&c->rtrx_timeout)) { - c->rtrx_timeout = now; - } - - utcp->connections[i]->rtt_start.tv_sec = 0; - - if(c->rto > START_RTO) { - c->rto = START_RTO; - } - } - } -} - -void utcp_set_clock_granularity(long granularity) { - CLOCK_GRANULARITY = granularity; -} diff --git a/src/utcp.h b/src/utcp.h deleted file mode 100644 index e34d96c..0000000 --- a/src/utcp.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - utcp.h -- Userspace TCP - Copyright (C) 2014 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 UTCP_H -#define UTCP_H - -#include -#include -#include -// TODO: Windows -#include - -#ifndef UTCP_INTERNAL -struct utcp { - void *priv; -}; - -struct utcp_connection { - void *priv; - struct utcp *const utcp; - const uint32_t flags; -}; -#else -struct utcp; -struct utcp_connection; -#endif - -#define UTCP_SHUT_RD 0 -#define UTCP_SHUT_WR 1 -#define UTCP_SHUT_RDWR 2 - -//#define UTCP_ORDERED 1 -//#define UTCP_RELIABLE 2 -//#define UTCP_FRAMED 4 -//#define UTCP_DROP_LATE 8 -//#define UTCP_NO_PARTIAL 16 - -//#define UTCP_TCP 3 -#define UTCP_UDP 0 -#define UTCP_CHANGEABLE_FLAGS 0x0U - -typedef bool (*utcp_listen_t)(struct utcp *utcp, uint16_t port); -typedef void (*utcp_accept_t)(struct utcp_connection *utcp_connection, uint16_t port); - -typedef ssize_t (*utcp_send_t)(struct utcp *utcp, const void *data, size_t len); -typedef ssize_t (*utcp_recv_t)(struct utcp_connection *connection, const void *data, size_t len); - - -struct utcp *utcp_init(utcp_accept_t accept, utcp_listen_t listen, utcp_send_t send, void *priv); -void utcp_exit(struct utcp *utcp); - -struct utcp_connection *utcp_connect_ex(struct utcp *utcp, uint16_t port, utcp_recv_t recv, void *priv, uint32_t flags); -void utcp_accept(struct utcp_connection *utcp, utcp_recv_t recv, void *priv); -ssize_t utcp_send(struct utcp_connection *connection, const void *data, size_t len); -ssize_t utcp_recv(struct utcp *utcp, const void *data, size_t len); -int utcp_close(struct utcp_connection *connection); -int utcp_abort(struct utcp_connection *connection); -int utcp_shutdown(struct utcp_connection *connection, int how); -struct timespec utcp_timeout(struct utcp *utcp); -void utcp_set_recv_cb(struct utcp_connection *connection, utcp_recv_t recv); -void utcp_set_accept_cb(struct utcp *utcp, utcp_accept_t accept, utcp_listen_t listen); -bool utcp_is_active(struct utcp *utcp); -void utcp_reset_all_connections(struct utcp *utcp); - -// Global socket options - -int utcp_get_user_timeout(struct utcp *utcp); -void utcp_set_user_timeout(struct utcp *utcp, int seconds); - -uint16_t utcp_get_mtu(struct utcp *utcp); -uint16_t utcp_get_mss(struct utcp *utcp); -void utcp_set_mtu(struct utcp *utcp, uint16_t mtu); - -void utcp_reset_timers(struct utcp *utcp); - -void utcp_offline(struct utcp *utcp, bool offline); - -// Per-socket options - -bool utcp_get_nodelay(struct utcp_connection *connection); -void utcp_set_nodelay(struct utcp_connection *connection, bool nodelay); - -bool utcp_get_keepalive(struct utcp_connection *connection); -void utcp_set_keepalive(struct utcp_connection *connection, bool keepalive); - -void utcp_expect_data(struct utcp_connection *connection, bool expect); - -void utcp_set_flags(struct utcp_connection *connection, uint32_t flags); - -// Completely global options - -void utcp_set_clock_granularity(long granularity); - -#endif diff --git a/src/utcp_priv.h b/src/utcp_priv.h deleted file mode 100644 index 2c41349..0000000 --- a/src/utcp_priv.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - utcp.h -- Userspace TCP - Copyright (C) 2014 Guus Sliepen - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 UTCP_PRIV_H -#define UTCP_PRIV_H - -#define UTCP_INTERNAL -#include "utcp.h" - -#define PREP(l) char pkt[(l) + sizeof struct hdr]; struct hdr *hdr = &pkt; - -#define SYN 1 -#define ACK 2 -#define FIN 4 -#define RST 8 -#define MF 16 - -#define AUX_INIT 1 -#define AUX_FRAME 2 -#define AUX_SAK 3 -#define AUX_TIMESTAMP 4 - -#define MAX_UNRELIABLE_SIZE 16777215 -#define DEFAULT_MTU 1000 - -#define USEC_PER_SEC 1000000L -#define NSEC_PER_SEC 1000000000L -#define DEFAULT_USER_TIMEOUT 60 -#define START_RTO (1 * USEC_PER_SEC) -#define MAX_RTO (3 * USEC_PER_SEC) - -struct hdr { - uint16_t src; // Source port - uint16_t dst; // Destination port - uint32_t seq; // Sequence number - uint32_t ack; // Acknowledgement number - uint32_t wnd; // Window size - uint16_t ctl; // Flags (SYN, ACK, FIN, RST) - uint16_t aux; // other stuff -}; - -enum state { - CLOSED, - LISTEN, - SYN_SENT, - SYN_RECEIVED, - ESTABLISHED, - FIN_WAIT_1, - FIN_WAIT_2, - CLOSE_WAIT, - CLOSING, - LAST_ACK, - TIME_WAIT -}; - -static const char *strstate[] __attribute__((unused)) = { - [CLOSED] = "CLOSED", - [LISTEN] = "LISTEN", - [SYN_SENT] = "SYN_SENT", - [SYN_RECEIVED] = "SYN_RECEIVED", - [ESTABLISHED] = "ESTABLISHED", - [FIN_WAIT_1] = "FIN_WAIT_1", - [FIN_WAIT_2] = "FIN_WAIT_2", - [CLOSE_WAIT] = "CLOSE_WAIT", - [CLOSING] = "CLOSING", - [LAST_ACK] = "LAST_ACK", - [TIME_WAIT] = "TIME_WAIT" -}; - -struct utcp_connection { - void *priv; - struct utcp *utcp; - uint32_t flags; - - bool reapable; - - // Callbacks - - utcp_recv_t recv; - - // TCP State - - uint16_t src; - uint16_t dst; - enum state state; - - struct { - uint32_t una; - uint32_t nxt; - uint32_t wnd; - uint32_t iss; - - uint32_t last; - uint32_t cwnd; - uint32_t ssthresh; - } snd; - - struct { - uint32_t nxt; - uint32_t irs; - } rcv; - - int dupack; - - // Timers - - struct timespec conn_timeout; - struct timespec rtrx_timeout; - struct timespec rtt_start; - uint32_t rtt_seq; - - // RTT variables - - uint32_t srtt; // usec - uint32_t rttvar; // usec - uint32_t rto; // usec - - // Per-socket options - - bool nodelay; - bool keepalive; - bool shut_wr; - - // Congestion avoidance state - - struct timespec tlast; - uint64_t bandwidth; -}; - -struct utcp { - void *priv; - - // Callbacks - - utcp_accept_t accept; - utcp_listen_t listen; - utcp_send_t send; - - // Packet buffer - - void *pkt; - - // Global socket options - - uint16_t mtu; // The maximum size of a UTCP packet, including headers. - uint16_t mss; // The maximum size of the payload of a UTCP packet. - int timeout; // sec - - // Connection management - - struct utcp_connection **connections; - int nconnections; - int nallocated; -}; - -#endif -- 2.39.5