Skip to content

Commit

Permalink
udp: open one socket each for IPv4 and IPv6 (#220)
Browse files Browse the repository at this point in the history
* tmp work on udp double sockets

* WIP: udp: open two sockets (one for ipv4, one for ipv6)

io_uring not ported yet

* udp: open one socket each for IPv4 and IPv6

Config file now has one setting for each

* file transfer ci: fix udp network.address_ipv4
  • Loading branch information
greatest-ape authored Jan 16, 2025
1 parent 192b22f commit 048c297
Show file tree
Hide file tree
Showing 19 changed files with 863 additions and 525 deletions.
2 changes: 1 addition & 1 deletion .github/actions/test-file-transfers/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ echo "
log_level = 'debug'
[network]
address = '127.0.0.1:3000'" > udp.toml
address_ipv4 = '127.0.0.1:3000'" > udp.toml
./target/debug/aquatic udp -c udp.toml > "$HOME/udp.log" 2>&1 &

# HTTP
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## Unreleased

### aquatic_udp

#### Changed

* (Breaking) Open one socket each for IPv4 and IPv6. The config file now has
one setting for each.

## 0.9.0 - 2024-04-03

### General
Expand Down
3 changes: 2 additions & 1 deletion crates/bencher/src/protocols/udp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ impl ProcessRunner for AquaticUdpRunner {
let mut c = aquatic_udp::config::Config::default();

c.socket_workers = self.socket_workers;
c.network.address = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 3000));
c.network.address_ipv4 = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 3000);
c.network.use_ipv6 = false;
c.network.use_io_uring = self.use_io_uring;
c.protocol.max_response_peers = 30;

