Compare commits

..

14 Commits

Author SHA1 Message Date
Dennis Schwerdel a9b960b04e Plan 2022-09-25 15:03:19 +02:00
Dennis Schwerdel 89d058134e Fix proxy read timeout 2022-09-23 23:24:08 +02:00
Dennis Schwerdel ca4ad2769d Fix interface address get/set 2022-09-23 23:05:22 +02:00
Dennis Schwerdel 2906ca5f40 Clippy 2022-09-23 22:28:15 +02:00
Dennis Schwerdel cc0e033402 Changelog 2022-09-23 22:20:14 +02:00
Dennis Schwerdel 736298a548 Update version 2022-09-23 22:19:24 +02:00
Dennis Schwerdel e235bc319d Update deps 2022-09-23 22:18:14 +02:00
Dennis Schwerdel 0d1bf73920 Update 2022-06-06 22:15:15 +02:00
Dennis Schwerdel 6c69c115aa Merge branch 'master' into threading 2022-01-04 12:34:39 +01:00
Dennis Schwerdel 5c361d08ab Fix static build 2021-12-23 21:55:43 +01:00
Dennis Schwerdel 16d5d47f01 Release v2.3.0 2021-12-23 21:33:50 +01:00
Dennis Schwerdel 50a6c01e93 Fix repeating broadcasts 2021-12-20 08:50:12 +01:00
Dennis Schwerdel 2dc774196e Update audit workflow 2021-12-20 08:25:26 +01:00
Dennis Schwerdel 6fb6cc9213 Update audit workflow 2021-12-20 08:25:06 +01:00
33 changed files with 860 additions and 421 deletions

View File

@ -3,7 +3,7 @@
FROM mcr.microsoft.com/vscode/devcontainers/rust:1 FROM mcr.microsoft.com/vscode/devcontainers/rust:1
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends asciidoctor valgrind && apt-get -y install --no-install-recommends asciidoctor valgrind
RUN rm /etc/localtime && ln -s /usr/share/zoneinfo/Europe/Berlin /etc/localtime RUN rm /etc/localtime && ln -s /usr/share/zoneinfo/Europe/Berlin /etc/localtime
@ -11,8 +11,8 @@ RUN chown vscode: -R /usr/local/rustup /usr/local/cargo
USER vscode USER vscode
RUN rustup default 1.57.0 \ RUN rustup default 1.64.0 \
&& rustup component add clippy rust-src rustfmt && rustup component add clippy rust-src rustfmt
RUN cargo install cargo-outdated cargo-cache cargo-criterion \ RUN cargo install cargo-outdated cargo-cache cargo-criterion \
&& cargo cache -a && cargo cache -a

View File

@ -24,9 +24,9 @@
"serayuzgur.crates", "serayuzgur.crates",
"editorconfig.editorconfig", "editorconfig.editorconfig",
"swellaby.vscode-rust-test-adapter", "swellaby.vscode-rust-test-adapter",
"matklad.rust-analyzer",
"asciidoctor.asciidoctor-vscode", "asciidoctor.asciidoctor-vscode",
"ms-vscode.test-adapter-converter" "ms-vscode.test-adapter-converter",
"rust-lang.rust-analyzer"
], ],
// Use 'forwardPorts' to make a list of ports inside the container available locally. // Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [], // "forwardPorts": [],

View File

@ -6,6 +6,8 @@ RUN yum install -y libstdc++-*.i686 \
&& yum install -y glibc-*.i686 \ && yum install -y glibc-*.i686 \
&& yum install -y libgcc.i686 && yum install -y libgcc.i686
RUN ln -s /usr/bin/gcc /usr/bin/i686-linux-gnu-gcc
ADD entrypoint.sh /entrypoint.sh ADD entrypoint.sh /entrypoint.sh
ENTRYPOINT /entrypoint.sh ENTRYPOINT /entrypoint.sh

View File

@ -21,7 +21,7 @@ rustup target add armv5te-unknown-linux-musleabi
rustup target add armv7-unknown-linux-musleabihf rustup target add armv7-unknown-linux-musleabihf
rustup target add aarch64-unknown-linux-musl rustup target add aarch64-unknown-linux-musl
curl https://github.com/upx/upx/releases/download/v${UPX_VERSION}/upx-${UPX_VERSION}-amd64_linux.tar.xz -Lf | tar -xJ --strip-components=1 -C /opt/rust/cargo/bin curl https://github.com/upx/upx/releases/download/v${UPX_VERSION}/upx-${UPX_VERSION}-amd64_linux.tar.xz -Lf | tar -xJ --strip-components=1 -C /usr/bin
mkdir dist mkdir dist

View File

