Skip to content

Commit

Permalink
Babeld config at startup
Browse files Browse the repository at this point in the history
This commit provides functionality to configure Babeld at Rita startup
with default interface settings. This allows us to control via Rita how
exactly babel is behaving and provides the functions for potnetially
tweaking these functions at runtime in the future.
  • Loading branch information
jkilpatr committed Jan 12, 2024
1 parent 4d5581d commit f64449d
Show file tree
Hide file tree
Showing 19 changed files with 453 additions and 245 deletions.
355 changes: 175 additions & 180 deletions Cargo.lock

Large diffs are not rendered by default.

15 changes: 4 additions & 11 deletions althea_types/src/interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use crate::regions::Regions;
use crate::{contact_info::ContactType, wg_key::WgKey, BillingDetails, InstallationDetails};
use crate::{ClientExtender, UsageTrackerFlat, UsageTrackerTransfer, WifiDevice};
use arrayvec::ArrayString;
use babel_monitor::structs::Neighbor;
use babel_monitor::structs::Route;
use babel_monitor::structs::{BabeldConfig, Neighbor};
use clarity::Address;
use deep_space::Address as AltheaAddress;
use ipnetwork::IpNetwork;
Expand Down Expand Up @@ -700,8 +700,9 @@ pub struct OperatorUpdateMessage {
/// When a user hits 'update router', it updates to this version
pub local_update_instruction_v2: Option<UpdateType>,
/// settings for the device bandwidth shaper
#[serde(default = "default_shaper_settings")]
pub shaper_settings: ShaperSettings,
pub shaper_settings: Option<ShaperSettings>,
/// settings for babeld
pub babeld_settings: Option<BabeldConfig>,
// Updated contact info from ops tools
#[serde(
serialize_with = "data_serialize",
Expand Down Expand Up @@ -770,14 +771,6 @@ pub struct ExitConnection {
pub client_pub_ipv6: Option<IpNetwork>,
}

fn default_shaper_settings() -> ShaperSettings {
ShaperSettings {
max_speed: 1000,
min_speed: 50,
enabled: true,
}
}

fn default_ops_last_seen_usage_hour() -> u64 {
0
}
Expand Down
75 changes: 70 additions & 5 deletions babel_monitor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use std::str::FromStr;
use std::str::{self};
use std::thread;
use std::time::Duration;
use structs::{Interface, Neighbor};
use structs::{BabeldInterfaceConfig, Interface, Neighbor};

/// we want to ceed the cpu just long enough for Babel
/// to finish what it's doing and warp up it's write
Expand Down Expand Up @@ -196,31 +196,96 @@ pub fn parse_interfaces(stream: &mut TcpStream) -> Result<Vec<Interface>, BabelM
parse_interfaces_sync(babel_output)
}

/// Gets this routers local fee, what the router charges for bandwidth. The unit is wei (1*10-18 of a dollar) per byte
pub fn get_local_fee(stream: &mut TcpStream) -> Result<u32, BabelMonitorError> {
let output = run_command(stream, "dump")?;

let babel_output = output;
get_local_fee_sync(babel_output)
}

/// Sets this routers local fee, what the router charges for bandwidth. The unit is wei (1*10-18 of a dollar) per byte
pub fn set_local_fee(stream: &mut TcpStream, new_fee: u32) -> Result<(), BabelMonitorError> {
let result = run_command(stream, &format!("fee {new_fee}"))?;

let _out = result;
Ok(())
}

/// Sets the metric factor for babel. This is a weighting value used to decide if this router should select
/// routes based on price or quality of service. A higher value will cause the router to prefer routes with
/// higher quailty of service, a lower value will cause the router to prefer routes with lower price.
pub fn set_metric_factor(stream: &mut TcpStream, new_factor: u32) -> Result<(), BabelMonitorError> {
let result = run_command(stream, &format!("metric-factor {new_factor}"))?;

let _out = result;
Ok(())
}