Expand Down
4 changes: 2 additions & 2 deletions crates/http/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,13 @@ impl aquatic_common::cli::Config for Config {
#[serde(default, deny_unknown_fields)]
pub struct NetworkConfig {
/// Bind to this address
///
///
/// When providing an IPv4 style address, only IPv4 traffic will be
/// handled. Examples:
/// - "0.0.0.0:3000" binds to port 3000 on all network interfaces
/// - "127.0.0.1:3000" binds to port 3000 on the loopback interface
/// (localhost)
///
///
/// When it comes to IPv6-style addresses, behaviour is more complex and
/// differs between operating systems. On Linux, to accept both IPv4 and
/// IPv6 traffic on any interface, use "[::]:3000". Set the "only_ipv6"
Expand Down
4 changes: 3 additions & 1 deletion crates/toml_config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub trait TomlConfig: Default {
}

pub mod __private {
use std::net::SocketAddr;
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
use std::path::PathBuf;

pub trait Private {
Expand Down Expand Up @@ -123,4 +123,6 @@ pub mod __private {

impl_trait!(PathBuf);
impl_trait!(SocketAddr);
impl_trait!(SocketAddrV4);
impl_trait!(SocketAddrV6);
}
4 changes: 2 additions & 2 deletions crates/udp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ Generate the configuration file:
./target/release/aquatic_udp -p > "aquatic-udp-config.toml"
```

Make necessary adjustments to the file. You will likely want to adjust `address`
(listening address) under the `network` section.
Make necessary adjustments to the file. You will likely want to adjust
listening addresses under the `network` section.

Once done, start the application:

Expand Down
59 changes: 35 additions & 24 deletions crates/udp/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use std::{net::SocketAddr, path::PathBuf};
use std::{
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
path::PathBuf,
};

use aquatic_common::{access_list::AccessListConfig, privileges::PrivilegeConfig};
use cfg_if::cfg_if;
Expand Down Expand Up @@ -54,25 +57,24 @@ impl aquatic_common::cli::Config for Config {
#[derive(Clone, Debug, PartialEq, TomlConfig, Deserialize, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct NetworkConfig {
/// Bind to this address
///
/// When providing an IPv4 style address, only IPv4 traffic will be
/// handled. Examples:
/// - "0.0.0.0:3000" binds to port 3000 on all network interfaces
/// - "127.0.0.1:3000" binds to port 3000 on the loopback interface
/// (localhost)
///
/// When it comes to IPv6-style addresses, behaviour is more complex and
/// differs between operating systems. On Linux, to accept both IPv4 and
/// IPv6 traffic on any interface, use "[::]:3000". Set the "only_ipv6"
/// flag below to limit traffic to IPv6. To bind to the loopback interface
/// and only accept IPv6 packets, use "[::1]:3000" and set the only_ipv6
/// flag. Receiving both IPv4 and IPv6 traffic on loopback is currently
/// not supported. For other operating systems, please refer to their
/// respective documentation.
pub address: SocketAddr,
/// Only allow access over IPv6
pub only_ipv6: bool,
/// Use IPv4
pub use_ipv4: bool,
/// Use IPv6
pub use_ipv6: bool,
/// IPv4 address and port
///
/// Examples:
/// - Use 0.0.0.0:3000 to bind to all interfaces on port 3000
/// - Use 127.0.0.1:3000 to bind to the loopback interface (localhost) on
/// port 3000
pub address_ipv4: SocketAddrV4,
/// IPv6 address and port
///
/// Examples:
/// - Use [::]:3000 to bind to all interfaces on port 3000
/// - Use [::1]:3000 to bind to the loopback interface (localhost) on
/// port 3000
pub address_ipv6: SocketAddrV6,
/// Size of socket recv buffer. Use 0 for OS default.
///
/// This setting can have a big impact on dropped packages. It might
Expand All @@ -95,6 +97,12 @@ pub struct NetworkConfig {
/// such as FreeBSD. Setting the value to zero disables resending
/// functionality.
pub resend_buffer_max_len: usize,
/// Set flag on IPv6 socket to only accept IPv6 traffic.
///
/// This should typically be set to true unless your OS does not support
/// double-stack sockets (that is, sockets that receive both IPv4 and IPv6
/// packets).
pub set_only_ipv6: bool,
#[cfg(feature = "io-uring")]
pub use_io_uring: bool,
/// Number of ring entries (io_uring backend only)
Expand All @@ -106,21 +114,24 @@ pub struct NetworkConfig {

impl NetworkConfig {
pub fn ipv4_active(&self) -> bool {
self.address.is_ipv4() || !self.only_ipv6
self.use_ipv4
}
pub fn ipv6_active(&self) -> bool {
self.address.is_ipv6()
self.use_ipv6
}
}

impl Default for NetworkConfig {
fn default() -> Self {
Self {
address: SocketAddr::from(([0, 0, 0, 0], 3000)),
only_ipv6: false,
use_ipv4: true,
use_ipv6: true,
address_ipv4: SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 3000),
address_ipv6: SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 3000, 0, 0),
socket_recv_buffer_size: 8_000_000,
poll_timeout_ms: 50,
resend_buffer_max_len: 0,
set_only_ipv6: true,
#[cfg(feature = "io-uring")]
use_io_uring: true,
#[cfg(feature = "io-uring")]
Expand Down
23 changes: 20 additions & 3 deletions crates/udp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,26 @@ pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn run(mut config: Config) -> ::anyhow::Result<()> {
let mut signals = Signals::new([SIGUSR1])?;

if !(config.network.use_ipv4 || config.network.use_ipv6) {
return Result::Err(anyhow::anyhow!(
"Both use_ipv4 and use_ipv6 can not be set to false"
));
}

if config.socket_workers == 0 {
config.socket_workers = available_parallelism().map(Into::into).unwrap_or(1);
};

let num_sockets_per_worker =
if config.network.use_ipv4 { 1 } else { 0 } + if config.network.use_ipv6 { 1 } else { 0 };

let state = State::default();
let statistics = Statistics::new(&config);
let connection_validator = ConnectionValidator::new(&config)?;
let priv_dropper = PrivilegeDropper::new(config.privileges.clone(), config.socket_workers);
let priv_dropper = PrivilegeDropper::new(
config.privileges.clone(),
config.socket_workers * num_sockets_per_worker,
);
let (statistics_sender, statistics_receiver) = unbounded();

update_access_list(&config.access_list, &state.access_list)?;
Expand All @@ -44,10 +56,15 @@ pub fn run(mut config: Config) -> ::anyhow::Result<()> {
let state = state.clone();
let config = config.clone();
let connection_validator = connection_validator.clone();
let priv_dropper = priv_dropper.clone();
let statistics = statistics.socket[i].clone();
let statistics_sender = statistics_sender.clone();

let mut priv_droppers = Vec::new();

for _ in 0..num_sockets_per_worker {
priv_droppers.push(priv_dropper.clone());
}

let handle = Builder::new()
.name(format!("socket-{:02}", i + 1))
.spawn(move || {
Expand All @@ -57,7 +74,7 @@ pub fn run(mut config: Config) -> ::anyhow::Result<()> {
statistics,
statistics_sender,
connection_validator,
priv_dropper,
priv_droppers,
)
})
.with_context(|| "spawn socket worker")?;
Expand Down
Loading

0 comments on commit 048c297

Please sign in to comment.