From be0d6feebaed96c656d0ba29e7c6ec198b2bbc97 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 12 Feb 2018 18:19:31 +0000 Subject: [PATCH 1/6] Remove ndp.go and add icmpv6.go --- src/yggdrasil/icmpv6.go | 203 ++++++++++++++++++++++++++++++++++++++++ src/yggdrasil/ndp.go | 165 -------------------------------- src/yggdrasil/tun.go | 33 ++++--- 3 files changed, 221 insertions(+), 180 deletions(-) create mode 100644 src/yggdrasil/icmpv6.go delete mode 100644 src/yggdrasil/ndp.go diff --git a/src/yggdrasil/icmpv6.go b/src/yggdrasil/icmpv6.go new file mode 100644 index 0000000..893cef3 --- /dev/null +++ b/src/yggdrasil/icmpv6.go @@ -0,0 +1,203 @@ +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 icmpv6 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 icmpv6Payload struct { + ether etherHeader + ipv6 ipv6Header + icmpv6 icmpv6Header + flags [4]byte + targetaddress ipv6Address + optiontype byte + optionlength byte + linklayeraddress macAddress +} + +type icmpv6Packet struct { + ipv6 ipv6Header + payload icmpv6Payload +} + +type icmpv6Frame struct { + ether etherHeader + packet icmpv6Packet +} + +func (i *icmpv6) init(t *tunDevice) { + i.tun = t + i.recv = make(chan []byte) + copy(i.mymac[:], []byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x02}) + copy(i.mylladdr[:], []byte{ + 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE}) + go i.listen() +} + +func (i *icmpv6) listen() { + for { + datain := <-i.recv + + if i.tun.iface.IsTAP() { + // TAP mode + dataout := make([]byte, ETHER+IPV6+32) + i.read_tap(datain, dataout) + i.tun.iface.Write(dataout) + } else { + // TUN mode + dataout := make([]byte, IPV6+32) + i.read_tun(datain, dataout) + i.tun.iface.Write(dataout) + } + } +} + +func (i *icmpv6) read_tap(datain []byte, dataout []byte) { + // Set up + in := (*icmpv6Frame)(unsafe.Pointer(&datain[0])) + out := (*icmpv6Frame)(unsafe.Pointer(&dataout[0])) + + // Store the peer MAC address + copy(i.peermac[:6], in.ether.source[:6]) + + // Ignore non-IPv6 frames + if binary.BigEndian.Uint16(in.ether.ethertype[:]) != uint16(0x86DD) { + return + } + + // Populate the out ethernet headers + copy(out.ether.destination[:], in.ether.destination[:]) + copy(out.ether.source[:], i.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]) + + // Then pass the IP packet onto the next function + i.read_tun(datain[ETHER:], dataout[ETHER:]) +} + +func (i *icmpv6) read_tun(datain []byte, dataout []byte) { + // Set up + in := (*icmpv6Packet)(unsafe.Pointer(&datain[0])) + out := (*icmpv6Packet)(unsafe.Pointer(&dataout[0])) + + // Store the peer link-local address + copy(i.peerlladdr[:16], in.ipv6.source[:16]) + + // Ignore non-ICMPv6 packets + if in.ipv6.nextheader != uint8(0x3A) { + return + } + + // What is the ICMPv6 message type? + switch in.payload.icmpv6.messagetype { + case uint8(135): + i.handle_ndp(&in.payload, &out.payload) + break + } + + // Update the source and destination addresses in the IPv6 header + copy(out.ipv6.destination[:], in.ipv6.source[:]) + copy(out.ipv6.source[:], i.mylladdr[:]) + binary.BigEndian.PutUint16(out.ipv6.length[:], uint16(32)) + + // Copy the payload + copy(dataout[IPV6:], datain[IPV6:]) + + // Calculate the checksum + i.calculate_checksum(dataout) +} + +func (i *icmpv6) calculate_checksum(dataout []byte) { + // Set up + out := (*icmpv6Packet)(unsafe.Pointer(&dataout[0])) + + // 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[IPV6:]) + if err != nil { + return + } + + // And copy the payload + payload, err := icmpv6.Marshal(ps) + if err != nil { + return + } + copy(dataout[IPV6:], payload) +} + +func (i *icmpv6) handle_ndp(in *icmpv6Payload, out *icmpv6Payload) { + // Ignore NDP requests for anything outside of fd00::/8 + if in.targetaddress[0] != 0xFD { + return + } + + // 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[:], i.mymac[:]) + binary.BigEndian.PutUint32(out.flags[:], uint32(0x20000000)) +} diff --git a/src/yggdrasil/ndp.go b/src/yggdrasil/ndp.go deleted file mode 100644 index c56174b..0000000 --- a/src/yggdrasil/ndp.go +++ /dev/null @@ -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) - } -} diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index 871fb3c..b1d96d9 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -17,17 +17,17 @@ type tunInterface interface { } type tunDevice struct { - core *Core - ndp ndp - send chan<- []byte - recv <-chan []byte - mtu int - iface tunInterface + core *Core + icmpv6 icmpv6 + send chan<- []byte + recv <-chan []byte + mtu int + iface tunInterface } func (tun *tunDevice) init(core *Core) { tun.core = core - tun.ndp.init(tun) + tun.icmpv6.init(tun) } func (tun *tunDevice) write() error { @@ -36,11 +36,11 @@ func (tun *tunDevice) write() error { if tun.iface.IsTAP() { 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 + tun.icmpv6.peermac[:6], // Destination MAC address + tun.icmpv6.mymac[:6], // Source MAC address + ethernet.NotTagged, // VLAN tagging + ethernet.IPv6, // Ethertype + len(data)) // Payload length copy(frame[ETHER_HEADER_LENGTH:], data[:]) if _, err := tun.iface.Write(frame); err != nil { panic(err) @@ -68,9 +68,6 @@ func (tun *tunDevice) read() error { o := 0 if tun.iface.IsTAP() { o = ETHER_HEADER_LENGTH - 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 { @@ -78,6 +75,12 @@ func (tun *tunDevice) read() error { //panic("Should not happen in testing") continue } + if buf[o+6] == 58 { + // Found an ICMPv6 packet + b := make([]byte, n) + copy(b, buf) + tun.icmpv6.recv <- b + } packet := append(util_getBytes(), buf[o:n]...) tun.send <- packet } From fac4bf796ed3f7068df9900d16f041f54235c187 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 12 Feb 2018 19:40:13 +0000 Subject: [PATCH 2/6] Add some error handling and other fixes to icmpv6.go --- src/yggdrasil/icmpv6.go | 75 +++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/src/yggdrasil/icmpv6.go b/src/yggdrasil/icmpv6.go index 893cef3..85d4595 100644 --- a/src/yggdrasil/icmpv6.go +++ b/src/yggdrasil/icmpv6.go @@ -6,6 +6,7 @@ package yggdrasil import "golang.org/x/net/icmp" import "encoding/binary" +import "errors" import "unsafe" // TODO investigate if this can be done without resorting to unsafe type macAddress [6]byte @@ -89,54 +90,77 @@ func (i *icmpv6) listen() { if i.tun.iface.IsTAP() { // TAP mode - dataout := make([]byte, ETHER+IPV6+32) - i.read_tap(datain, dataout) - i.tun.iface.Write(dataout) + response, err := i.parse_packet_tap(datain) + if err != nil { + i.tun.core.log.Printf("Error from icmpv6.parse_packet_tap: %v", err) + continue + } + if response != nil { + i.tun.iface.Write(response) + } } else { // TUN mode - dataout := make([]byte, IPV6+32) - i.read_tun(datain, dataout) - i.tun.iface.Write(dataout) + response, err := i.parse_packet_tun(datain) + if err != nil { + i.tun.core.log.Printf("Error from icmpv6.parse_packet_tun: %v", err) + continue + } + if response != nil { + i.tun.iface.Write(response) + } } } } -func (i *icmpv6) read_tap(datain []byte, dataout []byte) { +func (i *icmpv6) parse_packet_tap(datain []byte) ([]byte, error) { // Set up in := (*icmpv6Frame)(unsafe.Pointer(&datain[0])) - out := (*icmpv6Frame)(unsafe.Pointer(&dataout[0])) // Store the peer MAC address copy(i.peermac[:6], in.ether.source[:6]) // Ignore non-IPv6 frames if binary.BigEndian.Uint16(in.ether.ethertype[:]) != uint16(0x86DD) { - return + return nil, nil } - // Populate the out ethernet headers + // Create the response buffer + dataout := make([]byte, ETHER+IPV6+32) + out := (*icmpv6Frame)(unsafe.Pointer(&dataout[0])) + + // Populate the response ethernet headers copy(out.ether.destination[:], in.ether.destination[:]) copy(out.ether.source[:], i.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]) + // Hand over to parse_packet_tun to interpret the IPv6 packet + ipv6packet, err := i.parse_packet_tun(datain) + if err != nil { + return nil, nil + } - // Then pass the IP packet onto the next function - i.read_tun(datain[ETHER:], dataout[ETHER:]) + // Copy the returned packet to our response ethernet frame + if ipv6packet != nil { + copy(dataout[ETHER:ETHER+IPV6], ipv6packet) + return dataout, nil + } + + // At this point there is no response to send back + return nil, nil } -func (i *icmpv6) read_tun(datain []byte, dataout []byte) { +func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) { // Set up - in := (*icmpv6Packet)(unsafe.Pointer(&datain[0])) + dataout := make([]byte, IPV6+32) out := (*icmpv6Packet)(unsafe.Pointer(&dataout[0])) + in := (*icmpv6Packet)(unsafe.Pointer(&datain[0])) // Store the peer link-local address copy(i.peerlladdr[:16], in.ipv6.source[:16]) // Ignore non-ICMPv6 packets if in.ipv6.nextheader != uint8(0x3A) { - return + return nil, nil } // What is the ICMPv6 message type? @@ -155,10 +179,16 @@ func (i *icmpv6) read_tun(datain []byte, dataout []byte) { copy(dataout[IPV6:], datain[IPV6:]) // Calculate the checksum - i.calculate_checksum(dataout) + err := i.calculate_checksum(dataout) + if err != nil { + return nil, err + } + + // Return the response packet + return dataout, nil } -func (i *icmpv6) calculate_checksum(dataout []byte) { +func (i *icmpv6) calculate_checksum(dataout []byte) (error) { // Set up out := (*icmpv6Packet)(unsafe.Pointer(&dataout[0])) @@ -173,15 +203,18 @@ func (i *icmpv6) calculate_checksum(dataout []byte) { // Lazy-man's checksum using the icmp library icmpv6, err := icmp.ParseMessage(0x3A, dataout[IPV6:]) if err != nil { - return + return err } // And copy the payload payload, err := icmpv6.Marshal(ps) if err != nil { - return + return err } copy(dataout[IPV6:], payload) + + // Return nil if successful + return nil } func (i *icmpv6) handle_ndp(in *icmpv6Payload, out *icmpv6Payload) { From 6571a8c300e97cd4e583f23a6a8df7fc32b6c78f Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Mon, 12 Feb 2018 20:00:55 +0000 Subject: [PATCH 3/6] Don't use channels for ICMPv6 packets --- src/yggdrasil/icmpv6.go | 42 ++++++++++++++--------------------------- src/yggdrasil/tun.go | 7 ++++++- 2 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/yggdrasil/icmpv6.go b/src/yggdrasil/icmpv6.go index 85d4595..483902b 100644 --- a/src/yggdrasil/icmpv6.go +++ b/src/yggdrasil/icmpv6.go @@ -6,7 +6,6 @@ package yggdrasil import "golang.org/x/net/icmp" import "encoding/binary" -import "errors" import "unsafe" // TODO investigate if this can be done without resorting to unsafe type macAddress [6]byte @@ -21,7 +20,6 @@ type icmpv6 struct { peerlladdr ipv6Address mymac macAddress mylladdr ipv6Address - recv chan []byte } type etherHeader struct { @@ -76,39 +74,27 @@ type icmpv6Frame struct { func (i *icmpv6) init(t *tunDevice) { i.tun = t - i.recv = make(chan []byte) copy(i.mymac[:], []byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x02}) copy(i.mylladdr[:], []byte{ 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE}) - go i.listen() } -func (i *icmpv6) listen() { - for { - datain := <-i.recv +func (i *icmpv6) parse_packet(datain []byte) { + var response []byte + var err error - if i.tun.iface.IsTAP() { - // TAP mode - response, err := i.parse_packet_tap(datain) - if err != nil { - i.tun.core.log.Printf("Error from icmpv6.parse_packet_tap: %v", err) - continue - } - if response != nil { - i.tun.iface.Write(response) - } - } else { - // TUN mode - response, err := i.parse_packet_tun(datain) - if err != nil { - i.tun.core.log.Printf("Error from icmpv6.parse_packet_tun: %v", err) - continue - } - if response != nil { - i.tun.iface.Write(response) - } - } + if i.tun.iface.IsTAP() { + response, err = i.parse_packet_tap(datain) + } else { + response, err = i.parse_packet_tun(datain) + } + if err != nil { + i.tun.core.log.Printf("ICMPv6 error: %v", err) + return + } + if response != nil { + i.tun.iface.Write(response) } } diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index b1d96d9..dc2c673 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -79,7 +79,12 @@ func (tun *tunDevice) read() error { // Found an ICMPv6 packet b := make([]byte, n) copy(b, buf) - tun.icmpv6.recv <- b + // tun.icmpv6.recv <- b + if tun.iface.IsTAP() { + go tun.icmpv6.parse_packet_tap(b) + } else { + go tun.icmpv6.parse_packet_tun(b) + } } packet := append(util_getBytes(), buf[o:n]...) tun.send <- packet From d78e0f7067d3344d6dc69a54d80a0c3e30aeff41 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 14 Feb 2018 11:21:23 +0000 Subject: [PATCH 4/6] Improve icmpv6.go - Now doesn't use unsafe - Much cleaner - Doesn't run in a goroutine perpetually - Has a function to create ICMPv6 packets --- src/yggdrasil/icmpv6.go | 294 +++++++++++++++++++--------------------- src/yggdrasil/tun.go | 6 +- 2 files changed, 140 insertions(+), 160 deletions(-) diff --git a/src/yggdrasil/icmpv6.go b/src/yggdrasil/icmpv6.go index 483902b..5d014bd 100644 --- a/src/yggdrasil/icmpv6.go +++ b/src/yggdrasil/icmpv6.go @@ -4,219 +4,203 @@ package yggdrasil // 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" -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 icmpv6 struct { tun *tunDevice peermac macAddress - peerlladdr ipv6Address + peerlladdr net.IP + mylladdr net.IP mymac macAddress - mylladdr ipv6Address } -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 icmpv6Payload struct { - ether etherHeader - ipv6 ipv6Header - icmpv6 icmpv6Header - flags [4]byte - targetaddress ipv6Address - optiontype byte - optionlength byte - linklayeraddress macAddress -} - -type icmpv6Packet struct { - ipv6 ipv6Header - payload icmpv6Payload -} - -type icmpv6Frame struct { - ether etherHeader - packet icmpv6Packet +// 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 - copy(i.mymac[:], []byte{0x02, 0x00, 0x00, 0x00, 0x00, 0x02}) - copy(i.mylladdr[:], []byte{ + + // 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}) + 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 { - i.tun.core.log.Printf("ICMPv6 error: %v", err) return } - if response != nil { - i.tun.iface.Write(response) - } + + // Write the packet to TUN/TAP + i.tun.iface.Write(response) } func (i *icmpv6) parse_packet_tap(datain []byte) ([]byte, error) { - // Set up - in := (*icmpv6Frame)(unsafe.Pointer(&datain[0])) - // Store the peer MAC address - copy(i.peermac[:6], in.ether.source[:6]) + copy(i.peermac[:6], datain[6:12]) // Ignore non-IPv6 frames - if binary.BigEndian.Uint16(in.ether.ethertype[:]) != uint16(0x86DD) { + if binary.BigEndian.Uint16(datain[12:14]) != uint16(0x86DD) { return nil, nil } - // Create the response buffer - dataout := make([]byte, ETHER+IPV6+32) - out := (*icmpv6Frame)(unsafe.Pointer(&dataout[0])) - - // Populate the response ethernet headers - copy(out.ether.destination[:], in.ether.destination[:]) - copy(out.ether.source[:], i.mymac[:]) - binary.BigEndian.PutUint16(out.ether.ethertype[:], uint16(0x86DD)) - // Hand over to parse_packet_tun to interpret the IPv6 packet - ipv6packet, err := i.parse_packet_tun(datain) - if err != nil { - return nil, nil - } - - // Copy the returned packet to our response ethernet frame - if ipv6packet != nil { - copy(dataout[ETHER:ETHER+IPV6], ipv6packet) - return dataout, nil - } - - // At this point there is no response to send back - return nil, nil -} - -func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) { - // Set up - dataout := make([]byte, IPV6+32) - out := (*icmpv6Packet)(unsafe.Pointer(&dataout[0])) - in := (*icmpv6Packet)(unsafe.Pointer(&datain[0])) - - // Store the peer link-local address - copy(i.peerlladdr[:16], in.ipv6.source[:16]) - - // Ignore non-ICMPv6 packets - if in.ipv6.nextheader != uint8(0x3A) { - return nil, nil - } - - // What is the ICMPv6 message type? - switch in.payload.icmpv6.messagetype { - case uint8(135): - i.handle_ndp(&in.payload, &out.payload) - break - } - - // Update the source and destination addresses in the IPv6 header - copy(out.ipv6.destination[:], in.ipv6.source[:]) - copy(out.ipv6.source[:], i.mylladdr[:]) - binary.BigEndian.PutUint16(out.ipv6.length[:], uint16(32)) - - // Copy the payload - copy(dataout[IPV6:], datain[IPV6:]) - - // Calculate the checksum - err := i.calculate_checksum(dataout) + ipv6packet, err := i.parse_packet_tun(datain[ETHER:]) if err != nil { return nil, err } - // Return the response packet + // 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) calculate_checksum(dataout []byte) (error) { - // Set up - out := (*icmpv6Packet)(unsafe.Pointer(&dataout[0])) - - // 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[IPV6:]) +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 err + return nil, err } - // And copy the payload - payload, err := icmpv6.Marshal(ps) - if err != nil { - return err + // Check if the packet is IPv6 + if ipv6Header.Version != ipv6.Version { + return nil, err } - copy(dataout[IPV6:], payload) - // Return nil if successful - return nil + // 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(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) handle_ndp(in *icmpv6Payload, out *icmpv6Payload) { +func (i *icmpv6) create_icmpv6(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.targetaddress[0] != 0xFD { - return + if in[8] != 0xFD { + return nil, nil } - // Update the ICMPv6 headers - out.icmpv6.messagetype = uint8(136) - out.icmpv6.code = uint8(0) + // 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]) - // Update the ICMPv6 payload - copy(out.targetaddress[:], in.targetaddress[:]) - out.optiontype = uint8(2) - out.optionlength = uint8(1) - copy(out.linklayeraddress[:], i.mymac[:]) - binary.BigEndian.PutUint32(out.flags[:], uint32(0x20000000)) + // Send it back + return body, nil } diff --git a/src/yggdrasil/tun.go b/src/yggdrasil/tun.go index dc2c673..ea2b589 100644 --- a/src/yggdrasil/tun.go +++ b/src/yggdrasil/tun.go @@ -80,11 +80,7 @@ func (tun *tunDevice) read() error { b := make([]byte, n) copy(b, buf) // tun.icmpv6.recv <- b - if tun.iface.IsTAP() { - go tun.icmpv6.parse_packet_tap(b) - } else { - go tun.icmpv6.parse_packet_tun(b) - } + go tun.icmpv6.parse_packet(b) } packet := append(util_getBytes(), buf[o:n]...) tun.send <- packet From 0bf43b758f650334652030cc8d73cc6dc10590ff Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 14 Feb 2018 11:39:55 +0000 Subject: [PATCH 5/6] Split create_icmpv6 into create_icmpv6_tun and create_icmpv6_tap --- src/yggdrasil/icmpv6.go | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/yggdrasil/icmpv6.go b/src/yggdrasil/icmpv6.go index 5d014bd..56d3343 100644 --- a/src/yggdrasil/icmpv6.go +++ b/src/yggdrasil/icmpv6.go @@ -129,7 +129,7 @@ func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) { response, err := i.handle_ndp(datain[ipv6.HeaderLen:]) if err == nil { // Create our ICMPv6 response - responsePacket, err := i.create_icmpv6(ipv6Header.Src, ipv6.ICMPTypeNeighborAdvertisement, 0, response) + responsePacket, err := i.create_icmpv6_tun(ipv6Header.Src, ipv6.ICMPTypeNeighborAdvertisement, 0, response) if err != nil { return nil, err } @@ -148,7 +148,27 @@ func (i *icmpv6) parse_packet_tun(datain []byte) ([]byte, error) { return nil, nil } -func (i *icmpv6) create_icmpv6(dst net.IP, mtype ipv6.ICMPType, mcode int, mbody []byte) ([]byte, error) { +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, @@ -187,6 +207,8 @@ func (i *icmpv6) create_icmpv6(dst net.IP, mtype ipv6.ICMPType, mcode int, mbody 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 { From 34b264067e889b184b4addf47b2946f0234ad678 Mon Sep 17 00:00:00 2001 From: Neil Alexander Date: Wed, 14 Feb 2018 11:46:10 +0000 Subject: [PATCH 6/6] Run gofmt -s -w . --- src/yggdrasil/icmpv6.go | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/yggdrasil/icmpv6.go b/src/yggdrasil/icmpv6.go index 56d3343..f96a9c9 100644 --- a/src/yggdrasil/icmpv6.go +++ b/src/yggdrasil/icmpv6.go @@ -155,17 +155,17 @@ func (i *icmpv6) create_icmpv6_tap(dstmac macAddress, dst net.IP, mtype ipv6.ICM return nil, err } - // Create the response buffer - dataout := make([]byte, ETHER+ipv6.HeaderLen+len(mbody)) + // 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)) + // 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 + // 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) { @@ -207,8 +207,6 @@ func (i *icmpv6) create_icmpv6_tun(dst net.IP, mtype ipv6.ICMPType, mcode int, m 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 {