Skip to content

Commit 75de1a4

Browse files
committed
wgtypes: Add Remove flag to allowed IPs
[1] adds the WGALLOWEDIP_F_REMOVE_ME flag to WireGuard's Netlink API 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 capability was subsequently ported to wireguard-go as well. Add support for this feature to wgctrl-go, allowing clients to incrementally remove allowed IPs on a peer like so: wgtypes.Config{ Peers: []wgtypes.PeerConfig{ { PublicKey: peerKey, AllowedIPs: []wgtypes.AllowedIPConfig{ { IPNet: ip, Remove: true, }, }, }, }, } [1]: https://lore.kernel.org/netdev/[email protected]/ Signed-off-by: Jordan Rife <[email protected]>
1 parent a9ab227 commit 75de1a4

File tree

7 files changed

+76
-17
lines changed

7 files changed

+76
-17
lines changed

client_integration_test.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,18 @@ func testGet(t *testing.T, c *wgctrl.Client, d *wgtypes.Device) {
141141
}
142142
}
143143

144+
func ipsToAllowedIPConfig(ips []net.IPNet) []wgtypes.AllowedIPConfig {
145+
result := make([]wgtypes.AllowedIPConfig, len(ips))
146+
147+
for i := range ips {
148+
result[i] = wgtypes.AllowedIPConfig{
149+
IPNet: ips[i],
150+
}
151+
}
152+
153+
return result
154+
}
155+
144156
func testConfigure(t *testing.T, c *wgctrl.Client, d *wgtypes.Device) {
145157
var (
146158
port = 8888
@@ -162,7 +174,7 @@ func testConfigure(t *testing.T, c *wgctrl.Client, d *wgtypes.Device) {
162174
Peers: []wgtypes.PeerConfig{{
163175
PublicKey: peerKey,
164176
ReplaceAllowedIPs: true,
165-
AllowedIPs: ips,
177+
AllowedIPs: ipsToAllowedIPConfig(ips),
166178
}},
167179
}
168180

@@ -245,7 +257,7 @@ func testConfigureManyIPs(t *testing.T, c *wgctrl.Client, d *wgtypes.Device) {
245257
peers = append(peers, wgtypes.PeerConfig{
246258
PublicKey: wgtest.MustPublicKey(),
247259
ReplaceAllowedIPs: true,
248-
AllowedIPs: ips,
260+
AllowedIPs: ipsToAllowedIPConfig(ips),
249261
})
250262

251263
countIPs += len(ips)
@@ -295,7 +307,7 @@ func testConfigureManyPeers(t *testing.T, c *wgctrl.Client, d *wgtypes.Device) {
295307
Port: 1111,
296308
},
297309
PersistentKeepaliveInterval: &dur,
298-
AllowedIPs: ips,
310+
AllowedIPs: ipsToAllowedIPConfig(ips),
299311
})
300312
}
301313

@@ -370,7 +382,6 @@ func testConfigurePeersUpdateOnly(t *testing.T, c *wgctrl.Client, d *wgtypes.Dev
370382
t.Skip("FreeBSD kernel devices do not support UpdateOnly flag")
371383
}
372384

373-
374385
t.Fatalf("failed to configure second time on %q: %v", d.Name, err)
375386
}
376387

client_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,3 +240,7 @@ func (c *testClient) Device(name string) (*wgtypes.Device, error) {
240240
func (c *testClient) ConfigureDevice(name string, cfg wgtypes.Config) error {
241241
return c.ConfigureDeviceFunc(name, cfg)
242242
}
243+
244+
func (c *testClient) SupportsAllowedIPRemove(name string) (bool, error) {
245+
return false, nil
246+
}

internal/wgfreebsd/client_freebsd.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,10 @@ func (c *Client) ConfigureDevice(name string, cfg wgtypes.Config) error {
179179
}
180180
}
181181