@ -6,7 +6,7 @@ jobs:
audit: audit:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- uses: actions-rs/audit-check@v1 - uses: actions-rs/audit-check@v1
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -4,12 +4,18 @@ This project follows [semantic versioning](http://semver.org).
### UNRELEASED ### UNRELEASED
- [changed] Changed Rust version to 1.64.0
- [changed] Updated dependencies
### v2.3.0 (2021-12-23)
- [added] Added build for armv5te (thanks to xek) - [added] Added build for armv5te (thanks to xek)
- [added] Option to specify advertised addresses - [added] Option to specify advertised addresses
- [added] Peers now learn their own address from peers - [added] Peers now learn their own address from peers
- [changed] Changed Rust version to 1.57.0 - [changed] Changed Rust version to 1.57.0
- [changed] Updated dependencies - [changed] Updated dependencies
- [fixed] Fixed problem with IPv4 addresses in listen option - [fixed] Fixed problem with IPv4 addresses in listen option
- [fixed] Fixed periodic broadcast messages in switch mode
### v2.2.0 (2021-04-06) ### v2.2.0 (2021-04-06)

655
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
[package] [package]
name = "vpncloud" name = "vpncloud"
version = "2.2.0" version = "2.4.0"
authors = ["Dennis Schwerdel <schwerdel@googlemail.com>"] authors = ["Dennis Schwerdel <schwerdel@googlemail.com>"]
build = "build.rs" build = "build.rs"
license = "GPL-3.0" license = "GPL-3.0"
@ -12,14 +12,14 @@ readme = "README.md"
edition = "2018" edition = "2018"
[package.metadata] [package.metadata]
toolchain = "1.57.0" toolchain = "1.64.0"
upx_version = "3.96" upx_version = "3.96"
[dependencies] [dependencies]
chrono = { version = "0.4", features = ["std", "clock"], default_features = false} chrono = { version = "0.4", features = ["std", "clock"], default_features = false}
structopt = "0.3" structopt = "0.3"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8" serde_yaml = "0.9"
log = { version = "0.4", features = ["std"] } log = { version = "0.4", features = ["std"] }
libc = "0.2" libc = "0.2"
rand = "0.8" rand = "0.8"
@ -30,10 +30,10 @@ ring = "0.16"
privdrop = "0.5" privdrop = "0.5"
byteorder = "1.4" byteorder = "1.4"
thiserror = "1.0" thiserror = "1.0"
parking_lot = "^0.11.2" parking_lot = "0.12"
smallvec = "1.7" smallvec = "1.7"
dialoguer = { version = "0.9", optional = true } dialoguer = { version = "0.10", optional = true }
tungstenite = { version = "0.16", optional = true, default-features = false } tungstenite = { version = "0.17", optional = true, default-features = false }
url = { version = "2.2", optional = true } url = { version = "2.2", optional = true }
igd = { version = "0.12", optional = true } igd = { version = "0.12", optional = true }
timeout_io = "0.6" timeout_io = "0.6"
@ -41,7 +41,7 @@ signal = "0.7"
[dev-dependencies] [dev-dependencies]
tempfile = "3" tempfile = "3"
criterion = { version = "0.3", features = ["html_reports"] } criterion = { version = "0.4", features = ["html_reports"] }
iai = "0.1" iai = "0.1"
[features] [features]

View File

@ -7,7 +7,25 @@
- [x] Check how async affects performance - [x] Check how async affects performance
- [x] Sync traffic stats - [x] Sync traffic stats
- [x] Sync forwarding table - [x] Sync forwarding table
- [ ] Fix WS Proxy code - [x] Fix WS Proxy code
- [x] Fix Ctrl-C
- [x] Fix auto-claim IP
## More threads
- [ ] abstract socket + peers + traffic + table into one class + shared part
- [ ] management thread
- [ ] Send peer list
- [ ] Statsd
- [ ] Write out stats
- [ ] Port forwarding
## VIA Feature
- [ ] Implement message type VIA for relaying messages
- [ ] Advertize VIA addresses (optional) as claims from own peers
- [ ] Use VIA if no peer found
- [ ] Make sure VIA does not recurse
- [ ] Allow enabling VIA in config
## REST API ## REST API

View File

@ -1,3 +1,15 @@
vpncloud (2.3.0) stable; urgency=medium
* [added] Added build for armv5te (thanks to xek)
* [added] Option to specify advertised addresses
* [added] Peers now learn their own address from peers
* [changed] Changed Rust version to 1.57.0
* [changed] Updated dependencies
* [fixed] Fixed problem with IPv4 addresses in listen option
* [fixed] Fixed periodic broadcast messages in switch mode
-- Dennis Schwerdel <schwerdel+vpncloud@googlemail.com> Thu, 23 Dec 2021 20:41:00 +0100
vpncloud (2.2.0) stable; urgency=medium vpncloud (2.2.0) stable; urgency=medium
* [added] Service target file (thanks to mnhauke) * [added] Service target file (thanks to mnhauke)

View File

@ -82,11 +82,11 @@ fn decode_ethernet_with_vlan(c: &mut Criterion) {
fn lookup_warm(c: &mut Criterion) { fn lookup_warm(c: &mut Criterion) {
let mut table = ClaimTable::<MockTimeSource>::new(60, 60); let mut table = ClaimTable::<MockTimeSource>::new(60, 60);
let addr = Address::from_str("1.2.3.4").unwrap(); let addr = Address::from_str("1.2.3.4").unwrap();
table.cache(addr, SocketAddr::from_str("1.2.3.4:3210").unwrap()); table.cache(addr.clone(), SocketAddr::from_str("1.2.3.4:3210").unwrap());
let mut g = c.benchmark_group("table"); let mut g = c.benchmark_group("table");
g.throughput(Throughput::Bytes(1400)); g.throughput(Throughput::Bytes(1400));
g.bench_function("lookup_warm", |b| { g.bench_function("lookup_warm", |b| {
b.iter(|| table.lookup(addr)); b.iter(|| table.lookup(&addr));
}); });
g.finish(); g.finish();
} }
@ -100,7 +100,7 @@ fn lookup_cold(c: &mut Criterion) {
g.bench_function("lookup_cold", |b| { g.bench_function("lookup_cold", |b| {
b.iter(|| { b.iter(|| {
table.clear_cache(); table.clear_cache();
table.lookup(addr) table.lookup(&addr)
}); });
}); });
g.finish(); g.finish();

View File

@ -57,9 +57,9 @@ fn decode_ethernet_with_vlan() {
fn lookup_warm() { fn lookup_warm() {
let mut table = ClaimTable::<MockTimeSource>::new(60, 60); let mut table = ClaimTable::<MockTimeSource>::new(60, 60);
let addr = Address::from_str("1.2.3.4").unwrap(); let addr = Address::from_str("1.2.3.4").unwrap();
table.cache(addr, SocketAddr::from_str("1.2.3.4:3210").unwrap()); table.cache(addr.clone(), SocketAddr::from_str("1.2.3.4:3210").unwrap());
for _ in 0..1000 { for _ in 0..1000 {
table.lookup(black_box(addr)); table.lookup(black_box(&addr));
} }
} }
@ -69,7 +69,7 @@ fn lookup_cold() {
table.set_claims(SocketAddr::from_str("1.2.3.4:3210").unwrap(), smallvec![Range::from_str("1.2.3.4/32").unwrap()]); table.set_claims(SocketAddr::from_str("1.2.3.4:3210").unwrap(), smallvec![Range::from_str("1.2.3.4/32").unwrap()]);
for _ in 0..1000 { for _ in 0..1000 {
table.clear_cache(); table.clear_cache();
table.lookup(black_box(addr)); table.lookup(black_box(&addr));
} }
} }
@ -121,13 +121,85 @@ fn full_communication_tun_router() {
let mut payload = vec![0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]; let mut payload = vec![0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2];
payload.append(&mut vec![0; 1400]); payload.append(&mut vec![0; 1400]);
for _ in 0..1000 { for _ in 0..10000 {
sim.put_payload(node1, payload.clone()); sim.put_payload(node1, payload.clone());
sim.simulate_all_messages(); sim.simulate_all_messages();
assert_eq!(Some(&payload), black_box(sim.pop_payload(node2).as_ref())); assert_eq!(Some(&payload), black_box(sim.pop_payload(node2).as_ref()));
} }
} }
fn tun_router_send() {
log::set_max_level(log::LevelFilter::Error);
let config1 = Config {
device_type: Type::Tun,
auto_claim: false,
claims: vec!["1.1.1.1/32".to_string()],
..Config::default()
};
let config2 = Config {
device_type: Type::Tun,
auto_claim: false,
claims: vec!["2.2.2.2/32".to_string()],
..Config::default()
};
let mut sim = TunSimulator::new();
let node1 = sim.add_node(false, &config1);
let node2 = sim.add_node(false, &config2);
sim.connect(node1, node2);
sim.simulate_all_messages();
assert!(sim.is_connected(node1, node2));
assert!(sim.is_connected(node2, node1));
sim.trigger_housekeep();
let mut payload = vec![0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2];
payload.append(&mut vec![0; 1400]);
let node = sim.get_node(node1);
for _ in 0..10000 {
node.device().put_inbound(black_box(payload.clone()));
node.trigger_device_event();
assert!(node.socket().pop_outbound().is_some());
}
}
fn tun_router_receive() {
log::set_max_level(log::LevelFilter::Error);
let config1 = Config {
device_type: Type::Tun,
auto_claim: false,
claims: vec!["1.1.1.1/32".to_string()],
..Config::default()
};
let config2 = Config {
device_type: Type::Tun,
auto_claim: false,
claims: vec!["2.2.2.2/32".to_string()],
..Config::default()
};
let mut sim = TunSimulator::new();
let node1 = sim.add_node(false, &config1);
let node2 = sim.add_node(false, &config2);
sim.connect(node1, node2);
sim.simulate_all_messages();
assert!(sim.is_connected(node1, node2));
assert!(sim.is_connected(node2, node1));
sim.trigger_housekeep();
let mut payload = vec![0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2];
payload.append(&mut vec![0; 1400]);
let node = sim.get_node(node1);
node.device().put_inbound(payload.clone());
node.trigger_device_event();
let msg = node.socket().pop_outbound().unwrap().1;
let node = sim.get_node(node2);
for _ in 0..10000 {
node.socket().put_inbound(node1, black_box(msg.clone()));
node.trigger_socket_event();
assert!(node.device().pop_outbound().is_some());
}
}
fn full_communication_tap_switch() { fn full_communication_tap_switch() {
log::set_max_level(log::LevelFilter::Error); log::set_max_level(log::LevelFilter::Error);
let config = Config { device_type: Type::Tap, ..Config::default() }; let config = Config { device_type: Type::Tap, ..Config::default() };
@ -144,13 +216,65 @@ fn full_communication_tap_switch() {
let mut payload = vec![2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5]; let mut payload = vec![2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5];
payload.append(&mut vec![0; 1400]); payload.append(&mut vec![0; 1400]);
for _ in 0..1000 { for _ in 0..10000 {
sim.put_payload(node1, payload.clone()); sim.put_payload(node1, payload.clone());
sim.simulate_all_messages(); sim.simulate_all_messages();
assert_eq!(Some(&payload), black_box(sim.pop_payload(node2).as_ref())); assert_eq!(Some(&payload), black_box(sim.pop_payload(node2).as_ref()));
} }
} }
fn tap_switch_send() {
log::set_max_level(log::LevelFilter::Error);
let config = Config { device_type: Type::Tap, ..Config::default() };
let mut sim = TapSimulator::new();
let node1 = sim.add_node(false, &config);
let node2 = sim.add_node(false, &config);
sim.connect(node1, node2);
sim.simulate_all_messages();
assert!(sim.is_connected(node1, node2));
assert!(sim.is_connected(node2, node1));
sim.trigger_housekeep();
let mut payload = vec![2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5];
payload.append(&mut vec![0; 1400]);
let node = sim.get_node(node1);
for _ in 0..10000 {
node.device().put_inbound(black_box(payload.clone()));
node.trigger_device_event();
assert!(node.socket().pop_outbound().is_some());
}
}
fn tap_switch_receive() {
log::set_max_level(log::LevelFilter::Error);
let config = Config { device_type: Type::Tap, ..Config::default() };
let mut sim = TapSimulator::new();
let node1 = sim.add_node(false, &config);
let node2 = sim.add_node(false, &config);
sim.connect(node1, node2);
sim.simulate_all_messages();
assert!(sim.is_connected(node1, node2));
assert!(sim.is_connected(node2, node1));
sim.trigger_housekeep();
let mut payload = vec![2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5];
payload.append(&mut vec![0; 1400]);
let node = sim.get_node(node1);
node.device().put_inbound(payload.clone());
node.trigger_device_event();
let msg = node.socket().pop_outbound().unwrap().1;
let node = sim.get_node(node2);
for _ in 0..10000 {
node.socket().put_inbound(node1, black_box(msg.clone()));
node.trigger_socket_event();
assert!(node.device().pop_outbound().is_some());
}
}
iai::main!( iai::main!(
udp_send, udp_send,
decode_ipv4, decode_ipv4,
@ -162,6 +286,10 @@ iai::main!(
crypto_chacha20, crypto_chacha20,
crypto_aes128, crypto_aes128,
crypto_aes256, crypto_aes256,
tun_router_send,
tun_router_receive,
full_communication_tun_router, full_communication_tun_router,
tap_switch_send,
tap_switch_receive,
full_communication_tap_switch full_communication_tap_switch
); );

View File

@ -20,6 +20,8 @@ RUN yum install -y libstdc++-*.i686 \
&& yum install -y glibc-*.i686 \ && yum install -y glibc-*.i686 \
&& yum install -y libgcc.i686 && yum install -y libgcc.i686
RUN ln -s /usr/bin/gcc /usr/bin/i686-linux-gnu-gcc
RUN useradd -ms /bin/bash user RUN useradd -ms /bin/bash user
USER user USER user
WORKDIR /home/user WORKDIR /home/user

View File

@ -293,7 +293,7 @@ runcmd:
MinCount=self.node_count, MinCount=self.node_count,
NetworkInterfaces=[ NetworkInterfaces=[
{ {
'SubnetId': self.subnet.id, 'SubnetId': self.subnet,
'DeviceIndex': 0, 'DeviceIndex': 0,
'AssociatePublicIpAddress': True, 'AssociatePublicIpAddress': True,
'Groups': [sg.group_id] 'Groups': [sg.group_id]
@ -301,7 +301,7 @@ runcmd:
], ],
Placement=placement, Placement=placement,
UserData=userdata, UserData=userdata,
KeyName='vpncloud-perf-test-keypair' KeyName=key_pair.name
) )
for instance in self.instances: for instance in self.instances:
self.track_resource(instance) self.track_resource(instance)
@ -361,7 +361,8 @@ runcmd:
self.connections = [] self.connections = []
eprint("Terminating instances...") eprint("Terminating instances...")
for instance in self.instances: for instance in self.instances:
instance.terminate() if instance:
instance.terminate()
for instance in self.instances: for instance in self.instances:
eprint("\t{}".format(instance.id)) eprint("\t{}".format(instance.id))
instance.wait_until_terminated() instance.wait_until_terminated()

View File

@ -0,0 +1,165 @@
{
"meta": {
"region": "eu-central-1",
"instance_type": "m5.large",
"ami": "ami-099ccc441b2ef41ec",
"version": "2.3.0",
"duration": 622.5463161468506
},
"native": {
"iperf": {
"throughput": 9529265000.0,
"cpu_sender": 11.32918,
"cpu_receiver": 61.870429
},
"ping_100": {
"rtt_min": 0.046,
"rtt_max": 0.225,
"rtt_avg": 0.053,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.047,
"rtt_max": 10.98,
"rtt_avg": 0.054,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.048,
"rtt_max": 0.175,
"rtt_avg": 0.056,
"pkt_loss": 0.0
}
},
"plain": {
"iperf": {
"throughput": 6388312000.0,
"cpu_sender": 16.955082,
"cpu_receiver": 72.705695
},
"ping_100": {
"rtt_min": 0.076,
"rtt_max": 11.973,
"rtt_avg": 0.09,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.08,
"rtt_max": 10.95,
"rtt_avg": 0.094,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.081,
"rtt_max": 1.638,
"rtt_avg": 0.095,
"pkt_loss": 0.0
}
},
"aes256": {
"iperf": {
"throughput": 3801851000.0,
"cpu_sender": 5.826756,
"cpu_receiver": 61.612033
},
"ping_100": {
"rtt_min": 0.075,
"rtt_max": 0.9,
"rtt_avg": 0.093,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.079,
"rtt_max": 0.275,
"rtt_avg": 0.091,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.08,
"rtt_max": 1.015,
"rtt_avg": 0.093,
"pkt_loss": 0.0
}
},
"aes128": {
"iperf": {
"throughput": 3880325000.0,
"cpu_sender": 6.219277,
"cpu_receiver": 62.125445
},
"ping_100": {
"rtt_min": 0.077,
"rtt_max": 11.656,
"rtt_avg": 0.09,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.08,
"rtt_max": 0.211,
"rtt_avg": 0.095,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.082,
"rtt_max": 1.398,
"rtt_avg": 0.095,
"pkt_loss": 0.0
}
},
"chacha20": {
"iperf": {
"throughput": 3126447000.0,
"cpu_sender": 5.113819,
"cpu_receiver": 58.58095
},
"ping_100": {
"rtt_min": 0.079,
"rtt_max": 0.271,
"rtt_avg": 0.091,
"pkt_loss": 0.0
},
"ping_500": {
"rtt_min": 0.083,
"rtt_max": 0.272,
"rtt_avg": 0.098,
"pkt_loss": 0.0
},
"ping_1000": {
"rtt_min": 0.087,
"rtt_max": 1.615,
"rtt_avg": 0.101,
"pkt_loss": 0.0
}
},
"results": {
"throughput_mbits": {
"native": 9529.265,
"plain": 6388.312,
"aes256": 3801.851,
"aes128": 3880.325,
"chacha20": 3126.447
},
"latency_us": {
"plain": {
"100": 18.5,
"500": 20.0,
"1000": 19.5
},
"aes256": {
"100": 20.0,
"500": 18.5,
"1000": 18.5
},
"aes128": {
"100": 18.5,
"500": 20.5,
"1000": 19.5
},
"chacha20": {
"100": 19.0,
"500": 22.000000000000004,
"1000": 22.500000000000004
}
}
}
}

View File

@ -8,14 +8,14 @@ from datetime import date
# Note: this script will run for ~8 minutes and incur costs of about $ 0.02 # Note: this script will run for ~8 minutes and incur costs of about $ 0.02
FILE = "../../target/release/vpncloud" FILE = "../../target/release/vpncloud"
VERSION = "2.2.0" VERSION = "2.3.0"
REGION = "eu-central-1" REGION = "eu-central-1"
env = EC2Environment( env = EC2Environment(
region = REGION, region = REGION,
node_count = 2, node_count = 2,
instance_type = "m5.large", instance_type = "m5.large",
use_spot = True, use_spot = False,
max_price = "0.08", # USD per hour per VM max_price = "0.08", # USD per hour per VM
vpncloud_version = VERSION, vpncloud_version = VERSION,
vpncloud_file = FILE, vpncloud_file = FILE,
@ -70,7 +70,7 @@ class PerfTest:
eprint("\tSetting up vpncloud on receiver") eprint("\tSetting up vpncloud on receiver")
self.receiver.start_vpncloud(crypto=crypto, ip=f"{self.receiver_ip_vpncloud}/24") self.receiver.start_vpncloud(crypto=crypto, ip=f"{self.receiver_ip_vpncloud}/24")
eprint("\tSetting up vpncloud on sender") eprint("\tSetting up vpncloud on sender")
self.sender.start_vpncloud(crypto=crypto, peers=[f"{self.receiver_ip_vpncloud}:3210"], ip=f"{self.sender_ip_vpncloud}/24") self.sender.start_vpncloud(crypto=crypto, peers=[f"{self.receiver.private_ip}:3210"], ip=f"{self.sender_ip_vpncloud}/24")
time.sleep(1.0) time.sleep(1.0)
def stop_vpncloud(self): def stop_vpncloud(self):

View File

@ -12,7 +12,7 @@ env = EC2Environment(
region = REGION, region = REGION,
node_count = 2, node_count = 2,
instance_type = "m5.large", instance_type = "m5.large",
use_spot = True, use_spot = False,
max_price = "0.08", # USD per hour per VM max_price = "0.08", # USD per hour per VM
vpncloud_version = VERSION, vpncloud_version = VERSION,
vpncloud_file = FILE, vpncloud_file = FILE,
@ -45,11 +45,11 @@ class PerfTest:
self.receiver.stop_iperf_server() self.receiver.stop_iperf_server()
return result return result
def start_vpncloud(self): def start_vpncloud(self, crypto=None):
eprint("\tSetting up vpncloud on receiver") eprint("\tSetting up vpncloud on receiver")
self.receiver.start_vpncloud(ip=f"{self.receiver_ip_vpncloud}/24") self.receiver.start_vpncloud(crypto=crypto, ip=f"{self.receiver_ip_vpncloud}/24")
eprint("\tSetting up vpncloud on sender") eprint("\tSetting up vpncloud on sender")
self.sender.start_vpncloud(peers=[f"{self.receiver.private_ip}:3210"], ip=f"{self.sender_ip_vpncloud}/24") self.sender.start_vpncloud(crypto=crypto, peers=[f"{self.receiver.private_ip}:3210"], ip=f"{self.sender_ip_vpncloud}/24")
time.sleep(1.0) time.sleep(1.0)
def stop_vpncloud(self): def stop_vpncloud(self):
@ -58,7 +58,7 @@ class PerfTest:
def run(self): def run(self):
print() print()
self.start_vpncloud() self.start_vpncloud(crypto="plain")
throughput = self.run_iperf(self.receiver_ip_vpncloud)["throughput"] throughput = self.run_iperf(self.receiver_ip_vpncloud)["throughput"]
print(f"Throughput: {throughput / 1_000_000.0} MBit/s") print(f"Throughput: {throughput / 1_000_000.0} MBit/s")
native_ping_100 = self.run_ping(self.receiver.private_ip, 100)["rtt_avg"] native_ping_100 = self.run_ping(self.receiver.private_ip, 100)["rtt_avg"]

View File

@ -11,7 +11,7 @@ use structopt::{clap::Shell, StructOpt};
pub const DEFAULT_PEER_TIMEOUT: u16 = 300; pub const DEFAULT_PEER_TIMEOUT: u16 = 300;
pub const DEFAULT_PORT: u16 = 3210; pub const DEFAULT_PORT: u16 = 3210;
#[derive(Deserialize, Debug, PartialEq, Clone)] #[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Config { pub struct Config {
pub device_type: Type, pub device_type: Type,
pub device_name: String, pub device_name: String,
@ -622,7 +622,7 @@ pub enum Command {
}, },
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)] #[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct ConfigFileDevice { pub struct ConfigFileDevice {
#[serde(rename = "type")] #[serde(rename = "type")]
@ -633,7 +633,7 @@ pub struct ConfigFileDevice {
pub fix_rp_filter: Option<bool>, pub fix_rp_filter: Option<bool>,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)] #[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct ConfigFileBeacon { pub struct ConfigFileBeacon {
pub store: Option<String>, pub store: Option<String>,
@ -642,14 +642,14 @@ pub struct ConfigFileBeacon {
pub password: Option<String>, pub password: Option<String>,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)] #[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct ConfigFileStatsd { pub struct ConfigFileStatsd {
pub server: Option<String>, pub server: Option<String>,
pub prefix: Option<String>, pub prefix: Option<String>,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Default)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)] #[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct ConfigFile { pub struct ConfigFile {
pub device: Option<ConfigFileDevice>, pub device: Option<ConfigFileDevice>,

View File

@ -47,7 +47,7 @@ pub struct Algorithms {
pub allow_unencrypted: bool, pub allow_unencrypted: bool,
} }
#[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq)] #[derive(Debug, Default, Deserialize, Serialize, Clone, PartialEq, Eq)]
#[serde(rename_all = "kebab-case", deny_unknown_fields, default)] #[serde(rename_all = "kebab-case", deny_unknown_fields, default)]
pub struct Config { pub struct Config {
pub password: Option<String>, pub password: Option<String>,
@ -201,13 +201,14 @@ impl Crypto {
} }
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Eq)]
pub enum MessageResult { pub enum MessageResult {
Message(u8), Message(u8),
Reply, Reply,
None, None,
} }
#[allow(clippy::large_enum_variant)]
pub enum PeerCrypto { pub enum PeerCrypto {
Encrypted { Encrypted {
last_init_message: Vec<u8>, last_init_message: Vec<u8>,

View File

@ -384,7 +384,7 @@ impl InitMsg {
} }
} }
#[derive(PartialEq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub enum InitResult<P: Payload> { pub enum InitResult<P: Payload> {
Continue, Continue,
Success { peer_payload: P, is_initiator: bool }, Success { peer_payload: P, is_initiator: bool },

View File

@ -3,18 +3,38 @@
// This software is licensed under GPL-3 or newer (see LICENSE.md) // This software is licensed under GPL-3 or newer (see LICENSE.md)
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{cmp, collections::VecDeque, convert::TryInto, fmt, fs::{self, File}, io::{self, Cursor, Read, Write, Error as IoError, BufReader, BufRead}, net::{Ipv4Addr, UdpSocket}, os::unix::io::AsRawFd, str, str::FromStr, sync::Arc, time::Duration}; use std::{
cmp,
collections::VecDeque,
convert::TryInto,
fmt,
fs::{self, File},
io::{self, BufRead, BufReader, Cursor, Error as IoError, Read, Write},
net::{Ipv4Addr, UdpSocket},
os::unix::io::AsRawFd,
str,
str::FromStr,
sync::Arc,
time::Duration,
};
use timeout_io::Reader; use timeout_io::Reader;
use crate::{crypto, error::Error, util::MsgBuffer}; use crate::{crypto, error::Error, util::MsgBuffer};
static TUNSETIFF: libc::c_ulong = 1074025674; static TUNSETIFF: libc::c_ulong = 1074025674;
#[repr(C)]
#[derive(Copy, Clone)]
struct IfReqDataAddr {
af: libc::c_int,
addr: Ipv4Addr
}
#[repr(C)] #[repr(C)]
union IfReqData { union IfReqData {
flags: libc::c_short, flags: libc::c_short,
value: libc::c_int, value: libc::c_int,
addr: (libc::c_short, Ipv4Addr), addr: IfReqDataAddr,
_dummy: [u8; 24], _dummy: [u8; 24],
} }
@ -34,7 +54,7 @@ impl IfReq {
} }
/// The type of a tun/tap device /// The type of a tun/tap device
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum Type { pub enum Type {
/// Tun interface: This interface transports IP packets. /// Tun interface: This interface transports IP packets.
#[serde(rename = "tun")] #[serde(rename = "tun")]
@ -382,11 +402,11 @@ fn get_device_addr(ifname: &str) -> io::Result<Ipv4Addr> {
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFADDR.try_into().unwrap(), &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFADDR.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => { 0 => {
let af = unsafe { ifreq.data.addr.0 }; let af = unsafe { ifreq.data.addr.af };
if af as libc::c_int != libc::AF_INET { if af as libc::c_int != libc::AF_INET {
return Err(io::Error::new(io::ErrorKind::AddrNotAvailable, "Invalid address family".to_owned())); return Err(io::Error::new(io::ErrorKind::AddrNotAvailable, "Invalid address family".to_owned()));
} }
let ip = unsafe { ifreq.data.addr.1 }; let ip = unsafe { ifreq.data.addr.addr };
Ok(ip) Ok(ip)
} }
_ => Err(IoError::last_os_error()), _ => Err(IoError::last_os_error()),
@ -397,7 +417,8 @@ fn get_device_addr(ifname: &str) -> io::Result<Ipv4Addr> {
fn set_device_addr(ifname: &str, addr: Ipv4Addr) -> io::Result<()> { fn set_device_addr(ifname: &str, addr: Ipv4Addr) -> io::Result<()> {
let sock = UdpSocket::bind("0.0.0.0:0")?; let sock = UdpSocket::bind("0.0.0.0:0")?;
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
ifreq.data.addr = (libc::AF_INET as libc::c_short, addr); ifreq.data.addr.af = libc::AF_INET as libc::c_int;
ifreq.data.addr.addr = addr;
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFADDR.try_into().unwrap(), &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFADDR.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => Ok(()), 0 => Ok(()),
@ -413,11 +434,11 @@ fn get_device_netmask(ifname: &str) -> io::Result<Ipv4Addr> {
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFNETMASK.try_into().unwrap(), &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCGIFNETMASK.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => { 0 => {
let af = unsafe { ifreq.data.addr.0 }; let af = unsafe { ifreq.data.addr.af };
if af as libc::c_int != libc::AF_INET { if af as libc::c_int != libc::AF_INET {
return Err(io::Error::new(io::ErrorKind::AddrNotAvailable, "Invalid address family".to_owned())); return Err(io::Error::new(io::ErrorKind::AddrNotAvailable, "Invalid address family".to_owned()));
} }
let ip = unsafe { ifreq.data.addr.1 }; let ip = unsafe { ifreq.data.addr.addr };
Ok(ip) Ok(ip)
} }
_ => Err(IoError::last_os_error()), _ => Err(IoError::last_os_error()),
@ -428,7 +449,8 @@ fn get_device_netmask(ifname: &str) -> io::Result<Ipv4Addr> {
fn set_device_netmask(ifname: &str, addr: Ipv4Addr) -> io::Result<()> { fn set_device_netmask(ifname: &str, addr: Ipv4Addr) -> io::Result<()> {
let sock = UdpSocket::bind("0.0.0.0:0")?; let sock = UdpSocket::bind("0.0.0.0:0")?;
let mut ifreq = IfReq::new(ifname); let mut ifreq = IfReq::new(ifname);
ifreq.data.addr = (libc::AF_INET as libc::c_short, addr); ifreq.data.addr.af = libc::AF_INET as libc::c_int;
ifreq.data.addr.addr = addr;
let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFNETMASK.try_into().unwrap(), &mut ifreq) }; let res = unsafe { libc::ioctl(sock.as_raw_fd(), libc::SIOCSIFNETMASK.try_into().unwrap(), &mut ifreq) };
match res { match res {
0 => Ok(()), 0 => Ok(()),

View File

@ -57,7 +57,7 @@ impl<D: Device, P: Protocol, S: Socket, TS: TimeSource> GenericCloud<D, P, S, TS
let device_thread = DeviceThread::<S, D, P, TS>::new( let device_thread = DeviceThread::<S, D, P, TS>::new(
config.clone(), config.clone(),
device.duplicate()?, device.duplicate()?,
socket.clone(), socket.try_clone().map_err(|e| Error::SocketIo("Failed to clone socket", e))?,
traffic.clone(), traffic.clone(),
peer_crypto.clone(), peer_crypto.clone(),
table.clone(), table.clone(),
@ -132,11 +132,11 @@ impl<P: Protocol> GenericCloud<MockDevice, P, MockSocket, MockTimeSource> {
} }
pub fn trigger_socket_event(&mut self) { pub fn trigger_socket_event(&mut self) {
self.socket_thread.iteration() self.socket_thread.iteration();
} }
pub fn trigger_device_event(&mut self) { pub fn trigger_device_event(&mut self) {
self.device_thread.iteration() self.device_thread.iteration();
} }
pub fn trigger_housekeep(&mut self) { pub fn trigger_housekeep(&mut self) {

View File

@ -102,13 +102,14 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> DeviceThread<S, D, P, TS
fn forward_packet(&mut self) -> Result<(), Error> { fn forward_packet(&mut self) -> Result<(), Error> {
let (src, dst) = P::parse(self.buffer.message())?; let (src, dst) = P::parse(self.buffer.message())?;
debug!("Read data from interface: src: {}, dst: {}, {} bytes", src, dst, self.buffer.len()); debug!("Read data from interface: src: {}, dst: {}, {} bytes", src, dst, self.buffer.len());
self.traffic.count_out_payload(dst, src, self.buffer.len()); self.traffic.count_out_payload(dst.clone(), src, self.buffer.len());
match self.table.lookup(dst) { match self.table.lookup(&dst) {
Some(addr) => { Some(addr) => {
// Peer found for destination // Peer found for destination
debug!("Found destination for {} => {}", dst, addr); debug!("Found destination for {} => {}", dst, addr);
self.send_msg(addr, MESSAGE_TYPE_DATA)?; self.send_msg(addr, MESSAGE_TYPE_DATA)?;
} }
//TODO: VIA: find relay peer and relay message
None => { None => {
if self.broadcast { if self.broadcast {
debug!("No destination for {} found, broadcasting", dst); debug!("No destination for {} found, broadcasting", dst);
@ -129,7 +130,7 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> DeviceThread<S, D, P, TS
Ok(()) Ok(())
} }
pub fn iteration(&mut self) { pub fn iteration(&mut self) -> bool {
if self.device.read(&mut self.buffer).is_ok() { if self.device.read(&mut self.buffer).is_ok() {
//try_fail!(result, "Failed to read from device: {}"); //try_fail!(result, "Failed to read from device: {}");
if let Err(e) = self.forward_packet() { if let Err(e) = self.forward_packet() {
@ -141,17 +142,16 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> DeviceThread<S, D, P, TS
if let Err(e) = self.housekeep() { if let Err(e) = self.housekeep() {
error!("{}", e) error!("{}", e)
} }
self.next_housekeep = now + 1 self.next_housekeep = now + 1;
if !self.running.load(Ordering::SeqCst) {
debug!("Device: end");
return false;
}
} }
true
} }
pub fn run(mut self) { pub fn run(mut self) {
loop { while self.iteration() {}
self.iteration();
if !self.running.load(Ordering::SeqCst) {
debug!("Device: end");
return;
}
}
} }
} }

View File

@ -19,6 +19,7 @@ use std::{
use super::common::PeerData; use super::common::PeerData;
#[derive(Clone)] #[derive(Clone)]
#[allow(clippy::type_complexity)]
pub struct SharedPeerCrypto { pub struct SharedPeerCrypto {
peers: Arc<Mutex<HashMap<SocketAddr, Option<Arc<CryptoCore>>, Hash>>>, peers: Arc<Mutex<HashMap<SocketAddr, Option<Arc<CryptoCore>>, Hash>>>,
cache: HashMap<SocketAddr, Option<Arc<CryptoCore>>, Hash>, //TODO: local hashmap as cache cache: HashMap<SocketAddr, Option<Arc<CryptoCore>>, Hash>, //TODO: local hashmap as cache
@ -153,13 +154,13 @@ impl<TS: TimeSource> SharedTable<TS> {
self.cache.clear(); self.cache.clear();
} }
pub fn lookup(&mut self, addr: Address) -> Option<SocketAddr> { pub fn lookup(&mut self, addr: &Address) -> Option<SocketAddr> {
if let Some(val) = self.cache.get(&addr) { if let Some(val) = self.cache.get(addr) {
return *val; return *val;
} }
// if not found, use shared table and put into cache // if not found, use shared table and put into cache
let val = self.table.lock().lookup(addr); let val = self.table.lock().lookup(addr);
self.cache.insert(addr, val); self.cache.insert(addr.clone(), val);
val val
} }
@ -173,10 +174,10 @@ impl<TS: TimeSource> SharedTable<TS> {
self.cache.clear(); self.cache.clear();
} }
pub fn cache(&mut self, addr: Address, peer: SocketAddr) { pub fn cache(&mut self, addr: &Address, peer: SocketAddr) {
if self.cache.get(&addr) != Some(&Some(peer)) { if self.cache.get(addr) != Some(&Some(peer)) {
self.table.lock().cache(addr, peer); self.table.lock().cache(addr.clone(), peer);
self.cache.insert(addr, Some(peer)); self.cache.insert(addr.clone(), Some(peer));
} }
} }

View File

@ -89,6 +89,7 @@ pub struct SocketThread<S: Socket, D: Device, P: Protocol, TS: TimeSource> {
} }
impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS> { impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS> {
#[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
config: Config, device: D, socket: S, traffic: SharedTraffic, peer_crypto: SharedPeerCrypto, config: Config, device: D, socket: S, traffic: SharedTraffic, peer_crypto: SharedPeerCrypto,
table: SharedTable<TS>, port_forwarding: Option<PortForwarding>, stats_file: Option<File>, table: SharedTable<TS>, port_forwarding: Option<PortForwarding>, stats_file: Option<File>,
@ -331,7 +332,7 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
let (src, dst) = P::parse(self.buffer.message())?; let (src, dst) = P::parse(self.buffer.message())?;
let len = self.buffer.len(); let len = self.buffer.len();
debug!("Writing data to device: {} bytes", len); debug!("Writing data to device: {} bytes", len);
self.traffic.count_in_payload(src, dst, len); self.traffic.count_in_payload(src.clone(), dst, len);
if let Err(e) = self.device.write(&mut self.buffer) { if let Err(e) = self.device.write(&mut self.buffer) {
error!("Failed to send via device: {}", e); error!("Failed to send via device: {}", e);
return Err(e); return Err(e);
@ -339,7 +340,7 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
self.buffer.clear(); self.buffer.clear();
if self.learning { if self.learning {
// Learn single address // Learn single address
self.table.cache(src, peer); self.table.cache(&src, peer);
} }
Ok(()) Ok(())
} }
@ -348,6 +349,7 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
match msg_result { match msg_result {
MessageResult::Message(type_) => match type_ { MessageResult::Message(type_) => match type_ {
MESSAGE_TYPE_DATA => self.handle_payload_from(src)?, MESSAGE_TYPE_DATA => self.handle_payload_from(src)?,
//TODO: VIA: relay message
MESSAGE_TYPE_NODE_INFO => { MESSAGE_TYPE_NODE_INFO => {
let info = match NodeInfo::decode(Cursor::new(self.buffer.message())) { let info = match NodeInfo::decode(Cursor::new(self.buffer.message())) {
Ok(val) => val, Ok(val) => val,
@ -444,6 +446,7 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
self.table.housekeep(); self.table.housekeep();
self.crypto_housekeep()?; self.crypto_housekeep()?;
// Periodically extend the port-forwarding // Periodically extend the port-forwarding
//TODO: extra thread
if let Some(ref mut pfw) = self.port_forwarding { if let Some(ref mut pfw) = self.port_forwarding {
pfw.check_extend(); pfw.check_extend();
} }
@ -462,7 +465,9 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
self.reconnect_to_peers()?; self.reconnect_to_peers()?;
if self.next_stats_out < now { if self.next_stats_out < now {
// Write out the statistics // Write out the statistics
//TODO: extra thread
self.write_out_stats().map_err(|err| Error::FileIo("Failed to write stats file", err))?; self.write_out_stats().map_err(|err| Error::FileIo("Failed to write stats file", err))?;
//TODO: extra thread
self.send_stats_to_statsd()?; self.send_stats_to_statsd()?;
self.next_stats_out = now + STATS_INTERVAL; self.next_stats_out = now + STATS_INTERVAL;
self.traffic.period(Some(5)); self.traffic.period(Some(5));
@ -716,7 +721,7 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
Ok(()) Ok(())
} }
pub fn iteration(&mut self) { pub fn iteration(&mut self) -> bool {
if let Ok(src) = self.socket.receive(&mut self.buffer) if let Ok(src) = self.socket.receive(&mut self.buffer)
{ {
match self.handle_message(src) { match self.handle_message(src) {
@ -744,18 +749,17 @@ impl<S: Socket, D: Device, P: Protocol, TS: TimeSource> SocketThread<S, D, P, TS
if let Err(e) = self.housekeep() { if let Err(e) = self.housekeep() {
error!("{}", e) error!("{}", e)
} }
self.next_housekeep = now + 1 self.next_housekeep = now + 1;
if !self.running.load(Ordering::SeqCst) {
debug!("Socket: end");
return false;
}
} }
debug_assert!(self.buffer.is_empty()); debug_assert!(self.buffer.is_empty());
true
} }
pub fn run(mut self) { pub fn run(mut self) {
loop { while self.iteration() {}
self.iteration();
if !self.running.load(Ordering::SeqCst) {
debug!("Socket: end");
return;
}
}
} }
} }

View File

@ -23,19 +23,20 @@ pub const MESSAGE_TYPE_CLOSE: u8 = 0xff;
pub type AddrList = SmallVec<[SocketAddr; 4]>; pub type AddrList = SmallVec<[SocketAddr; 4]>;
pub type PeerList = SmallVec<[PeerInfo; 16]>; pub type PeerList = SmallVec<[PeerInfo; 16]>;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Eq)]
pub struct PeerInfo { pub struct PeerInfo {
pub node_id: Option<NodeId>, pub node_id: Option<NodeId>,
pub addrs: AddrList, pub addrs: AddrList,
} }
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Eq)]
pub struct NodeInfo { pub struct NodeInfo {
pub node_id: NodeId, pub node_id: NodeId,
pub peers: PeerList, pub peers: PeerList,
pub claims: RangeList, pub claims: RangeList,
pub peer_timeout: Option<u16>, pub peer_timeout: Option<u16>,
pub addrs: AddrList, pub addrs: AddrList,
//TODO: VIA: Add via ranges
} }
impl NodeInfo { impl NodeInfo {

View File

@ -31,11 +31,12 @@ pub fn get_ip() -> IpAddr {
s.local_addr().unwrap().ip() s.local_addr().unwrap().ip()
} }
pub trait Socket: Sized + Clone + Send + Sync + 'static { pub trait Socket: Sized + Send + Sync + 'static {
fn receive(&mut self, buffer: &mut MsgBuffer) -> Result<SocketAddr, io::Error>; fn receive(&mut self, buffer: &mut MsgBuffer) -> Result<SocketAddr, io::Error>;
fn send(&mut self, data: &[u8], addr: SocketAddr) -> Result<usize, io::Error>; fn send(&mut self, data: &[u8], addr: SocketAddr) -> Result<usize, io::Error>;
fn address(&self) -> Result<SocketAddr, io::Error>; fn address(&self) -> Result<SocketAddr, io::Error>;
fn create_port_forwarding(&self) -> Option<PortForwarding>; fn create_port_forwarding(&self) -> Option<PortForwarding>;
fn try_clone(&self) -> Result<Self, io::Error>;
} }
pub fn parse_listen(addr: &str, default_port: u16) -> SocketAddr { pub fn parse_listen(addr: &str, default_port: u16) -> SocketAddr {
@ -63,12 +64,6 @@ impl NetSocket {
} }
} }
impl Clone for NetSocket {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl Socket for NetSocket { impl Socket for NetSocket {
fn create_port_forwarding(&self) -> Option<PortForwarding> { fn create_port_forwarding(&self) -> Option<PortForwarding> {
PortForwarding::new(self.0.local_addr().unwrap().port()) PortForwarding::new(self.0.local_addr().unwrap().port())
@ -90,6 +85,10 @@ impl Socket for NetSocket {
addr.set_ip(get_ip()); addr.set_ip(get_ip());
Ok(addr) Ok(addr)
} }
fn try_clone(&self) -> Result<Self, io::Error> {
Ok(Self(self.0.clone()))
}
} }
thread_local! { thread_local! {
@ -173,6 +172,10 @@ impl Socket for MockSocket {
fn create_port_forwarding(&self) -> Option<PortForwarding> { fn create_port_forwarding(&self) -> Option<PortForwarding> {
None None
} }
fn try_clone(&self) -> Result<Self, io::Error> {
Ok(self.clone())
}
} }
#[cfg(feature = "bench")] #[cfg(feature = "bench")]

View File

@ -6,7 +6,7 @@ use super::{device::Type, types::Mode, util::Duration};
use crate::config::{ConfigFile, ConfigFileBeacon, ConfigFileDevice, ConfigFileStatsd, CryptoConfig}; use crate::config::{ConfigFile, ConfigFileBeacon, ConfigFileDevice, ConfigFileStatsd, CryptoConfig};
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
pub enum OldCryptoMethod { pub enum OldCryptoMethod {
#[serde(rename = "chacha20")] #[serde(rename = "chacha20")]
ChaCha20, ChaCha20,
@ -16,7 +16,7 @@ pub enum OldCryptoMethod {
AES128, AES128,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Default)]
pub struct OldConfigFile { pub struct OldConfigFile {
#[serde(alias = "device-type")] #[serde(alias = "device-type")]
pub device_type: Option<Type>, pub device_type: Option<Type>,

View File

@ -48,6 +48,7 @@ impl<TS: TimeSource> ClaimTable<TS> {
} }
pub fn set_claims(&mut self, peer: SocketAddr, mut claims: RangeList) { pub fn set_claims(&mut self, peer: SocketAddr, mut claims: RangeList) {
let mut removed_claim = false;
for entry in &mut self.claims { for entry in &mut self.claims {
if entry.peer == peer { if entry.peer == peer {
let pos = claims.iter().position(|r| r == &entry.claim); let pos = claims.iter().position(|r| r == &entry.claim);
@ -58,16 +59,19 @@ impl<TS: TimeSource> ClaimTable<TS> {
break; break;
} }
} else { } else {
entry.timeout = 0 entry.timeout = 0;
removed_claim = true;
} }
} }
} }
for claim in claims { for claim in claims {
self.claims.push(ClaimEntry { peer, claim, timeout: TS::now() + self.claim_timeout as Time }) self.claims.push(ClaimEntry { peer, claim, timeout: TS::now() + self.claim_timeout as Time })
} }
for entry in self.cache.values_mut() { if removed_claim {
if entry.peer == peer { for entry in self.cache.values_mut() {
entry.timeout = 0 if entry.peer == peer {
entry.timeout = 0
}
} }
} }
self.housekeep() self.housekeep()
@ -87,9 +91,9 @@ impl<TS: TimeSource> ClaimTable<TS> {
self.housekeep() self.housekeep()
} }
pub fn lookup(&mut self, addr: Address) -> Option<SocketAddr> { pub fn lookup(&mut self, addr: &Address) -> Option<SocketAddr> {
// HOT PATH // HOT PATH
if let Some(entry) = self.cache.get(&addr) { if let Some(entry) = self.cache.get(addr) {
return Some(entry.peer); return Some(entry.peer);
} }
// COLD PATH // COLD PATH
@ -103,7 +107,7 @@ impl<TS: TimeSource> ClaimTable<TS> {
} }
if let Some(entry) = found { if let Some(entry) = found {
self.cache.insert( self.cache.insert(
addr, addr.clone(),
CacheValue { peer: entry.peer, timeout: min(TS::now() + self.cache_timeout as Time, entry.timeout) }, CacheValue { peer: entry.peer, timeout: min(TS::now() + self.cache_timeout as Time, entry.timeout) },
); );
return Some(entry.peer); return Some(entry.peer);

View File

@ -228,7 +228,7 @@ impl TrafficStats {
if let Some(entry) = self.payload.get_mut(key) { if let Some(entry) = self.payload.get_mut(key) {
*entry += data *entry += data
} else { } else {
self.payload.insert(*key, data.clone()); self.payload.insert(key.clone(), data.clone());
} }
} }
self.dropped += &other.dropped self.dropped += &other.dropped

View File

@ -20,7 +20,7 @@ pub const NODE_ID_BYTES: usize = 16;
pub type NodeId = [u8; NODE_ID_BYTES]; pub type NodeId = [u8; NODE_ID_BYTES];
#[derive(Eq, Clone, Copy)] #[derive(Eq, Clone)]
pub struct Address { pub struct Address {
pub data: [u8; 16], pub data: [u8; 16],
pub len: u8, pub len: u8,
@ -126,7 +126,7 @@ impl FromStr for Address {
} }
} }
#[derive(PartialEq, Eq, Hash, Clone, Copy)] #[derive(PartialEq, Eq, Hash, Clone)]
pub struct Range { pub struct Range {
pub base: Address, pub base: Address,
pub prefix_len: u8, pub prefix_len: u8,
@ -135,7 +135,7 @@ pub struct Range {
pub type RangeList = SmallVec<[Range; 4]>; pub type RangeList = SmallVec<[Range; 4]>;
impl Range { impl Range {
pub fn matches(&self, addr: Address) -> bool { pub fn matches(&self, addr: &Address) -> bool {
if self.base.len != addr.len { if self.base.len != addr.len {
return false; return false;
} }
@ -190,7 +190,7 @@ impl fmt::Debug for Range {
} }
} }
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mode { pub enum Mode {
#[serde(rename = "normal")] #[serde(rename = "normal")]
Normal, Normal,

View File

@ -379,7 +379,7 @@ pub fn from_base62(data: &str) -> Result<Vec<u8>, char> {
let mut buf = Vec::with_capacity(data.len() / 2 + data.len() / 4); let mut buf = Vec::with_capacity(data.len() / 2 + data.len() / 4);
for c in data.chars() { for c in data.chars() {
let mut val = match c { let mut val = match c {
'0'..='9' => ((c as usize) % ('0' as usize)), '0'..='9' => (c as usize) % ('0' as usize),
'A'..='Z' => ((c as usize) % ('A' as usize)) + 10, 'A'..='Z' => ((c as usize) % ('A' as usize)) + 10,
'a'..='z' => ((c as usize) % ('a' as usize)) + 36, 'a'..='z' => ((c as usize) % ('a' as usize)) + 36,
_ => return Err(c), _ => return Err(c),

View File

@ -13,9 +13,14 @@ use std::{
io::{self, Cursor, Read, Write}, io::{self, Cursor, Read, Write},
net::{Ipv6Addr, SocketAddr, SocketAddrV6, TcpListener, TcpStream, UdpSocket}, net::{Ipv6Addr, SocketAddr, SocketAddrV6, TcpListener, TcpStream, UdpSocket},
os::unix::io::{AsRawFd, RawFd}, os::unix::io::{AsRawFd, RawFd},
thread, sync::Arc, thread, time::Duration,
};
use tungstenite::{
accept, connect,
protocol::WebSocket,
stream::{MaybeTlsStream, NoDelay},
Message,
}; };
use tungstenite::{connect, protocol::WebSocket, Message, accept, stream::{MaybeTlsStream, NoDelay}};
use url::Url; use url::Url;
macro_rules! io_error { macro_rules! io_error {
@ -107,10 +112,9 @@ pub fn run_proxy(listen: &str) -> Result<(), io::Error> {
Ok(()) Ok(())
} }
#[derive(Clone)]
pub struct ProxyConnection { pub struct ProxyConnection {
addr: SocketAddr, addr: SocketAddr,
socket: Arc<WebSocket<MaybeTlsStream<TcpStream>>>, socket: WebSocket<MaybeTlsStream<TcpStream>>,
} }
impl ProxyConnection { impl ProxyConnection {
@ -118,8 +122,14 @@ impl ProxyConnection {
let parsed_url = io_error!(Url::parse(url), "Invalid URL {}: {}", url)?; let parsed_url = io_error!(Url::parse(url), "Invalid URL {}: {}", url)?;
let (mut socket, _) = io_error!(connect(parsed_url), "Failed to connect to URL {}: {}", url)?; let (mut socket, _) = io_error!(connect(parsed_url), "Failed to connect to URL {}: {}", url)?;
socket.get_mut().set_nodelay(true)?; socket.get_mut().set_nodelay(true)?;
match socket.get_mut() {
&mut MaybeTlsStream::Plain(ref mut stream) => {
io_error!(stream.set_read_timeout(Some(Duration::from_secs(1))), "Failed to set read timeout: {}")?
},
_ => unimplemented!()
}
let addr = "0.0.0.0:0".parse::<SocketAddr>().unwrap(); let addr = "0.0.0.0:0".parse::<SocketAddr>().unwrap();
let mut con = ProxyConnection { addr, socket: Arc::new(socket) }; let mut con = ProxyConnection { addr, socket };
let addr_data = con.read_message()?; let addr_data = con.read_message()?;
con.addr = read_addr(Cursor::new(&addr_data))?; con.addr = read_addr(Cursor::new(&addr_data))?;
Ok(con) Ok(con)
@ -127,12 +137,9 @@ impl ProxyConnection {
fn read_message(&mut self) -> Result<Vec<u8>, io::Error> { fn read_message(&mut self) -> Result<Vec<u8>, io::Error> {
loop { loop {
unimplemented!();
/*
if let Message::Binary(data) = io_error!(self.socket.read_message(), "Failed to read from ws proxy: {}")? { if let Message::Binary(data) = io_error!(self.socket.read_message(), "Failed to read from ws proxy: {}")? {
return Ok(data); return Ok(data);
} }
*/
} }
} }
} }
@ -141,7 +148,7 @@ impl AsRawFd for ProxyConnection {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
match self.socket.get_ref() { match self.socket.get_ref() {
MaybeTlsStream::Plain(stream) => stream.as_raw_fd(), MaybeTlsStream::Plain(stream) => stream.as_raw_fd(),
_ => unimplemented!() _ => unimplemented!(),
} }
} }
} }
@ -163,14 +170,23 @@ impl Socket for ProxyConnection {
let mut msg = Vec::with_capacity(data.len() + 18); let mut msg = Vec::with_capacity(data.len() + 18);
write_addr(addr, &mut msg)?; write_addr(addr, &mut msg)?;
msg.write_all(data)?; msg.write_all(data)?;
unimplemented!();
/*
io_error!(self.socket.write_message(Message::Binary(msg)), "Failed to write to ws proxy: {}")?; io_error!(self.socket.write_message(Message::Binary(msg)), "Failed to write to ws proxy: {}")?;
Ok(data.len()) Ok(data.len())
*/
} }
fn address(&self) -> Result<SocketAddr, io::Error> { fn address(&self) -> Result<SocketAddr, io::Error> {
Ok(self.addr) Ok(self.addr)
} }
fn try_clone(&self) -> Result<Self, io::Error> {
let socket = match self.socket.get_ref() {
MaybeTlsStream::Plain(stream) => WebSocket::from_raw_socket(
MaybeTlsStream::Plain(stream.try_clone()?),
tungstenite::protocol::Role::Client,
Some(*self.socket.get_config()),
),
_ => unimplemented!(),
};
Ok(Self { addr: self.addr, socket })
}
} }