Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ set(PROJECT_VER "0.1.0")
include_directories(${CMAKE_SOURCE_DIR}/include)
add_compile_definitions(ESP_IDF_LWIP_HOOK_FILENAME="lwip_router_hook.h")
# ESP-IDF reserves low descriptors for stdio/VFS. Keep a descriptor range large
# enough for the 120 lwIP sockets plus those reserved descriptors.
add_compile_definitions(FD_SETSIZE=128)
# enough for 160 lwIP sockets plus the reserved descriptors.
add_compile_definitions(FD_SETSIZE=192)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ESP32-VLESS-Transparent-Proxy-Router)
4 changes: 2 additions & 2 deletions sdkconfig.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=64
CONFIG_ESP_WIFI_RX_BA_WIN=6
CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=32
CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=64
CONFIG_LWIP_MAX_SOCKETS=120
CONFIG_LWIP_MAX_SOCKETS=160
CONFIG_LWIP_DHCPS_MAX_STATION_NUM=16
# CONFIG_LWIP_IPV6 is not set
CONFIG_LWIP_MAX_ACTIVE_TCP=128
CONFIG_LWIP_MAX_ACTIVE_TCP=160
CONFIG_LWIP_MAX_LISTENING_TCP=64
CONFIG_LWIP_TCP_SND_BUF_DEFAULT=32768
CONFIG_LWIP_TCP_WND_DEFAULT=32768
Expand Down
8 changes: 6 additions & 2 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@
#define UDP_QUEUE_SEND_WAIT_MS 2
/* smux keeps transport sockets scarce. TCP payloads themselves are allocated
in PSRAM and only pointers travel through this small control queue. */
#define SINGMUX_TCP_STREAM_MAX 80
#define SINGMUX_CONTROL_QUEUE_DEPTH 96
#define SINGMUX_TCP_STREAM_MAX 100
/* Absorb a browser's burst of SYN/data/FIN control messages without making
the manager's queue
* the limiting factor before all 100 stream slots can be
used. */
#define SINGMUX_CONTROL_QUEUE_DEPTH 120
#define SINGMUX_CONTROL_BATCH_MAX 8
#define SINGMUX_TCP_RX_QUEUE_DEPTH 24
#define SINGMUX_TCP_DATA_MAX 2048
Expand Down
138 changes: 58 additions & 80 deletions src/transparent_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,87 +58,71 @@ static bool is_link_local_multicast_or_broadcast(uint32_t destination_ip)
return address[0] >= 224;
}

static uint32_t checksum_bytes(uint32_t sum, const uint8_t *data, size_t length, bool *odd,
uint8_t *tail)
/* RFC 1624 incremental Internet checksum update. Redirecting a packet changes
* only four 16-bit
* words (IPv4 source/destination and the two ports), so
* rescanning every payload byte is
* needless work in the AP+STA hot path. */
static uint16_t checksum_replace(uint16_t checksum, uint16_t old_word, uint16_t new_word)
{
if (*odd && length)
{
sum += ((uint16_t)*tail << 8) | *data++;
length--;
*odd = false;
}
while (length >= 2)
{
sum += ((uint16_t)data[0] << 8) | data[1];
data += 2;
length -= 2;
}
if (length)
{
*tail = *data;
*odd = true;
}
return sum;
uint32_t sum = (uint16_t)~checksum + (uint16_t)~old_word + new_word;
sum = (sum & 0xffff) + (sum >> 16);
sum = (sum & 0xffff) + (sum >> 16);
return (uint16_t)~sum;
}

static uint16_t checksum_finish(uint32_t sum, bool odd, uint8_t tail)
static uint16_t read_u16(const uint8_t *data)
{
if (odd)
{
sum += (uint16_t)tail << 8;
}
while (sum >> 16)
{
sum = (sum & 0xffff) + (sum >> 16);
}
return (uint16_t)~sum;
return ((uint16_t)data[0] << 8) | data[1];
}

static uint16_t pbuf_checksum(const struct pbuf *packet, size_t offset, size_t length,
uint32_t seed)
static uint16_t checksum_replace_u32(uint16_t checksum, uint32_t old_value, uint32_t new_value)
{
uint8_t scratch[96];
bool odd = false;
uint8_t tail = 0;
uint32_t sum = seed;
while (length)
{
size_t count = length > sizeof(scratch) ? sizeof(scratch) : length;
if (pbuf_copy_partial(packet, scratch, count, offset) != count)
{
return 0;
}
sum = checksum_bytes(sum, scratch, count, &odd, &tail);
offset += count;
length -= count;
}
return checksum_finish(sum, odd, tail);
const uint8_t *old_bytes = (const uint8_t *)&old_value;
const uint8_t *new_bytes = (const uint8_t *)&new_value;
checksum = checksum_replace(checksum, read_u16(old_bytes), read_u16(new_bytes));
return checksum_replace(checksum, read_u16(old_bytes + 2), read_u16(new_bytes + 2));
}

