mirror of
https://github.com/cwinfo/yggdrasil-go.git
synced 2024-11-14 22:00:28 +00: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
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…
Reference in New Issue
Block a user