mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 11:15:07 +03:00 
			
		
		
		
	More complete NDP implementation for TAP mode, which tracks individual MAC addresses for neighbors
This commit is contained in:
		
							parent
							
								
									7af85c7d70
								
							
						
					
					
						commit
						d50e1bc803
					
				
					 2 changed files with 120 additions and 41 deletions
				
			
		| 
						 | 
					@ -24,10 +24,9 @@ const len_ETHER = 14
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type icmpv6 struct {
 | 
					type icmpv6 struct {
 | 
				
			||||||
	tun      *tunDevice
 | 
						tun      *tunDevice
 | 
				
			||||||
	peermac    macAddress
 | 
					 | 
				
			||||||
	peerlladdr net.IP
 | 
					 | 
				
			||||||
	mylladdr net.IP
 | 
						mylladdr net.IP
 | 
				
			||||||
	mymac    macAddress
 | 
						mymac    macAddress
 | 
				
			||||||
 | 
						peermacs map[address]macAddress
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Marshal returns the binary encoding of h.
 | 
					// Marshal returns the binary encoding of h.
 | 
				
			||||||
| 
						 | 
					@ -52,13 +51,16 @@ func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) {
 | 
				
			||||||
// addresses.
 | 
					// addresses.
 | 
				
			||||||
func (i *icmpv6) init(t *tunDevice) {
 | 
					func (i *icmpv6) init(t *tunDevice) {
 | 
				
			||||||
	i.tun = t
 | 
						i.tun = t
 | 
				
			||||||
 | 
						i.peermacs = make(map[address]macAddress)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Our MAC address and link-local address
 | 
						// Our MAC address and link-local address
 | 
				
			||||||
	copy(i.mymac[:], []byte{
 | 
						i.mymac = macAddress{
 | 
				
			||||||
		0x02, 0x00, 0x00, 0x00, 0x00, 0x02})
 | 
							0x02, 0x00, 0x00, 0x00, 0x00, 0x02}
 | 
				
			||||||
	i.mylladdr = net.IP{
 | 
						i.mylladdr = net.IP{
 | 
				
			||||||
		0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
							0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE}
 | 
							0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE}
 | 
				
			||||||
 | 
						copy(i.mymac[1:], i.tun.core.boxPub[:])
 | 
				
			||||||
 | 
						copy(i.mylladdr[9:], i.tun.core.boxPub[:])
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Parses an incoming ICMPv6 packet. The packet provided may be either an
 | 
					// Parses an incoming ICMPv6 packet. The packet provided may be either an
 | 
				
			||||||
| 
						 | 
					@ -73,7 +75,7 @@ func (i *icmpv6) parse_packet(datain []byte) {
 | 
				
			||||||
	if i.tun.iface.IsTAP() {
 | 
						if i.tun.iface.IsTAP() {
 | 
				
			||||||
		response, err = i.parse_packet_tap(datain)
 | 
							response, err = i.parse_packet_tap(datain)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		response, err = i.parse_packet_tun(datain)
 | 
							response, err = i.parse_packet_tun(datain, nil)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -89,16 +91,14 @@ func (i *icmpv6) parse_packet(datain []byte) {
 | 
				
			||||||
// A response buffer is also created for the response message, also complete
 | 
					// A response buffer is also created for the response message, also complete
 | 
				
			||||||
// with ethernet headers.
 | 
					// with ethernet headers.
 | 
				
			||||||
func (i *icmpv6) parse_packet_tap(datain []byte) ([]byte, error) {
 | 
					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
 | 
						// Ignore non-IPv6 frames
 | 
				
			||||||
	if binary.BigEndian.Uint16(datain[12:14]) != uint16(0x86DD) {
 | 
						if binary.BigEndian.Uint16(datain[12:14]) != uint16(0x86DD) {
 | 
				
			||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Hand over to parse_packet_tun to interpret the IPv6 packet
 | 
						// 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 {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -120,7 +120,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
 | 
					// 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
 | 
					// ICMPv6 message match a known expected type. The relevant handler function
 | 
				
			||||||
// is then called and a response packet may be returned.
 | 
					// 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
 | 
						// Parse the IPv6 packet headers
 | 
				
			||||||
	ipv6Header, err := ipv6.ParseHeader(datain[:ipv6.HeaderLen])
 | 
						ipv6Header, err := ipv6.ParseHeader(datain[:ipv6.HeaderLen])
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -137,9 +137,6 @@ func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) {
 | 
				
			||||||
		return nil, err
 | 
							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
 | 
						// Parse the ICMPv6 message contents
 | 
				
			||||||
	icmpv6Header, err := icmp.ParseMessage(58, datain[ipv6.HeaderLen:])
 | 
						icmpv6Header, err := icmp.ParseMessage(58, datain[ipv6.HeaderLen:])
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -149,7 +146,6 @@ func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) {
 | 
				
			||||||
	// Check for a supported message type
 | 
						// Check for a supported message type
 | 
				
			||||||
	switch icmpv6Header.Type {
 | 
						switch icmpv6Header.Type {
 | 
				
			||||||
	case ipv6.ICMPTypeNeighborSolicitation:
 | 
						case ipv6.ICMPTypeNeighborSolicitation:
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
		response, err := i.handle_ndp(datain[ipv6.HeaderLen:])
 | 
							response, err := i.handle_ndp(datain[ipv6.HeaderLen:])
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			// Create our ICMPv6 response
 | 
								// Create our ICMPv6 response
 | 
				
			||||||
| 
						 | 
					@ -166,7 +162,15 @@ func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) {
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						case ipv6.ICMPTypeNeighborAdvertisement:
 | 
				
			||||||
 | 
							if datamac != nil {
 | 
				
			||||||
 | 
								var addr address
 | 
				
			||||||
 | 
								var mac macAddress
 | 
				
			||||||
 | 
								copy(addr[:], ipv6Header.Src[:])
 | 
				
			||||||
 | 
								copy(mac[:], (*datamac)[:])
 | 
				
			||||||
 | 
								i.peermacs[addr] = mac
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							return nil, errors.New("No response needed")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil, errors.New("ICMPv6 type not matched")
 | 
						return nil, errors.New("ICMPv6 type not matched")
 | 
				
			||||||
| 
						 | 
					@ -238,6 +242,55 @@ func (i *icmpv6) create_icmpv6_tun(dst net.IP, src net.IP, mtype ipv6.ICMPType,
 | 
				
			||||||
	return responsePacket, nil
 | 
						return responsePacket, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (i *icmpv6) create_ndp_tap(in []byte) ([]byte, error) {
 | 
				
			||||||
 | 
						// Parse the IPv6 packet headers
 | 
				
			||||||
 | 
						ipv6Header, err := ipv6.ParseHeader(in[:ipv6.HeaderLen])
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check if the packet is IPv6
 | 
				
			||||||
 | 
						if ipv6Header.Version != ipv6.Version {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check if the packet is ICMPv6
 | 
				
			||||||
 | 
						if ipv6Header.NextHeader != 58 {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create the ND payload
 | 
				
			||||||
 | 
						var payload [28]byte
 | 
				
			||||||
 | 
						copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00})
 | 
				
			||||||
 | 
						copy(payload[4:20], ipv6Header.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:], ipv6Header.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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return requestPacket, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Generates a response to an NDP discovery packet. This is effectively called
 | 
					// 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
 | 
					// 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
 | 
					// the fd00::/8 range, so that the operating system knows to route that traffic
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,6 +3,7 @@ package yggdrasil
 | 
				
			||||||
// This manages the tun driver to send/recv packets to/from applications
 | 
					// This manages the tun driver to send/recv packets to/from applications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"yggdrasil/defaults"
 | 
						"yggdrasil/defaults"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/songgao/packets/ethernet"
 | 
						"github.com/songgao/packets/ethernet"
 | 
				
			||||||
| 
						 | 
					@ -61,9 +62,24 @@ func (tun *tunDevice) write() error {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if tun.iface.IsTAP() {
 | 
							if tun.iface.IsTAP() {
 | 
				
			||||||
 | 
								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")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if peermac, ok := tun.icmpv6.peermacs[destAddr]; ok {
 | 
				
			||||||
				var frame ethernet.Frame
 | 
									var frame ethernet.Frame
 | 
				
			||||||
				frame.Prepare(
 | 
									frame.Prepare(
 | 
				
			||||||
				tun.icmpv6.peermac[:6], // Destination MAC address
 | 
										peermac[:6],          // Destination MAC address
 | 
				
			||||||
					tun.icmpv6.mymac[:6], // Source MAC address
 | 
										tun.icmpv6.mymac[:6], // Source MAC address
 | 
				
			||||||
					ethernet.NotTagged,   // VLAN tagging
 | 
										ethernet.NotTagged,   // VLAN tagging
 | 
				
			||||||
					ethernet.IPv6,        // Ethertype
 | 
										ethernet.IPv6,        // Ethertype
 | 
				
			||||||
| 
						 | 
					@ -72,6 +88,16 @@ func (tun *tunDevice) write() error {
 | 
				
			||||||
				if _, err := tun.iface.Write(frame); err != nil {
 | 
									if _, err := tun.iface.Write(frame); err != nil {
 | 
				
			||||||
					panic(err)
 | 
										panic(err)
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									request, err := tun.icmpv6.create_ndp_tap(data)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										panic(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if _, err := tun.iface.Write(request); err != nil {
 | 
				
			||||||
 | 
										panic(err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			if _, err := tun.iface.Write(data); err != nil {
 | 
								if _, err := tun.iface.Write(data); err != nil {
 | 
				
			||||||
				panic(err)
 | 
									panic(err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue