Skip to content

Commit

Permalink
Add new option (ipv4_only) to support OpenBSD
Browse files Browse the repository at this point in the history
  • Loading branch information
cnbatch committed Apr 23, 2023
1 parent 98ef92d commit 5384a6f
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 64 deletions.
3 changes: 1 addition & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@ cmake_minimum_required(VERSION 3.15.0 FATAL_ERROR)

project(punchnat CXX)

set(CMAKE_C_STANDARD 17)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

set_property(GLOBAL PROPERTY USE_FOLDERS ON)

if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" OR ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
include_directories("/usr/local/include")
include_directories("/usr/local/include/botan-2")
endif()
Expand Down
36 changes: 13 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ punchnat config1.conf config2.conf

如果不需要写入 Log 文件,那就删除 `log_path` 这一行。

### 参数介绍



### STUN Servers
不支持 TCP 的普通 STUN 服务器(来自于[NatTypeTeste](https://github.com/HMBSbige/NatTypeTester)
- stun.syncthing.net
Expand Down Expand Up @@ -70,6 +74,8 @@ punchnat config1.conf config2.conf
- FreeBSD
- Linux

预编译的二进制文件全部都是静态编译。Linux 版本基本上都是静态编译,但 libc 除外,因此准备了两个版本,一个用于 glibc (2.31),另一个用于 musl。

---

## 建立服务
Expand Down Expand Up @@ -157,27 +163,11 @@ make
---

## IPv4 映射 IPv6
由于该项目内部使用的是 IPv6 单栈 + 开启 IPv4 映射地址(IPv4-mapped IPv6)来使用 IPv4 网络,因此请确保 v6only 选项的值为 0。
由于 PunchNAT 内部使用的是 IPv6 单栈 + 开启 IPv4 映射地址(IPv4-mapped IPv6)来同时使用 IPv4 与 IPv6 网络,因此请确保 v6only 选项的值为 0。

**正常情况下不需要任何额外设置,FreeBSD 与 Linux 以及 Windows 都默认允许 IPv4 地址映射到 IPv6。**

如果不放心,那么可以这样做
### FreeBSD
按照FreeBSD手册 [33.9.5. IPv6 and IPv4 Address Mapping](https://docs.freebsd.org/en/books/handbook/advanced-networking/#_ipv6_and_ipv4_address_mapping) 介绍,在 `/etc/rc.conf` 加一行即可
```
ipv6_ipv4mapping="YES"
```
如果还是不放心,那就运行命令
```
sysctl net.inet6.ip6.v6only=0
```

### Linux
可运行命令
```
sysctl -w net.ipv6.bindv6only=0
```
正常情况下不需要这样做,它的默认值就是 0。
如果系统不支持 IPv6,或者禁用了 IPv6,请在配置文件中设置 ipv4_only=true,这样 PunchNAT 会退回到使用 IPv4 单栈模式。

## 其它注意事项
### NetBSD
Expand All @@ -187,13 +177,13 @@ sysctl -w net.inet6.ip6.v6only=0
```
设置后,单栈+映射地址模式可以侦听双栈。

但由于未知的原因,它无法主动连接 IPv4 映射地址,因此 `destination_address` 只能使用 IPv6 地址
但由于未知的原因,可能无法主动连接 IPv4 映射地址。

### OpenBSD
因为 OpenBSD 彻底屏蔽了 IPv4 映射地址,所以在 OpenBSD 平台只能使用 IPv6 单栈模式
因为 OpenBSD 彻底屏蔽了 IPv4 映射地址,所以在 OpenBSD 平台使用双栈的话,需要将配置文件保存成两个,其中一个启用 ipv4_only=1,然后在使用 PunchNAT 时同时载入两个配置文件

## 关于代码
### 为什么要用两个 asio::io_context
这里用了两个 asio::io_context,其中一个是用于处理 UDP 数据的异步循环,另一个用于处理内部逻辑以及 TCP 数据的收发
### 版面
代码写得很随意,想到哪写到哪,因此版面混乱

之所以要这样做,完全是为了迁就 BSD 系统。如果只用一个 io_context 去做所有的事,由于两次接收之间的延迟过高,在 BSD 平台会导致 UDP 丢包率过高
至于阅读者的感受嘛…… 那肯定会不爽
2 changes: 1 addition & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ int main(int argc, char *argv[])
if (argc <= 1)
{
char app_name[] = "punchnat";
printf("%s version 20230415\n", app_name);
printf("%s version 20230423\n", app_name);
printf("Usage: %s config1.conf\n", app_name);
printf(" %s config1.conf config2.conf...\n", app_name);
return 0;
Expand Down
57 changes: 39 additions & 18 deletions src/networks/connections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,16 @@ void empty_tcp_disconnect(tcp_session *tmp)
{
}

std::unique_ptr<rfc3489::stun_header> send_stun_3489_request(udp_server &sender, const std::string &stun_host)
std::unique_ptr<rfc3489::stun_header> send_stun_3489_request(udp_server &sender, const std::string &stun_host, bool v4_only)
{
auto udp_version = v4_only ? udp::v4() : udp::v6();
udp::resolver::resolver_base::flags input_flags = udp::resolver::numeric_service | udp::resolver::v4_mapped | udp::resolver::all_matching;
if (v4_only)
input_flags = udp::resolver::numeric_service;

asio::error_code ec;
udp::resolver &udp_resolver = sender.get_resolver();
udp::resolver::results_type remote_addresses = udp_resolver.resolve(udp::v6(), stun_host, "3478",
udp::resolver::numeric_service | udp::resolver::v4_mapped | udp::resolver::all_matching, ec);
udp::resolver::results_type remote_addresses = udp_resolver.resolve(udp_version, stun_host, "3478", input_flags, ec);

if (ec)
return nullptr;
Expand All @@ -50,12 +54,16 @@ std::unique_ptr<rfc3489::stun_header> send_stun_3489_request(udp_server &sender,
return header;
}

std::unique_ptr<rfc8489::stun_header> send_stun_8489_request(udp_server &sender, const std::string &stun_host)
std::unique_ptr<rfc8489::stun_header> send_stun_8489_request(udp_server &sender, const std::string &stun_host, bool v4_only)
{
auto udp_version = v4_only ? udp::v4() : udp::v6();
udp::resolver::resolver_base::flags input_flags = udp::resolver::numeric_service | udp::resolver::v4_mapped | udp::resolver::all_matching;
if (v4_only)
input_flags = udp::resolver::numeric_service;

asio::error_code ec;
udp::resolver &udp_resolver = sender.get_resolver();
udp::resolver::results_type remote_addresses = udp_resolver.resolve(udp::v6(), stun_host, "3478",
udp::resolver::numeric_service | udp::resolver::v4_mapped | udp::resolver::all_matching, ec);
udp::resolver::results_type remote_addresses = udp_resolver.resolve(udp_version, stun_host, "3478", input_flags, ec);

if (ec)
return nullptr;
Expand All @@ -74,12 +82,16 @@ std::unique_ptr<rfc8489::stun_header> send_stun_8489_request(udp_server &sender,
return header;
}

void resend_stun_8489_request(udp_server &sender, const std::string &stun_host, rfc8489::stun_header *header)
void resend_stun_8489_request(udp_server &sender, const std::string &stun_host, rfc8489::stun_header *header, bool v4_only)
{
auto udp_version = v4_only ? udp::v4() : udp::v6();
udp::resolver::resolver_base::flags input_flags = udp::resolver::numeric_service | udp::resolver::v4_mapped | udp::resolver::all_matching;
if (v4_only)
input_flags = udp::resolver::numeric_service;

asio::error_code ec;
udp::resolver &udp_resolver = sender.get_resolver();
udp::resolver::results_type remote_addresses = udp_resolver.resolve(udp::v6(), stun_host, "3478",
udp::resolver::numeric_service | udp::resolver::v4_mapped | udp::resolver::all_matching, ec);
udp::resolver::results_type remote_addresses = udp_resolver.resolve(udp_version, stun_host, "3478", input_flags, ec);

if (ec)
return;
Expand Down Expand Up @@ -265,7 +277,8 @@ void tcp_server::acceptor_initialise(tcp::endpoint ep)
{
asio::ip::v6_only v6_option(false);
tcp_acceptor.open(ep.protocol());
tcp_acceptor.set_option(v6_option);
if (ep.address().is_v6())
tcp_acceptor.set_option(v6_option);
tcp_acceptor.set_option(tcp::no_delay(true));
tcp_acceptor.bind(ep);
tcp_acceptor.listen(tcp_acceptor.max_connections);
Expand Down Expand Up @@ -320,8 +333,12 @@ bool tcp_client::set_remote_hostname(const std::string &remote_address, asio::ip

bool tcp_client::set_remote_hostname(const std::string &remote_address, const std::string &port_num, asio::error_code &ec)
{
remote_endpoints = resolver.resolve(tcp::v6(), remote_address, port_num,
tcp::resolver::numeric_service | tcp::resolver::v4_mapped | tcp::resolver::all_matching, ec);
auto tcp_version = ipv4_only ? tcp::v4() : tcp::v6();
tcp::resolver::resolver_base::flags input_flags = tcp::resolver::numeric_service | tcp::resolver::v4_mapped | tcp::resolver::all_matching;
if (ipv4_only)
input_flags = tcp::resolver::numeric_service;

remote_endpoints = resolver.resolve(tcp_version, remote_address, port_num, input_flags, ec);

return remote_endpoints.size() > 0;
}
Expand Down Expand Up @@ -356,7 +373,8 @@ void udp_server::initialise(udp::endpoint ep)
{
asio::ip::v6_only v6_option(false);
connection_socket.open(ep.protocol());
connection_socket.set_option(v6_option);
if (ep.address().is_v6())
connection_socket.set_option(v6_option);
connection_socket.bind(ep);
}

Expand Down Expand Up @@ -427,10 +445,12 @@ udp::resolver::results_type udp_client::get_remote_hostname(const std::string &r

udp::resolver::results_type udp_client::get_remote_hostname(const std::string &remote_address, const std::string &port_num, asio::error_code &ec)
{
udp::resolver::results_type remote_addresses = resolver.resolve(udp::v6(), remote_address, port_num,
udp::resolver::numeric_service | udp::resolver::v4_mapped | udp::resolver::all_matching, ec);

return remote_addresses;
if (ipv4_only)
return resolver.resolve(udp::v4(), remote_address, port_num,
udp::resolver::numeric_service | udp::resolver::address_configured, ec);
else
return resolver.resolve(udp::v6(), remote_address, port_num,
udp::resolver::numeric_service | udp::resolver::v4_mapped | udp::resolver::all_matching, ec);
}

void udp_client::disconnect()
Expand Down Expand Up @@ -523,7 +543,8 @@ void udp_client::initialise()
{
asio::ip::v6_only v6_option(false);
connection_socket.open(udp::v6());
connection_socket.set_option(v6_option);
if (!ipv4_only)
connection_socket.set_option(v6_option);
}

void udp_client::start_receive()
Expand Down
16 changes: 9 additions & 7 deletions src/networks/connections.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ class tcp_client
{
public:
tcp_client() = delete;
tcp_client(asio::io_context &io_context)
: internal_io_context(io_context), resolver(io_context)
tcp_client(asio::io_context &io_context, bool v4_only = false)
: internal_io_context(io_context), resolver(io_context), ipv4_only(v4_only)
{
}

Expand All @@ -137,6 +137,7 @@ class tcp_client
asio::io_context &internal_io_context;
tcp::resolver resolver;
asio::ip::basic_resolver_results<asio::ip::tcp> remote_endpoints;
const bool ipv4_only;
};


Expand Down Expand Up @@ -177,10 +178,10 @@ class udp_client
{
public:
udp_client() = delete;
udp_client(asio::io_context &io_context, asio::strand<asio::io_context::executor_type> &asio_strand, udp_callback_t callback_func)
udp_client(asio::io_context &io_context, asio::strand<asio::io_context::executor_type> &asio_strand, udp_callback_t callback_func, bool v4_only = false)
: connection_socket(io_context), resolver(io_context), callback(callback_func), task_assigner(asio_strand),
last_receive_time(right_now()), last_send_time(right_now()),
paused(false), stopped(false)
paused(false), stopped(false), ipv4_only(v4_only)
{
initialise();
}
Expand Down Expand Up @@ -225,12 +226,13 @@ class udp_client
std::atomic<int64_t> last_send_time;
std::atomic<bool> paused;
std::atomic<bool> stopped;
const bool ipv4_only;
};


std::unique_ptr<rfc3489::stun_header> send_stun_3489_request(udp_server &sender, const std::string &stun_host);
std::unique_ptr<rfc8489::stun_header> send_stun_8489_request(udp_server &sender, const std::string &stun_host);
void resend_stun_8489_request(udp_server &sender, const std::string &stun_host, rfc8489::stun_header *header);
std::unique_ptr<rfc3489::stun_header> send_stun_3489_request(udp_server &sender, const std::string &stun_host, bool v4_only = false);
std::unique_ptr<rfc8489::stun_header> send_stun_8489_request(udp_server &sender, const std::string &stun_host, bool v4_only = false);
void resend_stun_8489_request(udp_server &sender, const std::string &stun_host, rfc8489::stun_header *header, bool v4_only = false);
std::unique_ptr<rfc8489::stun_header> send_stun_8489_request(tcp_session &sender, const std::string &stun_host);
void resend_stun_8489_request(tcp_session &sender, const std::string &stun_host, rfc8489::stun_header *header);

Expand Down
11 changes: 8 additions & 3 deletions src/networks/tcp_mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@ bool tcp_mode::start()
if (port_number == 0)
return false;

tcp::endpoint listen_on_ep(tcp::v6(), port_number);
tcp::endpoint listen_on_ep;
if (current_settings.ipv4_only)
listen_on_ep = tcp::endpoint(tcp::v4(), port_number);
else
listen_on_ep = tcp::endpoint(tcp::v6(), port_number);

if (!current_settings.listen_on.empty())
{
asio::error_code ec;
Expand All @@ -39,7 +44,7 @@ bool tcp_mode::start()
return false;
}

if (local_address.is_v4())
if (local_address.is_v4() && !current_settings.ipv4_only)
listen_on_ep.address(asio::ip::make_address_v6(asio::ip::v4_mapped, local_address.to_v4()));
else
listen_on_ep.address(local_address);
Expand Down Expand Up @@ -70,7 +75,7 @@ void tcp_mode::tcp_server_accept_incoming(std::unique_ptr<tcp_session> &&incomin
if (!incoming_session->is_open())
return;

tcp_client target_connector(io_context);
tcp_client target_connector(io_context, current_settings.ipv4_only);
std::string &destination_address = current_settings.destination_address;
uint16_t destination_port = current_settings.destination_port;
asio::error_code ec;
Expand Down
15 changes: 10 additions & 5 deletions src/networks/udp_mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ bool udp_mode::start()
if (port_number == 0)
return false;

udp::endpoint listen_on_ep(udp::v6(), port_number);
udp::endpoint listen_on_ep;
if (current_settings.ipv4_only)
listen_on_ep = udp::endpoint(udp::v4(), port_number);
else
listen_on_ep = udp::endpoint(udp::v6(), port_number);

if (!current_settings.listen_on.empty())
{
asio::error_code ec;
Expand All @@ -36,7 +41,7 @@ bool udp_mode::start()
return false;
}

if (local_address.is_v4())
if (local_address.is_v4() && !current_settings.ipv4_only)
listen_on_ep.address(asio::ip::make_address_v6(asio::ip::v4_mapped, local_address.to_v4()));
else
listen_on_ep.address(local_address);
Expand All @@ -53,7 +58,7 @@ bool udp_mode::start()

if (!current_settings.stun_server.empty())
{
stun_header = send_stun_8489_request(*udp_access_point, current_settings.stun_server);
stun_header = send_stun_8489_request(*udp_access_point, current_settings.stun_server, current_settings.ipv4_only);
timer_stun.expires_after(std::chrono::seconds(1));
timer_stun.async_wait([this](const asio::error_code &e) { send_stun_request(e); });
}
Expand Down Expand Up @@ -106,7 +111,7 @@ void udp_mode::udp_server_incoming(std::unique_ptr<uint8_t[]> data, size_t data_
return;

auto udp_func = std::bind(&udp_mode::udp_client_incoming_to_udp, this, _1, _2, _3, _4);
auto udp_forwarder = std::make_unique<udp_client>(network_io, asio_strand, udp_func);
auto udp_forwarder = std::make_unique<udp_client>(network_io, asio_strand, udp_func, current_settings.ipv4_only);
if (udp_forwarder == nullptr)
return;

Expand Down Expand Up @@ -232,7 +237,7 @@ void udp_mode::send_stun_request(const asio::error_code &e)
return;

if (!current_settings.stun_server.empty())
resend_stun_8489_request(*udp_access_point, current_settings.stun_server, stun_header.get());
resend_stun_8489_request(*udp_access_point, current_settings.stun_server, stun_header.get(), current_settings.ipv4_only);

timer_stun.expires_after(STUN_RESEND);
timer_stun.async_wait([this](const asio::error_code &e) { send_stun_request(e); });
Expand Down
13 changes: 11 additions & 2 deletions src/shares/share_defines.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ user_settings parse_from_args(const std::vector<std::string> &args, std::vector<
current_user_settings.udp_timeout = static_cast<uint16_t>(time_interval);
break;

case strhash("ipv4_only"):
{
bool yes = value == "yes" || value == "true" || value == "1";
current_user_settings.ipv4_only = yes;
break;
}

default:
error_msg.emplace_back("unknow option: " + arg);
}
Expand Down Expand Up @@ -145,7 +152,8 @@ void print_ip_to_file(const std::string& message, const std::filesystem::path& l
static std::mutex mtx;
std::unique_lock locker{ mtx };
output_file.open(log_file, std::ios::out | std::ios::trunc);
output_file << message;
if (output_file.is_open() && output_file.good())
output_file << message;
output_file.close();
}

Expand All @@ -155,6 +163,7 @@ void print_message_to_file(const std::string& message, const std::filesystem::pa
static std::mutex mtx;
std::unique_lock locker{ mtx };
output_file.open(log_file, std::ios::out | std::ios::app);
output_file << message;
if (output_file.is_open() && output_file.good())
output_file << message;
output_file.close();
}
Loading

0 comments on commit 5384a6f

Please sign in to comment.