@@ -16,24 +16,46 @@ use tokio::{
1616use tracing:: { debug, error, event, info, instrument, warn, Level } ;
1717
1818use crate :: {
19+ config:: ConfigInner ,
1920 map_debug, map_error, map_warn,
2021 utils:: { self , defer_async, CsvIter , FutureAttach } ,
2122 Config , SilentResult , State ,
2223} ;
2324
2425const WIREGUARD_KEEPALIVE_LEN : usize = 32 ;
2526
26- pub async fn verify ( ) -> SilentResult < ( ) > {
27+ pub fn wg_cmd ( wg_type : & str ) -> Option < & ' static str > {
28+ match wg_type {
29+ "wireguard" => Some ( "wg" ) ,
30+ "amneziawg" => Some ( "awg" ) ,
31+ _ => None ,
32+ }
33+ }
34+
35+ pub async fn verify ( config : & ConfigInner ) -> SilentResult < ( ) > {
2736 let mut res = Ok ( ( ) ) ;
2837
29- let spec = [
38+ let mut spec = vec ! [
3039 ( "ip link" , "iproute2" ) ,
3140 ( "iptables --help" , "iptables" ) ,
3241 ( "ip6tables --help" , "iptables" ) ,
33- ( "wg help" , "wireguard-tools" ) ,
3442 ( "conntrack help" , "conntrack-tools" ) ,
3543 ] ;
3644
45+ for wg_type in Iterator :: chain (
46+ config. wireguard_types . iter ( ) ,
47+ config. wireguard_device_params . keys ( ) ,
48+ ) {
49+ match wg_type. as_str ( ) {
50+ "wireguard" => spec. push ( ( "wg help" , "wireguard-tools" ) ) ,
51+ "amneziawg" => spec. push ( ( "awg help" , "amneziawg-tools" ) ) ,
52+ _ => {
53+ error ! ( "Wireguard type {wg_type:?} is not supported" ) ;
54+ return Err ( ( ) ) ;
55+ }
56+ }
57+ }
58+
3759 for ( cmd, pkg) in spec {
3860 if run ( cmd) . await . is_err ( ) {
3961 error ! (
@@ -52,15 +74,30 @@ async fn run(line: impl AsRef<str>) -> SilentResult<Vec<u8>> {
5274}
5375
5476async fn run_stdin ( line : impl AsRef < str > , stdin : impl AsRef < [ u8 ] > ) -> SilentResult < Vec < u8 > > {
55- let line = line. as_ref ( ) ;
77+ run_stdin_args ( line. as_ref ( ) . split ( " " ) , stdin) . await
78+ }
79+
80+ async fn run_stdin_args < S : AsRef < str > > (
81+ args : impl IntoIterator < Item = S > ,
82+ stdin : impl AsRef < [ u8 ] > ,
83+ ) -> SilentResult < Vec < u8 > > {
5684 let stdin = stdin. as_ref ( ) ;
5785
58- debug ! ( "Running {line:?}" ) ;
86+ let mut args = args. into_iter ( ) ;
87+ let command = args. next ( ) . unwrap ( ) ;
88+ let command = command. as_ref ( ) ;
5989
60- let ( command, args) = line. split_once ( " " ) . unwrap ( ) ;
90+ let mut line = command. to_string ( ) ;
91+ let mut cmd = tokio:: process:: Command :: new ( command) ;
92+ for arg in args {
93+ cmd. arg ( arg. as_ref ( ) ) ;
94+ line. push_str ( " " ) ;
95+ line. push_str ( arg. as_ref ( ) ) ;
96+ }
97+
98+ debug ! ( "Running {line:?}" ) ;
6199
62- let mut proc = tokio:: process:: Command :: new ( command)
63- . args ( args. split ( " " ) )
100+ let mut proc = cmd
64101 . stdin ( if !stdin. is_empty ( ) {
65102 Stdio :: piped ( )
66103 } else {
@@ -118,8 +155,8 @@ struct WgLink {
118155 transfer_tx : u64 ,
119156}
120157
121- async fn wg_dump ( dev : & str ) -> SilentResult < WgDev > {
122- let lines = run ( format ! ( "wg show {dev} dump" ) ) . await ?;
158+ async fn wg_dump ( wg_cmd : & str , dev : & str ) -> SilentResult < WgDev > {
159+ let lines = run ( format ! ( "{wg_cmd} show {dev} dump" ) ) . await ?;
123160 let Some ( Ok ( line) ) = lines. lines ( ) . next ( ) else {
124161 warn ! ( "Can't get wireguard device" ) ;
125162 return Err ( ( ) ) ;
@@ -134,8 +171,8 @@ async fn wg_dump(dev: &str) -> SilentResult<WgDev> {
134171 Ok ( WgDev { listen_port } )
135172}
136173
137- async fn wg_dump_peer ( dev : & str , pub_key : & str ) -> SilentResult < WgLink > {
138- let lines = run ( format ! ( "wg show {dev} dump" ) ) . await ?;
174+ async fn wg_dump_peer ( wg_cmd : & str , dev : & str , pub_key : & str ) -> SilentResult < WgLink > {
175+ let lines = run ( format ! ( "{wg_cmd} show {dev} dump" ) ) . await ?;
139176 let Some ( line) = lines
140177 . lines ( )
141178 . skip ( 1 )
@@ -164,13 +201,14 @@ async fn wg_dump_peer(dev: &str, pub_key: &str) -> SilentResult<WgLink> {
164201 } )
165202}
166203
167- pub async fn wg_genkeys ( ) -> SilentResult < ( String , String ) > {
204+ pub async fn wg_genkeys ( config : & Config ) -> SilentResult < ( String , String ) > {
205+ let wg_cmd = wg_cmd ( config. wireguard_types . first ( ) . unwrap ( ) ) . unwrap ( ) ;
168206 let trim = |key : Vec < u8 > | {
169207 String :: from_utf8 ( key. trim_ascii ( ) . into ( ) ) . map_err ( map_error ! ( "Invalid key encoding" ) )
170208 } ;
171- let priv_key = trim ( run ( "wg genkey") . await ?) ?;
209+ let priv_key = trim ( run ( format ! ( "{wg_cmd} genkey") ) . await ?) ?;
172210
173- let pub_key = trim ( run_stdin ( "wg pubkey", & priv_key) . await ?) ?;
211+ let pub_key = trim ( run_stdin ( format ! ( "{wg_cmd} pubkey") , & priv_key) . await ?) ?;
174212
175213 Ok ( ( pub_key, priv_key) )
176214}
@@ -324,6 +362,8 @@ pub struct WgBridgeParams {
324362 pub monitor_address : Ipv6Addr ,
325363 pub inet_socket : UdpSocket ,
326364 pub yggdrasil_socket : UdpSocket ,
365+ pub shared_secret : u64 ,
366+ pub wg_type : String ,
327367}
328368
329369#[ instrument( parent = None , name = "Wireguard bridge " , skip_all,
@@ -359,6 +399,9 @@ pub async fn start_bridge(
359399
360400 let remote = & params. peer_addr ;
361401
402+ let wg_type = & params. wg_type ;
403+ let wg_cmd = wg_cmd ( wg_type) . unwrap ( ) ;
404+
362405 // Remove previous association of traversal socket
363406 flush_firewall ( * remote) . await ;
364407 let _guard = defer_async ( flush_firewall ( * remote) ) ;
@@ -369,20 +412,50 @@ pub async fn start_bridge(
369412
370413 // TODO: Multiplex on a single wireguard device
371414 // TODO: add a queue?
372- run ( format ! ( "ip link add dev {dev} type wireguard " ) ) . await ?;
415+ run ( format ! ( "ip link add dev {dev} type {wg_type} " ) ) . await ?;
373416 // Associated routing entries are removed automatically
374417 let _guard =
375418 defer_async ( run ( format ! ( "ip link del dev {dev}" ) ) . attach ( cancellation. get_active ( ) ) ) ;
376419
377420 run ( format ! ( "ip link set dev {dev} up" ) ) . await ?;
378421
379- let wg_port = wg_dump ( dev) . await ?. listen_port ;
422+ let wg_port = wg_dump ( wg_cmd, dev) . await ?. listen_port ;
423+
424+ let mut wg_dev_args: Vec < _ > =
425+ format ! ( "{wg_cmd} set {dev} listen-port {wg_port} private-key /dev/stdin" )
426+ . split ( " " )
427+ . map ( |s| s. to_string ( ) )
428+ . collect ( ) ;
429+
430+ if wg_type == "amneziawg" {
431+ use rand:: { Rng , SeedableRng } ;
432+ let mut seed = params. shared_secret ;
433+ let mut rand_param = |name : & str , range| {
434+ seed = seed. wrapping_add ( 1 ) ;
435+ let value = rand_chacha:: ChaCha12Rng :: seed_from_u64 ( seed) . random_range ( range) ;
436+ wg_dev_args. push ( name. into ( ) ) ;
437+ wg_dev_args. push ( format ! ( "{value}" ) ) ;
438+ } ;
439+
440+ rand_param ( "jc" , 4 ..=12 ) ;
441+ rand_param ( "jmin" , 8 ..=8 ) ;
442+ rand_param ( "jmax" , 80 ..=80 ) ;
443+ for s in [ "s1" , "s2" ] {
444+ rand_param ( s, 15 ..=150 ) ;
445+ }
446+ for h in [ "h1" , "h2" , "h3" , "h4" ] {
447+ rand_param ( h, 5 ..=2147483647 ) ;
448+ }
449+ }
380450
381- run_stdin (
382- format ! ( "wg set {dev} listen-port {wg_port} private-key /dev/stdin" ) ,
383- & self_priv,
384- )
385- . await ?;
451+ if let Some ( params) = config. wireguard_device_params . get ( wg_type) {
452+ for ( k, v) in params {
453+ wg_dev_args. push ( k. clone ( ) ) ;
454+ wg_dev_args. push ( v. clone ( ) ) ;
455+ }
456+ }
457+
458+ run_stdin_args ( wg_dev_args, & self_priv) . await ?;
386459
387460 setup_firewall ( "-I" , wg_port, & params) . await ;
388461 let _guard =
@@ -394,7 +467,7 @@ pub async fn start_bridge(
394467 . map_err ( map_warn ! ( "Can't bind socket to device" ) ) ;
395468
396469 run ( format ! (
397- "wg set {dev} peer {remote_pub} persistent-keepalive 20 endpoint {remote} allowed-ips {remote_ygg_addr}/128"
470+ "{wg_cmd} set {dev} peer {remote_pub} persistent-keepalive 20 endpoint {remote} allowed-ips {remote_ygg_addr}/128"
398471 ) )
399472 . await ?;
400473
@@ -488,7 +561,7 @@ pub async fn start_bridge(
488561 } ,
489562 }
490563
491- let dump = wg_dump_peer ( dev, & params. peer_pub ) . await ?;
564+ let dump = wg_dump_peer ( wg_cmd , dev, & params. peer_pub ) . await ?;
492565
493566 if !started {
494567 if dump. latest_handshake != 0 {
0 commit comments