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