mirror of
				https://github.com/yggdrasil-network/yggdrasil-go.git
				synced 2025-11-04 11:15:07 +03:00 
			
		
		
		
	TAP support added
- Supports Windows using OpenVPN NDIS 6 TAP driver - Supports NDP Neighbor Solicitation and Advertisements in ndp.go - Supports TAP encapsulation and decapsulation in tun.go
This commit is contained in:
		
							parent
							
								
									2b7c6eafcd
								
							
						
					
					
						commit
						ff55070458
					
				
					 4 changed files with 232 additions and 6 deletions
				
			
		
							
								
								
									
										165
									
								
								src/yggdrasil/ndp.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										165
									
								
								src/yggdrasil/ndp.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,165 @@
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "golang.org/x/net/icmp"
 | 
				
			||||||
 | 
					import "encoding/binary"
 | 
				
			||||||
 | 
					import "unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type macAddress [6]byte
 | 
				
			||||||
 | 
					type ipv6Address [16]byte
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const ETHER = 14
 | 
				
			||||||
 | 
					const IPV6 = 40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ndp struct {
 | 
				
			||||||
 | 
						tun        *tunDevice
 | 
				
			||||||
 | 
						peermac    macAddress
 | 
				
			||||||
 | 
						peerlladdr ipv6Address
 | 
				
			||||||
 | 
						mymac      macAddress
 | 
				
			||||||
 | 
						mylladdr   ipv6Address
 | 
				
			||||||
 | 
						recv       chan []byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type etherHeader struct {
 | 
				
			||||||
 | 
						destination macAddress
 | 
				
			||||||
 | 
						source      macAddress
 | 
				
			||||||
 | 
						ethertype   [2]byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ipv6Header struct {
 | 
				
			||||||
 | 
						preamble    [4]byte
 | 
				
			||||||
 | 
						length      [2]byte
 | 
				
			||||||
 | 
						nextheader  byte
 | 
				
			||||||
 | 
						hoplimit    byte
 | 
				
			||||||
 | 
						source      ipv6Address
 | 
				
			||||||
 | 
						destination ipv6Address
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type icmpv6Header struct {
 | 
				
			||||||
 | 
						messagetype byte
 | 
				
			||||||
 | 
						code        byte
 | 
				
			||||||
 | 
						checksum    uint16
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type icmpv6PseudoHeader struct {
 | 
				
			||||||
 | 
						source      ipv6Address
 | 
				
			||||||
 | 
						destination ipv6Address
 | 
				
			||||||
 | 
						length      [4]byte
 | 
				
			||||||
 | 
						zero        [3]byte
 | 
				
			||||||
 | 
						nextheader  byte
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type icmpv6Packet struct {
 | 
				
			||||||
 | 
						ether            etherHeader
 | 
				
			||||||
 | 
						ipv6             ipv6Header
 | 
				
			||||||
 | 
						icmpv6           icmpv6Header
 | 
				
			||||||
 | 
						flags            [4]byte
 | 
				
			||||||
 | 
						targetaddress    ipv6Address
 | 
				
			||||||
 | 
						optiontype       byte
 | 
				
			||||||
 | 
						optionlength     byte
 | 
				
			||||||
 | 
						linklayeraddress macAddress
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *ndp) init(t *tunDevice) {
 | 
				
			||||||
 | 
						n.tun = t
 | 
				
			||||||
 | 
						n.recv = make(chan []byte)
 | 
				
			||||||
 | 
						copy(n.mymac[:], []byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x02})
 | 
				
			||||||
 | 
						copy(n.mylladdr[:], []byte{
 | 
				
			||||||
 | 
							0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 | 
				
			||||||
 | 
							0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE})
 | 
				
			||||||
 | 
						go n.listen()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *ndp) listen() {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							// Receive from the channel and check if we're using TAP instead
 | 
				
			||||||
 | 
							// of TUN mode - NDP is only relevant for TAP
 | 
				
			||||||
 | 
							datain := <-n.recv
 | 
				
			||||||
 | 
							if !n.tun.iface.IsTAP() {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Create our return frame buffer and also the unsafe pointers to
 | 
				
			||||||
 | 
							// map them to the structs
 | 
				
			||||||
 | 
							dataout := make([]byte, ETHER+IPV6+32)
 | 
				
			||||||
 | 
							in := (*icmpv6Packet)(unsafe.Pointer(&datain[0]))
 | 
				
			||||||
 | 
							out := (*icmpv6Packet)(unsafe.Pointer(&dataout[0]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Store peer MAC address and link-local IP address -
 | 
				
			||||||
 | 
							// these will be used later by tun.go
 | 
				
			||||||
 | 
							copy(n.peermac[:6], in.ether.source[:6])
 | 
				
			||||||
 | 
							copy(n.peerlladdr[:16], in.ipv6.source[:16])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Ignore non-IPv6 packets
 | 
				
			||||||
 | 
							if binary.BigEndian.Uint16(in.ether.ethertype[:]) != uint16(0x86DD) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Ignore non-ICMPv6 packets
 | 
				
			||||||
 | 
							if in.ipv6.nextheader != uint8(0x3A) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Ignore non-NDP Solicitation packets
 | 
				
			||||||
 | 
							if in.icmpv6.messagetype != uint8(135) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Ignore NDP requests for anything outside of fd00::/8
 | 
				
			||||||
 | 
							if in.targetaddress[0] != 0xFD {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Populate the out ethernet headers
 | 
				
			||||||
 | 
							copy(out.ether.destination[:], in.ether.destination[:])
 | 
				
			||||||
 | 
							copy(out.ether.source[:], n.mymac[:])
 | 
				
			||||||
 | 
							binary.BigEndian.PutUint16(out.ether.ethertype[:], uint16(0x86DD))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// And for now just copy the rest of the packet we were sent
 | 
				
			||||||
 | 
							copy(dataout[ETHER:ETHER+IPV6], datain[ETHER:ETHER+IPV6])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Update the source and destination addresses in the IPv6 header
 | 
				
			||||||
 | 
							copy(out.ipv6.destination[:], in.ipv6.source[:])
 | 
				
			||||||
 | 
							copy(out.ipv6.source[:], n.mylladdr[:])
 | 
				
			||||||
 | 
							binary.BigEndian.PutUint16(out.ipv6.length[:], uint16(32))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Copy the payload
 | 
				
			||||||
 | 
							copy(dataout[ETHER+IPV6:], datain[ETHER+IPV6:])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Update the ICMPv6 headers
 | 
				
			||||||
 | 
							out.icmpv6.messagetype = uint8(136)
 | 
				
			||||||
 | 
							out.icmpv6.code = uint8(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Update the ICMPv6 payload
 | 
				
			||||||
 | 
							copy(out.targetaddress[:], in.targetaddress[:])
 | 
				
			||||||
 | 
							out.optiontype = uint8(2)
 | 
				
			||||||
 | 
							out.optionlength = uint8(1)
 | 
				
			||||||
 | 
							copy(out.linklayeraddress[:], n.mymac[:])
 | 
				
			||||||
 | 
							binary.BigEndian.PutUint32(out.flags[:], uint32(0x20000000))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Generate the pseudo-header for the checksum
 | 
				
			||||||
 | 
							ps := make([]byte, 44)
 | 
				
			||||||
 | 
							pseudo := (*icmpv6PseudoHeader)(unsafe.Pointer(&ps[0]))
 | 
				
			||||||
 | 
							copy(pseudo.destination[:], out.ipv6.destination[:])
 | 
				
			||||||
 | 
							copy(pseudo.source[:], out.ipv6.source[:])
 | 
				
			||||||
 | 
							binary.BigEndian.PutUint32(pseudo.length[:], uint32(binary.BigEndian.Uint16(out.ipv6.length[:])))
 | 
				
			||||||
 | 
							pseudo.nextheader = out.ipv6.nextheader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Lazy-man's checksum using the icmp library
 | 
				
			||||||
 | 
							icmpv6, err := icmp.ParseMessage(0x3A, dataout[ETHER+IPV6:])
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							payload, err := icmpv6.Marshal(ps)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							copy(dataout[ETHER+IPV6:], payload)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Send the frame back to the TAP adapter
 | 
				
			||||||
 | 
							n.tun.iface.Write(dataout)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,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 water "github.com/songgao/water"
 | 
					import ethernet "github.com/songgao/packets/ethernet"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const IPv6_HEADER_LENGTH = 40
 | 
					const IPv6_HEADER_LENGTH = 40
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,7 @@ type tunInterface interface {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type tunDevice struct {
 | 
					type tunDevice struct {
 | 
				
			||||||
	core  *Core
 | 
						core  *Core
 | 
				
			||||||
 | 
						ndp   ndp
 | 
				
			||||||
	send  chan<- []byte
 | 
						send  chan<- []byte
 | 
				
			||||||
	recv  <-chan []byte
 | 
						recv  <-chan []byte
 | 
				
			||||||
	mtu   int
 | 
						mtu   int
 | 
				
			||||||
| 
						 | 
					@ -25,13 +26,28 @@ type tunDevice struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tun *tunDevice) init(core *Core) {
 | 
					func (tun *tunDevice) init(core *Core) {
 | 
				
			||||||
	tun.core = core
 | 
						tun.core = core
 | 
				
			||||||
 | 
						tun.ndp.init(tun)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tun *tunDevice) write() error {
 | 
					func (tun *tunDevice) write() error {
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		data := <-tun.recv
 | 
							data := <-tun.recv
 | 
				
			||||||
		if _, err := tun.iface.Write(data); err != nil {
 | 
							if tun.iface.IsTAP() {
 | 
				
			||||||
			return err
 | 
								var frame ethernet.Frame
 | 
				
			||||||
 | 
								frame.Prepare(
 | 
				
			||||||
 | 
									tun.ndp.peermac[:6], // Destination MAC address
 | 
				
			||||||
 | 
									tun.ndp.mymac[:6],   // Source MAC address
 | 
				
			||||||
 | 
									ethernet.NotTagged,  // VLAN tagging
 | 
				
			||||||
 | 
									ethernet.IPv6,       // Ethertype
 | 
				
			||||||
 | 
									len(data))           // Payload length
 | 
				
			||||||
 | 
								copy(frame[14:], data[:])
 | 
				
			||||||
 | 
								if _, err := tun.iface.Write(frame); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								if _, err := tun.iface.Write(data); err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		util_putBytes(data)
 | 
							util_putBytes(data)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -44,13 +60,20 @@ func (tun *tunDevice) read() error {
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if buf[0]&0xf0 != 0x60 ||
 | 
							o := 0
 | 
				
			||||||
			n != 256*int(buf[4])+int(buf[5])+IPv6_HEADER_LENGTH {
 | 
							if tun.iface.IsTAP() {
 | 
				
			||||||
 | 
								o = 14
 | 
				
			||||||
 | 
								b := make([]byte, n)
 | 
				
			||||||
 | 
								copy(b, buf)
 | 
				
			||||||
 | 
								tun.ndp.recv <- b
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if buf[o]&0xf0 != 0x60 ||
 | 
				
			||||||
 | 
								n != 256*int(buf[o+4])+int(buf[o+5])+IPv6_HEADER_LENGTH+o {
 | 
				
			||||||
			// Either not an IPv6 packet or not the complete packet for some reason
 | 
								// Either not an IPv6 packet or not the complete packet for some reason
 | 
				
			||||||
			//panic("Should not happen in testing")
 | 
								//panic("Should not happen in testing")
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		packet := append(util_getBytes(), buf[:n]...)
 | 
							packet := append(util_getBytes(), buf[o:n]...)
 | 
				
			||||||
		tun.send <- packet
 | 
							tun.send <- packet
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
// +build !linux
 | 
					// +build !linux
 | 
				
			||||||
// +build !darwin
 | 
					// +build !darwin
 | 
				
			||||||
 | 
					// +build !windows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package yggdrasil
 | 
					package yggdrasil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										37
									
								
								src/yggdrasil/tun_windows.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/yggdrasil/tun_windows.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					package yggdrasil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import water "github.com/songgao/water"
 | 
				
			||||||
 | 
					import "os/exec"
 | 
				
			||||||
 | 
					import "strings"
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This is to catch Windows platforms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (tun *tunDevice) setup(ifname string, addr string, mtu int) error {
 | 
				
			||||||
 | 
						config := water.Config{DeviceType: water.TAP}
 | 
				
			||||||
 | 
						config.PlatformSpecificParams.ComponentID = "tap0901"
 | 
				
			||||||
 | 
						config.PlatformSpecificParams.Network = "169.254.0.1/32"
 | 
				
			||||||
 | 
						iface, err := water.New(config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tun.iface = iface
 | 
				
			||||||
 | 
						tun.mtu = mtu
 | 
				
			||||||
 | 
						return tun.setupAddress(addr)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (tun *tunDevice) setupAddress(addr string) error {
 | 
				
			||||||
 | 
						// Set address
 | 
				
			||||||
 | 
						// addr = strings.TrimRight(addr, "/8")
 | 
				
			||||||
 | 
						cmd := exec.Command("netsh", "interface", "ipv6", "set", "address",
 | 
				
			||||||
 | 
							fmt.Sprintf("interface=\"%s\"", tun.iface.Name()),
 | 
				
			||||||
 | 
							fmt.Sprintf("addr=\"%s\"", addr))
 | 
				
			||||||
 | 
						tun.core.log.Printf("netsh command: %v", strings.Join(cmd.Args, " "))
 | 
				
			||||||
 | 
						output, err := cmd.CombinedOutput()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							tun.core.log.Printf("Windows netsh failed: %v.", err)
 | 
				
			||||||
 | 
							tun.core.log.Println(string(output))
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue