mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 03:05:07 +03:00 
			
		
		
		
	Document ICMPv6 and TUN/TAP
This commit is contained in:
		
							parent
							
								
									ad6ea59049
								
							
						
					
					
						commit
						8e2c2aa977
					
				
					 10 changed files with 90 additions and 4 deletions
				
			
		| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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" {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue