mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 03:05:07 +03:00 
			
		
		
		
	Merge pull request #204 from neilalexander/tapmac
Neighbor discovery changes for TAP mode
This commit is contained in:
		
						commit
						b3887e554c
					
				
					 2 changed files with 169 additions and 41 deletions
				
			
		| 
						 | 
				
			
			@ -13,6 +13,7 @@ import (
 | 
			
		|||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/net/icmp"
 | 
			
		||||
	"golang.org/x/net/ipv6"
 | 
			
		||||
| 
						 | 
				
			
			@ -23,11 +24,17 @@ type macAddress [6]byte
 | 
			
		|||
const len_ETHER = 14
 | 
			
		||||
 | 
			
		||||
type icmpv6 struct {
 | 
			
		||||
	tun        *tunDevice
 | 
			
		||||
	peermac    macAddress
 | 
			
		||||
	peerlladdr net.IP
 | 
			
		||||
	mylladdr   net.IP
 | 
			
		||||
	mymac      macAddress
 | 
			
		||||
	tun      *tunDevice
 | 
			
		||||
	mylladdr net.IP
 | 
			
		||||
	mymac    macAddress
 | 
			
		||||
	peermacs map[address]neighbor
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type neighbor struct {
 | 
			
		||||
	mac               macAddress
 | 
			
		||||
	learned           bool
 | 
			
		||||
	lastadvertisement time.Time
 | 
			
		||||
	lastsolicitation  time.Time
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Marshal returns the binary encoding of h.
 | 
			
		||||
| 
						 | 
				
			
			@ -52,13 +59,16 @@ func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) {
 | 
			
		|||
// addresses.
 | 
			
		||||
func (i *icmpv6) init(t *tunDevice) {
 | 
			
		||||
	i.tun = t
 | 
			
		||||
	i.peermacs = make(map[address]neighbor)
 | 
			
		||||
 | 
			
		||||
	// Our MAC address and link-local address
 | 
			
		||||
	copy(i.mymac[:], []byte{
 | 
			
		||||
		0x02, 0x00, 0x00, 0x00, 0x00, 0x02})
 | 
			
		||||
	i.mymac = macAddress{
 | 
			
		||||
		0x02, 0x00, 0x00, 0x00, 0x00, 0x02}
 | 
			
		||||
	i.mylladdr = net.IP{
 | 
			
		||||
		0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE}
 | 
			
		||||
	copy(i.mymac[:], i.tun.core.router.addr[:])
 | 
			
		||||
	copy(i.mylladdr[9:], i.tun.core.router.addr[1:])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Parses an incoming ICMPv6 packet. The packet provided may be either an
 | 
			
		||||
| 
						 | 
				
			
			@ -73,7 +83,7 @@ func (i *icmpv6) parse_packet(datain []byte) {
 | 
			
		|||
	if i.tun.iface.IsTAP() {
 | 
			
		||||
		response, err = i.parse_packet_tap(datain)
 | 
			
		||||
	} else {
 | 
			
		||||
		response, err = i.parse_packet_tun(datain)
 | 
			
		||||
		response, err = i.parse_packet_tun(datain, nil)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -89,16 +99,14 @@ func (i *icmpv6) parse_packet(datain []byte) {
 | 
			
		|||
// 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])
 | 
			
		||||
 | 
			
		||||
	// Ignore non-IPv6 frames
 | 
			
		||||
	if binary.BigEndian.Uint16(datain[12:14]) != uint16(0x86DD) {
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Hand over to parse_packet_tun to interpret the IPv6 packet
 | 
			
		||||
	ipv6packet, err := i.parse_packet_tun(datain[len_ETHER:])
 | 
			
		||||
	mac := datain[6:12]
 | 
			
		||||
	ipv6packet, err := i.parse_packet_tun(datain[len_ETHER:], &mac)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -120,7 +128,7 @@ func (i *icmpv6) parse_packet_tap(datain []byte) ([]byte, error) {
 | 
			
		|||
// 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) {
 | 
			
		||||
func (i *icmpv6) parse_packet_tun(datain []byte, datamac *[]byte) ([]byte, error) {
 | 
			
		||||
	// Parse the IPv6 packet headers
 | 
			
		||||
	ipv6Header, err := ipv6.ParseHeader(datain[:ipv6.HeaderLen])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -137,9 +145,6 @@ func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) {
 | 
			
		|||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Store the peer link local address, it will come in useful later
 | 
			
		||||
	copy(i.peerlladdr[:], ipv6Header.Src[:])
 | 
			
		||||
 | 
			
		||||
	// Parse the ICMPv6 message contents
 | 
			
		||||
	icmpv6Header, err := icmp.ParseMessage(58, datain[ipv6.HeaderLen:])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -149,24 +154,35 @@ func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) {
 | 
			
		|||
	// Check for a supported message type
 | 
			
		||||
	switch icmpv6Header.Type {
 | 
			
		||||
	case ipv6.ICMPTypeNeighborSolicitation:
 | 
			
		||||
		{
 | 
			
		||||
			response, err := i.handle_ndp(datain[ipv6.HeaderLen:])
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				// Create our ICMPv6 response
 | 
			
		||||
				responsePacket, err := i.create_icmpv6_tun(
 | 
			
		||||
					ipv6Header.Src, i.mylladdr,
 | 
			
		||||
					ipv6.ICMPTypeNeighborAdvertisement, 0,
 | 
			
		||||
					&icmp.DefaultMessageBody{Data: response})
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return nil, err
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Send it back
 | 
			
		||||
				return responsePacket, nil
 | 
			
		||||
			} else {
 | 
			
		||||
		response, err := i.handle_ndp(datain[ipv6.HeaderLen:])
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			// Create our ICMPv6 response
 | 
			
		||||
			responsePacket, err := i.create_icmpv6_tun(
 | 
			
		||||
				ipv6Header.Src, i.mylladdr,
 | 
			
		||||
				ipv6.ICMPTypeNeighborAdvertisement, 0,
 | 
			
		||||
				&icmp.DefaultMessageBody{Data: response})
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Send it back
 | 
			
		||||
			return responsePacket, nil
 | 
			
		||||
		} else {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	case ipv6.ICMPTypeNeighborAdvertisement:
 | 
			
		||||
		if datamac != nil {
 | 
			
		||||
			var addr address
 | 
			
		||||
			var mac macAddress
 | 
			
		||||
			copy(addr[:], ipv6Header.Src[:])
 | 
			
		||||
			copy(mac[:], (*datamac)[:])
 | 
			
		||||
			neighbor := i.peermacs[addr]
 | 
			
		||||
			neighbor.mac = mac
 | 
			
		||||
			neighbor.learned = true
 | 
			
		||||
			neighbor.lastadvertisement = time.Now()
 | 
			
		||||
			i.peermacs[addr] = neighbor
 | 
			
		||||
		}
 | 
			
		||||
		return nil, errors.New("No response needed")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, errors.New("ICMPv6 type not matched")
 | 
			
		||||
| 
						 | 
				
			
			@ -238,6 +254,42 @@ func (i *icmpv6) create_icmpv6_tun(dst net.IP, src net.IP, mtype ipv6.ICMPType,
 | 
			
		|||
	return responsePacket, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (i *icmpv6) create_ndp_tap(dst address) ([]byte, error) {
 | 
			
		||||
	// Create the ND payload
 | 
			
		||||
	var payload [28]byte
 | 
			
		||||
	copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00})
 | 
			
		||||
	copy(payload[4:20], dst[:])
 | 
			
		||||
	copy(payload[20:22], []byte{0x01, 0x01})
 | 
			
		||||
	copy(payload[22:28], i.mymac[:6])
 | 
			
		||||
 | 
			
		||||
	// Create the ICMPv6 solicited-node address
 | 
			
		||||
	var dstaddr address
 | 
			
		||||
	copy(dstaddr[:13], []byte{
 | 
			
		||||
		0xFF, 0x02, 0x00, 0x00,
 | 
			
		||||
		0x00, 0x00, 0x00, 0x00,
 | 
			
		||||
		0x00, 0x00, 0x00, 0x01, 0xFF})
 | 
			
		||||
	copy(dstaddr[13:], dst[13:16])
 | 
			
		||||
 | 
			
		||||
	// Create the multicast MAC
 | 
			
		||||
	var dstmac macAddress
 | 
			
		||||
	copy(dstmac[:2], []byte{0x33, 0x33})
 | 
			
		||||
	copy(dstmac[2:6], dstaddr[12:16])
 | 
			
		||||
 | 
			
		||||
	// Create the ND request
 | 
			
		||||
	requestPacket, err := i.create_icmpv6_tap(
 | 
			
		||||
		dstmac, dstaddr[:], i.mylladdr,
 | 
			
		||||
		ipv6.ICMPTypeNeighborSolicitation, 0,
 | 
			
		||||
		&icmp.DefaultMessageBody{Data: payload[:]})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	neighbor := i.peermacs[dstaddr]
 | 
			
		||||
	neighbor.lastsolicitation = time.Now()
 | 
			
		||||
	i.peermacs[dstaddr] = neighbor
 | 
			
		||||
 | 
			
		||||
	return requestPacket, 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,9 @@ package yggdrasil
 | 
			
		|||
// This manages the tun driver to send/recv packets to/from applications
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"time"
 | 
			
		||||
	"yggdrasil/defaults"
 | 
			
		||||
 | 
			
		||||
	"github.com/songgao/packets/ethernet"
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +51,21 @@ func (tun *tunDevice) start(ifname string, iftapmode bool, addr string, mtu int)
 | 
			
		|||
	}
 | 
			
		||||
	go func() { panic(tun.read()) }()
 | 
			
		||||
	go func() { panic(tun.write()) }()
 | 
			
		||||
	go func() {
 | 
			
		||||
		for {
 | 
			
		||||
			if _, ok := tun.icmpv6.peermacs[tun.core.router.addr]; ok {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			request, err := tun.icmpv6.create_ndp_tap(tun.core.router.addr)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic(err)
 | 
			
		||||
			}
 | 
			
		||||
			if _, err := tun.iface.Write(request); err != nil {
 | 
			
		||||
				panic(err)
 | 
			
		||||
			}
 | 
			
		||||
			time.Sleep(time.Second)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -61,16 +79,74 @@ func (tun *tunDevice) write() error {
 | 
			
		|||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if tun.iface.IsTAP() {
 | 
			
		||||
			var frame ethernet.Frame
 | 
			
		||||
			frame.Prepare(
 | 
			
		||||
				tun.icmpv6.peermac[:6], // Destination MAC address
 | 
			
		||||
				tun.icmpv6.mymac[:6],   // Source MAC address
 | 
			
		||||
				ethernet.NotTagged,     // VLAN tagging
 | 
			
		||||
				ethernet.IPv6,          // Ethertype
 | 
			
		||||
				len(data))              // Payload length
 | 
			
		||||
			copy(frame[tun_ETHER_HEADER_LENGTH:], data[:])
 | 
			
		||||
			if _, err := tun.iface.Write(frame); err != nil {
 | 
			
		||||
				panic(err)
 | 
			
		||||
			var destAddr address
 | 
			
		||||
			if data[0]&0xf0 == 0x60 {
 | 
			
		||||
				if len(data) < 40 {
 | 
			
		||||
					panic("Tried to send a packet shorter than an IPv6 header...")
 | 
			
		||||
				}
 | 
			
		||||
				copy(destAddr[:16], data[24:])
 | 
			
		||||
			} else if data[0]&0xf0 == 0x40 {
 | 
			
		||||
				if len(data) < 20 {
 | 
			
		||||
					panic("Tried to send a packet shorter than an IPv4 header...")
 | 
			
		||||
				}
 | 
			
		||||
				copy(destAddr[:4], data[16:])
 | 
			
		||||
			} else {
 | 
			
		||||
				return errors.New("Invalid address family")
 | 
			
		||||
			}
 | 
			
		||||
			sendndp := func(destAddr address) {
 | 
			
		||||
				neigh, known := tun.icmpv6.peermacs[destAddr]
 | 
			
		||||
				known = known && (time.Since(neigh.lastsolicitation).Seconds() < 30)
 | 
			
		||||
				if !known {
 | 
			
		||||
					request, err := tun.icmpv6.create_ndp_tap(destAddr)
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						panic(err)
 | 
			
		||||
					}
 | 
			
		||||
					if _, err := tun.iface.Write(request); err != nil {
 | 
			
		||||
						panic(err)
 | 
			
		||||
					}
 | 
			
		||||
					tun.icmpv6.peermacs[destAddr] = neighbor{
 | 
			
		||||
						lastsolicitation: time.Now(),
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			var peermac macAddress
 | 
			
		||||
			var peerknown bool
 | 
			
		||||
			if data[0]&0xf0 == 0x40 {
 | 
			
		||||
				destAddr = tun.core.router.addr
 | 
			
		||||
			} else if data[0]&0xf0 == 0x60 {
 | 
			
		||||
				if !bytes.Equal(tun.core.router.addr[:16], destAddr[:16]) && !bytes.Equal(tun.core.router.subnet[:8], destAddr[:8]) {
 | 
			
		||||
					destAddr = tun.core.router.addr
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if neighbor, ok := tun.icmpv6.peermacs[destAddr]; ok && neighbor.learned {
 | 
			
		||||
				peermac = neighbor.mac
 | 
			
		||||
				peerknown = true
 | 
			
		||||
			} else if neighbor, ok := tun.icmpv6.peermacs[tun.core.router.addr]; ok && neighbor.learned {
 | 
			
		||||
				peermac = neighbor.mac
 | 
			
		||||
				peerknown = true
 | 
			
		||||
				sendndp(destAddr)
 | 
			
		||||
			} else {
 | 
			
		||||
				sendndp(tun.core.router.addr)
 | 
			
		||||
			}
 | 
			
		||||
			if peerknown {
 | 
			
		||||
				var proto ethernet.Ethertype
 | 
			
		||||
				switch {
 | 
			
		||||
				case data[0]&0xf0 == 0x60:
 | 
			
		||||
					proto = ethernet.IPv6
 | 
			
		||||
				case data[0]&0xf0 == 0x40:
 | 
			
		||||
					proto = ethernet.IPv4
 | 
			
		||||
				}
 | 
			
		||||
				var frame ethernet.Frame
 | 
			
		||||
				frame.Prepare(
 | 
			
		||||
					peermac[:6],          // Destination MAC address
 | 
			
		||||
					tun.icmpv6.mymac[:6], // Source MAC address
 | 
			
		||||
					ethernet.NotTagged,   // VLAN tagging
 | 
			
		||||
					proto,                // Ethertype
 | 
			
		||||
					len(data))            // Payload length
 | 
			
		||||
				copy(frame[tun_ETHER_HEADER_LENGTH:], data[:])
 | 
			
		||||
				if _, err := tun.iface.Write(frame); err != nil {
 | 
			
		||||
					panic(err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if _, err := tun.iface.Write(data); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue