mirror of
https://github.com/yggdrasil-network/yggdrasil-go
synced 2024-11-10 07:20:39 +03:00
Document ICMPv6 and TUN/TAP
This commit is contained in:
parent
ad6ea59049
commit
8e2c2aa977
@ -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…
Reference in New Issue
Block a user