Skip to content

Commit cf3b05b

Browse files
committed
internal/wgopenbsd: implement device configuration
Signed-off-by: Alex Lyon <[email protected]>
1 parent a9ab227 commit cf3b05b

File tree

7 files changed

+214
-38
lines changed

7 files changed

+214
-38
lines changed

internal/wgopenbsd/client_openbsd.go

Lines changed: 138 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type Client struct {
3030
// during tests.
3131
close func() error
3232
ioctlIfgroupreq func(ifg *wgh.Ifgroupreq) error
33-
ioctlWGDataIO func(data *wgh.WGDataIO) error
33+
ioctlWGDataIO func(req uint, data *wgh.WGDataIO) error
3434
}
3535

3636
// New creates a new Client and returns whether or not the ioctl interface
@@ -122,7 +122,7 @@ func (c *Client) Device(name string) (*wgtypes.Device, error) {
122122
// if it proves to be a concern.
123123
var mem []byte
124124
for {
125-
if err := c.ioctlWGDataIO(&data); err != nil {
125+
if err := c.ioctlWGDataIO(wgh.SIOCGWG, &data); err != nil {
126126
// ioctl functions always return a wrapped unix.Errno value.
127127
// Conform to the wgctrl contract by unwrapping some values:
128128
// ENXIO: "no such device": (no such WireGuard device)
@@ -223,14 +223,142 @@ func parseDevice(name string, ifio *wgh.WGInterfaceIO) (*wgtypes.Device, error)
223223

224224
// ConfigureDevice implements wginternal.Client.
225225
func (c *Client) ConfigureDevice(name string, cfg wgtypes.Config) error {
226-
// Currently read-only: we must determine if a device belongs to this driver,
227-
// and if it does, return a sentinel so integration tests that configure a
228-
// device can be skipped.
229-
if _, err := c.Device(name); err != nil {
226+
dname, err := deviceName(name)
227+
if err != nil {
230228
return err
231229
}
232230

233-
return wginternal.ErrReadOnly
231+
var port uint16
232+
var public wgtypes.Key
233+
var private wgtypes.Key
234+
var rtable int32
235+
236+
var flags uint8
237+
if cfg.ReplacePeers {
238+
flags |= wgh.WG_INTERFACE_REPLACE_PEERS
239+
}
240+
if cfg.FirewallMark != nil {
241+
flags |= wgh.WG_INTERFACE_HAS_RTABLE
242+
rtable = int32(*cfg.FirewallMark)
243+
}
244+
if cfg.ListenPort != nil {
245+
flags |= wgh.WG_INTERFACE_HAS_PORT
246+
port = uint16(*cfg.ListenPort)
247+
}
248+
if cfg.PrivateKey != nil {
249+
flags |= wgh.WG_INTERFACE_HAS_PRIVATE
250+
private = *cfg.PrivateKey
251+
}
252+
253+
iface := wgh.WGInterfaceIO{
254+
Peers_count: wgh.SizeT(len(cfg.Peers)),
255+
Port: port,
256+
Rtable: rtable,
257+
Public: public,
258+
Private: private,
259+
Flags: flags,
260+
}
261+
262+
aipCount := 0
263+
for _, peer := range cfg.Peers {
264+
aipCount += len(peer.AllowedIPs)
265+
}
266+
267+
ioctlBuf := make([]byte, wgh.SizeofWGInterfaceIO+len(cfg.Peers)*wgh.SizeofWGPeerIO+aipCount*wgh.SizeofWGAIPIO)
268+
copy(ioctlBuf, (*(*[wgh.SizeofWGInterfaceIO]byte)(unsafe.Pointer(&iface)))[:])
269+
270+
bufIdx := wgh.SizeofWGInterfaceIO
271+
272+
for _, peer := range cfg.Peers {
273+
var rawPeer wgh.WGPeerIO
274+
275+
rawPeer.Aips_count = wgh.SizeT(len(peer.AllowedIPs))
276+
277+
if peer.Endpoint != nil {
278+
if peer.Endpoint.IP.To4() != nil {
279+
rawAddr := unix.RawSockaddrInet4{
280+
Port: uint16(bePort(uint16(peer.Endpoint.Port))),
281+
Family: unix.AF_INET,
282+
Len: uint8(unsafe.Sizeof(unix.RawSockaddrInet4{})),
283+
}
284+
copy(rawAddr.Addr[:], peer.Endpoint.IP)
285+
copy(rawPeer.Endpoint[:], (*(*[unsafe.Sizeof(rawAddr)]byte)(unsafe.Pointer(&rawAddr)))[:])
286+
} else {
287+
rawAddr := unix.RawSockaddrInet6{
288+
Port: uint16(bePort(uint16(peer.Endpoint.Port))),
289+
Family: unix.AF_INET6,
290+
Len: uint8(unsafe.Sizeof(unix.RawSockaddrInet6{})),
291+
}
292+
copy(rawAddr.Addr[:], peer.Endpoint.IP)
293+
copy(rawPeer.Endpoint[:], (*(*[unsafe.Sizeof(rawAddr)]byte)(unsafe.Pointer(&rawAddr)))[:])
294+
}
295+
rawPeer.Flags |= wgh.WG_PEER_HAS_ENDPOINT
296+
}
297+
if peer.PersistentKeepaliveInterval != nil {
298+
rawPeer.Pka = uint16(*peer.PersistentKeepaliveInterval)
299+
rawPeer.Flags |= wgh.WG_PEER_HAS_PKA
300+
}
301+
if peer.PresharedKey != nil {
302+
rawPeer.Psk = *peer.PresharedKey
303+
rawPeer.Flags |= wgh.WG_PEER_HAS_PSK
304+
}
305+
rawPeer.Public = peer.PublicKey
306+
rawPeer.Flags |= wgh.WG_PEER_HAS_PUBLIC
307+
308+
if peer.Remove {
309+
rawPeer.Flags |= wgh.WG_PEER_REMOVE
310+
}
311+
if peer.ReplaceAllowedIPs {
312+
rawPeer.Flags |= wgh.WG_PEER_REPLACE_AIPS
313+
}
314+
if peer.UpdateOnly {
315+
// FIXME: not positive this flag is *only* update
316+
rawPeer.Flags |= wgh.WG_PEER_UPDATE
317+
}
318+
319+
rawPeer.Protocol_version = 1
320+
321+
copy(ioctlBuf[bufIdx:], (*(*[wgh.SizeofWGPeerIO]byte)(unsafe.Pointer(&rawPeer)))[:])
322+
bufIdx += wgh.SizeofWGPeerIO
323+
324+
for _, aip := range peer.AllowedIPs {
325+
var rawAip wgh.WGAIPIO
326+
if v4 := aip.IP.To4(); v4 != nil {
327+
rawAip.Af = unix.AF_INET
328+
copy(rawAip.Addr[:net.IPv4len], v4)
329+
} else {
330+
rawAip.Af = unix.AF_INET6
331+
rawAip.Addr = [net.IPv6len]byte(aip.IP)
332+
}
333+
ones, _ := aip.Mask.Size()
334+
rawAip.Cidr = int32(ones)
335+
336+
copy(ioctlBuf[bufIdx:], (*(*[wgh.SizeofWGAIPIO]byte)(unsafe.Pointer(&rawAip)))[:])
337+
bufIdx += wgh.SizeofWGAIPIO
338+
}
339+
340+
}
341+
342+
data := wgh.WGDataIO{
343+
Name: dname,
344+
Size: wgh.SizeT(len(ioctlBuf)),
345+
Interface: (*wgh.WGInterfaceIO)(unsafe.Pointer(&ioctlBuf[0])),
346+
}
347+
if err := c.ioctlWGDataIO(wgh.SIOCSWG, &data); err != nil {
348+
// ioctl functions always return a wrapped unix.Errno value.
349+
// Conform to the wgctrl contract by unwrapping some values:
350+
// ENXIO: "no such device": (no such WireGuard device)
351+
// EINVAL: "inappropriate ioctl for device" (device is not a
352+
// WireGuard device)
353+
switch err.(*os.SyscallError).Err {
354+
case unix.ENXIO, unix.EINVAL:
355+
return os.ErrNotExist
356+
default:
357+
return err
358+
}
359+
}
360+
361+
return nil
234362
}
235363

236364
// deviceName converts an interface name string to the format required to pass
@@ -350,9 +478,9 @@ func ioctlIfgroupreq(fd int) func(*wgh.Ifgroupreq) error {
350478

351479
// ioctlWGDataIO returns a function which performs the appropriate ioctl on
352480
// fd to issue a WireGuard data I/O.
353-
func ioctlWGDataIO(fd int) func(*wgh.WGDataIO) error {
354-
return func(data *wgh.WGDataIO) error {
355-
return ioctl(fd, wgh.SIOCGWG, unsafe.Pointer(data))
481+
func ioctlWGDataIO(fd int) func(uint, *wgh.WGDataIO) error {
482+
return func(req uint, data *wgh.WGDataIO) error {
483+
return ioctl(fd, req, unsafe.Pointer(data))
356484
}
357485
}
358486

internal/wgopenbsd/client_openbsd_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ func TestClientDevices(t *testing.T) {
5858
// to allocate for the memory slice.
5959

6060
var wgIOCalls int
61-
wgDataIOFunc := func(data *wgh.WGDataIO) error {
61+
// TODO/FIXME: SIOCSWG
62+
wgDataIOFunc := func(req uint, data *wgh.WGDataIO) error {
6263
// Expect two calls per device, where the first call indicates the
6364
// number of bytes to populate, and the second would normally populate
6465
// the caller's memory.
@@ -123,7 +124,8 @@ func TestClientDeviceBasic(t *testing.T) {
123124
ioctlIfgroupreq: func(_ *wgh.Ifgroupreq) error {
124125
panic("no calls to Client.Devices, should not be called")
125126
},
126-
ioctlWGDataIO: func(data *wgh.WGDataIO) error {
127+
// TODO/FIXME: get this working with SIOCSWG
128+
ioctlWGDataIO: func(req uint, data *wgh.WGDataIO) error {
127129
// Verify the caller is asking for WireGuard interface group members.
128130
if diff := cmp.Diff(devName(device), data.Name); diff != "" {
129131
t.Fatalf("unexpected interface name (-want +got):\n%s", diff)
@@ -280,7 +282,8 @@ func TestClientDeviceNotExist(t *testing.T) {
280282
for _, tt := range tests {
281283
t.Run(tt.name, func(t *testing.T) {
282284
c := &Client{
283-
ioctlWGDataIO: func(_ *wgh.WGDataIO) error {
285+
// TODO/FIXME: SIOCSWG
286+
ioctlWGDataIO: func(_ uint, _ *wgh.WGDataIO) error {
284287
return tt.err
285288
},
286289
}
@@ -294,7 +297,8 @@ func TestClientDeviceNotExist(t *testing.T) {
294297

295298
func TestClientDeviceWrongMemorySize(t *testing.T) {
296299
c := &Client{
297-
ioctlWGDataIO: func(data *wgh.WGDataIO) error {
300+
// TODO/FIXME: SIOCSWG
301+
ioctlWGDataIO: func(req uint, data *wgh.WGDataIO) error {
298302
// Pass a nonsensical number of bytes back to the caller.
299303
data.Size = 1
300304
return nil

internal/wgopenbsd/internal/wgh/defs.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,29 @@ type WGInterfaceIO C.struct_wg_interface_io
4848

4949
type WGPeerIO C.struct_wg_peer_io
5050

51+
type SizeT = C.size_t
52+
5153
const (
5254
SIOCGWG = C.SIOCGWG
55+
SIOCSWG = C.SIOCSWG
5356

5457
WG_INTERFACE_HAS_PUBLIC = C.WG_INTERFACE_HAS_PUBLIC
5558
WG_INTERFACE_HAS_PRIVATE = C.WG_INTERFACE_HAS_PRIVATE
5659
WG_INTERFACE_HAS_PORT = C.WG_INTERFACE_HAS_PORT
5760
WG_INTERFACE_HAS_RTABLE = C.WG_INTERFACE_HAS_RTABLE
5861
WG_INTERFACE_REPLACE_PEERS = C.WG_INTERFACE_REPLACE_PEERS
5962

60-
WG_PEER_HAS_PUBLIC = C.WG_INTERFACE_HAS_PUBLIC
61-
WG_PEER_HAS_PSK = C.WG_PEER_HAS_PSK
62-
WG_PEER_HAS_PKA = C.WG_PEER_HAS_PKA
63-
WG_PEER_HAS_ENDPOINT = C.WG_PEER_HAS_ENDPOINT
63+
WG_PEER_HAS_PUBLIC = C.WG_INTERFACE_HAS_PUBLIC
64+
WG_PEER_HAS_PSK = C.WG_PEER_HAS_PSK
65+
WG_PEER_HAS_PKA = C.WG_PEER_HAS_PKA
66+
WG_PEER_HAS_ENDPOINT = C.WG_PEER_HAS_ENDPOINT
67+
WG_PEER_REPLACE_AIPS = C.WG_PEER_REPLACE_AIPS
68+
WG_PEER_REMOVE = C.WG_PEER_REMOVE
69+
WG_PEER_UPDATE = C.WG_PEER_UPDATE
70+
WG_PEER_SET_DESCRIPTION = C.WG_PEER_SET_DESCRIPTION
6471

6572
SizeofWGAIPIO = C.sizeof_struct_wg_aip_io
6673
SizeofWGInterfaceIO = C.sizeof_struct_wg_interface_io
6774
SizeofWGPeerIO = C.sizeof_struct_wg_peer_io
75+
SizeofWGDataIO = C.sizeof_struct_wg_data_io
6876
)

internal/wgopenbsd/internal/wgh/defs_openbsd_386.go

Lines changed: 14 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/wgopenbsd/internal/wgh/defs_openbsd_amd64.go

Lines changed: 14 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/wgopenbsd/internal/wgh/defs_openbsd_arm.go

Lines changed: 14 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)