Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

udp: open one socket each for IPv4 and IPv6 #220

Merged
merged 4 commits into from
Jan 16, 2025
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
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
Loading