1package tuntap 2 3// The ICMPv6 module implements functions to easily create ICMPv6 4// packets. These functions, when mixed with the built-in Go IPv6 5// and ICMP libraries, can be used to send control messages back 6// to the host. Examples include: 7// - NDP messages, when running in TAP mode 8// - Packet Too Big messages, when packets exceed the session MTU 9// - Destination Unreachable messages, when a session prohibits 10// incoming traffic 11 12import ( 13 "encoding/binary" 14 "errors" 15 "net" 16 "sync" 17 "time" 18 19 "golang.org/x/net/icmp" 20 "golang.org/x/net/ipv6" 21 22 "github.com/yggdrasil-network/yggdrasil-go/src/address" 23) 24 25const len_ETHER = 14 26 27type ICMPv6 struct { 28 tun *TunAdapter 29 mylladdr net.IP 30 mymac net.HardwareAddr 31 peermacs map[address.Address]neighbor 32 peermacsmutex sync.RWMutex 33} 34 35type neighbor struct { 36 mac net.HardwareAddr 37 learned bool 38 lastadvertisement time.Time 39 lastsolicitation time.Time 40} 41 42// Marshal returns the binary encoding of h. 43func ipv6Header_Marshal(h *ipv6.Header) ([]byte, error) { 44 b := make([]byte, 40) 45 b[0] |= byte(h.Version) << 4 46 b[0] |= byte(h.TrafficClass) >> 4 47 b[1] |= byte(h.TrafficClass) << 4 48 b[1] |= byte(h.FlowLabel >> 16) 49 b[2] = byte(h.FlowLabel >> 8) 50 b[3] = byte(h.FlowLabel) 51 binary.BigEndian.PutUint16(b[4:6], uint16(h.PayloadLen)) 52 b[6] = byte(h.NextHeader) 53 b[7] = byte(h.HopLimit) 54 copy(b[8:24], h.Src) 55 copy(b[24:40], h.Dst) 56 return b, nil 57} 58 59// Initialises the ICMPv6 module by assigning our link-local IPv6 address and 60// our MAC address. ICMPv6 messages will always appear to originate from these 61// addresses. 62func (i *ICMPv6) Init(t *TunAdapter) { 63 i.tun = t 64 i.peermacsmutex.Lock() 65 i.peermacs = make(map[address.Address]neighbor) 66 i.peermacsmutex.Unlock() 67 68 // Our MAC address and link-local address 69 i.mymac = net.HardwareAddr{ 70 0x02, 0x00, 0x00, 0x00, 0x00, 0x02} 71 i.mylladdr = net.IP{ 72 0xFE, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFE} 74 copy(i.mymac[:], i.tun.addr[:]) 75 copy(i.mylladdr[9:], i.tun.addr[1:]) 76} 77 78// Parses an incoming ICMPv6 packet. The packet provided may be either an 79// ethernet frame containing an IP packet, or the IP packet alone. This is 80// determined by whether the TUN/TAP adapter is running in TUN (layer 3) or 81// TAP (layer 2) mode. Returns an error condition which is nil if the ICMPv6 82// module handled the packet or contains the error if not. 83func (i *ICMPv6) ParsePacket(datain []byte) error { 84 var response []byte 85 var err error 86 87 // Parse the frame/packet 88 if i.tun.IsTAP() { 89 response, err = i.UnmarshalPacketL2(datain) 90 } else { 91 response, err = i.UnmarshalPacket(datain, nil) 92 } 93 94 if err != nil { 95 return err 96 } 97 98 // Write the packet to TUN/TAP 99 i.tun.iface.Write(response) 100 return nil 101} 102 103// Unwraps the ethernet headers of an incoming ICMPv6 packet and hands off 104// the IP packet to the ParsePacket function for further processing. 105// A response buffer is also created for the response message, also complete 106// with ethernet headers. 107func (i *ICMPv6) UnmarshalPacketL2(datain []byte) ([]byte, error) { 108 // Ignore non-IPv6 frames 109 if binary.BigEndian.Uint16(datain[12:14]) != uint16(0x86DD) { 110 return nil, errors.New("Ignoring non-IPv6 frame") 111 } 112 113 // Hand over to ParsePacket to interpret the IPv6 packet 114 mac := datain[6:12] 115 ipv6packet, err := i.UnmarshalPacket(datain[len_ETHER:], &mac) 116 if err != nil { 117 return nil, err 118 } 119 120 // Create the response buffer 121 dataout := make([]byte, len_ETHER+ipv6.HeaderLen+32) 122 123 // Populate the response ethernet headers 124 copy(dataout[:6], datain[6:12]) 125 copy(dataout[6:12], i.mymac[:]) 126 binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD)) 127 128 // Copy the returned packet to our response ethernet frame 129 copy(dataout[len_ETHER:], ipv6packet) 130 return dataout, nil 131} 132 133// Unwraps the IP headers of an incoming IPv6 packet and performs various 134// sanity checks on the packet - i.e. is the packet an ICMPv6 packet, does the 135// ICMPv6 message match a known expected type. The relevant handler function 136// is then called and a response packet may be returned. 137func (i *ICMPv6) UnmarshalPacket(datain []byte, datamac *[]byte) ([]byte, error) { 138 // Parse the IPv6 packet headers 139 ipv6Header, err := ipv6.ParseHeader(datain[:ipv6.HeaderLen]) 140 if err != nil { 141 return nil, err 142 } 143 144 // Check if the packet is IPv6 145 if ipv6Header.Version != ipv6.Version { 146 return nil, errors.New("Ignoring non-IPv6 packet") 147 } 148 149 // Check if the packet is ICMPv6 150 if ipv6Header.NextHeader != 58 { 151 return nil, errors.New("Ignoring non-ICMPv6 packet") 152 } 153 154 // Parse the ICMPv6 message contents 155 icmpv6Header, err := icmp.ParseMessage(58, datain[ipv6.HeaderLen:]) 156 if err != nil { 157 return nil, err 158 } 159 160 // Check for a supported message type 161 switch icmpv6Header.Type { 162 case ipv6.ICMPTypeNeighborSolicitation: 163 if !i.tun.IsTAP() { 164 return nil, errors.New("Ignoring Neighbor Solicitation in TUN mode") 165 } 166 response, err := i.HandleNDP(datain[ipv6.HeaderLen:]) 167 if err == nil { 168 // Create our ICMPv6 response 169 responsePacket, err := CreateICMPv6( 170 ipv6Header.Src, i.mylladdr, 171 ipv6.ICMPTypeNeighborAdvertisement, 0, 172 &icmp.DefaultMessageBody{Data: response}) 173 if err != nil { 174 return nil, err 175 } 176 // Send it back 177 return responsePacket, nil 178 } else { 179 return nil, err 180 } 181 case ipv6.ICMPTypeNeighborAdvertisement: 182 if !i.tun.IsTAP() { 183 return nil, errors.New("Ignoring Neighbor Advertisement in TUN mode") 184 } 185 if datamac != nil { 186 var addr address.Address 187 var target address.Address 188 mac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 189 copy(addr[:], ipv6Header.Src[:]) 190 copy(target[:], datain[48:64]) 191 copy(mac[:], (*datamac)[:]) 192 i.peermacsmutex.Lock() 193 neighbor := i.peermacs[target] 194 neighbor.mac = mac 195 neighbor.learned = true 196 neighbor.lastadvertisement = time.Now() 197 i.peermacs[target] = neighbor 198 i.peermacsmutex.Unlock() 199 i.tun.log.Debugln("Learned peer MAC", mac.String(), "for", net.IP(target[:]).String()) 200 /* 201 i.tun.log.Debugln("Peer MAC table:") 202 i.peermacsmutex.RLock() 203 for t, n := range i.peermacs { 204 if n.learned { 205 i.tun.log.Debugln("- Target", net.IP(t[:]).String(), "has MAC", n.mac.String()) 206 } else { 207 i.tun.log.Debugln("- Target", net.IP(t[:]).String(), "is not learned yet") 208 } 209 } 210 i.peermacsmutex.RUnlock() 211 */ 212 } 213 return nil, errors.New("No response needed") 214 } 215 216 return nil, errors.New("ICMPv6 type not matched") 217} 218 219// Creates an ICMPv6 packet based on the given icmp.MessageBody and other 220// parameters, complete with ethernet and IP headers, which can be written 221// directly to a TAP adapter. 222func (i *ICMPv6) CreateICMPv6L2(dstmac net.HardwareAddr, dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) { 223 // Pass through to CreateICMPv6 224 ipv6packet, err := CreateICMPv6(dst, src, mtype, mcode, mbody) 225 if err != nil { 226 return nil, err 227 } 228 229 // Create the response buffer 230 dataout := make([]byte, len_ETHER+len(ipv6packet)) 231 232 // Populate the response ethernet headers 233 copy(dataout[:6], dstmac[:6]) 234 copy(dataout[6:12], i.mymac[:]) 235 binary.BigEndian.PutUint16(dataout[12:14], uint16(0x86DD)) 236 237 // Copy the returned packet to our response ethernet frame 238 copy(dataout[len_ETHER:], ipv6packet) 239 return dataout, nil 240} 241 242// Creates an ICMPv6 packet based on the given icmp.MessageBody and other 243// parameters, complete with IP headers only, which can be written directly to 244// a TUN adapter, or called directly by the CreateICMPv6L2 function when 245// generating a message for TAP adapters. 246func CreateICMPv6(dst net.IP, src net.IP, mtype ipv6.ICMPType, mcode int, mbody icmp.MessageBody) ([]byte, error) { 247 // Create the ICMPv6 message 248 icmpMessage := icmp.Message{ 249 Type: mtype, 250 Code: mcode, 251 Body: mbody, 252 } 253 254 // Convert the ICMPv6 message into []byte 255 icmpMessageBuf, err := icmpMessage.Marshal(icmp.IPv6PseudoHeader(src, dst)) 256 if err != nil { 257 return nil, err 258 } 259 260 // Create the IPv6 header 261 ipv6Header := ipv6.Header{ 262 Version: ipv6.Version, 263 NextHeader: 58, 264 PayloadLen: len(icmpMessageBuf), 265 HopLimit: 255, 266 Src: src, 267 Dst: dst, 268 } 269 270 // Convert the IPv6 header into []byte 271 ipv6HeaderBuf, err := ipv6Header_Marshal(&ipv6Header) 272 if err != nil { 273 return nil, err 274 } 275 276 // Construct the packet 277 responsePacket := make([]byte, ipv6.HeaderLen+ipv6Header.PayloadLen) 278 copy(responsePacket[:ipv6.HeaderLen], ipv6HeaderBuf) 279 copy(responsePacket[ipv6.HeaderLen:], icmpMessageBuf) 280 281 // Send it back 282 return responsePacket, nil 283} 284 285func (i *ICMPv6) Solicit(addr address.Address) { 286 retries := 5 287 for retries > 0 { 288 retries-- 289 i.peermacsmutex.RLock() 290 if n, ok := i.peermacs[addr]; ok && n.learned { 291 i.tun.log.Debugln("MAC learned for", net.IP(addr[:]).String()) 292 i.peermacsmutex.RUnlock() 293 return 294 } 295 i.peermacsmutex.RUnlock() 296 i.tun.log.Debugln("Sending neighbor solicitation for", net.IP(addr[:]).String()) 297 i.peermacsmutex.Lock() 298 if n, ok := i.peermacs[addr]; !ok { 299 i.peermacs[addr] = neighbor{ 300 lastsolicitation: time.Now(), 301 } 302 } else { 303 n.lastsolicitation = time.Now() 304 } 305 i.peermacsmutex.Unlock() 306 request, err := i.createNDPL2(addr) 307 if err != nil { 308 panic(err) 309 } 310 if _, err := i.tun.iface.Write(request); err != nil { 311 panic(err) 312 } 313 i.tun.log.Debugln("Sent neighbor solicitation for", net.IP(addr[:]).String()) 314 time.Sleep(time.Second) 315 } 316} 317 318func (i *ICMPv6) getNeighbor(addr address.Address) (neighbor, bool) { 319 i.peermacsmutex.RLock() 320 defer i.peermacsmutex.RUnlock() 321 322 n, ok := i.peermacs[addr] 323 return n, ok 324} 325 326func (i *ICMPv6) createNDPL2(dst address.Address) ([]byte, error) { 327 // Create the ND payload 328 var payload [28]byte 329 copy(payload[:4], []byte{0x00, 0x00, 0x00, 0x00}) // Flags 330 copy(payload[4:20], dst[:]) // Destination 331 copy(payload[20:22], []byte{0x01, 0x01}) // Type & length 332 copy(payload[22:28], i.mymac[:6]) // Link layer address 333 334 // Create the ICMPv6 solicited-node address 335 var dstaddr address.Address 336 copy(dstaddr[:13], []byte{ 337 0xFF, 0x02, 0x00, 0x00, 338 0x00, 0x00, 0x00, 0x00, 339 0x00, 0x00, 0x00, 0x01, 0xFF}) 340 copy(dstaddr[13:], dst[13:16]) 341 342 // Create the multicast MAC 343 dstmac := net.HardwareAddr{0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 344 copy(dstmac[:2], []byte{0x33, 0x33}) 345 copy(dstmac[2:6], dstaddr[12:16]) 346 347 // Create the ND request 348 requestPacket, err := i.CreateICMPv6L2( 349 dstmac, dstaddr[:], i.mylladdr, 350 ipv6.ICMPTypeNeighborSolicitation, 0, 351 &icmp.DefaultMessageBody{Data: payload[:]}) 352 if err != nil { 353 return nil, err 354 } 355 356 return requestPacket, nil 357} 358 359// Generates a response to an NDP discovery packet. This is effectively called 360// when the host operating system generates an NDP request for any address in 361// the fd00::/8 range, so that the operating system knows to route that traffic 362// to the Yggdrasil TAP adapter. 363func (i *ICMPv6) HandleNDP(in []byte) ([]byte, error) { 364 // Ignore NDP requests for anything outside of fd00::/8 365 var source address.Address 366 copy(source[:], in[8:]) 367 var snet address.Subnet 368 copy(snet[:], in[8:]) 369 switch { 370 case source.IsValid(): 371 case snet.IsValid(): 372 default: 373 return nil, errors.New("Not an NDP for 0200::/7") 374 } 375 376 // Create our NDP message body response 377 body := make([]byte, 28) 378 binary.BigEndian.PutUint32(body[:4], uint32(0x40000000)) // Flags 379 copy(body[4:20], in[8:24]) // Target address 380 body[20] = uint8(2) // Type: Target link-layer address 381 body[21] = uint8(1) // Length: 1x address (8 bytes) 382 copy(body[22:28], i.mymac[:6]) 383 384 // Send it back 385 return body, nil 386} 387