@@ -163,87 +163,131 @@ pub(crate) fn clear_dns(ifname: &str) -> Result<(), WireguardInterfaceError> {
163163#[ cfg( target_os = "linux" ) ]
164164const DEFAULT_FWMARK_TABLE : u32 = 51820 ;
165165
166- /// Helper function to add routing.
166+ #[ cfg( target_os = "linux" ) ]
167+ fn setup_default_route (
168+ ifname : & str ,
169+ addr : & crate :: IpAddrMask ,
170+ ) -> Result < ( ) , WireguardInterfaceError > {
171+ debug ! ( "Found default route in AllowedIPs: {addr:?}" ) ;
172+ let is_ipv6 = addr. ip . is_ipv6 ( ) ;
173+ let proto = if is_ipv6 { "-6" } else { "-4" } ;
174+ debug ! ( "Using the following IP version: {proto}" ) ;
175+
176+ debug ! ( "Getting current host configuration for interface {ifname}" ) ;
177+ let mut host = netlink:: get_host ( ifname) ?;
178+ debug ! ( "Host configuration read for interface {ifname}" ) ;
179+ trace ! ( "Current host: {host:?}" ) ;
180+
181+ debug ! ( "Choosing fwmark for marking WireGuard traffic" ) ;
182+ let fwmark = match host. fwmark {
183+ Some ( fwmark) if fwmark != 0 => fwmark,
184+ Some ( _) | None => {
185+ let mut table = DEFAULT_FWMARK_TABLE ;
186+ loop {
187+ let output = Command :: new ( "ip" )
188+ . args ( [ proto, "route" , "show" , "table" , & table. to_string ( ) ] )
189+ . output ( ) ?;
190+ if output. stdout . is_empty ( ) {
191+ host. fwmark = Some ( table) ;
192+ netlink:: set_host ( ifname, & host) ?;
193+ debug ! ( "Assigned fwmark: {table}" ) ;
194+ break ;
195+ }
196+ table += 1 ;
197+ }
198+ table
199+ }
200+ } ;
201+ debug ! ( "Using the following fwmark for marking WireGuard traffic: {fwmark}" ) ;
202+
203+ // Add routes and table rules
204+ debug ! ( "Adding default route: {addr}" ) ;
205+ netlink:: add_route ( ifname, addr, Some ( fwmark) ) ?;
206+ debug ! ( "Default route added successfully" ) ;
207+ debug ! ( "Adding fwmark rule for the WireGuard interface to prevent routing loops" ) ;
208+ netlink:: add_fwmark_rule ( addr, fwmark) ?;
209+ debug ! ( "Fwmark rule added successfully" ) ;
210+
211+ debug ! ( "Adding rule for main table to suppress current default gateway" ) ;
212+ netlink:: add_main_table_rule ( addr, 0 ) ?;
213+ debug ! ( "Main table rule added successfully" ) ;
214+
215+ if !is_ipv6 {
216+ debug ! ( "Setting net.ipv4.conf.all.src_valid_mark=1" ) ;
217+ OpenOptions :: new ( )
218+ . write ( true )
219+ . open ( "/proc/sys/net/ipv4/conf/all/src_valid_mark" ) ?
220+ . write_all ( b"1" ) ?;
221+ debug ! ( "net.ipv4.conf.all.src_valid_mark=1 set successfully" ) ;
222+ }
223+ Ok ( ( ) )
224+ }
225+
226+ /// Adds routing entries for allowed IPs of WireGuard peers on a Linux system.
227+ ///
228+ /// Iterates over the provided list of peers and installs routing rules based on their
229+ /// allowed IP addresses. It distinguishes between IPv4 and IPv6 addresses, and handles
230+ /// default routes (0.0.0.0/0 or ::/0) separately. If a default route is present, it
231+ /// takes precedence and all specific routes of that IP version are skipped.
232+ ///
233+ /// # Arguments
234+ /// * `peers` - A slice of `Peer` objects containing allowed IP configurations.
235+ /// * `ifname` - The name of the WireGuard interface to which routes should be applied.
236+ ///
237+ /// # Returns
238+ /// * `Ok(())` on success.
239+ /// * `Err(WireguardInterfaceError)` if any route setup fails.
240+ ///
167241#[ cfg( target_os = "linux" ) ]
168242pub ( crate ) fn add_peer_routing (
169243 peers : & [ Peer ] ,
170244 ifname : & str ,
171245) -> Result < ( ) , WireguardInterfaceError > {
172246 debug ! ( "Adding peer routing for interface: {ifname}" ) ;
173247
174- let mut unique_allowed_ips = HashSet :: new ( ) ;
175- let mut default_route = None ;
248+ // (ipv4, ipv6)
249+ let mut allowed_ips = ( HashSet :: new ( ) , HashSet :: new ( ) ) ;
250+ let mut default_routes = ( None , None ) ;
251+
252+ // Gather allowed IPs and default routes
176253 for peer in peers {
177254 for addr in & peer. allowed_ips {
178255 if addr. ip . is_unspecified ( ) {
179- // Handle default route
180- default_route = Some ( addr) ;
181- break ;
256+ // Default route - store for later
257+ if addr. ip . is_ipv4 ( ) {
258+ default_routes. 0 = Some ( addr) ;
259+ } else {
260+ default_routes. 1 = Some ( addr) ;
261+ }
262+ continue ;
263+ }
264+ // Regular route - add to set
265+ if addr. ip . is_ipv4 ( ) {
266+ allowed_ips. 0 . insert ( addr) ;
267+ } else {
268+ allowed_ips. 1 . insert ( addr) ;
182269 }
183- unique_allowed_ips. insert ( addr) ;
184270 }
185271 }
186- debug ! ( "Allowed IPs that will be used during the peer routing setup: {unique_allowed_ips:?}" ) ;
187-
188- // If there is default route skip adding other routes.
189- if let Some ( default_route) = default_route {
190- debug ! ( "Found default route in AllowedIPs: {default_route:?}" ) ;
191- let is_ipv6 = default_route. ip . is_ipv6 ( ) ;
192- let proto = if is_ipv6 { "-6" } else { "-4" } ;
193- debug ! ( "Using the following IP version: {proto}" ) ;
194-
195- debug ! ( "Getting current host configuration for interface {ifname}" ) ;
196- let mut host = netlink:: get_host ( ifname) ?;
197- debug ! ( "Host configuration read for interface {ifname}" ) ;
198- trace ! ( "Current host: {host:?}" ) ;
199-
200- debug ! ( "Choosing fwmark for marking WireGuard traffic" ) ;
201- let fwmark = match host. fwmark {
202- Some ( fwmark) if fwmark != 0 => fwmark,
203- Some ( _) | None => {
204- let mut table = DEFAULT_FWMARK_TABLE ;
205- loop {
206- let output = Command :: new ( "ip" )
207- . args ( [ proto, "route" , "show" , "table" , & table. to_string ( ) ] )
208- . output ( ) ?;
209- if output. stdout . is_empty ( ) {
210- host. fwmark = Some ( table) ;
211- netlink:: set_host ( ifname, & host) ?;
212- debug ! ( "Assigned fwmark: {table}" ) ;
213- break ;
214- }
215- table += 1 ;
216- }
217- table
218- }
219- } ;
220- debug ! ( "Using the following fwmark for marking WireGuard traffic: {fwmark}" ) ;
221-
222- // Add routes and table rules
223- debug ! ( "Adding default route: {default_route}" ) ;
224- netlink:: add_route ( ifname, default_route, Some ( fwmark) ) ?;
225- debug ! ( "Default route added successfully" ) ;
226- debug ! ( "Adding fwmark rule for the WireGuard interface to prevent routing loops" ) ;
227- netlink:: add_fwmark_rule ( default_route, fwmark) ?;
228- debug ! ( "Fwmark rule added successfully" ) ;
229-
230- debug ! ( "Adding rule for main table to suppress current default gateway" ) ;
231- netlink:: add_main_table_rule ( default_route, 0 ) ?;
232- debug ! ( "Main table rule added successfully" ) ;
233-
234- if !is_ipv6 {
235- debug ! ( "Setting net.ipv4.conf.all.src_valid_mark=1" ) ;
236- OpenOptions :: new ( )
237- . write ( true )
238- . open ( "/proc/sys/net/ipv4/conf/all/src_valid_mark" ) ?
239- . write_all ( b"1" ) ?;
240- debug ! ( "net.ipv4.conf.all.src_valid_mark=1 set successfully" ) ;
272+ debug ! ( "Allowed IPs that will be used during the peer routing setup: {allowed_ips:?}" ) ;
273+
274+ // Add default route if present, otherwise setup individual allowed IP routes
275+ if let Some ( default_route) = default_routes. 0 {
276+ setup_default_route ( ifname, default_route) ?;
277+ } else {
278+ for allowed_ip in allowed_ips. 0 {
279+ debug ! ( "Adding a route for allowed IPv4: {allowed_ip}" ) ;
280+ netlink:: add_route ( ifname, allowed_ip, None ) ?;
281+ debug ! ( "Route added for allowed IPv4: {allowed_ip}" ) ;
241282 }
283+ }
284+ if let Some ( default_route) = default_routes. 1 {
285+ setup_default_route ( ifname, default_route) ?;
242286 } else {
243- for allowed_ip in unique_allowed_ips {
244- debug ! ( "Adding a route for allowed IP : {allowed_ip}" ) ;
287+ for allowed_ip in allowed_ips . 1 {
288+ debug ! ( "Adding a route for allowed IPv6 : {allowed_ip}" ) ;
245289 netlink:: add_route ( ifname, allowed_ip, None ) ?;
246- debug ! ( "Route added for allowed IP : {allowed_ip}" ) ;
290+ debug ! ( "Route added for allowed IPv6 : {allowed_ip}" ) ;
247291 }
248292 }
249293 debug ! ( "Peers routing added successfully" ) ;
0 commit comments