diff --git a/internal/wgopenbsd/client_openbsd.go b/internal/wgopenbsd/client_openbsd.go index e2dba81..6ea6a9d 100644 --- a/internal/wgopenbsd/client_openbsd.go +++ b/internal/wgopenbsd/client_openbsd.go @@ -30,7 +30,7 @@ type Client struct { // during tests. close func() error ioctlIfgroupreq func(ifg *wgh.Ifgroupreq) error - ioctlWGDataIO func(data *wgh.WGDataIO) error + ioctlWGDataIO func(req uint, data *wgh.WGDataIO) error } // 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) { // if it proves to be a concern. var mem []byte for { - if err := c.ioctlWGDataIO(&data); err != nil { + if err := c.ioctlWGDataIO(wgh.SIOCGWG, &data); err != nil { // ioctl functions always return a wrapped unix.Errno value. // Conform to the wgctrl contract by unwrapping some values: // ENXIO: "no such device": (no such WireGuard device) @@ -223,14 +223,142 @@ func parseDevice(name string, ifio *wgh.WGInterfaceIO) (*wgtypes.Device, error) // ConfigureDevice implements wginternal.Client. func (c *Client) ConfigureDevice(name string, cfg wgtypes.Config) error { - // Currently read-only: we must determine if a device belongs to this driver, - // and if it does, return a sentinel so integration tests that configure a - // device can be skipped. - if _, err := c.Device(name); err != nil { + dname, err := deviceName(name) + if err != nil { return err } - return wginternal.ErrReadOnly + var port uint16 + var public wgtypes.Key + var private wgtypes.Key + var rtable int32 + + var flags uint8 + if cfg.ReplacePeers { + flags |= wgh.WG_INTERFACE_REPLACE_PEERS + } + if cfg.FirewallMark != nil { + flags |= wgh.WG_INTERFACE_HAS_RTABLE + rtable = int32(*cfg.FirewallMark) + } + if cfg.ListenPort != nil { + flags |= wgh.WG_INTERFACE_HAS_PORT + port = uint16(*cfg.ListenPort) + } + if cfg.PrivateKey != nil { + flags |= wgh.WG_INTERFACE_HAS_PRIVATE + private = *cfg.PrivateKey + } + + iface := wgh.WGInterfaceIO{ + Peers_count: wgh.SizeT(len(cfg.Peers)), + Port: port, + Rtable: rtable, + Public: public, + Private: private, + Flags: flags, + } + + aipCount := 0 + for _, peer := range cfg.Peers { + aipCount += len(peer.AllowedIPs) + } + + ioctlBuf := make([]byte, wgh.SizeofWGInterfaceIO+len(cfg.Peers)*wgh.SizeofWGPeerIO+aipCount*wgh.SizeofWGAIPIO) + copy(ioctlBuf, (*(*[wgh.SizeofWGInterfaceIO]byte)(unsafe.Pointer(&iface)))[:]) + + bufIdx := wgh.SizeofWGInterfaceIO + + for _, peer := range cfg.Peers { + var rawPeer wgh.WGPeerIO + + rawPeer.Aips_count = wgh.SizeT(len(peer.AllowedIPs)) + + if peer.Endpoint != nil { + if peer.Endpoint.IP.To4() != nil { + rawAddr := unix.RawSockaddrInet4{ + Port: uint16(bePort(uint16(peer.Endpoint.Port))), + Family: unix.AF_INET, + Len: uint8(unsafe.Sizeof(unix.RawSockaddrInet4{})), + } + copy(rawAddr.Addr[:], peer.Endpoint.IP) + copy(rawPeer.Endpoint[:], (*(*[unsafe.Sizeof(rawAddr)]byte)(unsafe.Pointer(&rawAddr)))[:]) + } else { + rawAddr := unix.RawSockaddrInet6{ + Port: uint16(bePort(uint16(peer.Endpoint.Port))), + Family: unix.AF_INET6, + Len: uint8(unsafe.Sizeof(unix.RawSockaddrInet6{})), + } + copy(rawAddr.Addr[:], peer.Endpoint.IP) + copy(rawPeer.Endpoint[:], (*(*[unsafe.Sizeof(rawAddr)]byte)(unsafe.Pointer(&rawAddr)))[:]) + } + rawPeer.Flags |= wgh.WG_PEER_HAS_ENDPOINT + } + if peer.PersistentKeepaliveInterval != nil { + rawPeer.Pka = uint16(*peer.PersistentKeepaliveInterval) + rawPeer.Flags |= wgh.WG_PEER_HAS_PKA + } + if peer.PresharedKey != nil { + rawPeer.Psk = *peer.PresharedKey + rawPeer.Flags |= wgh.WG_PEER_HAS_PSK + } + rawPeer.Public = peer.PublicKey + rawPeer.Flags |= wgh.WG_PEER_HAS_PUBLIC + + if peer.Remove { + rawPeer.Flags |= wgh.WG_PEER_REMOVE + } + if peer.ReplaceAllowedIPs { + rawPeer.Flags |= wgh.WG_PEER_REPLACE_AIPS + } + if peer.UpdateOnly { + // FIXME: not positive this flag is *only* update + rawPeer.Flags |= wgh.WG_PEER_UPDATE + } + + rawPeer.Protocol_version = 1 + + copy(ioctlBuf[bufIdx:], (*(*[wgh.SizeofWGPeerIO]byte)(unsafe.Pointer(&rawPeer)))[:]) + bufIdx += wgh.SizeofWGPeerIO + + for _, aip := range peer.AllowedIPs { + var rawAip wgh.WGAIPIO + if v4 := aip.IP.To4(); v4 != nil { + rawAip.Af = unix.AF_INET + copy(rawAip.Addr[:net.IPv4len], v4) + } else { + rawAip.Af = unix.AF_INET6 + rawAip.Addr = [net.IPv6len]byte(aip.IP) + } + ones, _ := aip.Mask.Size() + rawAip.Cidr = int32(ones) + + copy(ioctlBuf[bufIdx:], (*(*[wgh.SizeofWGAIPIO]byte)(unsafe.Pointer(&rawAip)))[:]) + bufIdx += wgh.SizeofWGAIPIO + } + + } + + data := wgh.WGDataIO{ + Name: dname, + Size: wgh.SizeT(len(ioctlBuf)), + Interface: (*wgh.WGInterfaceIO)(unsafe.Pointer(&ioctlBuf[0])), + } + if err := c.ioctlWGDataIO(wgh.SIOCSWG, &data); err != nil { + // ioctl functions always return a wrapped unix.Errno value. + // Conform to the wgctrl contract by unwrapping some values: + // ENXIO: "no such device": (no such WireGuard device) + // EINVAL: "inappropriate ioctl for device" (device is not a + // WireGuard device) + switch err.(*os.SyscallError).Err { + case unix.ENXIO, unix.EINVAL: + return os.ErrNotExist + default: + return err + } + } + + return nil } // deviceName converts an interface name string to the format required to pass @@ -350,9 +478,9 @@ func ioctlIfgroupreq(fd int) func(*wgh.Ifgroupreq) error { // ioctlWGDataIO returns a function which performs the appropriate ioctl on // fd to issue a WireGuard data I/O. -func ioctlWGDataIO(fd int) func(*wgh.WGDataIO) error { - return func(data *wgh.WGDataIO) error { - return ioctl(fd, wgh.SIOCGWG, unsafe.Pointer(data)) +func ioctlWGDataIO(fd int) func(uint, *wgh.WGDataIO) error { + return func(req uint, data *wgh.WGDataIO) error { + return ioctl(fd, req, unsafe.Pointer(data)) } } diff --git a/internal/wgopenbsd/client_openbsd_test.go b/internal/wgopenbsd/client_openbsd_test.go index 32043cd..6e14d60 100644 --- a/internal/wgopenbsd/client_openbsd_test.go +++ b/internal/wgopenbsd/client_openbsd_test.go @@ -58,7 +58,8 @@ func TestClientDevices(t *testing.T) { // to allocate for the memory slice. var wgIOCalls int - wgDataIOFunc := func(data *wgh.WGDataIO) error { + // TODO/FIXME: SIOCSWG + wgDataIOFunc := func(req uint, data *wgh.WGDataIO) error { // Expect two calls per device, where the first call indicates the // number of bytes to populate, and the second would normally populate // the caller's memory. @@ -123,7 +124,8 @@ func TestClientDeviceBasic(t *testing.T) { ioctlIfgroupreq: func(_ *wgh.Ifgroupreq) error { panic("no calls to Client.Devices, should not be called") }, - ioctlWGDataIO: func(data *wgh.WGDataIO) error { + // TODO/FIXME: get this working with SIOCSWG + ioctlWGDataIO: func(req uint, data *wgh.WGDataIO) error { // Verify the caller is asking for WireGuard interface group members. if diff := cmp.Diff(devName(device), data.Name); diff != "" { t.Fatalf("unexpected interface name (-want +got):\n%s", diff) @@ -280,7 +282,8 @@ func TestClientDeviceNotExist(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Client{ - ioctlWGDataIO: func(_ *wgh.WGDataIO) error { + // TODO/FIXME: SIOCSWG + ioctlWGDataIO: func(_ uint, _ *wgh.WGDataIO) error { return tt.err }, } @@ -294,7 +297,8 @@ func TestClientDeviceNotExist(t *testing.T) { func TestClientDeviceWrongMemorySize(t *testing.T) { c := &Client{ - ioctlWGDataIO: func(data *wgh.WGDataIO) error { + // TODO/FIXME: SIOCSWG + ioctlWGDataIO: func(req uint, data *wgh.WGDataIO) error { // Pass a nonsensical number of bytes back to the caller. data.Size = 1 return nil diff --git a/internal/wgopenbsd/internal/wgh/defs.go b/internal/wgopenbsd/internal/wgh/defs.go index 477b368..8ca1b1d 100644 --- a/internal/wgopenbsd/internal/wgh/defs.go +++ b/internal/wgopenbsd/internal/wgh/defs.go @@ -48,8 +48,11 @@ type WGInterfaceIO C.struct_wg_interface_io type WGPeerIO C.struct_wg_peer_io +type SizeT = C.size_t + const ( SIOCGWG = C.SIOCGWG + SIOCSWG = C.SIOCSWG WG_INTERFACE_HAS_PUBLIC = C.WG_INTERFACE_HAS_PUBLIC WG_INTERFACE_HAS_PRIVATE = C.WG_INTERFACE_HAS_PRIVATE @@ -57,12 +60,17 @@ const ( WG_INTERFACE_HAS_RTABLE = C.WG_INTERFACE_HAS_RTABLE WG_INTERFACE_REPLACE_PEERS = C.WG_INTERFACE_REPLACE_PEERS - WG_PEER_HAS_PUBLIC = C.WG_INTERFACE_HAS_PUBLIC - WG_PEER_HAS_PSK = C.WG_PEER_HAS_PSK - WG_PEER_HAS_PKA = C.WG_PEER_HAS_PKA - WG_PEER_HAS_ENDPOINT = C.WG_PEER_HAS_ENDPOINT + WG_PEER_HAS_PUBLIC = C.WG_INTERFACE_HAS_PUBLIC + WG_PEER_HAS_PSK = C.WG_PEER_HAS_PSK + WG_PEER_HAS_PKA = C.WG_PEER_HAS_PKA + WG_PEER_HAS_ENDPOINT = C.WG_PEER_HAS_ENDPOINT + WG_PEER_REPLACE_AIPS = C.WG_PEER_REPLACE_AIPS + WG_PEER_REMOVE = C.WG_PEER_REMOVE + WG_PEER_UPDATE = C.WG_PEER_UPDATE + WG_PEER_SET_DESCRIPTION = C.WG_PEER_SET_DESCRIPTION SizeofWGAIPIO = C.sizeof_struct_wg_aip_io SizeofWGInterfaceIO = C.sizeof_struct_wg_interface_io SizeofWGPeerIO = C.sizeof_struct_wg_peer_io + SizeofWGDataIO = C.sizeof_struct_wg_data_io ) diff --git a/internal/wgopenbsd/internal/wgh/defs_openbsd_386.go b/internal/wgopenbsd/internal/wgh/defs_openbsd_386.go index 14427fe..159924c 100644 --- a/internal/wgopenbsd/internal/wgh/defs_openbsd_386.go +++ b/internal/wgopenbsd/internal/wgh/defs_openbsd_386.go @@ -59,11 +59,15 @@ type WGPeerIO struct { Txbytes uint64 Rxbytes uint64 Last_handshake Timespec + Description [64]byte Aips_count uint32 } +type SizeT = uint32 + const ( SIOCGWG = 0xc01869d3 + SIOCSWG = 0xc01869d2 WG_INTERFACE_HAS_PUBLIC = 0x1 WG_INTERFACE_HAS_PRIVATE = 0x2 @@ -71,12 +75,17 @@ const ( WG_INTERFACE_HAS_RTABLE = 0x8 WG_INTERFACE_REPLACE_PEERS = 0x10 - WG_PEER_HAS_PUBLIC = 0x1 - WG_PEER_HAS_PSK = 0x2 - WG_PEER_HAS_PKA = 0x4 - WG_PEER_HAS_ENDPOINT = 0x8 + WG_PEER_HAS_PUBLIC = 0x1 + WG_PEER_HAS_PSK = 0x2 + WG_PEER_HAS_PKA = 0x4 + WG_PEER_HAS_ENDPOINT = 0x8 + WG_PEER_REPLACE_AIPS = 0x10 + WG_PEER_REMOVE = 0x20 + WG_PEER_UPDATE = 0x40 + WG_PEER_SET_DESCRIPTION = 0x80 SizeofWGAIPIO = 0x18 SizeofWGInterfaceIO = 0x4c - SizeofWGPeerIO = 0x88 + SizeofWGPeerIO = 0xc8 + SizeofWGDataIO = 0x18 ) diff --git a/internal/wgopenbsd/internal/wgh/defs_openbsd_amd64.go b/internal/wgopenbsd/internal/wgh/defs_openbsd_amd64.go index 00d379c..32d024d 100644 --- a/internal/wgopenbsd/internal/wgh/defs_openbsd_amd64.go +++ b/internal/wgopenbsd/internal/wgh/defs_openbsd_amd64.go @@ -59,11 +59,15 @@ type WGPeerIO struct { Txbytes uint64 Rxbytes uint64 Last_handshake Timespec + Description [64]byte Aips_count uint64 } +type SizeT = uint64 + const ( SIOCGWG = 0xc02069d3 + SIOCSWG = 0xc02069d2 WG_INTERFACE_HAS_PUBLIC = 0x1 WG_INTERFACE_HAS_PRIVATE = 0x2 @@ -71,12 +75,17 @@ const ( WG_INTERFACE_HAS_RTABLE = 0x8 WG_INTERFACE_REPLACE_PEERS = 0x10 - WG_PEER_HAS_PUBLIC = 0x1 - WG_PEER_HAS_PSK = 0x2 - WG_PEER_HAS_PKA = 0x4 - WG_PEER_HAS_ENDPOINT = 0x8 + WG_PEER_HAS_PUBLIC = 0x1 + WG_PEER_HAS_PSK = 0x2 + WG_PEER_HAS_PKA = 0x4 + WG_PEER_HAS_ENDPOINT = 0x8 + WG_PEER_REPLACE_AIPS = 0x10 + WG_PEER_REMOVE = 0x20 + WG_PEER_UPDATE = 0x40 + WG_PEER_SET_DESCRIPTION = 0x80 SizeofWGAIPIO = 0x18 SizeofWGInterfaceIO = 0x50 - SizeofWGPeerIO = 0x90 + SizeofWGPeerIO = 0xd0 + SizeofWGDataIO = 0x20 ) diff --git a/internal/wgopenbsd/internal/wgh/defs_openbsd_arm.go b/internal/wgopenbsd/internal/wgh/defs_openbsd_arm.go index 3dd8e96..5867693 100644 --- a/internal/wgopenbsd/internal/wgh/defs_openbsd_arm.go +++ b/internal/wgopenbsd/internal/wgh/defs_openbsd_arm.go @@ -61,13 +61,17 @@ type WGPeerIO struct { Txbytes uint64 Rxbytes uint64 Last_handshake Timespec + Description [64]byte Aips_count uint32 Aips [0]WGAIPIO Pad_cgo_1 [4]byte } +type SizeT = uint32 + const ( SIOCGWG = 0xc01869d3 + SIOCSWG = 0xc01869d2 WG_INTERFACE_HAS_PUBLIC = 0x1 WG_INTERFACE_HAS_PRIVATE = 0x2 @@ -75,12 +79,17 @@ const ( WG_INTERFACE_HAS_RTABLE = 0x8 WG_INTERFACE_REPLACE_PEERS = 0x10 - WG_PEER_HAS_PUBLIC = 0x1 - WG_PEER_HAS_PSK = 0x2 - WG_PEER_HAS_PKA = 0x4 - WG_PEER_HAS_ENDPOINT = 0x8 + WG_PEER_HAS_PUBLIC = 0x1 + WG_PEER_HAS_PSK = 0x2 + WG_PEER_HAS_PKA = 0x4 + WG_PEER_HAS_ENDPOINT = 0x8 + WG_PEER_REPLACE_AIPS = 0x10 + WG_PEER_REMOVE = 0x20 + WG_PEER_UPDATE = 0x40 + WG_PEER_SET_DESCRIPTION = 0x80 SizeofWGAIPIO = 0x18 SizeofWGInterfaceIO = 0x50 - SizeofWGPeerIO = 0x90 + SizeofWGPeerIO = 0xd0 + SizeofWGDataIO = 0x18 ) diff --git a/internal/wgopenbsd/internal/wgh/defs_openbsd_arm64.go b/internal/wgopenbsd/internal/wgh/defs_openbsd_arm64.go index 5dd84d8..0ddf9b8 100644 --- a/internal/wgopenbsd/internal/wgh/defs_openbsd_arm64.go +++ b/internal/wgopenbsd/internal/wgh/defs_openbsd_arm64.go @@ -59,11 +59,15 @@ type WGPeerIO struct { Txbytes uint64 Rxbytes uint64 Last_handshake Timespec + Description [64]byte Aips_count uint64 } +type SizeT = uint64 + const ( SIOCGWG = 0xc02069d3 + SIOCSWG = 0xc02069d2 WG_INTERFACE_HAS_PUBLIC = 0x1 WG_INTERFACE_HAS_PRIVATE = 0x2 @@ -71,12 +75,17 @@ const ( WG_INTERFACE_HAS_RTABLE = 0x8 WG_INTERFACE_REPLACE_PEERS = 0x10 - WG_PEER_HAS_PUBLIC = 0x1 - WG_PEER_HAS_PSK = 0x2 - WG_PEER_HAS_PKA = 0x4 - WG_PEER_HAS_ENDPOINT = 0x8 + WG_PEER_HAS_PUBLIC = 0x1 + WG_PEER_HAS_PSK = 0x2 + WG_PEER_HAS_PKA = 0x4 + WG_PEER_HAS_ENDPOINT = 0x8 + WG_PEER_REPLACE_AIPS = 0x10 + WG_PEER_REMOVE = 0x20 + WG_PEER_UPDATE = 0x40 + WG_PEER_SET_DESCRIPTION = 0x80 SizeofWGAIPIO = 0x18 SizeofWGInterfaceIO = 0x50 - SizeofWGPeerIO = 0x90 + SizeofWGPeerIO = 0xd0 + SizeofWGDataIO = 0x20 )