static bool rewrite_packet(struct pbuf *packet, const uint8_t ip_header[60], uint8_t ihl,
uint32_t source_ip, uint16_t source_port, uint32_t destination_ip,
uint16_t destination_port)
{
uint16_t total_length = ((uint16_t)ip_header[2] << 8) | ip_header[3];
uint16_t total_length = read_u16(ip_header + 2);
uint8_t protocol = ip_header[9];
uint16_t minimum_length = protocol == 6 ? 20 : (protocol == 17 ? 8 : 0);
uint16_t checksum_offset = protocol == 6 ? 16 : 6;
if (!minimum_length || total_length < ihl + minimum_length || total_length > packet->tot_len)
{
return false;
}
/* Maximum IPv4 header (60 bytes) plus the first four transport bytes. */
uint8_t header[64];
memcpy(header, ip_header, ihl);
uint32_t old_source_ip;
uint32_t old_destination_ip;
memcpy(&old_source_ip, ip_header + 12, sizeof(old_source_ip));
memcpy(&old_destination_ip, ip_header + 16, sizeof(old_destination_ip));
uint16_t old_ip_checksum = read_u16(header + 10);
uint8_t old_ports[4];
if (pbuf_copy_partial(packet, old_ports, sizeof(old_ports), ihl) != sizeof(old_ports))
{
return false;
}
uint16_t old_source_port = read_u16(old_ports);
uint16_t old_destination_port = read_u16(old_ports + 2);
uint8_t transport_checksum_bytes[2];
if (pbuf_copy_partial(packet, transport_checksum_bytes, sizeof(transport_checksum_bytes),
ihl + checksum_offset) != sizeof(transport_checksum_bytes))
{
return false;
}
uint16_t transport_checksum = read_u16(transport_checksum_bytes);
memcpy(header + 12, &source_ip, sizeof(source_ip));
memcpy(header + 16, &destination_ip, sizeof(destination_ip));
header[10] = header[11] = 0;
bool odd = false;
uint8_t tail = 0;
uint32_t ip_sum = checksum_bytes(0, header, ihl, &odd, &tail);
uint16_t ip_checksum = checksum_finish(ip_sum, odd, tail);
header[10] = ip_checksum >> 8;
header[11] = ip_checksum;
uint16_t ip_checksum = checksum_replace_u32(old_ip_checksum, old_source_ip, source_ip);
ip_checksum = checksum_replace_u32(ip_checksum, old_destination_ip, destination_ip);
header[10] = ip_checksum >> 8;
header[11] = ip_checksum;
if (pbuf_take_at(packet, header, ihl, 0) != ERR_OK)
{
return false;
Expand All @@ -149,31 +133,25 @@ static bool rewrite_packet(struct pbuf *packet, const uint8_t ip_header[60], uin
{
return false;
}
uint8_t zero[2] = {0, 0};
if (pbuf_take_at(packet, zero, sizeof(zero), ihl + checksum_offset) != ERR_OK)
/* UDP over IPv4 may deliberately omit its checksum; preserve that case. */
if (transport_checksum == 0 && protocol == 17)
{
return false;
return true;
}
transport_checksum = checksum_replace_u32(transport_checksum, old_source_ip, source_ip);
transport_checksum =
checksum_replace_u32(transport_checksum, old_destination_ip, destination_ip);
transport_checksum = checksum_replace(transport_checksum, old_source_port, source_port);
transport_checksum =
checksum_replace(transport_checksum, old_destination_port, destination_port);
if (protocol == 17 && transport_checksum == 0)
{
transport_checksum = 0xffff; /* RFC 768 wire representation. */
}
uint8_t pseudo[12];
memcpy(pseudo, &source_ip, 4);
memcpy(pseudo + 4, &destination_ip, 4);
pseudo[8] = 0;
pseudo[9] = protocol;
uint16_t transport_length = total_length - ihl;
pseudo[10] = transport_length >> 8;
pseudo[11] = transport_length;
bool pseudo_odd = false;
uint8_t pseudo_tail = 0;
uint32_t tcp_sum = checksum_bytes(0, pseudo, sizeof(pseudo), &pseudo_odd, &pseudo_tail);
/* The pseudo-header is even sized, so the TCP segment starts on a word boundary. */
uint16_t checksum = pbuf_checksum(packet, ihl, transport_length, tcp_sum);
if (protocol == 17 && checksum == 0)
{
checksum = 0xffff; /* RFC 768 wire representation. */
}
uint8_t checksum_bytes[2] = {checksum >> 8, checksum};
return pbuf_take_at(packet, checksum_bytes, sizeof(checksum_bytes), ihl + checksum_offset) ==
ERR_OK;
transport_checksum_bytes[0] = transport_checksum >> 8;
transport_checksum_bytes[1] = transport_checksum;
return pbuf_take_at(packet, transport_checksum_bytes, sizeof(transport_checksum_bytes),
ihl + checksum_offset) == ERR_OK;
}

int transparent_tcp_ip4_input(struct pbuf *packet, struct netif *input_netif)
Expand Down
4 changes: 2 additions & 2 deletions src/transparent_tcp.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
/* Keep the interception table aligned with the smux stream limit. Otherwise
* increasing SINGMUX_TCP_STREAM_MAX only admits streams after the transparent
* flow table has already rejected their client connections. */
#define TRANSPARENT_TCP_MAX_FLOWS 80
#define TRANSPARENT_TCP_LISTEN_BACKLOG 64
#define TRANSPARENT_TCP_MAX_FLOWS 100
#define TRANSPARENT_TCP_LISTEN_BACKLOG 96
#define TRANSPARENT_UDP_MAX_FLOWS 128
#define TRANSPARENT_UDP_RELAY_PORT 15002

Expand Down
Loading