diff --git a/.circleci/config.yml b/.circleci/config.yml index bc4b5cd4..16fb4e4d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -37,8 +37,20 @@ jobs: - run: name: Build for OpenBSD command: | - GOOS=openbsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/yggdrasil-$CIVERSION-linux-amd64; - GOOS=openbsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/yggdrasil-$CIVERSION-linux-i386; + GOOS=openbsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/yggdrasil-$CIVERSION-openbsd-amd64; + GOOS=openbsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/yggdrasil-$CIVERSION-openbsd-i386; + + - run: + name: Build for FreeBSD + command: | + GOOS=freebsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/yggdrasil-$CIVERSION-freebsd-amd64; + GOOS=freebsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/yggdrasil-$CIVERSION-freebsd-i386; + + - run: + name: Build for NetBSD + command: | + GOOS=netbsd GOARCH=amd64 ./build && mv yggdrasil /tmp/upload/yggdrasil-$CIVERSION-netbsd-amd64; + GOOS=netbsd GOARCH=386 ./build && mv yggdrasil /tmp/upload/yggdrasil-$CIVERSION-netbsd-i386; - run: name: Build for Windows diff --git a/README.md b/README.md index 63d2a11d..6558fc68 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ journalctl -u yggdrasil - May work in theory on any macOS version with `utun` support (which was added in macOS 10.7 Lion), although this is untested at present. - TAP mode is not supported on macOS. -#### FreeBSD, OpenBSD +#### FreeBSD, OpenBSD, NetBSD - Works in TAP mode, but currently doesn't work in TUN mode. - You may need to create the TAP adapter first if it doesn't already exist, i.e. `ifconfig tap0 create`. diff --git a/src/yggdrasil/tun_bsd.go b/src/yggdrasil/tun_bsd.go index bf8a6f82..5dbdd7d7 100644 --- a/src/yggdrasil/tun_bsd.go +++ b/src/yggdrasil/tun_bsd.go @@ -1,15 +1,20 @@ -// +build openbsd freebsd +// +build openbsd freebsd netbsd package yggdrasil -import "os" -import "os/exec" import "unsafe" +import "syscall" +import "strings" +import "strconv" +import "encoding/binary" +import "os/exec" import "golang.org/x/sys/unix" import "github.com/yggdrasil-network/water" +const SIOCSIFADDR_IN6 = (0x80000000) | ((288 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 12 + type in6_addrlifetime struct { ia6t_expire float64 ia6t_preferred float64 @@ -26,13 +31,43 @@ type sockaddr_in6 struct { sin6_scope_id uint32 } -type in6_aliasreq struct { - ifra_name [16]byte - ifra_addr sockaddr_in6 - ifra_dstaddr sockaddr_in6 - ifra_prefixmask sockaddr_in6 - ifra_flags uint32 - ifra_lifetime in6_addrlifetime +/* +from +struct in6_ifreq { + 277 char ifr_name[IFNAMSIZ]; + 278 union { + 279 struct sockaddr_in6 ifru_addr; + 280 struct sockaddr_in6 ifru_dstaddr; + 281 int ifru_flags; + 282 int ifru_flags6; + 283 int ifru_metric; + 284 caddr_t ifru_data; + 285 struct in6_addrlifetime ifru_lifetime; + 286 struct in6_ifstat ifru_stat; + 287 struct icmp6_ifstat ifru_icmp6stat; + 288 u_int32_t ifru_scope_id[16]; + 289 } ifr_ifru; + 290 }; +*/ + +type in6_ifreq_mtu struct { + ifr_name [syscall.IFNAMSIZ]byte + ifru_mtu int +} + +type in6_ifreq_addr struct { + ifr_name [syscall.IFNAMSIZ]byte + ifru_addr sockaddr_in6 +} + +type in6_ifreq_flags struct { + ifr_name [syscall.IFNAMSIZ]byte + flags int +} + +type in6_ifreq_lifetime struct { + ifr_name [syscall.IFNAMSIZ]byte + ifru_addrlifetime in6_addrlifetime } func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { @@ -62,38 +97,67 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) } func (tun *tunDevice) setupAddress(addr string) error { - fd := tun.iface.ReadWriteCloser.(*os.File).Fd() + var sfd int var err error - var ti tuninfo + // Create system socket + if sfd, err = unix.Socket(unix.AF_INET, unix.SOCK_DGRAM, 0); err != nil { + tun.core.log.Printf("Create AF_INET socket failed: %v.", err) + return err + } + + // Friendly output tun.core.log.Printf("Interface name: %s", tun.iface.Name()) tun.core.log.Printf("Interface IPv6: %s", addr) tun.core.log.Printf("Interface MTU: %d", tun.mtu) - // Get the existing interface flags - if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(TUNGIFINFO), uintptr(unsafe.Pointer(&ti))); errno != 0 { + // Create the MTU request + var ir in6_ifreq_mtu + copy(ir.ifr_name[:], tun.iface.Name()) + ir.ifru_mtu = int(tun.mtu) + + // Set the MTU + if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(syscall.SIOCSIFMTU), uintptr(unsafe.Pointer(&ir))); errno != 0 { err = errno - tun.core.log.Printf("Error in TUNGIFINFO: %v", errno) - return err + tun.core.log.Printf("Error in SIOCSIFMTU: %v", errno) + + // Fall back to ifconfig to set the MTU + cmd := exec.Command("ifconfig", tun.iface.Name(), "mtu", string(tun.mtu)) + tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) + output, err := cmd.CombinedOutput() + if err != nil { + tun.core.log.Printf("SIOCSIFMTU fallback failed: %v.", err) + tun.core.log.Println(string(output)) + } } - // Update with any OS-specific flags, MTU, etc. - ti.setInfo(tun) - - // Set the new interface flags - if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), uintptr(TUNSIFINFO), uintptr(unsafe.Pointer(&ti))); errno != 0 { - err = errno - tun.core.log.Printf("Error in TUNSIFINFO: %v", errno) - return err + // Create the address request + // FIXME: I don't work! + var ar in6_ifreq_addr + copy(ar.ifr_name[:], tun.iface.Name()) + ar.ifru_addr.sin6_len = uint8(unsafe.Sizeof(ar.ifru_addr)) + ar.ifru_addr.sin6_family = unix.AF_INET6 + parts := strings.Split(strings.TrimRight(addr, "/8"), ":") + for i := 0; i < 8; i++ { + addr, _ := strconv.ParseUint(parts[i], 16, 16) + b := make([]byte, 16) + binary.LittleEndian.PutUint16(b, uint16(addr)) + ar.ifru_addr.sin6_addr[i] = uint16(binary.BigEndian.Uint16(b)) } - // Set address - cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr) - //tun.core.log.Printf("ifconfig command: %v", strings.Join(cmd.Args, " ")) - output, err := cmd.CombinedOutput() - if err != nil { - tun.core.log.Printf("ifconfig failed: %v.", err) - tun.core.log.Println(string(output)) + // Set the interface address + if _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(sfd), uintptr(SIOCSIFADDR_IN6), uintptr(unsafe.Pointer(&ar))); errno != 0 { + err = errno + tun.core.log.Printf("Error in SIOCSIFADDR_IN6: %v", errno) + + // Fall back to ifconfig to set the address + cmd := exec.Command("ifconfig", tun.iface.Name(), "inet6", addr) + tun.core.log.Printf("Using ifconfig as fallback: %v", strings.Join(cmd.Args, " ")) + output, err := cmd.CombinedOutput() + if err != nil { + tun.core.log.Printf("SIOCSIFADDR_IN6 fallback failed: %v.", err) + tun.core.log.Println(string(output)) + } } return nil diff --git a/src/yggdrasil/tun_freebsd.go b/src/yggdrasil/tun_freebsd.go index 08946b9b..dd9a49bd 100644 --- a/src/yggdrasil/tun_freebsd.go +++ b/src/yggdrasil/tun_freebsd.go @@ -1,7 +1,5 @@ package yggdrasil -// This is to catch FreeBSD and NetBSD - func getDefaults() tunDefaultParameters { return tunDefaultParameters{ maximumIfMTU: 32767, @@ -10,34 +8,3 @@ func getDefaults() tunDefaultParameters { defaultIfTAPMode: true, } } - -// Warning! When porting this to other BSDs, the tuninfo struct can appear with -// the fields in a different order, and the consts below might also have -// different values - -/* -FreeBSD/NetBSD, net/if_tun.h: - -struct tuninfo { - int baudrate; - short mtu; - u_char type; - u_char dummy; -}; -*/ - -type tuninfo struct { - tun_baudrate int32 - tun_mtu int16 - tun_type uint8 - tun_dummy uint8 -} - -func (ti *tuninfo) setInfo(tun *tunDevice) { - ti.tun_mtu = int16(tun.mtu) -} - -const TUNSIFINFO = (0x80000000) | ((8 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 91 -const TUNGIFINFO = (0x40000000) | ((8 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 92 -const TUNSIFHEAD = (0x80000000) | ((4 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 96 -const SIOCAIFADDR_IN6 = (0x80000000) | ((4 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 27 diff --git a/src/yggdrasil/tun_netbsd.go b/src/yggdrasil/tun_netbsd.go new file mode 100644 index 00000000..f6d34ee3 --- /dev/null +++ b/src/yggdrasil/tun_netbsd.go @@ -0,0 +1,10 @@ +package yggdrasil + +func getDefaults() tunDefaultParameters { + return tunDefaultParameters{ + maximumIfMTU: 9000, + defaultIfMTU: 9000, + defaultIfName: "/dev/tap0", + defaultIfTAPMode: true, + } +} diff --git a/src/yggdrasil/tun_openbsd.go b/src/yggdrasil/tun_openbsd.go index 3146b39c..bc75f0c6 100644 --- a/src/yggdrasil/tun_openbsd.go +++ b/src/yggdrasil/tun_openbsd.go @@ -1,13 +1,5 @@ package yggdrasil -import "syscall" - -// This is to catch OpenBSD - -// TODO: Fix TUN mode for OpenBSD. It turns out that OpenBSD doesn't have a way -// to disable the PI header when in TUN mode, so we need to modify the read/ -// writes to handle those first four bytes - func getDefaults() tunDefaultParameters { return tunDefaultParameters{ maximumIfMTU: 16384, @@ -16,41 +8,3 @@ func getDefaults() tunDefaultParameters { defaultIfTAPMode: true, } } - -// Warning! When porting this to other BSDs, the tuninfo struct can appear with -// the fields in a different order, and the consts below might also have -// different values - -/* -OpenBSD, net/if_tun.h: - -struct tuninfo { - u_int mtu; - u_short type; - u_short flags; - u_int baudrate; -}; -*/ - -type tuninfo struct { - tun_mtu uint32 - tun_type uint16 - tun_flags uint16 - tun_baudrate uint32 -} - -func (ti *tuninfo) setInfo(tun *tunDevice) { - ti.tun_flags |= syscall.IFF_UP - switch { - case tun.iface.IsTAP(): - ti.tun_flags |= syscall.IFF_MULTICAST - ti.tun_flags |= syscall.IFF_BROADCAST - case tun.iface.IsTUN(): - ti.tun_flags |= syscall.IFF_POINTOPOINT - } - ti.tun_mtu = uint32(tun.mtu) -} - -const TUNSIFINFO = (0x80000000) | ((12 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 91 -const TUNGIFINFO = (0x40000000) | ((12 & 0x1fff) << 16) | uint32(byte('t'))<<8 | 92 -const SIOCAIFADDR_IN6 = (0x80000000) | ((4 & 0x1fff) << 16) | uint32(byte('i'))<<8 | 27 diff --git a/src/yggdrasil/tun_other.go b/src/yggdrasil/tun_other.go index 7b1f46ec..339faba4 100644 --- a/src/yggdrasil/tun_other.go +++ b/src/yggdrasil/tun_other.go @@ -1,4 +1,4 @@ -// +build !linux,!darwin,!windows,!openbsd,!freebsd +// +build !linux,!darwin,!windows,!openbsd,!freebsd,!netbsd package yggdrasil