mirror of
https://github.com/yggdrasil-network/yggdrasil-go.git
synced 2024-11-27 12:05:23 +00:00
Merge pull request #14 from neilalexander/icmpv6
Replace ndp.go with icmpv6.go
This commit is contained in:
commit
a3a9696880
226
src/yggdrasil/icmpv6.go
Normal file
226
src/yggdrasil/icmpv6.go
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
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 "net"
|
||||||
|
import "golang.org/x/net/ipv6"
|
||||||
|
import "golang.org/x/net/icmp"
|
||||||
|
import "encoding/binary"
|
||||||
|
|
||||||
|
type macAddress [6]byte
|
||||||
|
|
||||||
|
const ETHER = 14
|
||||||
|
|
||||||
|
type icmpv6 struct {
|
||||||
|
tun *tunDevice
|
||||||
|
peermac macAddress
|
||||||
|
peerlladdr net.IP
|
||||||
|
mylladdr net.IP
|
||||||
|
mymac macAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns the binary encoding of h.
|
||||||
|
func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) {
|
||||||
|
b := make([]byte, 40)
|
||||||
|
b[0] |= byte(h.Version) << 4
|
||||||
|
b[0] |= byte(h.TrafficClass) >> 4
|
||||||
|
b[1] |= byte(h.TrafficClass) << 4
|
||||||
|
b[1] |= byte(h.FlowLabel >> 16)
|
||||||
|
b[2] = byte(h.FlowLabel >> 8)
|
||||||
|
b[3] = byte(h.FlowLabel)
|
||||||
|
binary.BigEndian.PutUint16(b[4:6], uint16(h.PayloadLen))
|
||||||
|
b[6] = byte(h.NextHeader)
|
||||||
|
b[7] = byte(h.HopLimit)
|
||||||
|
copy(b[8:24], h.Src)
|
||||||
|
copy(b[24:40], h.Dst)
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *icmpv6) init(t *tunDevice) {
|
||||||
|
i.tun = t
|
||||||
|
|
||||||
|
// Our MAC address and link-local address
|
||||||
|
copy(i.mymac[:], []byte{
|
||||||
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x02})
|
||||||
|
i.mylladdr = net.IP{
|
||||||
|
0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *icmpv6) parse_packet(datain []byte) {
|
||||||
|
var response []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Parse the frame/packet
|
||||||
|
if i.tun.iface.IsTAP() {
|
||||||
|
response, err = i.parse_packet_tap(datain)
|
||||||
|
} else {
|
||||||
|
response, err = i.parse_packet_tun(datain)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the packet to TUN/TAP
|
||||||
|
i.tun.iface.Write(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
if binary.BigEndian.Uint16(datain[12:14]) != uint16(0x86DD) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hand over to parse_packet_tun to interpret the IPv6 packet
|
||||||
|
ipv6packet, err := i.parse_packet_tun(datain[ETHER:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the response buffer
|
||||||
|
dataout := make([]byte, ETHER+ipv6.HeaderLen+32)
|
||||||
|
|
||||||
|
// Populate the response ethernet headers
|
||||||
|
copy(dataout[:6], datain[6:12])
|
||||||
|
copy(dataout[6:12], i.mymac[:])
|
||||||
|
binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD))
|
||||||
|
|
||||||
|
// Copy the returned packet to our response ethernet frame
|
||||||
|
copy(dataout[ETHER:], ipv6packet)
|
||||||
|
return dataout, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) {
|
||||||
|
// Parse the IPv6 packet headers
|
||||||
|
ipv6Header, err := ipv6.ParseHeader(datain[: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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the peer link local address, it will come in useful later
|
||||||
|
copy(i.peerlladdr[:], ipv6Header.Src[:])
|
||||||
|
|
||||||
|
// Parse the ICMPv6 message contents
|
||||||
|
icmpv6Header, err := icmp.ParseMessage(58, datain[ipv6.HeaderLen:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for a supported message type
|
||||||
|
switch icmpv6Header.Type {
|
||||||
|
case ipv6.ICMPTypeNeighborSolicitation:
|
||||||
|
{
|
||||||
|
response, err := i.handle_ndp(datain[ipv6.HeaderLen:])
|
||||||
|
if err == nil {
|
||||||
|
// Create our ICMPv6 response
|
||||||
|
responsePacket, err := i.create_icmpv6_tun(ipv6Header.Src, ipv6.ICMPTypeNeighborAdvertisement, 0, response)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix the checksum because I don't even know why, net/icmp is stupid
|
||||||
|
responsePacket[17] ^= 0x4
|
||||||
|
|
||||||
|
// Send it back
|
||||||
|
return responsePacket, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *icmpv6) create_icmpv6_tap(dstmac macAddress, dst net.IP, mtype ipv6.ICMPType, mcode int, mbody []byte) ([]byte, error) {
|
||||||
|
// Pass through to create_icmpv6_tun
|
||||||
|
ipv6packet, err := i.create_icmpv6_tun(dst, mtype, mcode, mbody)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the response buffer
|
||||||
|
dataout := make([]byte, ETHER+ipv6.HeaderLen+len(mbody))
|
||||||
|
|
||||||
|
// Populate the response ethernet headers
|
||||||
|
copy(dataout[:6], dstmac[:6])
|
||||||
|
copy(dataout[6:12], i.mymac[:])
|
||||||
|
binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD))
|
||||||
|
|
||||||
|
// Copy the returned packet to our response ethernet frame
|
||||||
|
copy(dataout[ETHER:], ipv6packet)
|
||||||
|
return dataout, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *icmpv6) create_icmpv6_tun(dst net.IP, mtype ipv6.ICMPType, mcode int, mbody []byte) ([]byte, error) {
|
||||||
|
// Create the IPv6 header
|
||||||
|
ipv6Header := ipv6.Header{
|
||||||
|
Version: ipv6.Version,
|
||||||
|
NextHeader: 58,
|
||||||
|
PayloadLen: len(mbody),
|
||||||
|
HopLimit: 255,
|
||||||
|
Src: i.mylladdr,
|
||||||
|
Dst: dst,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the ICMPv6 message
|
||||||
|
icmpMessage := icmp.Message{
|
||||||
|
Type: mtype,
|
||||||
|
Code: mcode,
|
||||||
|
Body: &icmp.DefaultMessageBody{Data: mbody},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the IPv6 header into []byte
|
||||||
|
ipv6HeaderBuf, err := ipv6Header_Marshal(&ipv6Header)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the ICMPv6 message into []byte
|
||||||
|
icmpMessageBuf, err := icmpMessage.Marshal(icmp.IPv6PseudoHeader(ipv6Header.Dst, ipv6Header.Src))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Construct the packet
|
||||||
|
responsePacket := make([]byte, ipv6.HeaderLen+ipv6Header.PayloadLen)
|
||||||
|
copy(responsePacket[:ipv6.HeaderLen], ipv6HeaderBuf)
|
||||||
|
copy(responsePacket[ipv6.HeaderLen:], icmpMessageBuf)
|
||||||
|
|
||||||
|
// Send it back
|
||||||
|
return responsePacket, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *icmpv6) handle_ndp(in []byte) ([]byte, error) {
|
||||||
|
// Ignore NDP requests for anything outside of fd00::/8
|
||||||
|
if in[8] != 0xFD {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create our NDP message body response
|
||||||
|
body := make([]byte, 32)
|
||||||
|
binary.BigEndian.PutUint32(body[:4], uint32(0x20000000))
|
||||||
|
copy(body[4:20], in[8:24]) // Target address
|
||||||
|
body[20] = uint8(2)
|
||||||
|
body[21] = uint8(1)
|
||||||
|
copy(body[22:28], i.mymac[:6])
|
||||||
|
|
||||||
|
// Send it back
|
||||||
|
return body, nil
|
||||||
|
}
|
@ -1,165 +0,0 @@
|
|||||||
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" // TODO investigate if this can be done without resorting to 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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,7 +18,7 @@ type tunInterface interface {
|
|||||||
|
|
||||||
type tunDevice struct {
|
type tunDevice struct {
|
||||||
core *Core
|
core *Core
|
||||||
ndp ndp
|
icmpv6 icmpv6
|
||||||
send chan<- []byte
|
send chan<- []byte
|
||||||
recv <-chan []byte
|
recv <-chan []byte
|
||||||
mtu int
|
mtu int
|
||||||
@ -27,7 +27,7 @@ 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)
|
tun.icmpv6.init(tun)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tun *tunDevice) write() error {
|
func (tun *tunDevice) write() error {
|
||||||
@ -36,8 +36,8 @@ func (tun *tunDevice) write() error {
|
|||||||
if tun.iface.IsTAP() {
|
if tun.iface.IsTAP() {
|
||||||
var frame ethernet.Frame
|
var frame ethernet.Frame
|
||||||
frame.Prepare(
|
frame.Prepare(
|
||||||
tun.ndp.peermac[:6], // Destination MAC address
|
tun.icmpv6.peermac[:6], // Destination MAC address
|
||||||
tun.ndp.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
|
||||||
len(data)) // Payload length
|
len(data)) // Payload length
|
||||||
@ -68,9 +68,6 @@ func (tun *tunDevice) read() error {
|
|||||||
o := 0
|
o := 0
|
||||||
if tun.iface.IsTAP() {
|
if tun.iface.IsTAP() {
|
||||||
o = ETHER_HEADER_LENGTH
|
o = ETHER_HEADER_LENGTH
|
||||||
b := make([]byte, n)
|
|
||||||
copy(b, buf)
|
|
||||||
tun.ndp.recv <- b
|
|
||||||
}
|
}
|
||||||
if buf[o]&0xf0 != 0x60 ||
|
if buf[o]&0xf0 != 0x60 ||
|
||||||
n != 256*int(buf[o+4])+int(buf[o+5])+IPv6_HEADER_LENGTH+o {
|
n != 256*int(buf[o+4])+int(buf[o+5])+IPv6_HEADER_LENGTH+o {
|
||||||
@ -78,6 +75,13 @@ func (tun *tunDevice) read() error {
|
|||||||
//panic("Should not happen in testing")
|
//panic("Should not happen in testing")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if buf[o+6] == 58 {
|
||||||
|
// Found an ICMPv6 packet
|
||||||
|
b := make([]byte, n)
|
||||||
|
copy(b, buf)
|
||||||
|
// tun.icmpv6.recv <- b
|
||||||
|
go tun.icmpv6.parse_packet(b)
|
||||||
|
}
|
||||||
packet := append(util_getBytes(), buf[o:n]...)
|
packet := append(util_getBytes(), buf[o:n]...)
|
||||||
tun.send <- packet
|
tun.send <- packet
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user