mirror of https://github.com/oxen-io/lokinet
parent
1839da9c3c
commit
085563ac2f
@ -0,0 +1,15 @@
|
||||
#ifndef LLARP_LINK_UTP_HPP
|
||||
#define LLARP_LINK_UTP_HPP
|
||||
|
||||
#include <llarp/link_layer.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace utp
|
||||
{
|
||||
std::unique_ptr< ILinkLayer >
|
||||
NewServer(llarp_router* r);
|
||||
}
|
||||
} // namespace llarp
|
||||
|
||||
#endif
|
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __UTP_H__
|
||||
#define __UTP_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "utp_types.h"
|
||||
|
||||
typedef struct UTPSocket utp_socket;
|
||||
typedef struct struct_utp_context utp_context;
|
||||
|
||||
enum
|
||||
{
|
||||
UTP_UDP_DONTFRAG = 2, // Used to be a #define as UDP_IP_DONTFRAG
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
// socket has reveived syn-ack (notification only for outgoing connection
|
||||
// completion) this implies writability
|
||||
UTP_STATE_CONNECT = 1,
|
||||
|
||||
// socket is able to send more data
|
||||
UTP_STATE_WRITABLE = 2,
|
||||
|
||||
// connection closed
|
||||
UTP_STATE_EOF = 3,
|
||||
|
||||
// socket is being destroyed, meaning all data has been sent if possible.
|
||||
// it is not valid to refer to the socket after this state change occurs
|
||||
UTP_STATE_DESTROYING = 4,
|
||||
};
|
||||
|
||||
extern const char *utp_state_names[];
|
||||
|
||||
// Errors codes that can be passed to UTP_ON_ERROR callback
|
||||
enum
|
||||
{
|
||||
UTP_ECONNREFUSED = 0,
|
||||
UTP_ECONNRESET,
|
||||
UTP_ETIMEDOUT,
|
||||
};
|
||||
|
||||
extern const char *utp_error_code_names[];
|
||||
|
||||
enum
|
||||
{
|
||||
// callback names
|
||||
UTP_ON_FIREWALL = 0,
|
||||
UTP_ON_ACCEPT,
|
||||
UTP_ON_CONNECT,
|
||||
UTP_ON_ERROR,
|
||||
UTP_ON_READ,
|
||||
UTP_ON_OVERHEAD_STATISTICS,
|
||||
UTP_ON_STATE_CHANGE,
|
||||
UTP_GET_READ_BUFFER_SIZE,
|
||||
UTP_ON_DELAY_SAMPLE,
|
||||
UTP_GET_UDP_MTU,
|
||||
UTP_GET_UDP_OVERHEAD,
|
||||
UTP_GET_MILLISECONDS,
|
||||
UTP_GET_MICROSECONDS,
|
||||
UTP_GET_RANDOM,
|
||||
UTP_LOG,
|
||||
UTP_SENDTO,
|
||||
|
||||
// context and socket options that may be set/queried
|
||||
UTP_LOG_NORMAL,
|
||||
UTP_LOG_MTU,
|
||||
UTP_LOG_DEBUG,
|
||||
UTP_SNDBUF,
|
||||
UTP_RCVBUF,
|
||||
UTP_TARGET_DELAY,
|
||||
|
||||
UTP_ARRAY_SIZE, // must be last
|
||||
};
|
||||
|
||||
extern const char *utp_callback_names[];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
utp_context *context;
|
||||
utp_socket *socket;
|
||||
size_t len;
|
||||
uint32 flags;
|
||||
int callback_type;
|
||||
const byte *buf;
|
||||
|
||||
union {
|
||||
const struct sockaddr *address;
|
||||
int send;
|
||||
int sample_ms;
|
||||
int error_code;
|
||||
int state;
|
||||
};
|
||||
|
||||
union {
|
||||
socklen_t address_len;
|
||||
int type;
|
||||
};
|
||||
} utp_callback_arguments;
|
||||
|
||||
typedef uint64
|
||||
utp_callback_t(utp_callback_arguments *);
|
||||
|
||||
// Returned by utp_get_context_stats()
|
||||
typedef struct
|
||||
{
|
||||
uint32 _nraw_recv[5]; // total packets recieved less than 300/600/1200/MTU
|
||||
// bytes fpr all connections (context-wide)
|
||||
uint32 _nraw_send[5]; // total packets sent less than 300/600/1200/MTU
|
||||
// bytes for all connections (context-wide)
|
||||
} utp_context_stats;
|
||||
|
||||
// Returned by utp_get_stats()
|
||||
typedef struct
|
||||
{
|
||||
uint64 nbytes_recv; // total bytes received
|
||||
uint64 nbytes_xmit; // total bytes transmitted
|
||||
uint32 rexmit; // retransmit counter
|
||||
uint32 fastrexmit; // fast retransmit counter
|
||||
uint32 nxmit; // transmit counter
|
||||
uint32 nrecv; // receive counter (total)
|
||||
uint32 nduprecv; // duplicate receive counter
|
||||
uint32 mtu_guess; // Best guess at MTU
|
||||
} utp_socket_stats;
|
||||
|
||||
#define UTP_IOV_MAX 1024
|
||||
|
||||
// For utp_writev, to writes data from multiple buffers
|
||||
struct utp_iovec
|
||||
{
|
||||
void *iov_base;
|
||||
size_t iov_len;
|
||||
};
|
||||
|
||||
// Public Functions
|
||||
utp_context *
|
||||
utp_init(int version);
|
||||
void
|
||||
utp_destroy(utp_context *ctx);
|
||||
void
|
||||
utp_set_callback(utp_context *ctx, int callback_name, utp_callback_t *proc);
|
||||
void *
|
||||
utp_context_set_userdata(utp_context *ctx, void *userdata);
|
||||
void *
|
||||
utp_context_get_userdata(utp_context *ctx);
|
||||
int
|
||||
utp_context_set_option(utp_context *ctx, int opt, int val);
|
||||
int
|
||||
utp_context_get_option(utp_context *ctx, int opt);
|
||||
int
|
||||
utp_process_udp(utp_context *ctx, const byte *buf, size_t len,
|
||||
const struct sockaddr *to, socklen_t tolen);
|
||||
int
|
||||
utp_process_icmp_error(utp_context *ctx, const byte *buffer, size_t len,
|
||||
const struct sockaddr *to, socklen_t tolen);
|
||||
int
|
||||
utp_process_icmp_fragmentation(utp_context *ctx, const byte *buffer,
|
||||
size_t len, const struct sockaddr *to,
|
||||
socklen_t tolen, uint16 next_hop_mtu);
|
||||
void
|
||||
utp_check_timeouts(utp_context *ctx);
|
||||
void
|
||||
utp_issue_deferred_acks(utp_context *ctx);
|
||||
utp_context_stats *
|
||||
utp_get_context_stats(utp_context *ctx);
|
||||
utp_socket *
|
||||
utp_create_socket(utp_context *ctx);
|
||||
void *
|
||||
utp_set_userdata(utp_socket *s, void *userdata);
|
||||
void *
|
||||
utp_get_userdata(utp_socket *s);
|
||||
int
|
||||
utp_setsockopt(utp_socket *s, int opt, int val);
|
||||
int
|
||||
utp_getsockopt(utp_socket *s, int opt);
|
||||
int
|
||||
utp_connect(utp_socket *s, const struct sockaddr *to, socklen_t tolen);
|
||||
ssize_t
|
||||
utp_write(utp_socket *s, void *buf, size_t count);
|
||||
ssize_t
|
||||
utp_writev(utp_socket *s, struct utp_iovec *iovec, size_t num_iovecs);
|
||||
int
|
||||
utp_getpeername(utp_socket *s, struct sockaddr *addr, socklen_t *addrlen);
|
||||
void
|
||||
utp_read_drained(utp_socket *s);
|
||||
int
|
||||
utp_get_delays(utp_socket *s, uint32 *ours, uint32 *theirs, uint32 *age);
|
||||
utp_socket_stats *
|
||||
utp_get_stats(utp_socket *s);
|
||||
utp_context *
|
||||
utp_get_context(utp_socket *s);
|
||||
void
|
||||
utp_shutdown(utp_socket *s, int how);
|
||||
void
|
||||
utp_close(utp_socket *s);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__UTP_H__
|
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __UTP_TYPES_H__
|
||||
#define __UTP_TYPES_H__
|
||||
|
||||
// Allow libutp consumers or prerequisites to override PACKED_ATTRIBUTE
|
||||
#ifndef PACKED_ATTRIBUTE
|
||||
#if defined BROKEN_GCC_STRUCTURE_PACKING && defined __GNUC__
|
||||
// Used for gcc tool chains accepting but not supporting pragma pack
|
||||
// See http://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html
|
||||
#define PACKED_ATTRIBUTE __attribute__((__packed__))
|
||||
#else
|
||||
#define PACKED_ATTRIBUTE
|
||||
#endif // defined BROKEN_GCC_STRUCTURE_PACKING && defined __GNUC__
|
||||
#endif // ndef PACKED_ATTRIBUTE
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define ALIGNED_ATTRIBUTE(x) __attribute__((aligned(x)))
|
||||
#else
|
||||
#define ALIGNED_ATTRIBUTE(x)
|
||||
#endif
|
||||
|
||||
// hash.cpp needs socket definitions, which is why this networking specific
|
||||
// code is inclued in utypes.h
|
||||
#ifdef WIN32
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define IP_OPT_DONTFRAG IP_DONTFRAGMENT
|
||||
#define SHUT_RD SD_RECEIVE
|
||||
#define SHUT_WR SD_SEND
|
||||
#define SHUT_RDWR SD_BOTH
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifdef IP_DONTFRAG
|
||||
#define IP_OPT_DONTFRAG IP_DONTFRAG
|
||||
#elif defined IP_DONTFRAGMENT
|
||||
#define IP_OPT_DONTFRAG IP_DONTFRAGMENT
|
||||
#else
|
||||
//#warning "I don't know how to set DF bit on this system"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <BaseTsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#endif
|
||||
|
||||
#ifdef POSIX
|
||||
typedef struct sockaddr_storage SOCKADDR_STORAGE;
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define I64u "%I64u"
|
||||
#else
|
||||
#define I64u "%Lu"
|
||||
#endif
|
||||
|
||||
// standard types
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned char uint8;
|
||||
typedef signed char int8;
|
||||
typedef unsigned short uint16;
|
||||
typedef signed short int16;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned int uint32;
|
||||
typedef signed int int32;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned __int64 uint64;
|
||||
typedef signed __int64 int64;
|
||||
#else
|
||||
typedef unsigned long long uint64;
|
||||
typedef long long int64;
|
||||
#endif
|
||||
|
||||
/* compile-time assert */
|
||||
#ifndef CASSERT
|
||||
#define CASSERT(exp, name) typedef int is_not_##name[(exp) ? 1 : -1];
|
||||
#endif
|
||||
|
||||
CASSERT(8 == sizeof(uint64), sizeof_uint64_is_8)
|
||||
CASSERT(8 == sizeof(int64), sizeof_int64_is_8)
|
||||
|
||||
#ifndef INT64_MAX
|
||||
#define INT64_MAX 0x7fffffffffffffffLL
|
||||
#endif
|
||||
|
||||
// always ANSI
|
||||
typedef const char* cstr;
|
||||
typedef char* str;
|
||||
|
||||
#ifndef __cplusplus
|
||||
typedef uint8 bool;
|
||||
#endif
|
||||
|
||||
#endif //__UTP_TYPES_H__
|
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,48 @@
|
||||
OBJS = utp_internal.o utp_utils.o utp_hash.o utp_callbacks.o utp_api.o utp_packedsockaddr.o
|
||||
CFLAGS = -Wall -DPOSIX -g -fno-exceptions $(OPT)
|
||||
OPT ?= -O3
|
||||
CXXFLAGS = $(CFLAGS) -fPIC -fno-rtti
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
|
||||
CXXFLAGS += -Wno-sign-compare
|
||||
CXXFLAGS += -fpermissive
|
||||
|
||||
# Uncomment to enable utp_get_stats(), and a few extra sanity checks
|
||||
#CFLAGS += -D_DEBUG
|
||||
|
||||
# Uncomment to enable debug logging
|
||||
#CFLAGS += -DUTP_DEBUG_LOGGING
|
||||
|
||||
# Dynamically determine if librt is available. If so, assume we need to link
|
||||
# against it for clock_gettime(2). This is required for clean builds on OSX;
|
||||
# see <https://github.com/bittorrent/libutp/issues/1> for more. This should
|
||||
# probably be ported to CMake at some point, but is suitable for now.
|
||||
lrt := $(shell echo 'int main() {}' | $(CC) -xc -o /dev/null - -lrt >/dev/null 2>&1; echo $$?)
|
||||
ifeq ($(strip $(lrt)),0)
|
||||
LDFLAGS += -lrt
|
||||
endif
|
||||
|
||||
all: libutp.so libutp.a ucat ucat-static
|
||||
|
||||
libutp.so: $(OBJS)
|
||||
$(CXX) $(CXXFLAGS) -o libutp.so -shared $(OBJS)
|
||||
|
||||
libutp.a: $(OBJS)
|
||||
ar rvs libutp.a $(OBJS)
|
||||
|
||||
ucat: ucat.o libutp.so
|
||||
$(CC) $(CFLAGS) -o ucat ucat.o -L. -lutp $(LDFLAGS)
|
||||
|
||||
ucat-static: ucat.o libutp.a
|
||||
$(CXX) $(CXXFLAGS) -o ucat-static ucat.o libutp.a $(LDFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f *.o libutp.so libutp.a ucat ucat-static
|
||||
|
||||
tags: $(shell ls *.cpp *.h)
|
||||
rm -f tags
|
||||
ctags *.cpp *.h
|
||||
|
||||
anyway: clean all
|
||||
.PHONY: clean all anyway
|
@ -0,0 +1,68 @@
|
||||
# libutp - The uTorrent Transport Protocol library.
|
||||
Copyright (c) 2010 BitTorrent, Inc.
|
||||
|
||||
uTP is a TCP-like implementation of [LEDBAT][ledbat] documented as a BitTorrent
|
||||
extension in [BEP-29][bep29]. uTP provides reliable, ordered delivery
|
||||
while maintaining minimum extra delay. It is implemented on top of UDP to be
|
||||
cross-platform and functional today. As a result, uTP is the primary transport
|
||||
for uTorrent peer-to-peer connections.
|
||||
|
||||
uTP is written in C++, but the external interface is strictly C (ANSI C89).
|
||||
|
||||
## The Interface
|
||||
|
||||
The uTP socket interface is a bit different from the Berkeley socket API to
|
||||
avoid the need for our own select() implementation, and to make it easier to
|
||||
write event-based code with minimal buffering.
|
||||
|
||||
When you create a uTP socket, you register a set of callbacks. Most notably, the
|
||||
on_read callback is a reactive callback which occurs when bytes arrive off the
|
||||
network. The write side of the socket is proactive, and you call UTP_Write to
|
||||
indicate the number of bytes you wish to write. As packets are created, the
|
||||
on_write callback is called for each packet, so you can fill the buffers with
|
||||
data.
|
||||
|
||||
The libutp interface is not thread-safe. It was designed for use in a
|
||||
single-threaded asyncronous context, although with proper synchronization
|
||||
it may be used from a multi-threaded environment as well.
|
||||
|
||||
See utp.h for more details and other API documentation.
|
||||
|
||||
## Example
|
||||
|
||||
See ucat.c. Build with:
|
||||
|
||||
make ucat
|
||||
|
||||
## Building
|
||||
|
||||
uTP has been known to build on Windows with MSVC and on linux and OS X with gcc.
|
||||
On Windows, use the MSVC project files (utp.sln, and friends). On other platforms,
|
||||
building the shared library is as simple as:
|
||||
|
||||
make
|
||||
|
||||
To build one of the examples, which will statically link in everything it needs
|
||||
from libutp:
|
||||
|
||||
cd utp_test && make
|
||||
|
||||
## Packaging and API
|
||||
|
||||
The libutp API is considered unstable, and probably always will be. We encourage
|
||||
you to test with the version of libutp you have, and be mindful when upgrading.
|
||||
For this reason, it is probably also a good idea to bundle libutp with your
|
||||
application.
|
||||
|
||||
## License
|
||||
|
||||
libutp is released under the [MIT][lic] license.
|
||||
|
||||
## Related Work
|
||||
|
||||
Research and analysis of congestion control mechanisms can be found [here.][survey]
|
||||
|
||||
[ledbat]: http://datatracker.ietf.org/wg/ledbat/charter/
|
||||
[bep29]: http://www.bittorrent.org/beps/bep_0029.html
|
||||
[lic]: http://www.opensource.org/licenses/mit-license.php
|
||||
[survey]: http://datatracker.ietf.org/doc/draft-ietf-ledbat-survey/
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include "libutp_inet_ntop.h"
|
||||
|
||||
|
||||
//######################################################################
|
||||
const char *libutp::inet_ntop(int af, const void *src, char *dest, size_t length)
|
||||
{
|
||||
if (af != AF_INET && af != AF_INET6)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SOCKADDR_STORAGE address;
|
||||
DWORD address_length;
|
||||
|
||||
if (af == AF_INET)
|
||||
{
|
||||
address_length = sizeof(sockaddr_in);
|
||||
sockaddr_in* ipv4_address = (sockaddr_in*)(&address);
|
||||
ipv4_address->sin_family = AF_INET;
|
||||
ipv4_address->sin_port = 0;
|
||||
memcpy(&ipv4_address->sin_addr, src, sizeof(in_addr));
|
||||
}
|
||||
else // AF_INET6
|
||||
{
|
||||
address_length = sizeof(sockaddr_in6);
|
||||
sockaddr_in6* ipv6_address = (sockaddr_in6*)(&address);
|
||||
ipv6_address->sin6_family = AF_INET6;
|
||||
ipv6_address->sin6_port = 0;
|
||||
ipv6_address->sin6_flowinfo = 0;
|
||||
// hmmm
|
||||
ipv6_address->sin6_scope_id = 0;
|
||||
memcpy(&ipv6_address->sin6_addr, src, sizeof(in6_addr));
|
||||
}
|
||||
|
||||
DWORD string_length = (DWORD)(length);
|
||||
int result;
|
||||
result = WSAAddressToStringA((sockaddr*)(&address),
|
||||
address_length, 0, dest,
|
||||
&string_length);
|
||||
|
||||
// one common reason for this to fail is that ipv6 is not installed
|
||||
|
||||
return result == SOCKET_ERROR ? NULL : dest;
|
||||
}
|
||||
|
||||
//######################################################################
|
||||
int libutp::inet_pton(int af, const char* src, void* dest)
|
||||
{
|
||||
if (af != AF_INET && af != AF_INET6)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
SOCKADDR_STORAGE address;
|
||||
int address_length = sizeof(SOCKADDR_STORAGE);
|
||||
int result = WSAStringToAddressA((char*)(src), af, 0,
|
||||
(sockaddr*)(&address),
|
||||
&address_length);
|
||||
|
||||
if (af == AF_INET)
|
||||
{
|
||||
if (result != SOCKET_ERROR)
|
||||
{
|
||||
sockaddr_in* ipv4_address =(sockaddr_in*)(&address);
|
||||
memcpy(dest, &ipv4_address->sin_addr, sizeof(in_addr));
|
||||
}
|
||||
else if (strcmp(src, "255.255.255.255") == 0)
|
||||
{
|
||||
((in_addr*)(dest))->s_addr = INADDR_NONE;
|
||||
}
|
||||
}
|
||||
else // AF_INET6
|
||||
{
|
||||
if (result != SOCKET_ERROR)
|
||||
{
|
||||
sockaddr_in6* ipv6_address = (sockaddr_in6*)(&address);
|
||||
memcpy(dest, &ipv6_address->sin6_addr, sizeof(in6_addr));
|
||||
}
|
||||
}
|
||||
|
||||
return result == SOCKET_ERROR ? -1 : 1;
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
#ifndef LIBUTP_INET_NTOP_H
|
||||
#define LIBUTP_INET_NTOP_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// About us linking the system inet_pton and inet_ntop symbols:
|
||||
// 1) These symbols are usually defined on POSIX systems
|
||||
// 2) They are not defined on Windows versions earlier than Vista
|
||||
// Defined in:
|
||||
// ut_utils/src/sockaddr.cpp
|
||||
// libutp/win32_inet_ntop.obj
|
||||
//
|
||||
// When we drop support for XP we can just #include <ws2tcpip.h>, and use the system functions
|
||||
// For now, we will always use our functions on windows, on all builds
|
||||
// The reason is: we would like the debug build to behave as much as the release build as possible
|
||||
// It is much better to catch a problem in the debug build, than to link the system version
|
||||
// in debug, and our version int he wild.
|
||||
|
||||
#if defined(_WIN32_WINNT)
|
||||
#if _WIN32_WINNT >= 0x600 // Win32, post-XP
|
||||
#include <ws2tcpip.h> // for inet_ntop, inet_pton
|
||||
#define INET_NTOP inet_ntop
|
||||
#define INET_PTON inet_pton
|
||||
#else
|
||||
#define INET_NTOP libutp::inet_ntop // Win32, pre-XP: Use ours
|
||||
#define INET_PTON libutp::inet_pton
|
||||
#endif
|
||||
#else // not WIN32
|
||||
#include <arpa/inet.h> // for inet_ntop, inet_pton
|
||||
#define INET_NTOP inet_ntop
|
||||
#define INET_PTON inet_pton
|
||||
#endif
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
namespace libutp {
|
||||
|
||||
|
||||
//######################################################################
|
||||
const char *inet_ntop(int af, const void *src, char *dest, size_t length);
|
||||
|
||||
//######################################################################
|
||||
int inet_pton(int af, const char* src, void* dest);
|
||||
|
||||
|
||||
} //namespace libutp
|
||||
|
||||
#endif // LIBUTP_INET_NTOP_H
|
@ -0,0 +1,288 @@
|
||||
import os, sys, time
|
||||
|
||||
# usage: parse_log.py log-file [socket-index to focus on]
|
||||
|
||||
|
||||
socket_filter = None
|
||||
if len(sys.argv) >= 3:
|
||||
socket_filter = sys.argv[2].strip()
|
||||
|
||||
if socket_filter == None:
|
||||
print "scanning for socket with the most packets"
|
||||
file = open(sys.argv[1], 'rb')
|
||||
|
||||
sockets = {}
|
||||
|
||||
for l in file:
|
||||
if not 'our_delay' in l: continue
|
||||
|
||||
try:
|
||||
a = l.strip().split(" ")
|
||||
socket_index = a[1][:-1]
|
||||
except:
|
||||
continue
|
||||
|
||||
# msvc's runtime library doesn't prefix pointers
|
||||
# with '0x'
|
||||
# if socket_index[:2] != '0x':
|
||||
# continue
|
||||
|
||||
if socket_index in sockets:
|
||||
sockets[socket_index] += 1
|
||||
else:
|
||||
sockets[socket_index] = 1
|
||||
|
||||
items = sockets.items()
|
||||
items.sort(lambda x, y: y[1] - x[1])
|
||||
|
||||
count = 0
|
||||
for i in items:
|
||||
print '%s: %d' % (i[0], i[1])
|
||||
count += 1
|
||||
if count > 5: break
|
||||
|
||||
file.close()
|
||||
socket_filter = items[0][0]
|
||||
print '\nfocusing on socket %s' % socket_filter
|
||||
|
||||
file = open(sys.argv[1], 'rb')
|
||||
out_file = 'utp.out%s' % socket_filter;
|
||||
out = open(out_file, 'wb')
|
||||
|
||||
delay_samples = 'dots lc rgb "blue"'
|
||||
delay_base = 'steps lw 2 lc rgb "purple"'
|
||||
target_delay = 'steps lw 2 lc rgb "red"'
|
||||
off_target = 'dots lc rgb "blue"'
|
||||
cwnd = 'steps lc rgb "green"'
|
||||
window_size = 'steps lc rgb "sea-green"'
|
||||
rtt = 'lines lc rgb "light-blue"'
|
||||
|
||||
metrics = {
|
||||
'our_delay':['our delay (ms)', 'x1y2', delay_samples],
|
||||
'upload_rate':['send rate (B/s)', 'x1y1', 'lines'],
|
||||
'max_window':['cwnd (B)', 'x1y1', cwnd],
|
||||
'target_delay':['target delay (ms)', 'x1y2', target_delay],
|
||||
'cur_window':['bytes in-flight (B)', 'x1y1', window_size],
|
||||
'cur_window_packets':['number of packets in-flight', 'x1y2', 'steps'],
|
||||
'packet_size':['current packet size (B)', 'x1y2', 'steps'],
|
||||
'rtt':['rtt (ms)', 'x1y2', rtt],
|
||||
'off_target':['off-target (ms)', 'x1y2', off_target],
|
||||
'delay_sum':['delay sum (ms)', 'x1y2', 'steps'],
|
||||
'their_delay':['their delay (ms)', 'x1y2', delay_samples],
|
||||
'get_microseconds':['clock (us)', 'x1y1', 'steps'],
|
||||
'wnduser':['advertised window size (B)', 'x1y1', 'steps'],
|
||||
|
||||
'delay_base':['delay base (us)', 'x1y1', delay_base],
|
||||
'their_delay_base':['their delay base (us)', 'x1y1', delay_base],
|
||||
'their_actual_delay':['their actual delay (us)', 'x1y1', delay_samples],
|
||||
'actual_delay':['actual_delay (us)', 'x1y1', delay_samples]
|
||||
}
|
||||
|
||||
histogram_quantization = 1
|
||||
socket_index = None
|
||||
|
||||
columns = []
|
||||
|
||||
begin = None
|
||||
|
||||
title = "-"
|
||||
packet_loss = 0
|
||||
packet_timeout = 0
|
||||
|
||||
delay_histogram = {}
|
||||
window_size = {'0': 0, '1': 0}
|
||||
|
||||
# [35301484] 0x00ec1190: actual_delay:1021583 our_delay:102 their_delay:-1021345 off_target:297 max_window:2687 upload_rate:18942 delay_base:1021481154 delay_sum:-1021242 target_delay:400 acked_bytes:1441 cur_window:2882 scaled_gain:2.432
|
||||
|
||||
counter = 0
|
||||
|
||||
print "reading log file"
|
||||
|
||||
for l in file:
|
||||
if "UTP_Connect" in l:
|
||||
title = l[:-2]
|
||||
if socket_filter != None:
|
||||
title += ' socket: %s' % socket_filter
|
||||
else:
|
||||
title += ' sum of all sockets'
|
||||
continue
|
||||
|
||||
try:
|
||||
a = l.strip().split(" ")
|
||||
t = a[0][1:-1]
|
||||
socket_index = a[1][:-1]
|
||||
except:
|
||||
continue
|
||||
# if socket_index[:2] != '0x':
|
||||
# continue
|
||||
|
||||
if socket_filter != None and socket_index != socket_filter:
|
||||
continue
|
||||
|
||||
counter += 1
|
||||
if (counter % 300 == 0):
|
||||
print "\r%d " % counter,
|
||||
|
||||
if "lost." in l:
|
||||
packet_loss = packet_loss + 1
|
||||
continue
|
||||
if "Packet timeout" in l:
|
||||
packet_timeout = packet_timeout + 1
|
||||
continue
|
||||
if "our_delay:" not in l:
|
||||
continue
|
||||
|
||||
# used for Logf timestamps
|
||||
# t, m = t.split(".")
|
||||
# t = time.strptime(t, "%H:%M:%S")
|
||||
# t = list(t)
|
||||
# t[0] += 107
|
||||
# t = tuple(t)
|
||||
# m = float(m)
|
||||
# m /= 1000.0
|
||||
# t = time.mktime(t) + m
|
||||
|
||||
# used for tick count timestamps
|
||||
t = int(t)
|
||||
|
||||
if begin is None:
|
||||
begin = t
|
||||
t = t - begin
|
||||
# print time. Convert from milliseconds to seconds
|
||||
print >>out, '%f\t' % (float(t)/1000.),
|
||||
|
||||
#if t > 200000:
|
||||
# break
|
||||
|
||||
fill_columns = not columns
|
||||
for i in a[2:]:
|
||||
try:
|
||||
n, v = i.split(':')
|
||||
except:
|
||||
continue
|
||||
v = float(v)
|
||||
if n == "our_delay":
|
||||
bucket = v / histogram_quantization
|
||||
delay_histogram[bucket] = 1 + delay_histogram.get(bucket, 0)
|
||||
if not n in metrics: continue
|
||||
if fill_columns:
|
||||
columns.append(n)
|
||||
if n == "max_window":
|
||||
window_size[socket_index] = v
|
||||
print >>out, '%f\t' % int(reduce(lambda a,b: a+b, window_size.values())),
|
||||
else:
|
||||
print >>out, '%f\t' % v,
|
||||
print >>out, float(packet_loss * 8000), float(packet_timeout * 8000)
|
||||
packet_loss = 0
|
||||
packet_timeout = 0
|
||||
|
||||
out.close()
|
||||
|
||||
out = open('%s.histogram' % out_file, 'wb')
|
||||
for d,f in delay_histogram.iteritems():
|
||||
print >>out, float(d*histogram_quantization) + histogram_quantization / 2, f
|
||||
out.close()
|
||||
|
||||
|
||||
plot = [
|
||||
{
|
||||
'data': ['upload_rate', 'max_window', 'cur_window', 'wnduser', 'cur_window_packets', 'packet_size', 'rtt'],
|
||||
'title': 'send-packet-size',
|
||||
'y1': 'Bytes',
|
||||
'y2': 'Time (ms)'
|
||||
},
|
||||
{
|
||||
'data': ['our_delay', 'max_window', 'target_delay', 'cur_window', 'wnduser', 'cur_window_packets'],
|
||||
'title': 'uploading',
|
||||
'y1': 'Bytes',
|
||||
'y2': 'Time (ms)'
|
||||
},
|
||||
{
|
||||
'data': ['our_delay', 'max_window', 'target_delay', 'cur_window', 'cur_window_packets'],
|
||||
'title': 'uploading_packets',
|
||||
'y1': 'Bytes',
|
||||
'y2': 'Time (ms)'
|
||||
},
|
||||
{
|
||||
'data': ['get_microseconds'],
|
||||
'title': 'timer',
|
||||
'y1': 'Time microseconds',
|
||||
'y2': 'Time (ms)'
|
||||
},
|
||||
{
|
||||
'data': ['their_delay', 'target_delay', 'rtt'],
|
||||
'title': 'their_delay',
|
||||
'y1': '',
|
||||
'y2': 'Time (ms)'
|
||||
},
|
||||
{
|
||||
'data': ['their_actual_delay','their_delay_base'],
|
||||
'title': 'their_delay_base',
|
||||
'y1': 'Time (us)',
|
||||
'y2': ''
|
||||
},
|
||||
{
|
||||
'data': ['our_delay', 'target_delay', 'rtt'],
|
||||
'title': 'our-delay',
|
||||
'y1': '',
|
||||
'y2': 'Time (ms)'
|
||||
},
|
||||
{
|
||||
'data': ['actual_delay', 'delay_base'],
|
||||
'title': 'our_delay_base',
|
||||
'y1': 'Time (us)',
|
||||
'y2': ''
|
||||
}
|
||||
]
|
||||
|
||||
out = open('utp.gnuplot', 'w+')
|
||||
|
||||
files = ''
|
||||
|
||||
#print >>out, 'set xtics 0, 20'
|
||||
print >>out, "set term png size 1280,800"
|
||||
print >>out, 'set output "%s.delays.png"' % out_file
|
||||
print >>out, 'set xrange [0:250]'
|
||||
print >>out, 'set xlabel "delay (ms)"'
|
||||
print >>out, 'set boxwidth 1'
|
||||
print >>out, 'set style fill solid'
|
||||
print >>out, 'set ylabel "number of packets"'
|
||||
print >>out, 'plot "%s.histogram" using 1:2 with boxes' % out_file
|
||||
|
||||
print >>out, "set style data steps"
|
||||
#print >>out, "set yrange [0:*]"
|
||||
print >>out, "set y2range [*:*]"
|
||||
files += out_file + '.delays.png '
|
||||
#set hidden3d
|
||||
#set title "Peer bandwidth distribution"
|
||||
#set xlabel "Ratio"
|
||||
|
||||
for p in plot:
|
||||
print >>out, 'set title "%s %s"' % (p['title'], title)
|
||||
print >>out, 'set xlabel "time (s)"'
|
||||
print >>out, 'set ylabel "%s"' % p['y1']
|
||||
print >>out, "set tics nomirror"
|
||||
print >>out, 'set y2tics'
|
||||
print >>out, 'set y2label "%s"' % p['y2']
|
||||
print >>out, 'set xrange [0:*]'
|
||||
print >>out, "set key box"
|
||||
print >>out, "set term png size 1280,800"
|
||||
print >>out, 'set output "%s-%s.png"' % (out_file, p['title'])
|
||||
files += '%s-%s.png ' % (out_file, p['title'])
|
||||
|
||||
comma = ''
|
||||
print >>out, "plot",
|
||||
|
||||
for c in p['data']:
|
||||
if not c in metrics: continue
|
||||
i = columns.index(c)
|
||||
print >>out, '%s"%s" using 1:%d title "%s-%s" axes %s with %s' % (comma, out_file, i + 2, metrics[c][0], metrics[c][1], metrics[c][1], metrics[c][2]),
|
||||
comma = ', '
|
||||
print >>out, ''
|
||||
|
||||
out.close()
|
||||
|
||||
os.system("gnuplot utp.gnuplot")
|
||||
|
||||
os.system("open %s" % files)
|
||||
|
@ -0,0 +1,139 @@
|
||||
// vim:set ts=4 sw=4 ai:
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "utp_internal.h"
|
||||
#include "utp_utils.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
const char * utp_callback_names[] = {
|
||||
"UTP_ON_FIREWALL",
|
||||
"UTP_ON_ACCEPT",
|
||||
"UTP_ON_CONNECT",
|
||||
"UTP_ON_ERROR",
|
||||
"UTP_ON_READ",
|
||||
"UTP_ON_OVERHEAD_STATISTICS",
|
||||
"UTP_ON_STATE_CHANGE",
|
||||
"UTP_GET_READ_BUFFER_SIZE",
|
||||
"UTP_ON_DELAY_SAMPLE",
|
||||
"UTP_GET_UDP_MTU",
|
||||
"UTP_GET_UDP_OVERHEAD",
|
||||
"UTP_GET_MILLISECONDS",
|
||||
"UTP_GET_MICROSECONDS",
|
||||
"UTP_GET_RANDOM",
|
||||
"UTP_LOG",
|
||||
"UTP_SENDTO",
|
||||
};
|
||||
|
||||
const char * utp_error_code_names[] = {
|
||||
"UTP_ECONNREFUSED",
|
||||
"UTP_ECONNRESET",
|
||||
"UTP_ETIMEDOUT",
|
||||
};
|
||||
|
||||
const char *utp_state_names[] = {
|
||||
NULL,
|
||||
"UTP_STATE_CONNECT",
|
||||
"UTP_STATE_WRITABLE",
|
||||
"UTP_STATE_EOF",
|
||||
"UTP_STATE_DESTROYING",
|
||||
};
|
||||
|
||||
struct_utp_context::struct_utp_context()
|
||||
: userdata(NULL)
|
||||
, current_ms(0)
|
||||
, last_utp_socket(NULL)
|
||||
, log_normal(false)
|
||||
, log_mtu(false)
|
||||
, log_debug(false)
|
||||
{
|
||||
memset(&context_stats, 0, sizeof(context_stats));
|
||||
memset(callbacks, 0, sizeof(callbacks));
|
||||
target_delay = CCONTROL_TARGET;
|
||||
utp_sockets = new UTPSocketHT;
|
||||
|
||||
callbacks[UTP_GET_UDP_MTU] = &utp_default_get_udp_mtu;
|
||||
callbacks[UTP_GET_UDP_OVERHEAD] = &utp_default_get_udp_overhead;
|
||||
callbacks[UTP_GET_MILLISECONDS] = &utp_default_get_milliseconds;
|
||||
callbacks[UTP_GET_MICROSECONDS] = &utp_default_get_microseconds;
|
||||
callbacks[UTP_GET_RANDOM] = &utp_default_get_random;
|
||||
|
||||
// 1 MB of receive buffer (i.e. max bandwidth delay product)
|
||||
// means that from a peer with 200 ms RTT, we cannot receive
|
||||
// faster than 5 MB/s
|
||||
// from a peer with 10 ms RTT, we cannot receive faster than
|
||||
// 100 MB/s. This is assumed to be good enough, since bandwidth
|
||||
// often is proportional to RTT anyway
|
||||
// when setting a download rate limit, all sockets should have
|
||||
// their receive buffer set much lower, to say 60 kiB or so
|
||||
opt_rcvbuf = opt_sndbuf = 1024 * 1024;
|
||||
last_check = 0;
|
||||
}
|
||||
|
||||
struct_utp_context::~struct_utp_context() {
|
||||
delete this->utp_sockets;
|
||||
}
|
||||
|
||||
utp_context* utp_init (int version)
|
||||
{
|
||||
assert(version == 2);
|
||||
if (version != 2)
|
||||
return NULL;
|
||||
utp_context *ctx = new utp_context;
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void utp_destroy(utp_context *ctx) {
|
||||
assert(ctx);
|
||||
if (ctx) delete ctx;
|
||||
}
|
||||
|
||||
void utp_set_callback(utp_context *ctx, int callback_name, utp_callback_t *proc) {
|
||||
assert(ctx);
|
||||
if (ctx) ctx->callbacks[callback_name] = proc;
|
||||
}
|
||||
|
||||
void* utp_context_set_userdata(utp_context *ctx, void *userdata) {
|
||||
assert(ctx);
|
||||
if (ctx) ctx->userdata = userdata;
|
||||
return ctx ? ctx->userdata : NULL;
|
||||
}
|
||||
|
||||
void* utp_context_get_userdata(utp_context *ctx) {
|
||||
assert(ctx);
|
||||
return ctx ? ctx->userdata : NULL;
|
||||
}
|
||||
|
||||
utp_context_stats* utp_get_context_stats(utp_context *ctx) {
|
||||
assert(ctx);
|
||||
return ctx ? &ctx->context_stats : NULL;
|
||||
}
|
||||
|
||||
ssize_t utp_write(utp_socket *socket, void *buf, size_t len) {
|
||||
struct utp_iovec iovec = { buf, len };
|
||||
return utp_writev(socket, &iovec, 1);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,208 @@
|
||||
// vim:set ts=4 sw=4 ai:
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "utp_callbacks.h"
|
||||
|
||||
int utp_call_on_firewall(utp_context *ctx, const struct sockaddr *address, socklen_t address_len)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_ON_FIREWALL]) return 0;
|
||||
args.callback_type = UTP_ON_FIREWALL;
|
||||
args.context = ctx;
|
||||
args.socket = NULL;
|
||||
args.address = address;
|
||||
args.address_len = address_len;
|
||||
return (int)ctx->callbacks[UTP_ON_FIREWALL](&args);
|
||||
}
|
||||
|
||||
void utp_call_on_accept(utp_context *ctx, utp_socket *socket, const struct sockaddr *address, socklen_t address_len)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_ON_ACCEPT]) return;
|
||||
args.callback_type = UTP_ON_ACCEPT;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.address = address;
|
||||
args.address_len = address_len;
|
||||
ctx->callbacks[UTP_ON_ACCEPT](&args);
|
||||
}
|
||||
|
||||
void utp_call_on_connect(utp_context *ctx, utp_socket *socket)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_ON_CONNECT]) return;
|
||||
args.callback_type = UTP_ON_CONNECT;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
ctx->callbacks[UTP_ON_CONNECT](&args);
|
||||
}
|
||||
|
||||
void utp_call_on_error(utp_context *ctx, utp_socket *socket, int error_code)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_ON_ERROR]) return;
|
||||
args.callback_type = UTP_ON_ERROR;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.error_code = error_code;
|
||||
ctx->callbacks[UTP_ON_ERROR](&args);
|
||||
}
|
||||
|
||||
void utp_call_on_read(utp_context *ctx, utp_socket *socket, const byte *buf, size_t len)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_ON_READ]) return;
|
||||
args.callback_type = UTP_ON_READ;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.buf = buf;
|
||||
args.len = len;
|
||||
ctx->callbacks[UTP_ON_READ](&args);
|
||||
}
|
||||
|
||||
void utp_call_on_overhead_statistics(utp_context *ctx, utp_socket *socket, int send, size_t len, int type)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_ON_OVERHEAD_STATISTICS]) return;
|
||||
args.callback_type = UTP_ON_OVERHEAD_STATISTICS;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.send = send;
|
||||
args.len = len;
|
||||
args.type = type;
|
||||
ctx->callbacks[UTP_ON_OVERHEAD_STATISTICS](&args);
|
||||
}
|
||||
|
||||
void utp_call_on_delay_sample(utp_context *ctx, utp_socket *socket, int sample_ms)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_ON_DELAY_SAMPLE]) return;
|
||||
args.callback_type = UTP_ON_DELAY_SAMPLE;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.sample_ms = sample_ms;
|
||||
ctx->callbacks[UTP_ON_DELAY_SAMPLE](&args);
|
||||
}
|
||||
|
||||
void utp_call_on_state_change(utp_context *ctx, utp_socket *socket, int state)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_ON_STATE_CHANGE]) return;
|
||||
args.callback_type = UTP_ON_STATE_CHANGE;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.state = state;
|
||||
ctx->callbacks[UTP_ON_STATE_CHANGE](&args);
|
||||
}
|
||||
|
||||
uint16 utp_call_get_udp_mtu(utp_context *ctx, utp_socket *socket, const struct sockaddr *address, socklen_t address_len)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_GET_UDP_MTU]) return 0;
|
||||
args.callback_type = UTP_GET_UDP_MTU;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.address = address;
|
||||
args.address_len = address_len;
|
||||
return (uint16)ctx->callbacks[UTP_GET_UDP_MTU](&args);
|
||||
}
|
||||
|
||||
uint16 utp_call_get_udp_overhead(utp_context *ctx, utp_socket *socket, const struct sockaddr *address, socklen_t address_len)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_GET_UDP_OVERHEAD]) return 0;
|
||||
args.callback_type = UTP_GET_UDP_OVERHEAD;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.address = address;
|
||||
args.address_len = address_len;
|
||||
return (uint16)ctx->callbacks[UTP_GET_UDP_OVERHEAD](&args);
|
||||
}
|
||||
|
||||
uint64 utp_call_get_milliseconds(utp_context *ctx, utp_socket *socket)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_GET_MILLISECONDS]) return 0;
|
||||
args.callback_type = UTP_GET_MILLISECONDS;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
return ctx->callbacks[UTP_GET_MILLISECONDS](&args);
|
||||
}
|
||||
|
||||
uint64 utp_call_get_microseconds(utp_context *ctx, utp_socket *socket)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_GET_MICROSECONDS]) return 0;
|
||||
args.callback_type = UTP_GET_MICROSECONDS;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
return ctx->callbacks[UTP_GET_MICROSECONDS](&args);
|
||||
}
|
||||
|
||||
uint32 utp_call_get_random(utp_context *ctx, utp_socket *socket)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_GET_RANDOM]) return 0;
|
||||
args.callback_type = UTP_GET_RANDOM;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
return (uint32)ctx->callbacks[UTP_GET_RANDOM](&args);
|
||||
}
|
||||
|
||||
size_t utp_call_get_read_buffer_size(utp_context *ctx, utp_socket *socket)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_GET_READ_BUFFER_SIZE]) return 0;
|
||||
args.callback_type = UTP_GET_READ_BUFFER_SIZE;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
return (size_t)ctx->callbacks[UTP_GET_READ_BUFFER_SIZE](&args);
|
||||
}
|
||||
|
||||
void utp_call_log(utp_context *ctx, utp_socket *socket, const byte *buf)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_LOG]) return;
|
||||
args.callback_type = UTP_LOG;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.buf = buf;
|
||||
ctx->callbacks[UTP_LOG](&args);
|
||||
}
|
||||
|
||||
void utp_call_sendto(utp_context *ctx, utp_socket *socket, const byte *buf, size_t len, const struct sockaddr *address, socklen_t address_len, uint32 flags)
|
||||
{
|
||||
utp_callback_arguments args;
|
||||
if (!ctx->callbacks[UTP_SENDTO]) return;
|
||||
args.callback_type = UTP_SENDTO;
|
||||
args.context = ctx;
|
||||
args.socket = socket;
|
||||
args.buf = buf;
|
||||
args.len = len;
|
||||
args.address = address;
|
||||
args.address_len = address_len;
|
||||
args.flags = flags;
|
||||
ctx->callbacks[UTP_SENDTO](&args);
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __UTP_CALLBACKS_H__
|
||||
#define __UTP_CALLBACKS_H__
|
||||
|
||||
#include "utp.h"
|
||||
#include "utp_internal.h"
|
||||
|
||||
// Generated by running: grep ^[a-z] utp_callbacks.cpp | sed 's/$/;/'
|
||||
int utp_call_on_firewall(utp_context *ctx, const struct sockaddr *address, socklen_t address_len);
|
||||
void utp_call_on_accept(utp_context *ctx, utp_socket *s, const struct sockaddr *address, socklen_t address_len);
|
||||
void utp_call_on_connect(utp_context *ctx, utp_socket *s);
|
||||
void utp_call_on_error(utp_context *ctx, utp_socket *s, int error_code);
|
||||
void utp_call_on_read(utp_context *ctx, utp_socket *s, const byte *buf, size_t len);
|
||||
void utp_call_on_overhead_statistics(utp_context *ctx, utp_socket *s, int send, size_t len, int type);
|
||||
void utp_call_on_delay_sample(utp_context *ctx, utp_socket *s, int sample_ms);
|
||||
void utp_call_on_state_change(utp_context *ctx, utp_socket *s, int state);
|
||||
uint16 utp_call_get_udp_mtu(utp_context *ctx, utp_socket *s, const struct sockaddr *address, socklen_t address_len);
|
||||
uint16 utp_call_get_udp_overhead(utp_context *ctx, utp_socket *s, const struct sockaddr *address, socklen_t address_len);
|
||||
uint64 utp_call_get_milliseconds(utp_context *ctx, utp_socket *s);
|
||||
uint64 utp_call_get_microseconds(utp_context *ctx, utp_socket *s);
|
||||
uint32 utp_call_get_random(utp_context *ctx, utp_socket *s);
|
||||
size_t utp_call_get_read_buffer_size(utp_context *ctx, utp_socket *s);
|
||||
void utp_call_log(utp_context *ctx, utp_socket *s, const byte *buf);
|
||||
void utp_call_sendto(utp_context *ctx, utp_socket *s, const byte *buf, size_t len, const struct sockaddr *address, socklen_t address_len, uint32 flags);
|
||||
|
||||
#endif // __UTP_CALLBACKS_H__
|
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "utp_hash.h"
|
||||
#include "utp_types.h"
|
||||
|
||||
#define LIBUTP_HASH_UNUSED ((utp_link_t)-1)
|
||||
|
||||
#ifdef STRICT_ALIGN
|
||||
inline uint32 Read32(const void *p)
|
||||
{
|
||||
uint32 tmp;
|
||||
memcpy(&tmp, p, sizeof tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#else
|
||||
inline uint32 Read32(const void *p) { return *(uint32*)p; }
|
||||
#endif
|
||||
|
||||
|
||||
// Get the amount of memory required for the hash parameters and the bucket set
|
||||
// Waste a space for an unused bucket in order to ensure the following managed memory have 32-bit aligned addresses
|
||||
// TODO: make this 64-bit clean
|
||||
#define BASE_SIZE(bc) (sizeof(utp_hash_t) + sizeof(utp_link_t) * ((bc) + 1))
|
||||
|
||||
// Get a pointer to the base of the structure array managed by the hash table
|
||||
#define get_bep(h) ((byte*)(h)) + BASE_SIZE((h)->N)
|
||||
|
||||
// Get the address of the information associated with a specific structure in the array,
|
||||
// given the address of the base of the structure.
|
||||
// This assumes a utp_link_t link member is at the end of the structure.
|
||||
// Given compilers filling out the memory to a 32-bit clean value, this may mean that
|
||||
// the location named in the structure may not be the location actually used by the hash table,
|
||||
// since the compiler may have padded the end of the structure with 2 bytes after the utp_link_t member.
|
||||
// TODO: this macro should not require that the variable pointing at the hash table be named 'hash'
|
||||
#define ptr_to_link(p) (utp_link_t *) (((byte *) (p)) + hash->E - sizeof(utp_link_t))
|
||||
|
||||
// Calculate how much to allocate for a hash table with bucket count, total size, and structure count
|
||||
// TODO: make this 64-bit clean
|
||||
#define ALLOCATION_SIZE(bc, ts, sc) (BASE_SIZE((bc)) + (ts) * (sc))
|
||||
|
||||
utp_hash_t *utp_hash_create(int N, int key_size, int total_size, int initial, utp_hash_compute_t hashfun, utp_hash_equal_t compfun)
|
||||
{
|
||||
// Must have odd number of hash buckets (prime number is best)
|
||||
assert(N % 2);
|
||||
// Ensure structures will be at aligned memory addresses
|
||||
// TODO: make this 64-bit clean
|
||||
assert(0 == (total_size % 4));
|
||||
|
||||
int size = ALLOCATION_SIZE(N, total_size, initial);
|
||||
utp_hash_t *hash = (utp_hash_t *) malloc( size );
|
||||
memset( hash, 0, size );
|
||||
|
||||
for (int i = 0; i < N + 1; ++i)
|
||||
hash->inits[i] = LIBUTP_HASH_UNUSED;
|
||||
hash->N = N;
|
||||
hash->K = key_size;
|
||||
hash->E = total_size;
|
||||
hash->hash_compute = hashfun;
|
||||
hash->hash_equal = compfun;
|
||||
hash->allocated = initial;
|
||||
hash->count = 0;
|
||||
hash->used = 0;
|
||||
hash->free = LIBUTP_HASH_UNUSED;
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint utp_hash_mem(const void *keyp, size_t keysize)
|
||||
{
|
||||
uint hash = 0;
|
||||
uint n = keysize;
|
||||
while (n >= 4) {
|
||||
hash ^= Read32(keyp);
|
||||
keyp = (byte*)keyp + sizeof(uint32);
|
||||
hash = (hash << 13) | (hash >> 19);
|
||||
n -= 4;
|
||||
}
|
||||
while (n != 0) {
|
||||
hash ^= *(byte*)keyp;
|
||||
keyp = (byte*)keyp + sizeof(byte);
|
||||
hash = (hash << 8) | (hash >> 24);
|
||||
n--;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
uint utp_hash_mkidx(utp_hash_t *hash, const void *keyp)
|
||||
{
|
||||
// Generate a key from the hash
|
||||
return hash->hash_compute(keyp, hash->K) % hash->N;
|
||||
}
|
||||
|
||||
static inline bool compare(byte *a, byte *b,int n)
|
||||
{
|
||||
assert(n >= 4);
|
||||
if (Read32(a) != Read32(b)) return false;
|
||||
return memcmp(a+4, b+4, n-4) == 0;
|
||||
}
|
||||
|
||||
#define COMPARE(h,k1,k2,ks) (((h)->hash_equal) ? (h)->hash_equal((void*)k1,(void*)k2,ks) : compare(k1,k2,ks))
|
||||
|
||||
// Look-up a key in the hash table.
|
||||
// Returns NULL if not found
|
||||
void *utp_hash_lookup(utp_hash_t *hash, const void *key)
|
||||
{
|
||||
utp_link_t idx = utp_hash_mkidx(hash, key);
|
||||
|
||||
// base pointer
|
||||
byte *bep = get_bep(hash);
|
||||
|
||||
utp_link_t cur = hash->inits[idx];
|
||||
while (cur != LIBUTP_HASH_UNUSED) {
|
||||
byte *key2 = bep + (cur * hash->E);
|
||||
if (COMPARE(hash, (byte*)key, key2, hash->K))
|
||||
return key2;
|
||||
cur = *ptr_to_link(key2);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Add a new element to the hash table.
|
||||
// Returns a pointer to the new element.
|
||||
// This assumes the element is not already present!
|
||||
void *utp_hash_add(utp_hash_t **hashp, const void *key)
|
||||
{
|
||||
//Allocate a new entry
|
||||
byte *elemp;
|
||||
utp_link_t elem;
|
||||
utp_hash_t *hash = *hashp;
|
||||
utp_link_t idx = utp_hash_mkidx(hash, key);
|
||||
|
||||
if ((elem=hash->free) == LIBUTP_HASH_UNUSED) {
|
||||
utp_link_t all = hash->allocated;
|
||||
if (hash->used == all) {
|
||||
utp_hash_t *nhash;
|
||||
if (all <= (LIBUTP_HASH_UNUSED/2)) {
|
||||
all *= 2;
|
||||
} else if (all != LIBUTP_HASH_UNUSED) {
|
||||
all = LIBUTP_HASH_UNUSED;
|
||||
} else {
|
||||
// too many items! can't grow!
|
||||
assert(0);
|
||||
return NULL;
|
||||
}
|
||||
// otherwise need to allocate.
|
||||
nhash = (utp_hash_t*)realloc(hash, ALLOCATION_SIZE(hash->N, hash->E, all));
|
||||
if (!nhash) {
|
||||
// out of memory (or too big to allocate)
|
||||
assert(nhash);
|
||||
return NULL;
|
||||
}
|
||||
hash = *hashp = nhash;
|
||||
hash->allocated = all;
|
||||
}
|
||||
|
||||
elem = hash->used++;
|
||||
elemp = get_bep(hash) + elem * hash->E;
|
||||
} else {
|
||||
elemp = get_bep(hash) + elem * hash->E;
|
||||
hash->free = *ptr_to_link(elemp);
|
||||
}
|
||||
|
||||
*ptr_to_link(elemp) = hash->inits[idx];
|
||||
hash->inits[idx] = elem;
|
||||
hash->count++;
|
||||
|
||||
// copy key into it
|
||||
memcpy(elemp, key, hash->K);
|
||||
return elemp;
|
||||
}
|
||||
|
||||
// Delete an element from the utp_hash_t
|
||||
// Returns a pointer to the already deleted element.
|
||||
void *utp_hash_del(utp_hash_t *hash, const void *key)
|
||||
{
|
||||
utp_link_t idx = utp_hash_mkidx(hash, key);
|
||||
|
||||
// base pointer
|
||||
byte *bep = get_bep(hash);
|
||||
|
||||
utp_link_t *curp = &hash->inits[idx];
|
||||
utp_link_t cur;
|
||||
while ((cur=*curp) != LIBUTP_HASH_UNUSED) {
|
||||
byte *key2 = bep + (cur * hash->E);
|
||||
if (COMPARE(hash,(byte*)key,(byte*)key2, hash->K )) {
|
||||
// found an item that matched. unlink it
|
||||
*curp = *ptr_to_link(key2);
|
||||
// Insert into freelist
|
||||
*ptr_to_link(key2) = hash->free;
|
||||
hash->free = cur;
|
||||
hash->count--;
|
||||
return key2;
|
||||
}
|
||||
curp = ptr_to_link(key2);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *utp_hash_iterate(utp_hash_t *hash, utp_hash_iterator_t *iter)
|
||||
{
|
||||
utp_link_t elem;
|
||||
|
||||
if ((elem=iter->elem) == LIBUTP_HASH_UNUSED) {
|
||||
// Find a bucket with an element
|
||||
utp_link_t buck = iter->bucket + 1;
|
||||
for(;;) {
|
||||
if (buck >= hash->N)
|
||||
return NULL;
|
||||
if ((elem = hash->inits[buck]) != LIBUTP_HASH_UNUSED)
|
||||
break;
|
||||
buck++;
|
||||
}
|
||||
iter->bucket = buck;
|
||||
}
|
||||
|
||||
byte *elemp = get_bep(hash) + (elem * hash->E);
|
||||
iter->elem = *ptr_to_link(elemp);
|
||||
return elemp;
|
||||
}
|
||||
|
||||
void utp_hash_free_mem(utp_hash_t* hash)
|
||||
{
|
||||
free(hash);
|
||||
}
|
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __UTP_HASH_H__
|
||||
#define __UTP_HASH_H__
|
||||
|
||||
#include <string.h> // memset
|
||||
#include <stdlib.h> // malloc
|
||||
|
||||
#include "utp_types.h"
|
||||
#include "utp_templates.h"
|
||||
|
||||
// TODO: make utp_link_t a template parameter to HashTable
|
||||
typedef uint32 utp_link_t;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Silence the warning about the C99-compliant zero-length array at the end of the structure
|
||||
#pragma warning (disable: 4200)
|
||||
#endif
|
||||
|
||||
typedef uint32 (*utp_hash_compute_t)(const void *keyp, size_t keysize);
|
||||
typedef uint (*utp_hash_equal_t)(const void *key_a, const void *key_b, size_t keysize);
|
||||
|
||||
// In memory the HashTable is laid out as follows:
|
||||
// ---------------------------- low
|
||||
// | hash table data members |
|
||||
// ---------------------------- _
|
||||
// | indices | ^
|
||||
// | . | | utp_link_t indices into the key-values.
|
||||
// | . | .
|
||||
// ---------------------------- - <----- bep
|
||||
// | keys and values | each key-value pair has size total_size
|
||||
// | . |
|
||||
// | . |
|
||||
// ---------------------------- high
|
||||
//
|
||||
// The code depends on the ability of the compiler to pad the length
|
||||
// of the hash table data members structure to
|
||||
// a length divisible by 32-bits with no remainder.
|
||||
//
|
||||
// Since the number of hash buckets (indices) should be odd, the code
|
||||
// asserts this and adds one to the hash bucket count to ensure that the
|
||||
// following key-value pairs array starts on a 32-bit boundary.
|
||||
//
|
||||
// The key-value pairs array should start on a 32-bit boundary, otherwise
|
||||
// processors like the ARM will silently mangle 32-bit data in these structures
|
||||
// (e.g., turning 0xABCD into 0XCDAB when moving a value from memory to register
|
||||
// when the memory address is 16 bits offset from a 32-bit boundary),
|
||||
// also, the value will be stored at an address two bytes lower than the address
|
||||
// value would ordinarily indicate.
|
||||
//
|
||||
// The key-value pair is of type T. The first field in T must
|
||||
// be the key, i.e., the first K bytes of T contains the key.
|
||||
// total_size = sizeof(T) and thus sizeof(T) >= sizeof(K)
|
||||
//
|
||||
// N is the number of buckets.
|
||||
//
|
||||
struct utp_hash_t {
|
||||
utp_link_t N;
|
||||
byte K;
|
||||
byte E;
|
||||
size_t count;
|
||||
utp_hash_compute_t hash_compute;
|
||||
utp_hash_equal_t hash_equal;
|
||||
utp_link_t allocated;
|
||||
utp_link_t used;
|
||||
utp_link_t free;
|
||||
utp_link_t inits[0];
|
||||
};
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning (default: 4200)
|
||||
#endif
|
||||
|
||||
struct utp_hash_iterator_t {
|
||||
utp_link_t bucket;
|
||||
utp_link_t elem;
|
||||
|
||||
utp_hash_iterator_t() : bucket(0xffffffff), elem(0xffffffff) {}
|
||||
};
|
||||
|
||||
uint utp_hash_mem(const void *keyp, size_t keysize);
|
||||
uint utp_hash_comp(const void *key_a, const void *key_b, size_t keysize);
|
||||
|
||||
utp_hash_t *utp_hash_create(int N, int key_size, int total_size, int initial, utp_hash_compute_t hashfun = utp_hash_mem, utp_hash_equal_t eqfun = NULL);
|
||||
void *utp_hash_lookup(utp_hash_t *hash, const void *key);
|
||||
void *utp_hash_add(utp_hash_t **hashp, const void *key);
|
||||
void *utp_hash_del(utp_hash_t *hash, const void *key);
|
||||
|
||||
void *utp_hash_iterate(utp_hash_t *hash, utp_hash_iterator_t *iter);
|
||||
void utp_hash_free_mem(utp_hash_t *hash);
|
||||
|
||||
/*
|
||||
This HashTable requires that T have at least sizeof(K)+sizeof(utp_link_t) bytes.
|
||||
Usually done like this:
|
||||
|
||||
struct K {
|
||||
int whatever;
|
||||
};
|
||||
|
||||
struct T {
|
||||
K wtf;
|
||||
utp_link_t link; // also wtf
|
||||
};
|
||||
*/
|
||||
|
||||
template<typename K, typename T> class utpHashTable {
|
||||
utp_hash_t *hash;
|
||||
public:
|
||||
static uint compare(const void *k1, const void *k2, size_t ks) {
|
||||
return *((K*)k1) == *((K*)k2);
|
||||
}
|
||||
static uint32 compute_hash(const void *k, size_t ks) {
|
||||
return ((K*)k)->compute_hash();
|
||||
}
|
||||
void Init() { hash = NULL; }
|
||||
bool Allocated() { return (hash != NULL); }
|
||||
void Free() { utp_hash_free_mem(hash); hash = NULL; }
|
||||
void Create(int N, int initial) { hash = utp_hash_create(N, sizeof(K), sizeof(T), initial, &compute_hash, &compare); }
|
||||
T *Lookup(const K &key) { return (T*)utp_hash_lookup(hash, &key); }
|
||||
T *Add(const K &key) { return (T*)utp_hash_add(&hash, &key); }
|
||||
T *Delete(const K &key) { return (T*)utp_hash_del(hash, &key); }
|
||||
T *Iterate(utp_hash_iterator_t &iterator) { return (T*)utp_hash_iterate(hash, &iterator); }
|
||||
size_t GetCount() { return hash->count; }
|
||||
};
|
||||
|
||||
#endif //__UTP_HASH_H__
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __UTP_INTERNAL_H__
|
||||
#define __UTP_INTERNAL_H__
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utp.h"
|
||||
#include "utp_callbacks.h"
|
||||
#include "utp_templates.h"
|
||||
#include "utp_hash.h"
|
||||
#include "utp_hash.h"
|
||||
#include "utp_packedsockaddr.h"
|
||||
|
||||
/* These originally lived in utp_config.h */
|
||||
#define CCONTROL_TARGET (100 * 1000) // us
|
||||
|
||||
enum bandwidth_type_t {
|
||||
payload_bandwidth, connect_overhead,
|
||||
close_overhead, ack_overhead,
|
||||
header_overhead, retransmit_overhead
|
||||
};
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _MSC_VER
|
||||
#include "libutp_inet_ntop.h"
|
||||
#endif
|
||||
|
||||
// newer versions of MSVC define these in errno.h
|
||||
#ifndef ECONNRESET
|
||||
#define ECONNRESET WSAECONNRESET
|
||||
#define EMSGSIZE WSAEMSGSIZE
|
||||
#define ECONNREFUSED WSAECONNREFUSED
|
||||
#define ETIMEDOUT WSAETIMEDOUT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct PACKED_ATTRIBUTE RST_Info {
|
||||
PackedSockAddr addr;
|
||||
uint32 connid;
|
||||
uint16 ack_nr;
|
||||
uint64 timestamp;
|
||||
};
|
||||
|
||||
// It's really important that we don't have duplicate keys in the hash table.
|
||||
// If we do, we'll eventually crash. if we try to remove the second instance
|
||||
// of the key, we'll accidentally remove the first instead. then later,
|
||||
// checkTimeouts will try to access the second one's already freed memory.
|
||||
void UTP_FreeAll(struct UTPSocketHT *utp_sockets);
|
||||
|
||||
struct UTPSocketKey {
|
||||
PackedSockAddr addr;
|
||||
uint32 recv_id; // "conn_seed", "conn_id"
|
||||
|
||||
UTPSocketKey(const PackedSockAddr& _addr, uint32 _recv_id) {
|
||||
memset(this, 0, sizeof(*this));
|
||||
addr = _addr;
|
||||
recv_id = _recv_id;
|
||||
}
|
||||
|
||||
bool operator == (const UTPSocketKey &other) const {
|
||||
return recv_id == other.recv_id && addr == other.addr;
|
||||
}
|
||||
|
||||
uint32 compute_hash() const {
|
||||
return recv_id ^ addr.compute_hash();
|
||||
}
|
||||
};
|
||||
|
||||
struct UTPSocketKeyData {
|
||||
UTPSocketKey key;
|
||||
UTPSocket *socket;
|
||||
utp_link_t link;
|
||||
};
|
||||
|
||||
#define UTP_SOCKET_BUCKETS 79
|
||||
#define UTP_SOCKET_INIT 15
|
||||
|
||||
struct UTPSocketHT : utpHashTable<UTPSocketKey, UTPSocketKeyData> {
|
||||
UTPSocketHT() {
|
||||
const int buckets = UTP_SOCKET_BUCKETS;
|
||||
const int initial = UTP_SOCKET_INIT;
|
||||
this->Create(buckets, initial);
|
||||
}
|
||||
~UTPSocketHT() {
|
||||
UTP_FreeAll(this);
|
||||
this->Free();
|
||||
}
|
||||
};
|
||||
|
||||
struct struct_utp_context {
|
||||
void *userdata;
|
||||
utp_callback_t* callbacks[UTP_ARRAY_SIZE];
|
||||
|
||||
uint64 current_ms;
|
||||
utp_context_stats context_stats;
|
||||
UTPSocket *last_utp_socket;
|
||||
Array<UTPSocket*> ack_sockets;
|
||||
Array<RST_Info> rst_info;
|
||||
UTPSocketHT *utp_sockets;
|
||||
size_t target_delay;
|
||||
size_t opt_sndbuf;
|
||||
size_t opt_rcvbuf;
|
||||
uint64 last_check;
|
||||
|
||||
struct_utp_context();
|
||||
~struct_utp_context();
|
||||
|
||||
void log(int level, utp_socket *socket, char const *fmt, ...);
|
||||
void log_unchecked(utp_socket *socket, char const *fmt, ...);
|
||||
bool would_log(int level);
|
||||
|
||||
bool log_normal:1; // log normal events?
|
||||
bool log_mtu:1; // log MTU related events?
|
||||
bool log_debug:1; // log debugging events? (Must also compile with UTP_DEBUG_LOGGING defined)
|
||||
};
|
||||
|
||||
#endif //__UTP_INTERNAL_H__
|
@ -0,0 +1,139 @@
|
||||
// vim:set ts=4 sw=4 ai:
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "utp_types.h"
|
||||
#include "utp_hash.h"
|
||||
#include "utp_packedsockaddr.h"
|
||||
|
||||
#include "libutp_inet_ntop.h"
|
||||
|
||||
byte PackedSockAddr::get_family() const
|
||||
{
|
||||
#if defined(__sh__)
|
||||
return ((_sin6d[0] == 0) && (_sin6d[1] == 0) && (_sin6d[2] == htonl(0xffff)) != 0) ?
|
||||
AF_INET : AF_INET6;
|
||||
#else
|
||||
return (IN6_IS_ADDR_V4MAPPED(&_in._in6addr) != 0) ? AF_INET : AF_INET6;
|
||||
#endif // defined(__sh__)
|
||||
}
|
||||
|
||||
bool PackedSockAddr::operator==(const PackedSockAddr& rhs) const
|
||||
{
|
||||
if (&rhs == this)
|
||||
return true;
|
||||
if (_port != rhs._port)
|
||||
return false;
|
||||
return memcmp(_sin6, rhs._sin6, sizeof(_sin6)) == 0;
|
||||
}
|
||||
|
||||
bool PackedSockAddr::operator!=(const PackedSockAddr& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
uint32 PackedSockAddr::compute_hash() const {
|
||||
return utp_hash_mem(&_in, sizeof(_in)) ^ _port;
|
||||
}
|
||||
|
||||
void PackedSockAddr::set(const SOCKADDR_STORAGE* sa, socklen_t len)
|
||||
{
|
||||
if (sa->ss_family == AF_INET) {
|
||||
assert(len >= sizeof(sockaddr_in));
|
||||
const sockaddr_in *sin = (sockaddr_in*)sa;
|
||||
_sin6w[0] = 0;
|
||||
_sin6w[1] = 0;
|
||||
_sin6w[2] = 0;
|
||||
_sin6w[3] = 0;
|
||||
_sin6w[4] = 0;
|
||||
_sin6w[5] = 0xffff;
|
||||
_sin4 = sin->sin_addr.s_addr;
|
||||
_port = ntohs(sin->sin_port);
|
||||
} else {
|
||||
assert(len >= sizeof(sockaddr_in6));
|
||||
const sockaddr_in6 *sin6 = (sockaddr_in6*)sa;
|
||||
_in._in6addr = sin6->sin6_addr;
|
||||
_port = ntohs(sin6->sin6_port);
|
||||
}
|
||||
}
|
||||
|
||||
PackedSockAddr::PackedSockAddr(const SOCKADDR_STORAGE* sa, socklen_t len)
|
||||
{
|
||||
set(sa, len);
|
||||
}
|
||||
|
||||
PackedSockAddr::PackedSockAddr(void)
|
||||
{
|
||||
SOCKADDR_STORAGE sa;
|
||||
socklen_t len = sizeof(SOCKADDR_STORAGE);
|
||||
memset(&sa, 0, len);
|
||||
sa.ss_family = AF_INET;
|
||||
set(&sa, len);
|
||||
}
|
||||
|
||||
SOCKADDR_STORAGE PackedSockAddr::get_sockaddr_storage(socklen_t *len = NULL) const
|
||||
{
|
||||
SOCKADDR_STORAGE sa;
|
||||
const byte family = get_family();
|
||||
if (family == AF_INET) {
|
||||
sockaddr_in *sin = (sockaddr_in*)&sa;
|
||||
if (len) *len = sizeof(sockaddr_in);
|
||||
memset(sin, 0, sizeof(sockaddr_in));
|
||||
sin->sin_family = family;
|
||||
sin->sin_port = htons(_port);
|
||||
sin->sin_addr.s_addr = _sin4;
|
||||
} else {
|
||||
sockaddr_in6 *sin6 = (sockaddr_in6*)&sa;
|
||||
memset(sin6, 0, sizeof(sockaddr_in6));
|
||||
if (len) *len = sizeof(sockaddr_in6);
|
||||
sin6->sin6_family = family;
|
||||
sin6->sin6_addr = _in._in6addr;
|
||||
sin6->sin6_port = htons(_port);
|
||||
}
|
||||
return sa;
|
||||
}
|
||||
|
||||
// #define addrfmt(x, s) x.fmt(s, sizeof(s))
|
||||
cstr PackedSockAddr::fmt(str s, size_t len) const
|
||||
{
|
||||
memset(s, 0, len);
|
||||
const byte family = get_family();
|
||||
str i;
|
||||
if (family == AF_INET) {
|
||||
INET_NTOP(family, (uint32*)&_sin4, s, len);
|
||||
i = s;
|
||||
while (*++i) {}
|
||||
} else {
|
||||
i = s;
|
||||
*i++ = '[';
|
||||
INET_NTOP(family, (in6_addr*)&_in._in6addr, i, len-1);
|
||||
while (*++i) {}
|
||||
*i++ = ']';
|
||||
}
|
||||
snprintf(i, len - (i-s), ":%u", _port);
|
||||
return s;
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __UTP_PACKEDSOCKADDR_H__
|
||||
#define __UTP_PACKEDSOCKADDR_H__
|
||||
|
||||
#include "utp_types.h"
|
||||
|
||||
struct PACKED_ATTRIBUTE PackedSockAddr {
|
||||
// The values are always stored here in network byte order
|
||||
union {
|
||||
byte _in6[16]; // IPv6
|
||||
uint16 _in6w[8]; // IPv6, word based (for convenience)
|
||||
uint32 _in6d[4]; // Dword access
|
||||
in6_addr _in6addr; // For convenience
|
||||
} _in;
|
||||
|
||||
// Host byte order
|
||||
uint16 _port;
|
||||
|
||||
#define _sin4 _in._in6d[3] // IPv4 is stored where it goes if mapped
|
||||
|
||||
#define _sin6 _in._in6
|
||||
#define _sin6w _in._in6w
|
||||
#define _sin6d _in._in6d
|
||||
|
||||
byte get_family() const;
|
||||
bool operator==(const PackedSockAddr& rhs) const;
|
||||
bool operator!=(const PackedSockAddr& rhs) const;
|
||||
void set(const SOCKADDR_STORAGE* sa, socklen_t len);
|
||||
|
||||
PackedSockAddr(const SOCKADDR_STORAGE* sa, socklen_t len);
|
||||
PackedSockAddr(void);
|
||||
|
||||
SOCKADDR_STORAGE get_sockaddr_storage(socklen_t *len) const;
|
||||
cstr fmt(str s, size_t len) const;
|
||||
|
||||
uint32 compute_hash() const;
|
||||
} ALIGNED_ATTRIBUTE(4);
|
||||
|
||||
#endif //__UTP_PACKEDSOCKADDR_H__
|
@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __TEMPLATES_H__
|
||||
#define __TEMPLATES_H__
|
||||
|
||||
#include "utp_types.h"
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(POSIX)
|
||||
/* Allow over-writing FORCEINLINE from makefile because gcc 3.4.4 for buffalo
|
||||
doesn't seem to support __attribute__((always_inline)) in -O0 build
|
||||
(strangely, it works in -Os build) */
|
||||
#ifndef FORCEINLINE
|
||||
// The always_inline attribute asks gcc to inline the function even if no optimization is being requested.
|
||||
// This macro should be used exclusive-or with the inline directive (use one or the other but not both)
|
||||
// since Microsoft uses __forceinline to also mean inline,
|
||||
// and this code is following a Microsoft compatibility model.
|
||||
// Just setting the attribute without also specifying the inline directive apparently won't inline the function,
|
||||
// as evidenced by multiply-defined symbols found at link time.
|
||||
#define FORCEINLINE inline __attribute__((always_inline))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Utility templates
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
template <typename T> static inline T min(T a, T b) { if (a < b) return a; return b; }
|
||||
template <typename T> static inline T max(T a, T b) { if (a > b) return a; return b; }
|
||||
|
||||
template <typename T> static inline T min(T a, T b, T c) { return min(min(a,b),c); }
|
||||
template <typename T> static inline T max(T a, T b, T c) { return max(max(a,b),c); }
|
||||
template <typename T> static inline T clamp(T v, T mi, T ma)
|
||||
{
|
||||
if (v > ma) v = ma;
|
||||
if (v < mi) v = mi;
|
||||
return v;
|
||||
}
|
||||
|
||||
#if (defined(__SVR4) && defined(__sun))
|
||||
#pragma pack(1)
|
||||
#else
|
||||
#pragma pack(push,1)
|
||||
#endif
|
||||
|
||||
|
||||
namespace aux
|
||||
{
|
||||
FORCEINLINE uint16 host_to_network(uint16 i) { return htons(i); }
|
||||
FORCEINLINE uint32 host_to_network(uint32 i) { return htonl(i); }
|
||||
FORCEINLINE int32 host_to_network(int32 i) { return htonl(i); }
|
||||
FORCEINLINE uint16 network_to_host(uint16 i) { return ntohs(i); }
|
||||
FORCEINLINE uint32 network_to_host(uint32 i) { return ntohl(i); }
|
||||
FORCEINLINE int32 network_to_host(int32 i) { return ntohl(i); }
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct PACKED_ATTRIBUTE big_endian
|
||||
{
|
||||
T operator=(T i) { m_integer = aux::host_to_network(i); return i; }
|
||||
operator T() const { return aux::network_to_host(m_integer); }
|
||||
private:
|
||||
T m_integer;
|
||||
};
|
||||
|
||||
typedef big_endian<int32> int32_big;
|
||||
typedef big_endian<uint32> uint32_big;
|
||||
typedef big_endian<uint16> uint16_big;
|
||||
|
||||
#if (defined(__SVR4) && defined(__sun))
|
||||
#pragma pack(0)
|
||||
#else
|
||||
#pragma pack(pop)
|
||||
#endif
|
||||
|
||||
template<typename T> static inline void zeromem(T *a, size_t count = 1) { memset(a, 0, count * sizeof(T)); }
|
||||
|
||||
typedef int SortCompareProc(const void *, const void *);
|
||||
|
||||
template<typename T> static FORCEINLINE void QuickSortT(T *base, size_t num, int (*comp)(const T *, const T *)) { qsort(base, num, sizeof(T), (SortCompareProc*)comp); }
|
||||
|
||||
|
||||
// WARNING: The template parameter MUST be a POD type!
|
||||
template <typename T, size_t minsize = 16> class Array {
|
||||
protected:
|
||||
T *mem;
|
||||
size_t alloc,count;
|
||||
|
||||
public:
|
||||
Array(size_t init) { Init(init); }
|
||||
Array() { Init(); }
|
||||
~Array() { Free(); }
|
||||
|
||||
void inline Init() { mem = NULL; alloc = count = 0; }
|
||||
void inline Init(size_t init) { Init(); if (init) Resize(init); }
|
||||
size_t inline GetCount() const { return count; }
|
||||
size_t inline GetAlloc() const { return alloc; }
|
||||
void inline SetCount(size_t c) { count = c; }
|
||||
|
||||
inline T& operator[](size_t offset) { assert(offset ==0 || offset<alloc); return mem[offset]; }
|
||||
inline const T& operator[](size_t offset) const { assert(offset ==0 || offset<alloc); return mem[offset]; }
|
||||
|
||||
void inline Resize(size_t a) {
|
||||
if (a == 0) { free(mem); Init(); }
|
||||
else { mem = (T*)realloc(mem, (alloc=a) * sizeof(T)); }
|
||||
}
|
||||
|
||||
void Grow() { Resize(::max<size_t>(minsize, alloc * 2)); }
|
||||
|
||||
inline size_t Append(const T &t) {
|
||||
if (count >= alloc) Grow();
|
||||
size_t r=count++;
|
||||
mem[r] = t;
|
||||
return r;
|
||||
}
|
||||
|
||||
T inline &Append() {
|
||||
if (count >= alloc) Grow();
|
||||
return mem[count++];
|
||||
}
|
||||
|
||||
void inline Compact() {
|
||||
Resize(count);
|
||||
}
|
||||
|
||||
void inline Free() {
|
||||
free(mem);
|
||||
Init();
|
||||
}
|
||||
|
||||
void inline Clear() {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
bool inline MoveUpLast(size_t index) {
|
||||
assert(index < count);
|
||||
size_t c = --count;
|
||||
if (index != c) {
|
||||
mem[index] = mem[c];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool inline MoveUpLastExist(const T &v) {
|
||||
return MoveUpLast(LookupElementExist(v));
|
||||
}
|
||||
|
||||
size_t inline LookupElement(const T &v) const {
|
||||
for(size_t i = 0; i != count; i++)
|
||||
if (mem[i] == v)
|
||||
return i;
|
||||
return (size_t) -1;
|
||||
}
|
||||
|
||||
bool inline HasElement(const T &v) const {
|
||||
return LookupElement(v) != -1;
|
||||
}
|
||||
|
||||
typedef int SortCompareProc(const T *a, const T *b);
|
||||
|
||||
void Sort(SortCompareProc* proc, size_t start, size_t end) {
|
||||
QuickSortT(&mem[start], end - start, proc);
|
||||
}
|
||||
|
||||
void Sort(SortCompareProc* proc, size_t start) {
|
||||
Sort(proc, start, count);
|
||||
}
|
||||
|
||||
void Sort(SortCompareProc* proc) {
|
||||
Sort(proc, 0, count);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //__TEMPLATES_H__
|
@ -0,0 +1,254 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include "utp.h"
|
||||
#include "utp_types.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else //!WIN32
|
||||
#include <time.h>
|
||||
#include <sys/time.h> // Linux needs both time.h and sys/time.h
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <mach/mach_time.h>
|
||||
#endif
|
||||
|
||||
#include "utp_utils.h"
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
typedef ULONGLONG (WINAPI GetTickCount64Proc)(void);
|
||||
static GetTickCount64Proc *pt2GetTickCount64;
|
||||
static GetTickCount64Proc *pt2RealGetTickCount;
|
||||
|
||||
static uint64 startPerformanceCounter;
|
||||
static uint64 startGetTickCount;
|
||||
// MSVC 6 standard doesn't like division with uint64s
|
||||
static double counterPerMicrosecond;
|
||||
|
||||
static uint64 UTGetTickCount64()
|
||||
{
|
||||
if (pt2GetTickCount64) {
|
||||
return pt2GetTickCount64();
|
||||
}
|
||||
if (pt2RealGetTickCount) {
|
||||
uint64 v = pt2RealGetTickCount();
|
||||
// fix return value from GetTickCount
|
||||
return (DWORD)v | ((v >> 0x18) & 0xFFFFFFFF00000000);
|
||||
}
|
||||
return (uint64)GetTickCount();
|
||||
}
|
||||
|
||||
static void Time_Initialize()
|
||||
{
|
||||
HMODULE kernel32 = GetModuleHandleA("kernel32.dll");
|
||||
pt2GetTickCount64 = (GetTickCount64Proc*)GetProcAddress(kernel32, "GetTickCount64");
|
||||
// not a typo. GetTickCount actually returns 64 bits
|
||||
pt2RealGetTickCount = (GetTickCount64Proc*)GetProcAddress(kernel32, "GetTickCount");
|
||||
|
||||
uint64 frequency;
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&startPerformanceCounter);
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&frequency);
|
||||
counterPerMicrosecond = (double)frequency / 1000000.0f;
|
||||
startGetTickCount = UTGetTickCount64();
|
||||
}
|
||||
|
||||
static int64 abs64(int64 x) { return x < 0 ? -x : x; }
|
||||
|
||||
static uint64 __GetMicroseconds()
|
||||
{
|
||||
static bool time_init = false;
|
||||
if (!time_init) {
|
||||
time_init = true;
|
||||
Time_Initialize();
|
||||
}
|
||||
|
||||
uint64 counter;
|
||||
uint64 tick;
|
||||
|
||||
QueryPerformanceCounter((LARGE_INTEGER*) &counter);
|
||||
tick = UTGetTickCount64();
|
||||
|
||||
// unfortunately, QueryPerformanceCounter is not guaranteed
|
||||
// to be monotonic. Make it so.
|
||||
int64 ret = (int64)(((int64)counter - (int64)startPerformanceCounter) / counterPerMicrosecond);
|
||||
// if the QPC clock leaps more than one second off GetTickCount64()
|
||||
// something is seriously fishy. Adjust QPC to stay monotonic
|
||||
int64 tick_diff = tick - startGetTickCount;
|
||||
if (abs64(ret / 100000 - tick_diff / 100) > 10) {
|
||||
startPerformanceCounter -= (uint64)((int64)(tick_diff * 1000 - ret) * counterPerMicrosecond);
|
||||
ret = (int64)((counter - startPerformanceCounter) / counterPerMicrosecond);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline uint64 UTP_GetMilliseconds()
|
||||
{
|
||||
return GetTickCount();
|
||||
}
|
||||
|
||||
#else //!WIN32
|
||||
|
||||
static inline uint64 UTP_GetMicroseconds(void);
|
||||
static inline uint64 UTP_GetMilliseconds()
|
||||
{
|
||||
return UTP_GetMicroseconds() / 1000;
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
static uint64 __GetMicroseconds()
|
||||
{
|
||||
// http://developer.apple.com/mac/library/qa/qa2004/qa1398.html
|
||||
// http://www.macresearch.org/tutorial_performance_and_time
|
||||
static mach_timebase_info_data_t sTimebaseInfo;
|
||||
static uint64_t start_tick = 0;
|
||||
uint64_t tick;
|
||||
// Returns a counter in some fraction of a nanoseconds
|
||||
tick = mach_absolute_time();
|
||||
if (sTimebaseInfo.denom == 0) {
|
||||
// Get the timer ratio to convert mach_absolute_time to nanoseconds
|
||||
mach_timebase_info(&sTimebaseInfo);
|
||||
start_tick = tick;
|
||||
}
|
||||
// Calculate the elapsed time, convert it to microseconds and return it.
|
||||
return ((tick - start_tick) * sTimebaseInfo.numer) / (sTimebaseInfo.denom * 1000);
|
||||
}
|
||||
|
||||
#else // !__APPLE__
|
||||
|
||||
#if ! (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(CLOCK_MONOTONIC))
|
||||
#warning "Using non-monotonic function gettimeofday() in UTP_GetMicroseconds()"
|
||||
#endif
|
||||
|
||||
/* Unfortunately, #ifdef CLOCK_MONOTONIC is not enough to make sure that
|
||||
POSIX clocks work -- we could be running a recent libc with an ancient
|
||||
kernel (think OpenWRT). -- jch */
|
||||
|
||||
static uint64_t __GetMicroseconds()
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
#if defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(CLOCK_MONOTONIC)
|
||||
static int have_posix_clocks = -1;
|
||||
int rc;
|
||||
|
||||
if (have_posix_clocks < 0) {
|
||||
struct timespec ts;
|
||||
rc = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (rc < 0) {
|
||||
have_posix_clocks = 0;
|
||||
} else {
|
||||
have_posix_clocks = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (have_posix_clocks) {
|
||||
struct timespec ts;
|
||||
rc = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return uint64(ts.tv_sec) * 1000000 + uint64(ts.tv_nsec) / 1000;
|
||||
}
|
||||
#endif
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
return uint64(tv.tv_sec) * 1000000 + tv.tv_usec;
|
||||
}
|
||||
|
||||
#endif //!__APPLE__
|
||||
|
||||
#endif //!WIN32
|
||||
|
||||
/*
|
||||
* Whew. Okay. After that #ifdef maze above, we now know we have a working
|
||||
* __GetMicroseconds() implementation on all platforms.
|
||||
*
|
||||
* Because there are a number of assertions in libutp that will cause a crash
|
||||
* if monotonic time isn't monotonic, now apply some safety checks. While in
|
||||
* principle we're already protecting ourselves in cases where non-monotonic
|
||||
* time is likely to happen, this protects all versions.
|
||||
*/
|
||||
|
||||
static inline uint64 UTP_GetMicroseconds()
|
||||
{
|
||||
static uint64 offset = 0, previous = 0;
|
||||
|
||||
uint64 now = __GetMicroseconds() + offset;
|
||||
if (previous > now) {
|
||||
/* Eek! */
|
||||
offset += previous - now;
|
||||
now = previous;
|
||||
}
|
||||
previous = now;
|
||||
return now;
|
||||
}
|
||||
|
||||
#define ETHERNET_MTU 1500
|
||||
#define IPV4_HEADER_SIZE 20
|
||||
#define IPV6_HEADER_SIZE 40
|
||||
#define UDP_HEADER_SIZE 8
|
||||
#define GRE_HEADER_SIZE 24
|
||||
#define PPPOE_HEADER_SIZE 8
|
||||
#define MPPE_HEADER_SIZE 2
|
||||
// packets have been observed in the wild that were fragmented
|
||||
// with a payload of 1416 for the first fragment
|
||||
// There are reports of routers that have MTU sizes as small as 1392
|
||||
#define FUDGE_HEADER_SIZE 36
|
||||
#define TEREDO_MTU 1280
|
||||
|
||||
#define UDP_IPV4_OVERHEAD (IPV4_HEADER_SIZE + UDP_HEADER_SIZE)
|
||||
#define UDP_IPV6_OVERHEAD (IPV6_HEADER_SIZE + UDP_HEADER_SIZE)
|
||||
#define UDP_TEREDO_OVERHEAD (UDP_IPV4_OVERHEAD + UDP_IPV6_OVERHEAD)
|
||||
|
||||
#define UDP_IPV4_MTU (ETHERNET_MTU - IPV4_HEADER_SIZE - UDP_HEADER_SIZE - GRE_HEADER_SIZE - PPPOE_HEADER_SIZE - MPPE_HEADER_SIZE - FUDGE_HEADER_SIZE)
|
||||
#define UDP_IPV6_MTU (ETHERNET_MTU - IPV6_HEADER_SIZE - UDP_HEADER_SIZE - GRE_HEADER_SIZE - PPPOE_HEADER_SIZE - MPPE_HEADER_SIZE - FUDGE_HEADER_SIZE)
|
||||
#define UDP_TEREDO_MTU (TEREDO_MTU - IPV6_HEADER_SIZE - UDP_HEADER_SIZE)
|
||||
|
||||
uint64 utp_default_get_udp_mtu(utp_callback_arguments *args) {
|
||||
// Since we don't know the local address of the interface,
|
||||
// be conservative and assume all IPv6 connections are Teredo.
|
||||
return (args->address->sa_family == AF_INET6) ? UDP_TEREDO_MTU : UDP_IPV4_MTU;
|
||||
}
|
||||
|
||||
uint64 utp_default_get_udp_overhead(utp_callback_arguments *args) {
|
||||
// Since we don't know the local address of the interface,
|
||||
// be conservative and assume all IPv6 connections are Teredo.
|
||||
return (args->address->sa_family == AF_INET6) ? UDP_TEREDO_OVERHEAD : UDP_IPV4_OVERHEAD;
|
||||
}
|
||||
|
||||
uint64 utp_default_get_random(utp_callback_arguments *args) {
|
||||
return rand();
|
||||
}
|
||||
|
||||
uint64 utp_default_get_milliseconds(utp_callback_arguments *args) {
|
||||
return UTP_GetMilliseconds();
|
||||
}
|
||||
|
||||
uint64 utp_default_get_microseconds(utp_callback_arguments *args) {
|
||||
return UTP_GetMicroseconds();
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2013 BitTorrent, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
uint64 utp_default_get_udp_mtu(utp_callback_arguments *args);
|
||||
uint64 utp_default_get_udp_overhead(utp_callback_arguments *args);
|
||||
uint64 utp_default_get_random(utp_callback_arguments *args);
|
||||
uint64 utp_default_get_milliseconds(utp_callback_arguments *args);
|
||||
uint64 utp_default_get_microseconds(utp_callback_arguments *args);
|
@ -1,32 +0,0 @@
|
||||
#include <llarp/link/curvecp.hpp>
|
||||
#include "router.hpp"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace curvecp
|
||||
{
|
||||
struct LinkLayer : public llarp::ILinkLayer
|
||||
{
|
||||
LinkLayer(llarp_router* r) : llarp::ILinkLayer(r)
|
||||
{
|
||||
}
|
||||
|
||||
~LinkLayer()
|
||||
{
|
||||
}
|
||||
|
||||
const char*
|
||||
Name() const
|
||||
{
|
||||
return "curvecp";
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr< llarp::ILinkLayer >
|
||||
NewServer(llarp_router* r)
|
||||
{
|
||||
return std::unique_ptr< llarp::ILinkLayer >(new LinkLayer(r));
|
||||
}
|
||||
} // namespace curvecp
|
||||
|
||||
} // namespace llarp
|
@ -1,5 +1,6 @@
|
||||
#include <llarp/link/curvecp.hpp>
|
||||
#include "router.hpp"
|
||||
#include <llarp/messages/link_intro.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
@ -0,0 +1,576 @@
|
||||
#include <llarp/link/utp.hpp>
|
||||
#include "router.hpp"
|
||||
#include <llarp/messages/link_intro.hpp>
|
||||
#include <llarp/buffer.hpp>
|
||||
#include <llarp/endian.h>
|
||||
#include <utp.h>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace utp
|
||||
{
|
||||
constexpr size_t FragmentBufferSize = 1088;
|
||||
constexpr size_t FragmentHashSize = 32;
|
||||
constexpr size_t FragmentNonceSize = 24;
|
||||
constexpr size_t FragmentOverheadSize =
|
||||
FragmentHashSize + FragmentNonceSize;
|
||||
constexpr size_t FragmentBodySize =
|
||||
FragmentBufferSize - FragmentOverheadSize;
|
||||
|
||||
#ifdef __int128
|
||||
typedef unsigned __int128 Long_t;
|
||||
#else
|
||||
typedef uint64_t Long_t;
|
||||
#endif
|
||||
|
||||
typedef llarp::AlignedBuffer< FragmentBufferSize, false, Long_t >
|
||||
FragmentBuffer;
|
||||
typedef llarp::AlignedBuffer< MAX_LINK_MSG_SIZE, false, Long_t >
|
||||
MessageBuffer;
|
||||
|
||||
struct LinkLayer;
|
||||
|
||||
struct BaseSession : public ILinkSession
|
||||
{
|
||||
utp_socket* sock;
|
||||
llarp_router* router;
|
||||
RouterContact remoteRC;
|
||||
Addr remoteAddr;
|
||||
SharedSecret sessionKey;
|
||||
llarp_time_t lastActive;
|
||||
llarp_time_t sessionTimeout = 10 * 1000;
|
||||
std::queue< FragmentBuffer > sendq;
|
||||
FragmentBuffer recvBuf;
|
||||
size_t recvBufOffset = 0;
|
||||
MessageBuffer recvMsg;
|
||||
size_t recvMsgOffset = 0;
|
||||
|
||||
enum State
|
||||
{
|
||||
eInitial,
|
||||
eConnecting,
|
||||
eLinkEstablished, // when utp connection is established
|
||||
eCryptoHandshake, // crypto handshake initiated
|
||||
eSessionReady, // session is ready
|
||||
eClose // utp connection is closed
|
||||
};
|
||||
|
||||
State state;
|
||||
|
||||
virtual void
|
||||
LinkEstablished() = 0;
|
||||
|
||||
void
|
||||
EnterState(State st)
|
||||
{
|
||||
state = st;
|
||||
lastActive = llarp_time_now_ms();
|
||||
}
|
||||
|
||||
BaseSession(llarp_router* r, utp_socket* s);
|
||||
virtual ~BaseSession();
|
||||
|
||||
void
|
||||
Pump()
|
||||
{
|
||||
// TODO: use utp_writev
|
||||
while(sendq.size())
|
||||
{
|
||||
auto& front = sendq.front();
|
||||
write_ll(front.data(), front.size());
|
||||
sendq.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
write_ll(void* buf, size_t sz)
|
||||
{
|
||||
llarp::LogDebug("utp_write ", sz, " bytes to ", remoteAddr);
|
||||
ssize_t wrote = utp_write(sock, buf, sz);
|
||||
if(wrote < 0)
|
||||
{
|
||||
llarp::LogWarn("utp_write returned ", wrote);
|
||||
}
|
||||
llarp::LogDebug("utp_write wrote ", wrote, " bytes to ", remoteAddr);
|
||||
}
|
||||
|
||||
bool
|
||||
VerifyThenDecrypt(FragmentBuffer& buf)
|
||||
{
|
||||
ShortHash digest;
|
||||
if(!router->crypto.hmac(
|
||||
digest,
|
||||
InitBuffer(buf.data() + FragmentHashSize,
|
||||
FragmentBufferSize - FragmentHashSize),
|
||||
sessionKey))
|
||||
{
|
||||
llarp::LogError("keyed hash failed");
|
||||
return false;
|
||||
}
|
||||
if(digest != ShortHash(buf.data()))
|
||||
{
|
||||
llarp::LogError("Message Integrity Failed");
|
||||
return false;
|
||||
}
|
||||
AlignedBuffer< FragmentNonceSize > nonce(buf.data() + FragmentHashSize);
|
||||
|
||||
auto body = InitBuffer(buf.data() + FragmentOverheadSize,
|
||||
FragmentBufferSize - FragmentOverheadSize);
|
||||
|
||||
router->crypto.xchacha20(body, sessionKey, nonce);
|
||||
|
||||
uint32_t upper, lower;
|
||||
if(!(llarp_buffer_read_uint32(&body, &upper)
|
||||
&& llarp_buffer_read_uint32(&body, &lower)))
|
||||
return false;
|
||||
bool fragmentEnd = upper == 0;
|
||||
if(lower > recvMsgOffset + recvMsg.size())
|
||||
{
|
||||
llarp::LogError("Fragment too big: ", lower, " bytes");
|
||||
return false;
|
||||
}
|
||||
byte_t* ptr = recvMsg.data() + recvMsgOffset;
|
||||
memcpy(ptr, body.cur, lower);
|
||||
recvMsgOffset += lower;
|
||||
if(fragmentEnd)
|
||||
{
|
||||
// got a message
|
||||
auto msgbuf = InitBuffer(recvMsg.data(), recvMsgOffset);
|
||||
recvMsgOffset = 0;
|
||||
return router->HandleRecvLinkMessageBuffer(this, msgbuf);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
EncryptThenHash(FragmentBuffer& buf, const byte_t* ptr, uint32_t sz,
|
||||
bool isLastFragment)
|
||||
|
||||
{
|
||||
buf.Randomize();
|
||||
const byte_t* nonce = buf.data() + FragmentHashSize;
|
||||
byte_t* body = buf.data() + FragmentOverheadSize;
|
||||
byte_t* base = body;
|
||||
if(isLastFragment)
|
||||
htobe32buf(body, 0);
|
||||
body += sizeof(uint32_t);
|
||||
htobe32buf(body, sz);
|
||||
body += sizeof(uint32_t);
|
||||
memcpy(body, ptr, sz);
|
||||
auto payload = InitBuffer(base, FragmentBodySize);
|
||||
router->crypto.xchacha20(payload, sessionKey, nonce);
|
||||
router->crypto.hmac(buf, payload, sessionKey);
|
||||
}
|
||||
|
||||
bool
|
||||
SendMessageBuffer(llarp_buffer_t buf)
|
||||
{
|
||||
if(state != eSessionReady)
|
||||
return false;
|
||||
size_t sz = buf.sz;
|
||||
while(sz)
|
||||
{
|
||||
uint32_t s =
|
||||
std::min((FragmentBodySize - (llarp_randint() % 128)), sz);
|
||||
sendq.emplace();
|
||||
EncryptThenHash(sendq.back(), buf.cur, s, ((sz - s) == 0));
|
||||
buf.cur += s;
|
||||
sz -= s;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DoKeyExchange(llarp_transport_dh_func dh, const KeyExchangeNonce& n,
|
||||
const PubKey& other, const SecretKey& secret)
|
||||
{
|
||||
if(!dh(sessionKey, other, secret, n))
|
||||
{
|
||||
llarp::LogError("key exchange with ", other, " failed");
|
||||
SendClose();
|
||||
return;
|
||||
}
|
||||
EnterState(eSessionReady);
|
||||
}
|
||||
|
||||
void
|
||||
Tick(llarp_time_t now)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
SendKeepAlive()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
SendClose()
|
||||
{
|
||||
if(state != eClose)
|
||||
{
|
||||
utp_set_userdata(sock, nullptr);
|
||||
utp_close(sock);
|
||||
}
|
||||
EnterState(eClose);
|
||||
sock = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
IsEstablished() const
|
||||
{
|
||||
return state == eSessionReady;
|
||||
}
|
||||
|
||||
bool
|
||||
Recv(const void* buf, size_t sz)
|
||||
{
|
||||
const byte_t* ptr = (const byte_t*)buf;
|
||||
llarp::LogDebug("utp read ", sz, " from ", remoteAddr);
|
||||
while(sz + recvBufOffset > FragmentBufferSize)
|
||||
{
|
||||
memcpy(recvBuf.data() + recvBufOffset, ptr, FragmentBufferSize);
|
||||
sz -= FragmentBufferSize;
|
||||
ptr += FragmentBufferSize;
|
||||
VerifyThenDecrypt(recvBuf);
|
||||
recvBufOffset = 0;
|
||||
}
|
||||
memcpy(recvBuf.data() + recvBufOffset, ptr, sz);
|
||||
if(sz + recvBufOffset <= FragmentBufferSize)
|
||||
{
|
||||
recvBufOffset = 0;
|
||||
VerifyThenDecrypt(recvBuf);
|
||||
}
|
||||
else
|
||||
recvBufOffset += sz;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
RecvHandshake(const void* buf, size_t sz, ILinkLayer* parent)
|
||||
{
|
||||
if((recvBuf.size() - recvBufOffset) < sz)
|
||||
{
|
||||
llarp::LogInfo("handshake too big");
|
||||
SendClose();
|
||||
return;
|
||||
}
|
||||
memcpy(recvBuf.data() + recvBufOffset, buf, sz);
|
||||
recvBufOffset += sz;
|
||||
|
||||
if(recvBufOffset > 8)
|
||||
{
|
||||
// process handshake header
|
||||
uint8_t* ptr = recvBuf.data();
|
||||
uint32_t version = bufbe32toh(ptr);
|
||||
if(version != LLARP_PROTO_VERSION)
|
||||
{
|
||||
llarp::LogWarn("protocol version missmatch ", version,
|
||||
" != ", LLARP_PROTO_VERSION);
|
||||
return;
|
||||
}
|
||||
ptr += sizeof(uint32_t);
|
||||
uint32_t limsz = bufbe32toh(ptr);
|
||||
ptr += sizeof(uint32_t);
|
||||
if(((sizeof(uint32_t) * 2) + limsz) > sz)
|
||||
{
|
||||
// not enough data
|
||||
// TODO: don't bail here, continue reading
|
||||
SendClose();
|
||||
}
|
||||
LinkIntroMessage msg(this);
|
||||
auto mbuf = InitBuffer(ptr, limsz);
|
||||
if(!msg.BDecode(&mbuf))
|
||||
{
|
||||
llarp::LogError("malfromed LIM from ", remoteAddr);
|
||||
return;
|
||||
}
|
||||
if(!msg.HandleMessage(router))
|
||||
{
|
||||
llarp::LogError("failed to handle LIM from ", remoteAddr);
|
||||
SendClose();
|
||||
return;
|
||||
}
|
||||
remoteRC = msg.rc;
|
||||
DoKeyExchange(router->crypto.dh_server, msg.N, msg.rc.enckey,
|
||||
parent->TransportSecretKey());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TimedOut(llarp_time_t now) const
|
||||
{
|
||||
if(now < lastActive)
|
||||
return false;
|
||||
return lastActive - now > sessionTimeout;
|
||||
}
|
||||
|
||||
const PubKey&
|
||||
GetPubKey() const
|
||||
{
|
||||
return remoteRC.pubkey;
|
||||
}
|
||||
|
||||
const Addr&
|
||||
GetRemoteEndpoint() const
|
||||
{
|
||||
return remoteAddr;
|
||||
}
|
||||
|
||||
void
|
||||
MarkEstablished();
|
||||
};
|
||||
|
||||
struct LinkLayer : public ILinkLayer
|
||||
{
|
||||
utp_context* _utp_ctx = nullptr;
|
||||
|
||||
static uint64
|
||||
OnRead(utp_callback_arguments* arg)
|
||||
{
|
||||
BaseSession* self =
|
||||
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
|
||||
if(self)
|
||||
{
|
||||
if(self->IsEstablished())
|
||||
self->Recv(arg->buf, arg->len);
|
||||
else
|
||||
{
|
||||
LinkLayer* parent = static_cast< LinkLayer* >(
|
||||
utp_context_get_userdata(arg->context));
|
||||
self->RecvHandshake(arg->buf, arg->len, parent);
|
||||
}
|
||||
utp_read_drained(arg->socket);
|
||||
}
|
||||
else
|
||||
{
|
||||
llarp::LogWarn("utp_socket got data with no underlying session");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64
|
||||
SendTo(utp_callback_arguments* arg)
|
||||
{
|
||||
LinkLayer* l =
|
||||
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
|
||||
llarp_ev_udp_sendto(&l->m_udp, arg->address, arg->buf, arg->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64
|
||||
OnConnect(utp_callback_arguments* arg)
|
||||
{
|
||||
BaseSession* session =
|
||||
static_cast< BaseSession* >(utp_get_userdata(arg->socket));
|
||||
session->LinkEstablished();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64
|
||||
OnAccept(utp_callback_arguments*);
|
||||
|
||||
LinkLayer(llarp_router* r) : ILinkLayer(r)
|
||||
{
|
||||
_utp_ctx = utp_init(2);
|
||||
utp_context_set_userdata(_utp_ctx, this);
|
||||
utp_set_callback(_utp_ctx, UTP_SENDTO, &LinkLayer::SendTo);
|
||||
utp_set_callback(_utp_ctx, UTP_ON_ACCEPT, &LinkLayer::OnAccept);
|
||||
utp_set_callback(_utp_ctx, UTP_ON_CONNECT, &LinkLayer::OnConnect);
|
||||
utp_set_callback(_utp_ctx, UTP_ON_READ, &LinkLayer::OnRead);
|
||||
}
|
||||
|
||||
~LinkLayer()
|
||||
{
|
||||
utp_destroy(_utp_ctx);
|
||||
}
|
||||
|
||||
uint16_t
|
||||
Rank() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
RecvFrom(const Addr& from, const void* buf, size_t sz)
|
||||
{
|
||||
utp_process_udp(_utp_ctx, (const byte_t*)buf, sz, from, from.SockLen());
|
||||
}
|
||||
|
||||
void
|
||||
Pump()
|
||||
{
|
||||
utp_check_timeouts(_utp_ctx);
|
||||
utp_issue_deferred_acks(_utp_ctx);
|
||||
ILinkLayer::Pump();
|
||||
}
|
||||
|
||||
void
|
||||
Stop()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
KeyGen(SecretKey& k)
|
||||
{
|
||||
m_router->crypto.encryption_keygen(k);
|
||||
return true;
|
||||
}
|
||||
|
||||
ILinkSession*
|
||||
NewOutboundSession(const RouterContact& rc, const AddressInfo& addr);
|
||||
|
||||
ILinkSession*
|
||||
NewInboundSession(const Addr& addr);
|
||||
|
||||
utp_socket*
|
||||
NewSocket()
|
||||
{
|
||||
return utp_create_socket(_utp_ctx);
|
||||
}
|
||||
|
||||
const char*
|
||||
Name() const
|
||||
{
|
||||
return "utp";
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr< ILinkLayer >
|
||||
NewServer(llarp_router* r)
|
||||
{
|
||||
return std::unique_ptr< ILinkLayer >(new LinkLayer(r));
|
||||
}
|
||||
|
||||
struct OutboundSession : public BaseSession
|
||||
{
|
||||
PubKey remoteTransportPubKey;
|
||||
|
||||
OutboundSession(llarp_router* r, utp_socket* s, const RouterContact& rc,
|
||||
const AddressInfo& addr)
|
||||
: BaseSession(r, s)
|
||||
{
|
||||
remoteRC = rc;
|
||||
remoteAddr = addr;
|
||||
remoteTransportPubKey = addr.pubkey;
|
||||
}
|
||||
|
||||
void
|
||||
LinkEstablished()
|
||||
{
|
||||
llarp::LogDebug("link established with ", remoteAddr);
|
||||
EnterState(eLinkEstablished);
|
||||
KeyExchangeNonce nonce;
|
||||
nonce.Randomize();
|
||||
SendHandshake(nonce);
|
||||
EnterState(eCryptoHandshake);
|
||||
DoKeyExchange(router->crypto.dh_client, nonce, remoteTransportPubKey,
|
||||
router->encryption);
|
||||
}
|
||||
|
||||
// send our RC to the remote
|
||||
void
|
||||
SendHandshake(const KeyExchangeNonce& n)
|
||||
{
|
||||
byte_t tmp[MAX_RC_SIZE + 128] = {0};
|
||||
auto buf = StackBuffer< decltype(tmp) >(tmp);
|
||||
// fastforward buffer for handshake to fit before
|
||||
buf.cur += sizeof(uint32_t) * 2;
|
||||
|
||||
LinkIntroMessage msg(this);
|
||||
msg.rc = router->rc;
|
||||
msg.N = n;
|
||||
if(!msg.BEncode(&buf))
|
||||
return;
|
||||
|
||||
uint32_t sz = buf.cur - buf.base;
|
||||
sz -= sizeof(uint32_t) * 2;
|
||||
// write handshake header
|
||||
buf.cur = buf.base;
|
||||
llarp_buffer_put_uint32(&buf, LLARP_PROTO_VERSION);
|
||||
llarp_buffer_put_uint32(&buf, sz);
|
||||
// send it
|
||||
write_ll(buf.base, sz);
|
||||
}
|
||||
|
||||
void
|
||||
Start()
|
||||
{
|
||||
utp_connect(sock, remoteAddr, remoteAddr.SockLen());
|
||||
EnterState(eConnecting);
|
||||
}
|
||||
};
|
||||
|
||||
struct InboundSession : public BaseSession
|
||||
{
|
||||
InboundSession(llarp_router* r, utp_socket* s, const Addr& addr)
|
||||
: BaseSession(r, s)
|
||||
{
|
||||
remoteAddr = addr;
|
||||
}
|
||||
|
||||
void
|
||||
Start()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
LinkEstablished()
|
||||
{
|
||||
EnterState(eLinkEstablished);
|
||||
}
|
||||
};
|
||||
|
||||
BaseSession::BaseSession(llarp_router* r, utp_socket* s)
|
||||
{
|
||||
router = r;
|
||||
sock = s;
|
||||
utp_set_userdata(sock, this);
|
||||
lastActive = llarp_time_now_ms();
|
||||
}
|
||||
|
||||
BaseSession::~BaseSession()
|
||||
{
|
||||
}
|
||||
|
||||
ILinkSession*
|
||||
LinkLayer::NewOutboundSession(const RouterContact& rc,
|
||||
const AddressInfo& addr)
|
||||
{
|
||||
return new OutboundSession(m_router, utp_create_socket(_utp_ctx), rc,
|
||||
addr);
|
||||
}
|
||||
|
||||
ILinkSession*
|
||||
LinkLayer::NewInboundSession(const Addr& addr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint64
|
||||
LinkLayer::OnAccept(utp_callback_arguments* arg)
|
||||
{
|
||||
LinkLayer* self =
|
||||
static_cast< LinkLayer* >(utp_context_get_userdata(arg->context));
|
||||
Addr remote(*arg->address);
|
||||
llarp::LogDebug("utp accepted from ", remote);
|
||||
if(self->HasSessionVia(remote))
|
||||
{
|
||||
// TODO should we do this?
|
||||
llarp::LogWarn(
|
||||
"utp socket closed because we already have a session "
|
||||
"via ",
|
||||
remote);
|
||||
utp_close(arg->socket);
|
||||
return 0;
|
||||
}
|
||||
InboundSession* session =
|
||||
new InboundSession(self->m_router, arg->socket, remote);
|
||||
self->PutSession(remote, session);
|
||||
session->LinkEstablished();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace utp
|
||||
|
||||
} // namespace llarp
|
Loading…
Reference in New Issue