Repository: tonarino/innernet (language: rust)

Issue:
allow for other routing methods
Currently innernet does static routing using the OS's built in tools in `shared/src/wg.rs`'s `add_route` function which is a good default functionality. However, if this functionality could be turned of somehow (not necessarily at runtime), that would enable users to choose other routing methods/protocols such as [babel](https://github.com/jech/babeld/).

Making innernet do that configuration would probably be a bit much, so by just switching this behaviour off (e.g. with a feature flag at compile time), people could configure their other routing methods themselves.

If this sounds like a reasonable idea, I can prepare a pull request on this.


Discussion / hints:
Interesting, never heard of babel before!

I'd gladly accept a pull request that adds CLI flags to disable routing table modifications.

Fix patch:
diff --git a/client/src/main.rs b/client/src/main.rs
index 723ef6ee..412b0680 100644
--- a/client/src/main.rs
+++ b/client/src/main.rs
@@ -5,7 +5,8 @@ use indoc::printdoc;
 use shared::{
     interface_config::InterfaceConfig, prompts, AddAssociationOpts, AddCidrOpts, AddPeerOpts,
     Association, AssociationContents, Cidr, CidrTree, EndpointContents, InstallOpts, Interface,
-    IoErrorContext, Peer, RedeemContents, State, CLIENT_CONFIG_PATH, REDEEM_TRANSITION_WAIT,
+    IoErrorContext, Peer, RedeemContents, RoutingOpt, State, CLIENT_CONFIG_PATH,
+    REDEEM_TRANSITION_WAIT,
 };
 use std::{
     fmt,
@@ -60,6 +61,9 @@ enum Command {
 
         #[structopt(flatten)]
         opts: InstallOpts,
+
+        #[structopt(flatten)]
+        routing: RoutingOpt,
     },
 
     /// Enumerate all innernet connections.
@@ -91,6 +95,9 @@ enum Command {
         #[structopt(flatten)]
         hosts: HostsOpt,
 
+        #[structopt(flatten)]
+        routing: RoutingOpt,
+
         interface: Interface,
     },
 
@@ -100,12 +107,15 @@ enum Command {
 
         #[structopt(flatten)]
         hosts: HostsOpt,
+
+        #[structopt(flatten)]
+        routing: RoutingOpt,
     },
 
     /// Uninstall an innernet network.
     Uninstall { interface: Interface },
 
-    /// Bring down the interface (equivalent to "wg-quick down [interface]")
+    /// Bring down the interface (equivalent to "wg-quick down <interface>")
     Down { interface: Interface },
 
     /// Add a new peer.
@@ -207,7 +217,12 @@ fn update_hosts_file(
     Ok(())
 }
 
-fn install(invite: &Path, hosts_file: Option<PathBuf>, opts: InstallOpts) -> Result<(), Error> {
+fn install(
+    invite: &Path,
+    hosts_file: Option<PathBuf>,
+    opts: InstallOpts,
+    routing: RoutingOpt,
+) -> Result<(), Error> {
     shared::ensure_dirs_exist(&[*CLIENT_CONFIG_PATH])?;
     let config = InterfaceConfig::from_file(invite)?;
 
@@ -228,7 +243,7 @@ fn install(invite: &Path, hosts_file: Option<PathBuf>, opts: InstallOpts) -> Res
     }
 
     let iface = iface.parse()?;
-    redeem_invite(&iface, config, target_conf).map_err(|e| {
+    redeem_invite(&iface, config, target_conf, routing).map_err(|e| {
         println!("{} bringing down the interface.", "[*]".dimmed());
         if let Err(e) = wg::down(&iface) {
             println!("{} failed to bring down interface: {}.", "[*]".yellow(), e.to_string());
@@ -239,7 +254,7 @@ fn install(invite: &Path, hosts_file: Option<PathBuf>, opts: InstallOpts) -> Res
 
     let mut fetch_success = false;
     for _ in 0..3 {
-        if fetch(&iface, false, hosts_file.clone()).is_ok() {
+        if fetch(&iface, false, hosts_file.clone(), routing).is_ok() {
             fetch_success = true;
             break;
         }
@@ -293,6 +308,7 @@ fn redeem_invite(
     iface: &InterfaceName,
     mut config: InterfaceConfig,
     target_conf: PathBuf,
+    routing: RoutingOpt,
 ) -> Result<(), Error> {
     println!("{} bringing up the interface.", "[*]".dimmed());
     let resolved_endpoint = config.server.external_endpoint.resolve()?;
@@ -306,6 +322,7 @@ fn redeem_invite(
             config.server.internal_endpoint.ip(),
             resolved_endpoint,
         )),
+        !routing.no_routing,
     )?;
 
     println!("{} Generating new keypair.", "[*]".dimmed());
@@ -348,9 +365,10 @@ fn up(
     interface: &InterfaceName,
     loop_interval: Option<Duration>,
     hosts_path: Option<PathBuf>,
+    routing: RoutingOpt,
 ) -> Result<(), Error> {
     loop {
-        fetch(interface, true, hosts_path.clone())?;
+        fetch(interface, true, hosts_path.clone(), routing)?;
         match loop_interval {
             Some(interval) => thread::sleep(interval),
             None => break,
@@ -364,6 +382,7 @@ fn fetch(
     interface: &InterfaceName,
     bring_up_interface: bool,
     hosts_path: Option<PathBuf>,
+    routing: RoutingOpt,
 ) -> Result<(), Error> {
     let config = InterfaceConfig::from_interface(interface)?;
     let interface_up = if let Ok(interfaces) = DeviceInfo::enumerate() {
@@ -393,6 +412,7 @@ fn fetch(
                 config.server.internal_endpoint.ip(),
                 resolved_endpoint,
             )),
+            !routing.no_routing,
         )?
     }
 
@@ -892,22 +912,29 @@ fn run(opt: Opts) -> Result<(), Error> {
             invite,
             hosts,
             opts,
-        } => install(&invite, hosts.into(), opts)?,
+            routing,
+        } => install(&invite, hosts.into(), opts, routing)?,
         Command::Show {
             short,
             tree,
             interface,
         } => show(short, tree, interface)?,
-        Command::Fetch { interface, hosts } => fetch(&interface, false, hosts.into())?,
+        Command::Fetch {
+            interface,
+            hosts,
+            routing,
+        } => fetch(&interface, false, hosts.into(), routing)?,
         Command::Up {
             interface,
             daemon,
             hosts,
+            routing,
             interval,
         } => up(
             &interface,
             daemon.then(|| Duration::from_secs(interval)),
             hosts.into(),
+            routing,
         )?,
         Command::Down { interface } => wg::down(&interface)?,
         Command::Uninstall { interface } => uninstall(&interface)?,
diff --git a/server/src/main.rs b/server/src/main.rs
index 841f47b0..cc4e17b5 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -6,7 +6,7 @@ use ipnetwork::IpNetwork;
 use parking_lot::{Mutex, RwLock};
 use rusqlite::Connection;
 use serde::{Deserialize, Serialize};
-use shared::{AddCidrOpts, AddPeerOpts, IoErrorContext, INNERNET_PUBKEY_HEADER};
+use shared::{AddCidrOpts, AddPeerOpts, IoErrorContext, RoutingOpt, INNERNET_PUBKEY_HEADER};
 use std::{
     collections::{HashMap, VecDeque},
     convert::TryInto,
@@ -60,7 +60,12 @@ enum Command {
     Uninstall { interface: Interface },
 
     /// Serve the coordinating server for an existing network.
-    Serve { interface: Interface },
+    Serve {
+        interface: Interface,
+
+        #[structopt(flatten)]
+        routing: RoutingOpt,
+    },
 
     /// Add a peer to an existing network.
     AddPeer {
@@ -206,7 +211,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
             }
         },
         Command::Uninstall { interface } => uninstall(&interface, &conf)?,
-        Command::Serve { interface } => serve(*interface, &conf).await?,
+        Command::Serve { interface, routing } => serve(*interface, &conf, routing).await?,
         Command::AddPeer { interface, args } => add_peer(&interface, &conf, args)?,
         Command::AddCidr { interface, args } => add_cidr(&interface, &conf, args)?,
     }
@@ -370,7 +375,11 @@ fn spawn_expired_invite_sweeper(db: Db) {
     });
 }
 
-async fn serve(interface: InterfaceName, conf: &ServerConfig) -> Result<(), Error> {
+async fn serve(
+    interface: InterfaceName,
+    conf: &ServerConfig,
+    routing: RoutingOpt,
+) -> Result<(), Error> {
     let config = ConfigFile::from_file(conf.config_path(&interface))?;
     let conn = open_database_connection(&interface, conf)?;
 
@@ -387,6 +396,7 @@ async fn serve(interface: InterfaceName, conf: &ServerConfig) -> Result<(), Erro
         IpNetwork::new(config.address, config.network_cidr_prefix)?,
         Some(config.listen_port),
         None,
+        !routing.no_routing,
     )?;
 
     DeviceConfigBuilder::new()
diff --git a/shared/src/types.rs b/shared/src/types.rs
index 84ac6c1f..51ac87ac 100644
--- a/shared/src/types.rs
+++ b/shared/src/types.rs
@@ -346,6 +346,14 @@ pub struct AddAssociationOpts {
     pub cidr2: Option<String>,
 }
 
+#[derive(Debug, Clone, Copy, StructOpt)]
+pub struct RoutingOpt {
+    #[structopt(long)]
+    /// Whether the routing should be done by innernet or is done by an
+    /// external tool like e.g. babeld.
+    pub no_routing: bool,
+}
+
 #[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
 pub struct PeerContents {
     pub name: Hostname,
diff --git a/shared/src/wg.rs b/shared/src/wg.rs
index 8143d0ff..4db89789 100644
--- a/shared/src/wg.rs
+++ b/shared/src/wg.rs
@@ -67,6 +67,7 @@ pub fn up(
     address: IpNetwork,
     listen_port: Option<u16>,
     peer: Option<(&str, IpAddr, SocketAddr)>,
+    do_routing: bool,
 ) -> Result<(), Error> {
     let mut device = DeviceConfigBuilder::new();
     if let Some((public_key, address, endpoint)) = peer {
@@ -83,7 +84,9 @@ pub fn up(
         .set_private_key(wgctrl::Key::from_base64(&private_key).unwrap())
         .apply(interface)?;
     set_addr(interface, address)?;
-    add_route(interface, address)?;
+    if do_routing {
+        add_route(interface, address)?;
+    }
     Ok(())
 }
 


Test patch: