The current netlink API for WireGuard does not directly support removal of allowed ips from a peer. A user can remove an allowed ip from a peer in one of two ways: 1. By using the WGPEER_F_REPLACE_ALLOWEDIPS flag and providing a new list of allowed ips which omits the allowed ip that is to be removed. 2. By reassigning an allowed ip to a "dummy" peer then removing that peer with WGPEER_F_REMOVE_ME. With the first approach, the driver completely rebuilds the allowed ip list for a peer. If my current configuration is such that a peer has allowed ips 192.168.0.2 and 192.168.0.3 and I want to remove 192.168.0.2 the actual transition looks like this. [192.168.0.2, 192.168.0.3] <-- Initial state [] <-- Step 1: Allowed ips removed for peer [192.168.0.3] <-- Step 2: Allowed ips added back for peer This is true even if the allowed ip list is small and the update does not need to be batched into multiple WG_CMD_SET_DEVICE requests, as the removal and subsequent addition of ips is non-atomic within a single request. Consequently, wg_allowedips_lookup_dst and wg_allowedips_lookup_src may return NULL while reconfiguring a peer even for packets bound for ips a user did not intend to remove leading to unintended interruptions in connectivity. This presents in userspace as failed calls to sendto and sendmsg for UDP sockets. In my case, I ran netperf while repeatedly reconfiguring the allowed ips for a peer with wg. /usr/local/bin/netperf -H 10.102.73.72 -l 10m -t UDP_STREAM -- -R 1 -m 1024 send_data: data send error: No route to host (errno 113) netperf: send_omni: send_data failed: No route to host While this may not be of particular concern for environments where peers and allowed ips are mostly static, systems like Cilium manage peers and allowed ips in a dynamic environment where peers (i.e. Kubernetes nodes) and allowed ips (i.e. pods running on those nodes) can frequently change making WGPEER_F_REPLACE_ALLOWEDIPS problematic. The second approach avoids any possible connectivity interruptions but is hacky and less direct, requiring the creation of a temporary peer just to dispose of an allowed ip. Introduce a new flag called WGALLOWEDIP_F_REMOVE_ME which in the same way that WGPEER_F_REMOVE_ME allows a user to remove a single peer from a WireGuard device's configuration allows a user to remove an ip from a peer's set of allowed ips. This enables incremental updates to a device's configuration without any connectivity blips or messy workarounds. A corresponding patch for wg extends the existing `wg set` interface to leverage this feature. $ wg set wg0 peer <PUBKEY> allowed-ips +192.168.88.0/24,-192.168.0.1/32 When '+' or '-' is prepended to any ip in the list, wg clears WGPEER_F_REPLACE_ALLOWEDIPS and sets the WGALLOWEDIP_F_REMOVE_ME flag on any ip prefixed with '-'.
Showing the changes to include/uapi/linux (1 of 7 files). Show all
Unified · Split
| @@ -101,6 +101,10 @@ · 100 unmodified lines | ||
| 101 | 101 | * WGALLOWEDIP_A_FAMILY: NLA_U16 |
| 102 | 102 | * WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr |
| 103 | 103 | * WGALLOWEDIP_A_CIDR_MASK: NLA_U8 |
| 104 | + * WGALLOWEDIP_A_FLAGS: NLA_U32, WGALLOWEDIP_F_REMOVE_ME if | |
| 105 | + * the specified IP should be removed; | |
| 106 | + * otherwise, this IP will be added if | |
| 107 | + * it is not already present. | |
| 104 | 108 | * 0: NLA_NESTED |
| 105 | 109 | * ... |
| 106 | 110 | * 0: NLA_NESTED |
| @@ -184,11 +188,16 @@ enum wgpeer_attribute { · 77 unmodified lines | ||
| 184 | 188 | }; |
| 185 | 189 | #define WGPEER_A_MAX (__WGPEER_A_LAST - 1) |
| 186 | 190 | |
| 191 | +enum wgallowedip_flag { | |
| 192 | + WGALLOWEDIP_F_REMOVE_ME = 1U << 0, | |
| 193 | + __WGALLOWEDIP_F_ALL = WGALLOWEDIP_F_REMOVE_ME | |
| 194 | +}; | |
| 187 | 195 | enum wgallowedip_attribute { |
| 188 | 196 | WGALLOWEDIP_A_UNSPEC, |
| 189 | 197 | WGALLOWEDIP_A_FAMILY, |
| 190 | 198 | WGALLOWEDIP_A_IPADDR, |
| 191 | 199 | WGALLOWEDIP_A_CIDR_MASK, |
| 200 | + WGALLOWEDIP_A_FLAGS, | |
| 192 | 201 | __WGALLOWEDIP_A_LAST |
| 193 | 202 | }; |
| 194 | 203 | #define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1) |