Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

internal/freebsd: add initial version of FreeBSD support #126

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
internal/freebsd: add initial version of FreeBSD support
Signed-off-by: Steffen Vogel <post@steffenvogel.de>
  • Loading branch information
stv0g committed Oct 11, 2022
commit b76f2e2af4b609f72072dab5d16dcc90f214c149
519 changes: 519 additions & 0 deletions internal/wgfreebsd/client_freebsd.go

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions internal/wgfreebsd/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Package wgfreebsd provides internal access to FreeBSD's WireGuard
// ioctl interface.
//
// This package is internal-only and not meant for end users to consume.
// Please use package wgctrl (an abstraction over this package) instead.
package wgfreebsd
71 changes: 71 additions & 0 deletions internal/wgfreebsd/internal/nv/decode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//go:build freebsd
// +build freebsd

package nv

// #cgo LDFLAGS: -lnv
// #include <sys/nv.h>
import "C"

import (
"unsafe"
)

// Unmarshal decodes a FreeBSD name-value list (nv(9)) to a Go map
func Unmarshal(d []byte, out List) error {
sz := C.ulong(len(d))
dp := unsafe.Pointer(&d[0])
nvl := C.nvlist_unpack(dp, sz, 0)

return unmarshal(nvl, out)
}

func unmarshal(nvl *C.struct_nvlist, out List) error {
// For debugging
// C.nvlist_dump(nvl, C.int(os.Stdout.Fd()))

var cookie unsafe.Pointer
for {
var typ C.int
ckey := C.nvlist_next(nvl, &typ, &cookie)
if ckey == nil {
break
}

var sz C.size_t
var value interface{}
switch typ {
case C.NV_TYPE_BINARY:
v := C.nvlist_get_binary(nvl, ckey, &sz)
value = C.GoBytes(v, C.int(sz))

case C.NV_TYPE_BOOL:
value = C.nvlist_get_bool(nvl, ckey)

case C.NV_TYPE_NUMBER:
v := C.nvlist_get_number(nvl, ckey)
value = uint64(v)

case C.NV_TYPE_NVLIST_ARRAY:
items := []List{}

nvlSubListsBuf := C.nvlist_get_nvlist_array(nvl, ckey, &sz)
nvlSubLists := unsafe.Slice(nvlSubListsBuf, sz)
for _, nvlSubList := range nvlSubLists {
item := map[string]interface{}{}
if err := unmarshal(nvlSubList, item); err != nil {
return err
}

items = append(items, item)
}

value = items
}

name := C.GoString(ckey)
out[name] = value
}

return nil
}
3 changes: 3 additions & 0 deletions internal/wgfreebsd/internal/nv/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Package nv marshals and unmarshals Go maps to/from FreeBSDs nv(9) name/value lists
// See: https://www.freebsd.org/cgi/man.cgi?query=nv&sektion=9
package nv
75 changes: 75 additions & 0 deletions internal/wgfreebsd/internal/nv/encode.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//go:build freebsd
// +build freebsd

package nv

/*
#cgo LDFLAGS: -lnv
#include <stdlib.h>
#include <sys/nv.h>

// For sizeof(*struct nvlist)
typedef struct nvlist *nvlist_ptr;
*/
import "C"

import (
"unsafe"
)

// Marshal encodes a Go map to a FreeBSD name-value list (nv(9))
func Marshal(m List) (*byte, int, error) {
nvl, err := marshal(m)
if err != nil {
return nil, -1, err
}

// For debugging
// C.nvlist_dump(nvl, C.int(os.Stdout.Fd()))

var sz C.size_t
buf := C.nvlist_pack(nvl, &sz)

return (*byte)(buf), int(sz), nil
}

func marshal(m List) (nvl *C.struct_nvlist, err error) {
nvl = C.nvlist_create(0)

for key, value := range m {
ckey := C.CString(key)

switch value := value.(type) {
case bool:
C.nvlist_add_bool(nvl, ckey, C.bool(value))

case uint64:
C.nvlist_add_number(nvl, ckey, C.ulong(value))

case []byte:
sz := len(value)
ptr := C.CBytes(value)
C.nvlist_add_binary(nvl, ckey, ptr, C.size_t(sz))
C.free(ptr)

case []List:
sz := len(value)
buf := C.malloc(C.size_t(C.sizeof_nvlist_ptr * sz))
items := (*[1<<30 - 1]*C.struct_nvlist)(buf)

for i, val := range value {
if items[i], err = marshal(val); err != nil {
C.free(unsafe.Pointer(ckey))
return nil, err
}
}

C.nvlist_add_nvlist_array(nvl, ckey, (**C.struct_nvlist)(buf), C.size_t(sz))
C.free(buf)
}

C.free(unsafe.Pointer(ckey))
}

return
}
45 changes: 45 additions & 0 deletions internal/wgfreebsd/internal/nv/nvlist_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//go:build freebsd
// +build freebsd

package nv_test

import (
"fmt"
"testing"
"unsafe"

"golang.zx2c4.com/wireguard/wgctrl/internal/wgfreebsd/internal/nv"
)

func TestMarshaling(t *testing.T) {
m1 := nv.List{
"number": uint64(0x1234),
"boolean": true,
"binary": []byte{0xA, 0xB, 0xC, 0xD},
"array_of_nvlists": []nv.List{
{
"a": uint64(1),
},
{
"b": uint64(2),
},
},
}

buf, sz, err := nv.Marshal(m1)
if err != nil {
t.Fatalf("Failed to marshal: %s", err)
}

m2 := nv.List{}
buf2 := unsafe.Slice(buf, sz)

err = nv.Unmarshal(buf2, m2)
if err != nil {
t.Fatalf("Failed to marshal: %s", err)
}

if fmt.Sprint(m1) != fmt.Sprint(m2) {
t.Fatalf("unequal: %+#v != %+#v", m1, m2)
}
}
6 changes: 6 additions & 0 deletions internal/wgfreebsd/internal/nv/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//go:build freebsd
// +build freebsd

package nv

type List map[string]interface{}
51 changes: 51 additions & 0 deletions internal/wgfreebsd/internal/wgh/defs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//go:build ignore
// +build ignore

// TODO(mdlayher): attempt to integrate into x/sys/unix infrastructure.

package wgh

/*
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/sockio.h>
#include <time.h>
#include <net/if.h>

struct wg_data_io {
char wgd_name[IFNAMSIZ];
void *wgd_data;
size_t wgd_size;
};

// This is a copy of ifgroupreq but the union's *ifg_req variant is broken out
// into an explicit field, and the other variant is omitted and replaced with
// struct padding to the expected size.
#undef ifgr_groups
struct go_ifgroupreq {
char ifgr_name[IFNAMSIZ];
u_int ifgr_len;
char ifgr_pad1[-1 * (4 - sizeof(void*))];
struct ifg_req *ifgr_groups;
char ifgr_pad2[16 - sizeof(void*)];
};

#define SIOCSWG _IOWR('i', 210, struct wg_data_io)
#define SIOCGWG _IOWR('i', 211, struct wg_data_io)
*/
import "C"

// Interface group types and constants.

const (
SizeofIfgreq = C.sizeof_struct_ifg_req

SIOCGWG = C.SIOCGWG
SIOCSWG = C.SIOCSWG
)

type Ifgroupreq C.struct_go_ifgroupreq

type Ifgreq C.struct_ifg_req

type WGDataIO C.struct_wg_data_io
32 changes: 32 additions & 0 deletions internal/wgfreebsd/internal/wgh/defs_freebsd_386.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions internal/wgfreebsd/internal/wgh/defs_freebsd_amd64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions internal/wgfreebsd/internal/wgh/defs_freebsd_arm.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions internal/wgfreebsd/internal/wgh/defs_freebsd_arm64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions internal/wgfreebsd/internal/wgh/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Package wgh is an auto-generated package which contains constants and
// types used to access WireGuard information using ioctl calls.
package wgh
Loading