From cc03ff9e9a559e99e9f7ee904c67496d2b1ef813 Mon Sep 17 00:00:00 2001 From: Mateusz Kwiatkowski Date: Wed, 27 May 2020 04:23:23 +0200 Subject: [PATCH] Unbreak building for FreeBSD (#103) Add support for freebsd. You have to set `tun.dev` in your config. The second pass of this would be to remove the exec calls and use ioctl(2) and route(4) instead, but we can do that in a second PR. Co-authored-by: Wade Simmons --- .github/workflows/release.yml | 12 +++++- Makefile | 6 +++ tun_freebsd.go | 77 +++++++++++++++++++++++++++++++++++ udp_freebsd.go | 34 ++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tun_freebsd.go create mode 100644 udp_freebsd.go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c34ed69..ed5fcc3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: - name: Build run: | - make BUILD_NUMBER="${GITHUB_REF#refs/tags/v}" release-linux + make BUILD_NUMBER="${GITHUB_REF#refs/tags/v}" release-linux release-freebsd mkdir release mv build/*.tar.gz release @@ -278,3 +278,13 @@ jobs: asset_path: ./linux-latest/nebula-linux-mips64le.tar.gz asset_name: nebula-linux-mips64le.tar.gz asset_content_type: application/gzip + + - name: Upload freebsd-amd64 + uses: actions/upload-release-asset@v1.0.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.create_release.outputs.upload_url }} + asset_path: ./linux-latest/nebula-freebsd-amd64.tar.gz + asset_name: nebula-freebsd-amd64.tar.gz + asset_content_type: application/gzip diff --git a/Makefile b/Makefile index b71ff92..65b24f9 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ ALL_LINUX = linux-amd64 \ ALL = $(ALL_LINUX) \ darwin-amd64 \ + freebsd-amd64 \ windows-amd64 all: $(ALL:%=build/%/nebula) $(ALL:%=build/%/nebula-cert) @@ -25,12 +26,17 @@ release: $(ALL:%=build/nebula-%.tar.gz) release-linux: $(ALL_LINUX:%=build/nebula-%.tar.gz) +release-freebsd: build/nebula-freebsd-amd64.tar.gz + bin-windows: build/windows-amd64/nebula.exe build/windows-amd64/nebula-cert.exe mv $? . bin-darwin: build/darwin-amd64/nebula build/darwin-amd64/nebula-cert mv $? . +bin-freebsd: build/freebsd-amd64/nebula build/freebsd-amd64/nebula-cert + mv $? . + bin: go build -trimpath -ldflags "-X main.Build=$(BUILD_NUMBER)" -o ./nebula ${NEBULA_CMD_PATH} go build -trimpath -ldflags "-X main.Build=$(BUILD_NUMBER)" -o ./nebula-cert ./cmd/nebula-cert diff --git a/tun_freebsd.go b/tun_freebsd.go new file mode 100644 index 0000000..9570443 --- /dev/null +++ b/tun_freebsd.go @@ -0,0 +1,77 @@ +package nebula + +import ( + "fmt" + "io" + "net" + "os" + "os/exec" + "regexp" + "strconv" + "strings" +) + +var deviceNameRE = regexp.MustCompile(`^tun[0-9]+$`) + +type Tun struct { + Device string + Cidr *net.IPNet + MTU int + UnsafeRoutes []route + + io.ReadWriteCloser +} + +func newTun(deviceName string, cidr *net.IPNet, defaultMTU int, routes []route, unsafeRoutes []route, txQueueLen int) (ifce *Tun, err error) { + if len(routes) > 0 { + return nil, fmt.Errorf("Route MTU not supported in FreeBSD") + } + if strings.HasPrefix(deviceName, "/dev/") { + deviceName = strings.TrimPrefix(deviceName, "/dev/") + } + if !deviceNameRE.MatchString(deviceName) { + return nil, fmt.Errorf("tun.dev must match `tun[0-9]+`") + } + return &Tun{ + Device: deviceName, + Cidr: cidr, + MTU: defaultMTU, + UnsafeRoutes: unsafeRoutes, + }, nil +} + +func (c *Tun) Activate() error { + var err error + c.ReadWriteCloser, err = os.OpenFile("/dev/"+c.Device, os.O_RDWR, 0) + if err != nil { + return fmt.Errorf("Activate failed: %v", err) + } + + // TODO use syscalls instead of exec.Command + l.Debug("command: ifconfig", c.Device, c.Cidr.String(), c.Cidr.IP.String()) + if err = exec.Command("/sbin/ifconfig", c.Device, c.Cidr.String(), c.Cidr.IP.String()).Run(); err != nil { + return fmt.Errorf("failed to run 'ifconfig': %s", err) + } + l.Debug("command: route", "-n", "add", "-net", c.Cidr.String(), "-interface", c.Device) + if err = exec.Command("/sbin/route", "-n", "add", "-net", c.Cidr.String(), "-interface", c.Device).Run(); err != nil { + return fmt.Errorf("failed to run 'route add': %s", err) + } + l.Debug("command: ifconfig", c.Device, "mtu", strconv.Itoa(c.MTU)) + if err = exec.Command("/sbin/ifconfig", c.Device, "mtu", strconv.Itoa(c.MTU)).Run(); err != nil { + return fmt.Errorf("failed to run 'ifconfig': %s", err) + } + // Unsafe path routes + for _, r := range c.UnsafeRoutes { + l.Debug("command: route", "-n", "add", "-net", r.route.String(), "-interface", c.Device) + if err = exec.Command("/sbin/route", "-n", "add", "-net", r.route.String(), "-interface", c.Device).Run(); err != nil { + return fmt.Errorf("failed to run 'route add' for unsafe_route %s: %s", r.route.String(), err) + } + } + + return nil +} + +func (c *Tun) WriteRaw(b []byte) error { + _, err := c.Write(b) + return err +} diff --git a/udp_freebsd.go b/udp_freebsd.go new file mode 100644 index 0000000..88ff618 --- /dev/null +++ b/udp_freebsd.go @@ -0,0 +1,34 @@ +package nebula + +// FreeBSD support is primarily implemented in udp_generic, besides NewListenConfig + +import ( + "fmt" + "net" + "syscall" + + "golang.org/x/sys/unix" +) + +func NewListenConfig(multi bool) net.ListenConfig { + return net.ListenConfig{ + Control: func(network, address string, c syscall.RawConn) error { + if multi { + var controlErr error + err := c.Control(func(fd uintptr) { + if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { + controlErr = fmt.Errorf("SO_REUSEPORT failed: %v", err) + return + } + }) + if err != nil { + return err + } + if controlErr != nil { + return controlErr + } + } + return nil + }, + } +}