diff --git a/src/yggdrasil/icmpv6.go b/src/yggdrasil/icmpv6.go index fa14b2ac..f3b00ffc 100644 --- a/src/yggdrasil/icmpv6.go +++ b/src/yggdrasil/icmpv6.go @@ -1,8 +1,13 @@ package yggdrasil -// The NDP functions are needed when you are running with a -// TAP adapter - as the operating system expects neighbor solicitations -// for on-link traffic, this goroutine provides them +// The ICMPv6 module implements functions to easily create ICMPv6 +// packets. These functions, when mixed with the built-in Go IPv6 +// and ICMP libraries, can be used to send control messages back +// to the host. Examples include: +// - NDP messages, when running in TAP mode +// - Packet Too Big messages, when packets exceed the session MTU +// - Destination Unreachable messages, when a session prohibits +// incoming traffic import "net" import "golang.org/x/net/ipv6" @@ -39,6 +44,9 @@ func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) { return b, nil } +// Initialises the ICMPv6 module by assigning our link-local IPv6 address and +// our MAC address. ICMPv6 messages will always appear to originate from these +// addresses. func (i *icmpv6) init(t *tunDevice) { i.tun = t @@ -50,6 +58,10 @@ func (i *icmpv6) init(t *tunDevice) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE} } +// Parses an incoming ICMPv6 packet. The packet provided may be either an +// ethernet frame containing an IP packet, or the IP packet alone. This is +// determined by whether the TUN/TAP adapter is running in TUN (layer 3) or +// TAP (layer 2) mode. func (i *icmpv6) parse_packet(datain []byte) { var response []byte var err error @@ -69,6 +81,10 @@ func (i *icmpv6) parse_packet(datain []byte) { i.tun.iface.Write(response) } +// Unwraps the ethernet headers of an incoming ICMPv6 packet and hands off +// the IP packet to the parse_packet_tun function for further processing. +// A response buffer is also created for the response message, also complete +// with ethernet headers. func (i *icmpv6) parse_packet_tap(datain []byte) ([]byte, error) { // Store the peer MAC address copy(i.peermac[:6], datain[6:12]) @@ -97,6 +113,10 @@ func (i *icmpv6) parse_packet_tap(datain []byte) ([]byte, error) { return dataout, nil } +// Unwraps the IP headers of an incoming IPv6 packet and performs various +// sanity checks on the packet - i.e. is the packet an ICMPv6 packet, does the +// ICMPv6 message match a known expected type. The relevant handler function +// is then called and a response packet may be returned. func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) { // Parse the IPv6 packet headers ipv6Header, err := ipv6.ParseHeader(datain[:ipv6.HeaderLen]) @@ -149,6 +169,9 @@ func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) { return nil, errors.New("ICMPv6 type not matched") } +// Creates an ICMPv6 packet based on the given icmp.MessageBody and other +// parameters, complete with ethernet and IP headers, which can be written +// directly to a TAP adapter. func (i *icmpv6) create_icmpv6_tap(dstmac macAddress, dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) { // Pass through to create_icmpv6_tun ipv6packet, err := i.create_icmpv6_tun(dst, src, mtype, mcode, mbody) @@ -169,6 +192,10 @@ func (i *icmpv6) create_icmpv6_tap(dstmac macAddress, dst net.IP, src net.IP, mt return dataout, nil } +// Creates an ICMPv6 packet based on the given icmp.MessageBody and other +// parameters, complete with IP headers only, which can be written directly to +// a TUN adapter, or called directly by the create_icmpv6_tap function when +// generating a message for TAP adapters. func (i *icmpv6) create_icmpv6_tun(dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) { // Create the ICMPv6 message icmpMessage := icmp.Message{ @@ -208,6 +235,11 @@ func (i *icmpv6) create_icmpv6_tun(dst net.IP, src net.IP, mtype ipv6.ICMPType, return responsePacket, nil } +// Generates a response to an NDP discovery packet. This is effectively called +// when the host operating system generates an NDP request for any address in +// the fd00::/8 range, so that the operating system knows to route that traffic +// to the Yggdrasil TAP adapter. +// TODO: Make this respect the value of address_prefix in address.go func (i *icmpv6) handle_ndp(in []byte) ([]byte, error) { // Ignore NDP requests for anything outside of fd00::/8 if in[8] != 0xFD { diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index ca94713a..b29aabef 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -8,6 +8,7 @@ import "github.com/yggdrasil-network/water" const tun_IPv6_HEADER_LENGTH = 40 const tun_ETHER_HEADER_LENGTH = 14 +// Represents a running TUN/TAP interface. type tunDevice struct { core *Core icmpv6 icmpv6 @@ -17,6 +18,9 @@ type tunDevice struct { iface *water.Interface } +// Defines which parameters are expected by default for a TUN/TAP adapter on a +// specific platform. These values are populated in the relevant tun_*.go for +// the platform being targeted. They must be set. type tunDefaultParameters struct { maximumIfMTU int defaultIfMTU int @@ -24,6 +28,8 @@ type tunDefaultParameters struct { defaultIfTAPMode bool } +// Gets the maximum supported MTU for the platform based on the defaults in +// getDefaults(). func getSupportedMTU(mtu int) int { if mtu > getDefaults().maximumIfMTU { return getDefaults().maximumIfMTU @@ -31,11 +37,14 @@ func getSupportedMTU(mtu int) int { return mtu } +// Initialises the TUN/TAP adapter. func (tun *tunDevice) init(core *Core) { tun.core = core tun.icmpv6.init(tun) } +// Starts the setup process for the TUN/TAP adapter, and if successful, starts +// the read/write goroutines to handle packets on that interface. func (tun *tunDevice) start(ifname string, iftapmode bool, addr string, mtu int) error { if ifname == "none" { return nil @@ -48,6 +57,9 @@ func (tun *tunDevice) start(ifname string, iftapmode bool, addr string, mtu int) return nil } +// Writes a packet to the TUN/TAP adapter. If the adapter is running in TAP +// mode then additional ethernet encapsulation is added for the benefit of the +// host operating system. func (tun *tunDevice) write() error { for { data := <-tun.recv @@ -75,6 +87,10 @@ func (tun *tunDevice) write() error { } } +// Reads any packets that are waiting on the TUN/TAP adapter. If the adapter +// is running in TAP mode then the ethernet headers will automatically be +// processed and stripped if necessary. If an ICMPv6 packet is found, then +// the relevant helper functions in icmpv6.go are called. func (tun *tunDevice) read() error { mtu := tun.mtu if tun.iface.IsTAP() { @@ -109,6 +125,9 @@ func (tun *tunDevice) read() error { } } +// Closes the TUN/TAP adapter. This is only usually called when the Yggdrasil +// process stops. Typically this operation will happen quickly, but on macOS +// it can block until a read operation is completed. func (tun *tunDevice) close() error { if tun.iface == nil { return nil diff --git a/src/yggdrasil/tun_bsd.go b/src/yggdrasil/tun_bsd.go index 721b6778..9455d5d2 100644 --- a/src/yggdrasil/tun_bsd.go +++ b/src/yggdrasil/tun_bsd.go @@ -70,6 +70,11 @@ type in6_ifreq_lifetime struct { ifru_addrlifetime in6_addrlifetime } +// Sets the IPv6 address of the utun adapter. On all BSD platforms (FreeBSD, +// OpenBSD, NetBSD) an attempt is made to set the adapter properties by using +// a system socket and making syscalls to the kernel. This is not refined though +// and often doesn't work (if at all), therefore if a call fails, it resorts +// to calling "ifconfig" instead. func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { var config water.Config if ifname[:4] == "auto" { diff --git a/src/yggdrasil/tun_darwin.go b/src/yggdrasil/tun_darwin.go index 6096d6ac..4211fe7d 100644 --- a/src/yggdrasil/tun_darwin.go +++ b/src/yggdrasil/tun_darwin.go @@ -10,6 +10,8 @@ import "golang.org/x/sys/unix" import water "github.com/yggdrasil-network/water" +// Sane defaults for the Darwin/macOS platform. The "default" options may be +// may be replaced by the running configuration. func getDefaults() tunDefaultParameters { return tunDefaultParameters{ maximumIfMTU: 65535, @@ -19,6 +21,7 @@ func getDefaults() tunDefaultParameters { } } +// Configures the "utun" adapter with the correct IPv6 address and MTU. func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { if iftapmode { tun.core.log.Printf("TAP mode is not supported on this platform, defaulting to TUN") @@ -65,6 +68,8 @@ type ifreq struct { ifru_mtu uint32 } +// Sets the IPv6 address of the utun adapter. On Darwin/macOS this is done using +// a system socket and making direct syscalls to the kernel. func (tun *tunDevice) setupAddress(addr string) error { var fd int var err error diff --git a/src/yggdrasil/tun_freebsd.go b/src/yggdrasil/tun_freebsd.go index dd9a49bd..4cfdcee1 100644 --- a/src/yggdrasil/tun_freebsd.go +++ b/src/yggdrasil/tun_freebsd.go @@ -1,5 +1,7 @@ package yggdrasil +// Sane defaults for the FreeBSD platform. The "default" options may be +// may be replaced by the running configuration. func getDefaults() tunDefaultParameters { return tunDefaultParameters{ maximumIfMTU: 32767, diff --git a/src/yggdrasil/tun_linux.go b/src/yggdrasil/tun_linux.go index 42ff4a4a..d038d4e8 100644 --- a/src/yggdrasil/tun_linux.go +++ b/src/yggdrasil/tun_linux.go @@ -1,7 +1,6 @@ package yggdrasil // The linux platform specific tun parts -// It depends on iproute2 being installed to set things on the tun device import "errors" import "fmt" @@ -11,6 +10,8 @@ import water "github.com/yggdrasil-network/water" import "github.com/docker/libcontainer/netlink" +// Sane defaults for the Linux platform. The "default" options may be +// may be replaced by the running configuration. func getDefaults() tunDefaultParameters { return tunDefaultParameters{ maximumIfMTU: 65535, @@ -20,6 +21,7 @@ func getDefaults() tunDefaultParameters { } } +// Configures the TAP adapter with the correct IPv6 address and MTU. func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { var config water.Config if iftapmode { @@ -39,6 +41,10 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) return tun.setupAddress(addr) } +// Configures the TAP adapter with the correct IPv6 address and MTU. Netlink +// is used to do this, so there is not a hard requirement on "ip" or "ifconfig" +// to exist on the system, but this will fail if Netlink is not present in the +// kernel (it nearly always is). func (tun *tunDevice) setupAddress(addr string) error { // Set address var netIF *net.Interface diff --git a/src/yggdrasil/tun_netbsd.go b/src/yggdrasil/tun_netbsd.go index f6d34ee3..d3e93c4d 100644 --- a/src/yggdrasil/tun_netbsd.go +++ b/src/yggdrasil/tun_netbsd.go @@ -1,5 +1,7 @@ package yggdrasil +// Sane defaults for the NetBSD platform. The "default" options may be +// may be replaced by the running configuration. func getDefaults() tunDefaultParameters { return tunDefaultParameters{ maximumIfMTU: 9000, diff --git a/src/yggdrasil/tun_openbsd.go b/src/yggdrasil/tun_openbsd.go index bc75f0c6..c96c8658 100644 --- a/src/yggdrasil/tun_openbsd.go +++ b/src/yggdrasil/tun_openbsd.go @@ -1,5 +1,7 @@ package yggdrasil +// Sane defaults for the OpenBSD platform. The "default" options may be +// may be replaced by the running configuration. func getDefaults() tunDefaultParameters { return tunDefaultParameters{ maximumIfMTU: 16384, diff --git a/src/yggdrasil/tun_other.go b/src/yggdrasil/tun_other.go index 339faba4..7bc7100c 100644 --- a/src/yggdrasil/tun_other.go +++ b/src/yggdrasil/tun_other.go @@ -7,6 +7,8 @@ import water "github.com/yggdrasil-network/water" // This is to catch unsupported platforms // If your platform supports tun devices, you could try configuring it manually +// These are sane defaults for any platform that has not been matched by one of +// the other tun_*.go files. func getDefaults() tunDefaultParameters { return tunDefaultParameters{ maximumIfMTU: 65535, @@ -16,6 +18,8 @@ func getDefaults() tunDefaultParameters { } } +// Creates the TUN/TAP adapter, if supported by the Water library. Note that +// no guarantees are made at this point on an unsupported platform. func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { var config water.Config if iftapmode { @@ -32,6 +36,8 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) return tun.setupAddress(addr) } +// We don't know how to set the IPv6 address on an unknown platform, therefore +// write about it to stdout and don't try to do anything further. func (tun *tunDevice) setupAddress(addr string) error { tun.core.log.Println("Platform not supported, you must set the address of", tun.iface.Name(), "to", addr) return nil diff --git a/src/yggdrasil/tun_windows.go b/src/yggdrasil/tun_windows.go index d4a15f93..644cfdb4 100644 --- a/src/yggdrasil/tun_windows.go +++ b/src/yggdrasil/tun_windows.go @@ -7,6 +7,8 @@ import "fmt" // This is to catch Windows platforms +// Sane defaults for the Windows platform. The "default" options may be +// may be replaced by the running configuration. func getDefaults() tunDefaultParameters { return tunDefaultParameters{ maximumIfMTU: 65535, @@ -16,6 +18,9 @@ func getDefaults() tunDefaultParameters { } } +// Configures the TAP adapter with the correct IPv6 address and MTU. On Windows +// we don't make use of a direct operating system API to do this - we instead +// delegate the hard work to "netsh". func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) error { if !iftapmode { tun.core.log.Printf("TUN mode is not supported on this platform, defaulting to TAP") @@ -63,6 +68,7 @@ func (tun *tunDevice) setup(ifname string, iftapmode bool, addr string, mtu int) return tun.setupAddress(addr) } +// Sets the MTU of the TAP adapter. func (tun *tunDevice) setupMTU(mtu int) error { // Set MTU cmd := exec.Command("netsh", "interface", "ipv6", "set", "subinterface", @@ -79,6 +85,7 @@ func (tun *tunDevice) setupMTU(mtu int) error { return nil } +// Sets the IPv6 address of the TAP adapter. func (tun *tunDevice) setupAddress(addr string) error { // Set address cmd := exec.Command("netsh", "interface", "ipv6", "add", "address",