pub fn monitor(stream: &mut TcpStream, iface: &str) -> Result<(), BabelMonitorError> {
let command = &format!("interface {iface} max-rtt-penalty 2000 enable-timestamps true");
let iface = iface.to_string();
let result = run_command(stream, command)?;
/// Sets the interval at which Babel will update it's routes from the kernel routing table. If set to zero Babel will only recieve
/// updates from the kernel as changes are made and will never perform a full dump.
pub fn set_kernel_check_interval(
stream: &mut TcpStream,
kernel_check_interval: Option<Duration>,
) -> Result<(), BabelMonitorError> {
let interval = match kernel_check_interval {
// unit is centiseconds
Some(d) => (d.as_millis() / 100) as u16,
None => 0,
};
let result = run_command(stream, &format!("kernel-check-interval {interval}"))?;

let _out = result;
Ok(())
}

/// Sets the default interface parameters for babel. These are applied at startup and can be overridden per interface, note if modified
/// at runtime then existing interfaces will not be updated.
pub fn set_interface_defaults(
stream: &mut TcpStream,
defaults: BabeldInterfaceConfig,
) -> Result<(), BabelMonitorError> {
let mut command = "default ".to_string();
command.push_str(&build_interface_config_string(defaults));
let result = run_command(stream, &command)?;

let _out = result;
Ok(())
}

/// internal utility for building the configuration string
fn build_interface_config_string(config: BabeldInterfaceConfig) -> String {
let mut command = String::new();
if config.link_quality {
command.push_str("link-quality yes ");
} else {
command.push_str("link-quality no ");
}
if config.split_horizon {
command.push_str("split-horizon yes ");
} else {
command.push_str("split-horizon no ");
}
command.push_str(&format!("max-rtt-penalty {} ", config.max_rtt_penalty));
command.push_str(&format!("rtt-min {} ", config.rtt_min));
command.push_str(&format!("rtt-max {} ", config.rtt_max));
command.push_str(&format!("hello-interval {} ", config.hello_interval));
command.push_str(&format!("update-interval {} ", config.update_interval));
command
}

/// Adds an interface to babel to monitor, neighbors will be discovered on this interface and routes will be advertised
/// optionally this interface can have it's own configuration parameters
pub fn monitor(
stream: &mut TcpStream,
iface: &str,
options: Option<BabeldInterfaceConfig>,
) -> Result<(), BabelMonitorError> {
let mut command = format!("interface {iface} ");
if let Some(options) = options {
command.push_str(&build_interface_config_string(options));
}
let result = run_command(stream, &command)?;

trace!("Babel started monitoring: {}", iface);
let _out = result;
Expand Down
45 changes: 45 additions & 0 deletions babel_monitor/src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::net::{AddrParseError, IpAddr};
use std::num::{ParseFloatError, ParseIntError};
use std::str::{self, ParseBoolError};
use std::string::FromUtf8Error;
use std::time::Duration;

#[derive(Debug)]
pub enum BabelMonitorError {
Expand Down Expand Up @@ -139,3 +140,47 @@ pub struct Neighbor {
pub rttcost: u16,
pub cost: u16,
}

/// This struct lists config options for babeld, these are applied at startup
/// it is not complete and only lists options that will probably be used
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BabeldConfig {
/// how often to update the Babeld routing table, by doing a full kernel dump
/// Babeld listens for routing table changes and applies them immediately. If set to
/// None Babel will only listen for updates and never perform a full dump
pub kernel_check_interval: Option<Duration>,
/// The Price of bandwidth advertised over this router, in terms of wei (1*10^18 of a dollar) per byte
pub local_fee: u32,
/// Decides how much weight to give to route quality versus price, a higher value means higher weight on quality
pub metric_factor: u32,
/// The default values for various interfaces options
pub interface_defaults: BabeldInterfaceConfig,
}

/// This struct lists all config options for babeld interfaces, this can be used
/// to set the global default or used to set per interface options
/// this config is not complete, it only includes the options that will proably be used
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct BabeldInterfaceConfig {
/// If link quality estimation is enabled for this interface
pub link_quality: bool,
/// The maximum penalty for a route with a high latency
/// the rtt value is multiplied by this value and added to the metric
/// up to rtt_max
pub max_rtt_penalty: u16,
/// The minimum rtt at which to start applying the penalty, unit is milliseconds
pub rtt_min: u16,
/// The maximum rtt at which to apply the the full value of max_rtt_penalty, unit is milliseconds
pub rtt_max: u16,
/// The interval at which to send hello messages, unit is seconds, these are used to compute packet loss
/// and latency, by default this is 1 second. The last 16 hellos are used to determine
/// route quality, increasing this value has a smoothing effect on route quality estimations
pub hello_interval: u16,
/// The interval at which to send routing table updates, since babel uses triggered
/// updates when routes becoming infeasible this can be set to a relatively high value
/// by default it is 4x the hello interval
pub update_interval: u16,
/// If set this router will avoid advertising routes it has learned from this interface
/// back to the interface, this is useful for avoiding routing loops
pub split_horizon: bool,
}
4 changes: 2 additions & 2 deletions integration_tests/src/setup_utils/rita.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ pub fn spawn_rita(
)));
rcsettings.network.wg_private_key_path = wg_keypath;
rcsettings.network.peer_interfaces = veth_interfaces;
rcsettings.payment.local_fee = local_fee;
rcsettings.network.babeld_settings.local_fee = local_fee;

// mirrored from rita_bin/src/client.rs
let s = clu::init(rcsettings);
Expand Down Expand Up @@ -243,7 +243,7 @@ pub fn spawn_rita_exit(
resettings.network.wg_private_key_path = wg_keypath;
resettings.exit_network.wg_private_key_path = wg_keypath_exit;
resettings.network.peer_interfaces = veth_interfaces;
resettings.payment.local_fee = local_fee;
resettings.network.babeld_settings.local_fee = local_fee;
resettings.payment.eth_private_key = Some(instance.eth_private_key);
resettings.exit_network.exit_price = exit_fee;
let veth_exit_to_native = format!("vout-{}-o", ns);
Expand Down
36 changes: 28 additions & 8 deletions rita_bin/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ use rita_common::rita_loop::start_rita_common_loops;
use rita_common::rita_loop::write_to_disk::save_to_disk_loop;
use rita_common::rita_loop::write_to_disk::SettingsOnDisk;
use rita_common::usage_tracker::save_usage_on_shutdown;
use rita_common::utils::apply_babeld_settings_defaults;
use rita_common::utils::env_vars_contains;
use settings::client::RitaClientSettings;
use settings::save_settings_on_shutdown;
Expand All @@ -60,12 +61,6 @@ fn main() {
})
.expect("Error setting Ctrl-C handler");

// Because Rita clears and sets up new Wireguard Tunnels on every restart Babel, which was attached and listening to
// the old tunnels is now in an incorrect state. We must either restart babel or empty it's interfaces list so that the newly
// created wireguard tunnels can be re-added by this instance of Rita. Due to errors in babel (see git history there)
// restarting is the way to go as removing dead interfaces often does not work
KI.restart_babel();

let args: Args = Docopt::new(get_client_usage(
env!("CARGO_PKG_VERSION"),
env!("GIT_HASH"),
Expand All @@ -80,10 +75,25 @@ fn main() {
// and populate the memory cache of settings used throughout the program
let settings: RitaClientSettings = {
RitaClientSettings::new_watched(settings_file.clone()).unwrap();
let s = settings::get_rita_client();
let mut s = settings::get_rita_client();

settings::set_flag_config(settings_file.clone());

// start migrations //

// handle babel migration for old settings files
// this can be removed after all routers are upgraded paste Beta 21RC4 or Beta 20 RC31
if let Some(local_fee) = s.payment.local_fee {
s.network.babeld_settings.local_fee = local_fee;
s.payment.local_fee = None;
}
if let Some(metric_factor) = s.network.metric_factor {
s.network.babeld_settings.metric_factor = metric_factor;
s.network.metric_factor = None;
}

// end migrations //

let s = clu::init(s);

s.write(settings_file).unwrap();
Expand All @@ -92,11 +102,21 @@ fn main() {
s
};

// Because Rita clears and sets up new Wireguard Tunnels on every restart Babel, which was attached and listening to
// the old tunnels is now in an incorrect state. We must either restart babel or empty it's interfaces list so that the newly
// created wireguard tunnels can be re-added by this instance of Rita. Due to errors in babel (see git history there)
// restarting is the way to go as removing dead interfaces often does not work
KI.restart_babel();
apply_babeld_settings_defaults(
settings.network.babel_port,
settings.network.babeld_settings,
);

// On Linux static builds we need to probe ssl certs path to be able to
// do TLS stuff.
openssl_probe::init_ssl_cert_env_vars();

// we should remove log if there's an operator address or if logging is enabled
// we should remote log if there's an operator address or if logging is enabled
let should_remote_log = settings.log.enabled || settings.operator.operator_address.is_some();
// if remote logging is disabled, or the NO_REMOTE_LOG env var is set we should use the
// local logger and log to std-out. Note we don't care what is actually set in NO_REMOTE_LOG
Expand Down
5 changes: 5 additions & 0 deletions rita_bin/src/exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use rita_common::rita_loop::start_rita_common_loops;
use rita_common::rita_loop::write_to_disk::save_to_disk_loop;
use rita_common::rita_loop::write_to_disk::SettingsOnDisk;
use rita_common::usage_tracker::save_usage_on_shutdown;
use rita_common::utils::apply_babeld_settings_defaults;
use rita_common::utils::env_vars_contains;
use rita_exit::operator_update::update_loop::start_operator_update_loop;
use rita_exit::rita_loop::start_rita_exit_endpoints;
Expand Down Expand Up @@ -90,6 +91,10 @@ fn main() {
println!("Look the exit settings! {settings:?}");
settings
};
apply_babeld_settings_defaults(
settings.network.babel_port,
settings.network.babeld_settings,
);

// On Linux static builds we need to probe ssl certs path to be able to
// do TLS stuff.
Expand Down
29 changes: 17 additions & 12 deletions rita_client/src/operator_update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,13 @@ pub async fn operator_update(

let network = rita_client.network.clone();
trace!("Updating from operator settings");
let payment = update_payment_settings(
rita_client.payment,
update_payment_and_network_settings(
&mut rita_client.payment,
&mut rita_client.network,
use_operator_price,
is_gateway,
new_settings.clone(),
);
rita_client.payment = payment;
trace!("Done with payment");

// merge the new settings into the local settings
Expand Down Expand Up @@ -334,7 +334,12 @@ fn perform_operator_update(
}
None => {}
}
network.shaper_settings = new_settings.shaper_settings;
if let Some(shaper_settings) = new_settings.shaper_settings {
network.shaper_settings = shaper_settings;
}
if let Some(babeld_settings) = new_settings.babeld_settings {
network.babeld_settings = babeld_settings;
}
rita_client.network = network;
settings::set_rita_client(rita_client);
trace!("Successfully completed OperatorUpdate");
Expand Down Expand Up @@ -458,21 +463,22 @@ fn update_authorized_keys(

Ok(())
}
/// Creates a payment settings from OperatorUpdateMessage to be returned and applied
fn update_payment_settings(
mut payment: PaymentSettings,
/// Updates payment settings from OperatorUpdateMessage
fn update_payment_and_network_settings(
payment: &mut PaymentSettings,
network: &mut NetworkSettings,
use_operator_price: bool,
is_gateway: bool,
new_settings: OperatorUpdateMessage,
) -> PaymentSettings {
) {
if use_operator_price {
// This will be true on devices that have integrated switches
// and a wan port configured. Mostly not a problem since we stopped
// shipping wan ports by default
if is_gateway {
payment.local_fee = new_settings.gateway;
network.babeld_settings.local_fee = new_settings.gateway;
} else {
payment.local_fee = new_settings.relay;
network.babeld_settings.local_fee = new_settings.relay;
}
} else {
info!("User has disabled the OperatorUpdate!");
Expand All @@ -481,13 +487,12 @@ fn update_payment_settings(
payment.balance_warning_level = new_settings.warning.into();
if let Some(new_chain) = new_settings.system_chain {
if payment.system_chain != new_chain {
set_system_blockchain(new_chain, &mut payment);
set_system_blockchain(new_chain, payment);
}
}
if let Some(new_chain) = new_settings.withdraw_chain {
payment.withdraw_chain = new_chain;
}
payment
}

/// Returns true if the contact info sent through OperatorUpdateMessage have been more
Expand Down
Loading

0 comments on commit f64449d

Please sign in to comment.