182-
m := unparseConfig(cfg)
182+
m, err := unparseConfig(cfg)
183+
if err != nil {
184+
return err
185+
}
183186
mem, sz, err := nv.Marshal(m)
184187
if err != nil {
185188
return err
@@ -459,7 +462,7 @@ func parseDevice(data []byte) (*wgtypes.Device, error) {
459462
}
460463

461464
// unparsePeerConfig encodes a PeerConfig to a name-value list (nvlist).
462-
func unparsePeerConfig(cfg wgtypes.PeerConfig) nv.List {
465+
func unparsePeerConfig(cfg wgtypes.PeerConfig) (nv.List, error) {
463466
m := nv.List{}
464467

465468
m["public-key"] = cfg.PublicKey[:]
@@ -488,17 +491,21 @@ func unparsePeerConfig(cfg wgtypes.PeerConfig) nv.List {
488491
aips := []nv.List{}
489492

490493
for _, aip := range cfg.AllowedIPs {
491-
aips = append(aips, unparseAllowedIP(aip))
494+
if aip.Remove {
495+
return nv.List{}, fmt.Errorf("allowed ips remove not supported: %w", os.ErrInvalid)
496+
}
497+
498+
aips = append(aips, unparseAllowedIP(aip.IPNet))
492499
}
493500

494501
m["allowed-ips"] = aips
495502
}
496503

497-
return m
504+
return m, nil
498505
}
499506

500507
// unparseDevice encodes the device configuration as a FreeBSD name-value list (nvlist).
501-
func unparseConfig(cfg wgtypes.Config) nv.List {
508+
func unparseConfig(cfg wgtypes.Config) (nv.List, error) {
502509
m := nv.List{}
503510

504511
if v := cfg.PrivateKey; v != nil {
@@ -521,12 +528,15 @@ func unparseConfig(cfg wgtypes.Config) nv.List {
521528
peers := []nv.List{}
522529

523530
for _, p := range v {
524-
peer := unparsePeerConfig(p)
531+
peer, err := unparsePeerConfig(p)
532+
if err != nil {
533+
return nv.List{}, err
534+
}
525535
peers = append(peers, peer)
526536
}
527537

528538
m["peers"] = peers
529539
}
530540

531-
return m
541+
return m, nil
532542
}

internal/wglinux/configure_linux.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ import (
1515
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
1616
)
1717

18+
// TODO(jrife): These are placeholders. Replace these with values from the
19+
// golang.org/x/sys/unix package after it is updated for the 6.16 kernel.
20+
const (
21+
WGALLOWEDIP_A_FLAGS = 4
22+
WGALLOWEDIP_F_REMOVE_ME = 1
23+
)
24+
1825
// configAttrs creates the required encoded netlink attributes to configure
1926
// the device specified by name using the non-nil fields in cfg.
2027
func configAttrs(name string, cfg wgtypes.Config) ([]byte, error) {
@@ -101,16 +108,16 @@ func buildBatches(cfg wgtypes.Config) []wgtypes.Config {
101108
// Iterate until no more allowed IPs.
102109
var done bool
103110
for !done {
104-
var tmp []net.IPNet
111+
var tmp []wgtypes.AllowedIPConfig
105112
if len(p.AllowedIPs) < ipBatchChunk {
106113
// IPs all fit within a batch; we are done.
107-
tmp = make([]net.IPNet, len(p.AllowedIPs))
114+
tmp = make([]wgtypes.AllowedIPConfig, len(p.AllowedIPs))
108115
copy(tmp, p.AllowedIPs)
109116
done = true
110117
} else {
111118
// IPs are larger than a single batch, copy a batch out and
112119
// advance the cursor.
113-
tmp = make([]net.IPNet, ipBatchChunk)
120+
tmp = make([]wgtypes.AllowedIPConfig, ipBatchChunk)
114121
copy(tmp, p.AllowedIPs[:ipBatchChunk])
115122

116123
p.AllowedIPs = p.AllowedIPs[ipBatchChunk:]
@@ -247,7 +254,7 @@ func encodeSockaddr(endpoint net.UDPAddr) func() ([]byte, error) {
247254
}
248255

249256
// encodeAllowedIPs returns a function to encode allowed IP nested attributes.
250-
func encodeAllowedIPs(ipns []net.IPNet) func(ae *netlink.AttributeEncoder) error {
257+
func encodeAllowedIPs(ipns []wgtypes.AllowedIPConfig) func(ae *netlink.AttributeEncoder) error {
251258
return func(ae *netlink.AttributeEncoder) error {
252259
for i, ipn := range ipns {
253260
if !isValidIP(ipn.IP) {
@@ -268,6 +275,9 @@ func encodeAllowedIPs(ipns []net.IPNet) func(ae *netlink.AttributeEncoder) error
268275

269276
ones, _ := ipn.Mask.Size()
270277
nae.Uint8(unix.WGALLOWEDIP_A_CIDR_MASK, uint8(ones))
278+
if ipn.Remove {
279+
nae.Uint32(WGALLOWEDIP_A_FLAGS, WGALLOWEDIP_F_REMOVE_ME)
280+
}
271281
return nil
272282
})
273283
}

internal/wguser/configure.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,20 @@ func writeConfig(w io.Writer, cfg wgtypes.Config) {
9595
}
9696

9797
for _, ip := range p.AllowedIPs {
98-
fmt.Fprintf(w, "allowed_ip=%s\n", ip.String())
98+
fmt.Fprintf(w, "allowed_ip=%s\n", aipStr(ip))
9999
}
100100
}
101101
}
102102

103+
func aipStr(aip wgtypes.AllowedIPConfig) string {
104+
s := aip.String()
105+
if aip.Remove {
106+
s = "-" + s
107+
}
108+
109+
return s
110+
}
111+
103112
// hexKey encodes a wgtypes.Key into a hexadecimal string.
104113
func hexKey(k wgtypes.Key) string {
105114
return hex.EncodeToString(k[:])

internal/wgwindows/client_windows.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package wgwindows
22

33
import (
4+
"fmt"
45
"net"
56
"os"
67
"time"
@@ -284,6 +285,10 @@ func (c *Client) ConfigureDevice(name string, cfg wgtypes.Config) error {
284285
}
285286
b.AppendPeer(peer)
286287
for j := range cfg.Peers[i].AllowedIPs {
288+
if cfg.Peers[i].AllowedIPs[j].Remove {
289+
return fmt.Errorf("allowed ips remove not supported: %w", os.ErrInvalid)
290+
}
291+
287292
var family ioctl.AddressFamily
288293
var ip net.IP
289294
if ip = cfg.Peers[i].AllowedIPs[j].IP.To4(); ip != nil {

wgtypes/types.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,5 +272,15 @@ type PeerConfig struct {
272272

273273
// AllowedIPs specifies a list of allowed IP addresses in CIDR notation
274274
// for this peer.
275-
AllowedIPs []net.IPNet
275+
AllowedIPs []AllowedIPConfig
276+
}
277+
278+
// An AllowedIPConfig contains an allowed IP address in CIDR notation and a flag
279+
// indicating whether to add/remove this allowed IP to/from the peer.
280+
type AllowedIPConfig struct {
281+
net.IPNet
282+
283+
// Remove specifies whether or not to remove this allowed IP from this
284+
// peer.
285+
Remove bool
276286
}

0 commit comments

Comments
 (0)