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:
commit
b3887e554c
@ -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
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user