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.
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
meshlink_queue.h \
meta.c meta.h \
net.c net.h \
- net_packet.c \
net_setup.c \
net_socket.c \
netutl.c netutl.h \
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 \
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)
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"
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 */
node_t *n = (node_t *)node;
connection_t *c = n->connection;
- n->last_req_key = -3600;
-
if(c) {
c->last_key_renewal = -3600;
}
namespace meshlink {
class mesh;
class node;
-class channel;
/// Severity of log messages generated by MeshLink.
typedef meshlink_log_level_t log_level_t;
*/
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:
(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.
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);
}
/** 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.
*
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,
that->connection_try(static_cast<node *>(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<mesh *>(handle->priv);
- return that->channel_listen(static_cast<meshlink::node *>(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<mesh *>(handle->priv);
- bool accepted = that->channel_accept(static_cast<meshlink::channel *>(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<mesh *>(handle->priv);
- that->channel_receive(static_cast<meshlink::channel *>(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<mesh *>(handle->priv);
- that->channel_poll(static_cast<meshlink::channel *>(channel), len);
- }
-
meshlink_handle_t *handle;
};
/// 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;
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.
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.
* 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.
/// 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.
/** 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.
*
*/
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.
#include "packmsg.h"
#include "prf.h"
#include "protocol.h"
-#include "route.h"
#include "sockaddr.h"
#include "utils.h"
#include "xalloc.h"
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) {
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);
return true;
}
- if(!data) {
+ if(!data || len > MTU) {
meshlink_errno = MESHLINK_EINVAL;
return false;
}
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)) {
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);
}
}
// @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);
}
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);
static void __attribute__((constructor)) meshlink_init(void) {
crypto_init();
- utcp_set_clock_granularity(10000);
}
static void __attribute__((destructor)) meshlink_exit(void) {
// 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;
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;
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);
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') {
}
// 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);
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) {
#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"
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__));
+++ /dev/null
-/*
- net_packet.c -- Handles in- and outgoing VPN packets
- Copyright (C) 2014-2017 Guus Sliepen <guus@meshlink.io>
-
- 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;
-}
#include "netutl.h"
#include "packmsg.h"
#include "protocol.h"
-#include "route.h"
#include "utils.h"
#include "xalloc.h"
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);
#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 */
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) */
[KEY_CHANGED] = key_changed_h,
[REQ_KEY] = req_key_h,
[ANS_KEY] = ans_key_h,
+ [PACKET] = raw_packet_h,
};
/* Request names */
[KEY_CHANGED] = "KEY_CHANGED",
[REQ_KEY] = "REQ_KEY",
[ANS_KEY] = "ANS_KEY",
+ [PACKET] = "PACKET",
};
bool check_id(const char *id) {
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.
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 */
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
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) {
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;
}
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;
}
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;
}
#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;
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
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;
+}
+++ /dev/null
-/*
- route.c -- routing
- Copyright (C) 2014 Guus Sliepen <guus@meshlink.io>
-
- 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;
-}
+++ /dev/null
-#ifndef MESHLINK_ROUTE_H
-#define MESHLINK_ROUTE_H
-
-/*
- route.h -- header file for route.c
- Copyright (C) 2014, 2017 Guus Sliepen <guus@meshlink.io>
-
- 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
+++ /dev/null
-/*
- utcp-test.c -- Test application for UTCP
- Copyright (C) 2014-2020 Guus Sliepen <guus@tinc-vpn.org>
-
- 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 <poll.h>
-#include <time.h>
-
-#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;
-}
+++ /dev/null
-/*
- utcp.c -- Userspace TCP
- Copyright (C) 2014-2017 Guus Sliepen <guus@tinc-vpn.org>
-
- 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 <time.h>
-
-#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 <stdarg.h>
-
-#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;
-}
+++ /dev/null
-/*
- utcp.h -- Userspace TCP
- Copyright (C) 2014 Guus Sliepen <guus@tinc-vpn.org>
-
- 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 <unistd.h>
-#include <stdint.h>
-#include <stdbool.h>
-// TODO: Windows
-#include <sys/time.h>
-
-#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
+++ /dev/null
-/*
- utcp.h -- Userspace TCP
- Copyright (C) 2014 Guus Sliepen <guus@tinc-vpn.org>
-
- 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