5
0
mirror of https://github.com/cwinfo/yggdrasil-go.git synced 2024-11-12 23:20:28 +00:00

Merge pull request #204 from neilalexander/tapmac

Neighbor discovery changes for TAP mode
This commit is contained in:
Neil Alexander 2018-11-10 19:41:50 +00:00 committed by GitHub
commit b3887e554c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 169 additions and 41 deletions

View File

@ -13,6 +13,7 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"net" "net"
"time"
"golang.org/x/net/icmp" "golang.org/x/net/icmp"
"golang.org/x/net/ipv6" "golang.org/x/net/ipv6"
@ -23,11 +24,17 @@ type macAddress [6]byte
const len_ETHER = 14 const len_ETHER = 14
type icmpv6 struct { type icmpv6 struct {
tun *tunDevice tun *tunDevice
peermac macAddress mylladdr net.IP
peerlladdr net.IP mymac macAddress
mylladdr net.IP peermacs map[address]neighbor
mymac macAddress }
type neighbor struct {
mac macAddress
learned bool
lastadvertisement time.Time
lastsolicitation time.Time
} }
// Marshal returns the binary encoding of h. // Marshal returns the binary encoding of h.
@ -52,13 +59,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]neighbor)
// 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[:], 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 // 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() { 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 +99,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 +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 // 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 +145,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,24 +154,35 @@ 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 responsePacket, err := i.create_icmpv6_tun(
responsePacket, err := i.create_icmpv6_tun( ipv6Header.Src, i.mylladdr,
ipv6Header.Src, i.mylladdr, ipv6.ICMPTypeNeighborAdvertisement, 0,
ipv6.ICMPTypeNeighborAdvertisement, 0, &icmp.DefaultMessageBody{Data: response})
&icmp.DefaultMessageBody{Data: response}) if err != nil {
if err != nil {
return nil, err
}
// Send it back
return responsePacket, nil
} else {
return nil, err 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") 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 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 // 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

View File

@ -3,6 +3,9 @@ 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 (
"bytes"
"errors"
"time"
"yggdrasil/defaults" "yggdrasil/defaults"
"github.com/songgao/packets/ethernet" "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.read()) }()
go func() { panic(tun.write()) }() 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 return nil
} }
@ -61,16 +79,74 @@ func (tun *tunDevice) write() error {
continue continue
} }
if tun.iface.IsTAP() { if tun.iface.IsTAP() {
var frame ethernet.Frame var destAddr address
frame.Prepare( if data[0]&0xf0 == 0x60 {
tun.icmpv6.peermac[:6], // Destination MAC address if len(data) < 40 {
tun.icmpv6.mymac[:6], // Source MAC address panic("Tried to send a packet shorter than an IPv6 header...")
ethernet.NotTagged, // VLAN tagging }
ethernet.IPv6, // Ethertype copy(destAddr[:16], data[24:])
len(data)) // Payload length } else if data[0]&0xf0 == 0x40 {
copy(frame[tun_ETHER_HEADER_LENGTH:], data[:]) if len(data) < 20 {
if _, err := tun.iface.Write(frame); err != nil { panic("Tried to send a packet shorter than an IPv4 header...")
panic(err) }
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 { } else {
if _, err := tun.iface.Write(data); err != nil { if _, err := tun.iface.Write(data); err != nil {