package main import ( "bytes" "errors" "fmt" "math/rand" "net" "net/netip" "os" "sync" "sync/atomic" "time" "github.com/google/gopacket" "github.com/google/gopacket/layers" "github.com/grmrgecko/virtual-vxlan/tun" log "github.com/sirupsen/logrus" ) // ARP Table Entry. type ArpEntry struct { Addr netip.Addr MAC net.HardwareAddr Expires time.Time Permanent bool Updating bool } // MAC Table Entry. type MacEntry struct { MAC net.HardwareAddr Dst *net.UDPAddr Permanent bool } // The vxlan interface containing the tun device. type Interface struct { name string vni uint32 state struct { state atomic.Uint32 stopping sync.WaitGroup sync.Mutex } tun struct { device tun.Device mtu atomic.Int32 mtuc chan int mac net.HardwareAddr addresses []netip.Prefix sync.RWMutex } tables struct { arp []ArpEntry arpEvent map[netip.Addr][]chan net.HardwareAddr mac []MacEntry sync.RWMutex } Permanent bool l *Listener closed chan struct{} log *log.Entry } // Create an interface for the vxlan VNI. func NewInterface(name string, vni uint32, mtu int, l *Listener, perm bool) (i *Interface, err error) { // Verify we have an listener, and lock it. if l == nil { return nil, fmt.Errorf("we need a listener to attach to") } if !l.Permanent && perm { return nil, fmt.Errorf("cannot add permanent interface to non-permanent listener") } l.net.Lock() defer l.net.Unlock() // Check that an interface doesn't already exist with this VNI. for _, ifce := range l.net.interfaces { if ifce.vni == vni { return nil, fmt.Errorf("existing vni %d interface on listener %s", vni, l.PrettyName()) } } // Verify an interface on the machine doesn't already have the requested name. ifaces, err := net.Interfaces() if err != nil { return } for _, iface := range ifaces { if iface.Name == name { return nil, fmt.Errorf("existing interface with name %s", name) } } // If MTU is not provided, use the listener's MTU minus 50 bytes for vxlan. if mtu <= 1 { mtu = l.net.maxMessageSize - 50 } // Create the tunnel interface. tun, err := tun.CreateTUN(name, mtu) if err != nil { return nil, fmt.Errorf("failed to create TUN device: %v", err) } // Verify the real name from the interface in-case the OS decided // to change it from the request. realName, err := tun.Name() if err != nil { return nil, fmt.Errorf("failed to get device name: %v", err) } // Verify the MTU incase the OS changed from requested MTU. mtu, err = tun.MTU() if err != nil { return nil, fmt.Errorf("failed to get device mtu: %v", err) } // Setup the interface structure. i = new(Interface) i.name = realName i.vni = vni i.state.state.Store(uint32(deviceStateDown)) i.closed = make(chan struct{}) i.tun.device = tun i.tun.mtu.Store(int32(mtu)) i.tun.mtuc = make(chan int) i.tun.mac = generateRandomMAC() i.tables.arpEvent = make(map[netip.Addr][]chan net.HardwareAddr) i.l = l i.log = log.WithFields(log.Fields{ "listener": l.PrettyName(), "device": name, "vni": vni, }) i.Permanent = perm l.net.interfaces = append(l.net.interfaces, i) // Start the task queues. go i.runARPExpiryJob() go i.eventReader() go i.packetReader() // Inform that the interface has started. i.log.Print("Interface started.") return } // The state of the tun device. type deviceState uint32 //go:generate go run golang.org/x/tools/cmd/stringer -type deviceState -trimprefix=deviceState const ( deviceStateDown deviceState = iota deviceStateUp deviceStateClosed ) // Gets the current device state. func (i *Interface) DeviceState() deviceState { return deviceState(i.state.state.Load()) } // Is the device closed? func (i *Interface) IsClosed() bool { return i.DeviceState() == deviceStateClosed } // Is the device up? func (i *Interface) IsUp() bool { return i.DeviceState() == deviceStateUp } // Change the device state. func (i *Interface) changeState(want deviceState) { i.state.Lock() defer i.state.Unlock() old := i.DeviceState() if old == deviceStateClosed { // once closed, always closed i.log.Debugf("Interface closed, ignored requested state %v", want) return } switch want { case old: return case deviceStateUp: i.state.state.Store(uint32(deviceStateUp)) fallthrough // up failed; bring the device all the way back down case deviceStateDown: i.state.state.Store(uint32(deviceStateDown)) } i.log.Debugf("Interface state was %v, requested %v, now %v", old, want, i.DeviceState()) } // Bring the internal device state to up. func (i *Interface) Up() { i.changeState(deviceStateUp) } // Bring the internal device state to down. func (i *Interface) Down() { i.changeState(deviceStateDown) } // Set the interface MTU. func (i *Interface) SetMTU(mtu int) error { // If MTU is not provided, use the listener's MTU minus 50 bytes for vxlan. if mtu <= 1 { mtu = i.l.net.maxMessageSize - 50 } return i.tun.device.SetMTU(mtu) } // Get the interface MTU. func (i *Interface) MTU() int { return int(i.tun.mtu.Load()) } // Get the interface name. func (i *Interface) Name() string { return i.name } // Get the VNI assigned for this interface. func (i *Interface) VNI() uint32 { return i.vni } // Close this interface. func (i *Interface) Close() (err error) { i.state.Lock() defer i.state.Unlock() if i.IsClosed() { return } i.state.state.Store(uint32(deviceStateClosed)) i.log.Debug("Interface is closing.") err = i.tun.device.Close() i.state.stopping.Wait() // Remove self from listener. netc := &i.l.net netc.Lock() defer netc.Unlock() for p, iface := range netc.interfaces { if iface == i { netc.interfaces = append(netc.interfaces[:p], netc.interfaces[p+1:]...) break } } close(i.closed) i.log.Print("Interface closed.") return } // Set the MAC address for this interface. func (i *Interface) SetMACAddress(mac net.HardwareAddr) (err error) { i.tun.Lock() i.tun.mac = mac i.tun.Unlock() return } // Get the MAC address for this interface. func (i *Interface) GetMACAddress() (net.HardwareAddr, error) { i.tun.RLock() defer i.tun.RUnlock() return i.tun.mac, nil } // Set IP addresses on this interface. func (i *Interface) SetIPAddresses(addresses []netip.Prefix) (err error) { i.tun.Lock() defer i.tun.Unlock() err = i.tun.device.SetIPAddresses(addresses) if err != nil { return } i.tun.addresses = addresses return } // Get IP adddreses on this interface. func (i *Interface) GetIPAddresses() ([]netip.Prefix, error) { return i.tun.device.GetIPAddresses() } // Add an entry to the MAC table. func (i *Interface) AddMACEntry(mac net.HardwareAddr, dst net.IP, perm bool) error { // Lock tables to prevent concurrent requests. i.tables.Lock() defer i.tables.Unlock() // Verify this entry doesn't already exist. for _, ent := range i.tables.mac { if bytes.Equal(ent.MAC, mac) && ent.Dst.IP.Equal(dst) { return fmt.Errorf("entry already exists for %v via %v", mac, dst) } } // Get the UDP address for the provided IP. addr, _ := netip.AddrFromSlice(dst) udpAddr := net.UDPAddrFromAddrPort(netip.AddrPortFrom(addr, uint16(i.l.net.addr.Port))) // Add entry to MAC table. entry := MacEntry{ MAC: mac, Dst: udpAddr, Permanent: perm, } i.tables.mac = append(i.tables.mac, entry) return nil } // Remove an individual MAC table entry. func (i *Interface) RemoveMACEntry(mac net.HardwareAddr, dst net.IP) error { // Lock table during modification. i.tables.Lock() defer i.tables.Unlock() // Find matching entry. found := -1 for i, ent := range i.tables.mac { if bytes.Equal(ent.MAC, mac) && ent.Dst.IP.Equal(dst) { found = i break } } // If not found return error. if found == -1 { return fmt.Errorf("unable to find matching MAC entry") } // If found, remove it. i.tables.mac = append(i.tables.mac[:found], i.tables.mac[found+1:]...) return nil } // Returns the MAC to destination table. func (i *Interface) GetMACEntries() []MacEntry { i.tables.RLock() defer i.tables.RUnlock() return i.tables.mac } // Clears the MAC to UDP Address association table. func (i *Interface) FlushMACTable() { i.tables.Lock() i.tables.mac = nil i.tables.Unlock() } // Find a destination UDP address for a MAC address. func (i *Interface) GetDestinationFor(mac net.HardwareAddr) *net.UDPAddr { // Lock tables to prevent concurrent requests. i.tables.RLock() defer i.tables.RUnlock() // Keep track of both controller and direct matches. var controllers []*net.UDPAddr var matches []*net.UDPAddr // Find matches. for _, ent := range i.tables.mac { if bytes.Equal(ent.MAC, app.ControllerMac) { controllers = append(controllers, ent.Dst) } else if bytes.Equal(ent.MAC, mac) { matches = append(matches, ent.Dst) } } // If we have an direct match, return a random entry. if len(matches) != 0 { return matches[rand.Intn(len(matches))] } // If we have no direct matches, but controllers, return a random controller. if len(controllers) != 0 { return controllers[rand.Intn(len(controllers))] } // If we found no matches at all, return nil. return nil } // Add an ARP entry that is static. func (i *Interface) AddStaticARPEntry(addr netip.Addr, mac net.HardwareAddr, perm bool) { // Prevent concurrent table modifications. i.tables.Lock() defer i.tables.Unlock() // Keep track on if we updated an existing entry. updated := false // Check existing arp entires for this address. for p, ent := range i.tables.arp { // If this address is this entry, update or remove based on MAC. if ent.Addr == addr { // If MAC matches, update it. Otherwise, remove // tne ARP entry as we're adding a new one. if bytes.Equal(ent.MAC, mac) { updated = true i.tables.arp[p].Expires = time.Time{} i.tables.arp[p].Updating = false i.tables.arp[p].Permanent = perm } else { i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) } break } } // If we didn't update an existing entry, we should add a new one. if !updated { newMac := make(net.HardwareAddr, 6) copy(newMac, mac) entry := ArpEntry{ Addr: addr, MAC: newMac, Updating: false, Permanent: perm, } i.tables.arp = append(i.tables.arp, entry) } } // Remove an individual MAC table entry. func (i *Interface) RemoveARPEntry(addr netip.Addr) error { // Lock table during modification. i.tables.Lock() defer i.tables.Unlock() // Find matching entry. found := -1 for i, ent := range i.tables.arp { if ent.Addr == addr { found = i break } } // If not found return error. if found == -1 { return fmt.Errorf("unable to find matching ARP entry") } // If found, remove it. i.tables.arp = append(i.tables.arp[:found], i.tables.arp[found+1:]...) return nil } // Get ARP entries. func (i *Interface) GetARPEntries() []ArpEntry { i.tables.RLock() defer i.tables.RUnlock() return i.tables.arp } // Add ARP entry, or update expiry on existing entry. func (i *Interface) AddOrUpdateARP(addr netip.Addr, mac net.HardwareAddr) { // Prevent concurrent table modifications. i.tables.Lock() defer i.tables.Unlock() // Keep track on if we updated an existing entry. updated := false // Check existing arp entires for this address. for p, ent := range i.tables.arp { // If this address is this entry, update or remove based on MAC. if ent.Addr == addr { // If MAC matches, update it. Otherwise, remove // tne ARP entry as we're adding a new one. if bytes.Equal(ent.MAC, mac) { updated = true i.tables.arp[p].Expires = time.Now().Add(60 * time.Second) i.tables.arp[p].Updating = false } else { i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) } break } } // If we didn't update an existing entry, we should add a new one. if !updated { newMac := make(net.HardwareAddr, 6) copy(newMac, mac) entry := ArpEntry{ Addr: addr, MAC: newMac, Expires: time.Now().Add(60 * time.Second), Updating: false, Permanent: false, } i.tables.arp = append(i.tables.arp, entry) } } // In the event of finding an expired MAC entry, we should // try updating the entry to ensure its not changed. func (i *Interface) updateARPFor(addr netip.Addr, brdMac net.HardwareAddr, ifceAddr netip.Addr) { // Lock tables to make update channel. i.tables.Lock() // As we did not find an existing entry, we need to make an ARP request. // Start a channel to receive the ARP event back with the MAC. c := make(chan net.HardwareAddr) // Add to the event list. i.tables.arpEvent[addr] = append(i.tables.arpEvent[addr], c) // When this function is done, ensure the event channel is removed. defer func() { i.tables.Lock() for p, ch := range i.tables.arpEvent[addr] { if ch == c { i.tables.arpEvent[addr] = append(i.tables.arpEvent[addr][:p], i.tables.arpEvent[addr][p+1:]...) break } } i.tables.Unlock() }() // Unlock the tables to allow ARP replies to add to the tables. i.tables.Unlock() // Get an destination UDP address for the ARP request. dst := i.GetDestinationFor(brdMac) if dst == nil { return } // Make Ethernet layer for packet. eth := layers.Ethernet{ SrcMAC: i.tun.mac, DstMAC: brdMac, EthernetType: layers.EthernetTypeARP, } // Make ARP layer for packet. arp := layers.ARP{ AddrType: layers.LinkTypeEthernet, HwAddressSize: 6, Operation: layers.ARPRequest, SourceHwAddress: []byte(i.tun.mac), SourceProtAddress: ifceAddr.AsSlice(), DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, DstProtAddress: addr.AsSlice(), } // Size and protocol type differs on IPv6 vs IPv4. if addr.Is4() { arp.Protocol = layers.EthernetTypeIPv4 arp.ProtAddressSize = 4 } else { arp.Protocol = layers.EthernetTypeIPv6 arp.ProtAddressSize = 16 } // We give 3 ARP request tries, otherwise we timeout. tries := 0 for tries < 3 { tries += 1 // Try sending packet. i.SendLayers(dst, ð, &arp) select { // If we receive the response, return the value. case <-c: return // If we timeout, after 3 seconds, then continue. case <-time.After(3 * time.Second): i.log.Debugf("timeout on ARP request for %s with tries %d", addr.String(), tries) continue } } // If all tries are done, the addres is expired. i.log.Debugf("MAC address for %s has expired", addr.String()) // Lock tables and find the entry to remove. i.tables.Lock() for p, ent := range i.tables.arp { // If this is the right entry, remove it. if ent.Addr == addr { i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) break } } i.tables.Unlock() } // Find the MAC address for an IP address from the ARP table. // If an entry doesn't exist, we will attempt to request it. func (i *Interface) GetMacFor(addr netip.Addr) (net.HardwareAddr, error) { // Lots of math depending on is4. is4 := addr.Is4() // Multicast traffic should be destined to a multicast MAC address. if addr.IsMulticast() { if is4 { // Get the lower 23 bits of the IPv4 address. ip := addr.As4() lower23 := uint32(ip[1])<<16 | uint32(ip[2])<<8 | uint32(ip[3]) lower23 &= 0x7FFFFF // Mask to 23 bits // Construct the MAC address. mac := net.HardwareAddr{ 0x01, 0x00, 0x5E, byte((lower23 >> 16) & 0xFF), byte((lower23 >> 8) & 0xFF), byte(lower23 & 0xFF), } return mac, nil } else { // Construct MAC Address using the lower 32 bits of the IPv6 address. ip := addr.As16() mac := net.HardwareAddr{ 0x33, 0x33, ip[12], ip[13], ip[14], ip[15], } return mac, nil } } // Find an IP address on this interface that matches the IP we're pinging. i.tun.RLock() var ifcePrefix netip.Prefix var ifceAddr netip.Addr for _, prefix := range i.tun.addresses { // If this prefix is an exact match, this is the IP we use. // Otherwise, we will accept the IP of the same type. if prefix.Contains(addr) { ifcePrefix = prefix ifceAddr = prefix.Addr() break } else if prefix.Addr().Is4() == is4 { ifcePrefix = prefix ifceAddr = prefix.Addr() } } i.tun.RUnlock() // Set broadcast MAC. brdMac := net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff} // If this is IPv4, determine if the address provided is broadcast // and return broadcast mac. if is4 && ifcePrefix.Contains(ifceAddr) { // Get the host bits to mask IP to broadcast. hostBits := 32 - ifcePrefix.Bits() // Set all host bits to 1 to calculate the broadcast address. broadcastAddr := ifceAddr.As4() for i := 0; i < hostBits; i++ { broadcastAddr[3-i/8] |= byte(1 << (i % 8)) } // If the provided address is the broadcast, return the broadcast MAC. if addr == netip.AddrFrom4(broadcastAddr) { return brdMac, nil } } // Lock tables, no concurrent requests. i.tables.Lock() // Check arp table for existing entry. for _, ent := range i.tables.arp { // If we found an existing entry, return the value. if ent.Addr == addr { defer i.tables.Unlock() // If we're not already updating, check if this entry is expired. if !ent.Updating && !ent.Expires.IsZero() { now := time.Now() // If expired, attempt to update MAC address in background. if now.After(ent.Expires) { ent.Updating = true go i.updateARPFor(addr, brdMac, ifceAddr) } } return ent.MAC, nil } } // As we did not find an existing entry, we need to make an ARP request. // Start a channel to receive the ARP event back with the MAC. c := make(chan net.HardwareAddr) // Add to the event list. i.tables.arpEvent[addr] = append(i.tables.arpEvent[addr], c) // When this function is done, ensure the event channel is removed. defer func() { i.tables.Lock() for p, ch := range i.tables.arpEvent[addr] { if ch == c { i.tables.arpEvent[addr] = append(i.tables.arpEvent[addr][:p], i.tables.arpEvent[addr][p+1:]...) break } } i.tables.Unlock() }() // Unlock the tables to allow ARP replies to add to the tables. i.tables.Unlock() // Get an destination UDP address for the ARP request. dst := i.GetDestinationFor(brdMac) if dst == nil { return nil, fmt.Errorf("no destination to send ARP to") } // Make Ethernet layer for packet. eth := layers.Ethernet{ SrcMAC: i.tun.mac, DstMAC: brdMac, EthernetType: layers.EthernetTypeARP, } // Make ARP layer for packet. arp := layers.ARP{ AddrType: layers.LinkTypeEthernet, HwAddressSize: 6, Operation: layers.ARPRequest, SourceHwAddress: []byte(i.tun.mac), SourceProtAddress: ifceAddr.AsSlice(), DstHwAddress: []byte{0, 0, 0, 0, 0, 0}, DstProtAddress: addr.AsSlice(), } // Size and protocol type differs on IPv6 vs IPv4. if is4 { arp.Protocol = layers.EthernetTypeIPv4 arp.ProtAddressSize = 4 } else { arp.Protocol = layers.EthernetTypeIPv6 arp.ProtAddressSize = 16 } // We give 3 ARP request tries, otherwise we timeout. tries := 0 for tries < 3 { tries += 1 // Try sending packet. i.SendLayers(dst, ð, &arp) select { // If we receive the response, return the value. case mac := <-c: return mac, nil // If we timeout, after 3 seconds, then continue. case <-time.After(3 * time.Second): i.log.Debugf("timeout on ARP request for %s with tries %d", addr.String(), tries) continue } } // If all tries are done, return error. return nil, fmt.Errorf("unable to get MAC address for %s", addr.String()) } // A job to check for expired arp entries and remove them from the arp table. func (i *Interface) runARPExpiryJob() { // Run job every minute. ticker := time.NewTicker(1 * time.Minute) for { // Run the job queue if this interface doesn't close. select { case <-ticker.C: // Lock tables for ARP operations. i.tables.Lock() // Get current time to compare against expiry date. now := time.Now() // Start with a found entry so the loop runs first. foundEntry := true for foundEntry { // Set to no found entry, so we stop the loop if no entry is found. foundEntry = false // Scan arp table for expired entries. for p, ent := range i.tables.arp { // If this entry is expired and past 2 minute grace, remove it and set that we found an entry. if !ent.Expires.IsZero() && now.Before(ent.Expires.Add(120*time.Second)) { foundEntry = true i.tables.arp = append(i.tables.arp[:p], i.tables.arp[p+1:]...) break } } } // No further expired entries found, unlock the table. i.tables.Unlock() case <-i.closed: return } } } // Clears the arp table. func (i *Interface) FlushARPTable() { i.tables.Lock() i.tables.arp = nil i.tables.Unlock() } // Handle an ARP packet received. func (i *Interface) HandleARP(arp layers.ARP) { // If type isn't ethernet, we don't care. if arp.AddrType != layers.LinkTypeEthernet { return } // If ARP request, we should verify its for our IP. if arp.Operation == layers.ARPRequest { // Parse the destination address. addr, ok := netip.AddrFromSlice(arp.DstProtAddress) if !ok { return } // Find the address that matches the requested address. i.tun.RLock() defer i.tun.RUnlock() for _, prefix := range i.tun.addresses { // If the requested address is this one, we should reply. if prefix.Addr() == addr { // Get the MAC address and destination to send reply to. srcMac := net.HardwareAddr(arp.SourceHwAddress) dst := i.GetDestinationFor(srcMac) if dst == nil { return } // Parse the source address. srcAddr, ok := netip.AddrFromSlice(arp.SourceProtAddress) if !ok { return } // Add the ARP entry for the source. i.AddOrUpdateARP(srcAddr, srcMac) // Make an ethernet layer for sending ARP reply. eth := layers.Ethernet{ SrcMAC: i.tun.mac, DstMAC: srcMac, EthernetType: layers.EthernetTypeARP, } // Make the ARP reply layer. arpReply := layers.ARP{ AddrType: arp.AddrType, Protocol: arp.Protocol, HwAddressSize: arp.HwAddressSize, ProtAddressSize: arp.ProtAddressSize, Operation: layers.ARPReply, SourceHwAddress: []byte(i.tun.mac), SourceProtAddress: addr.AsSlice(), DstHwAddress: srcMac, DstProtAddress: srcAddr.AsSlice(), } // Send the ARP reply. i.SendLayers(dst, ð, &arpReply) } } // On ARP reply, we should verify its to us and update table. } else if arp.Operation == layers.ARPReply { // If not destined to us, we should stop here. if !bytes.Equal(i.tun.mac, arp.DstHwAddress) { i.log.Debugf("Mac doesn't match our MAC: %s", net.HardwareAddr(arp.DstHwAddress)) return } // Parse the source. srcMac := net.HardwareAddr(arp.SourceHwAddress) srcAddr, ok := netip.AddrFromSlice(arp.SourceProtAddress) if !ok { return } // Add the source of the ARP reply to the ARP table. i.AddOrUpdateARP(srcAddr, srcMac) // Send event to any ARP reply event listeners. i.tables.RLock() chs, ok := i.tables.arpEvent[srcAddr] if ok { for _, c := range chs { c <- srcMac } } i.tables.RUnlock() } } // Process IPv4 packet. func (i *Interface) HandleIPv4(eth layers.Ethernet, ip4 layers.IPv4) { // Verify the destination is us. if !bytes.Equal(eth.DstMAC, i.tun.mac) { return } // Pass packet to the interface. data := append(ip4.Contents, ip4.Payload...) i.Write(data) } // Process IPv6 packet. func (i *Interface) HandleIPv6(eth layers.Ethernet, ip6 layers.IPv6) { // Verify the destination is us. if !bytes.Equal(eth.DstMAC, i.tun.mac) { return } // Pass packet to the interface. data := append(ip6.Contents, ip6.Payload...) i.Write(data) } // Send a packet to a destination with ethernet layers and bytes to append to encoded packet. // The packet will be encapsulated in vxlan tunnel. func (i *Interface) SendLayers(dst *net.UDPAddr, layersToSend ...gopacket.SerializableLayer) { // Make the VXLAN layer with our VNI. vxlan := layers.VXLAN{ ValidIDFlag: true, VNI: i.vni, } // Setup buffer for encoding the packet. buf := gopacket.NewSerializeBuffer() opts := gopacket.SerializeOptions{ FixLengths: true, ComputeChecksums: true, } // Encode packet with the vxlan and other provided layers. err := gopacket.SerializeLayers(buf, opts, append([]gopacket.SerializableLayer{&vxlan}, layersToSend...)...) if err != nil { i.log.Errorf("failed to encode layers: %v", err) } // Send packet to destination. _, err = i.l.WriteTo(buf.Bytes(), dst) if err != nil { i.log.Errorf("failed to send layers: %v", err) } } // Passsthru to tun write. func (i *Interface) Write(b []byte) (int, error) { return i.tun.device.Write(b) } // Listen to events from the tun device. func (i *Interface) eventReader() { i.log.Debug("event worker - started") // For each event received, process it. for event := range i.tun.device.Events() { // If event is an MTU change, update our MTU. if event&tun.EventMTUUpdate != 0 { // Get the device's current MTU. mtu, err := i.tun.device.MTU() if err != nil { i.log.Errorf("failed to load updated MTU of device: %v", err) continue } // If the MTU is negative, that isn't valid. if mtu < 0 { i.log.Errorf("MTU not updated to negative value: %v", mtu) continue } // Determine if the MTU set is larger than our maximum based on listern's max size. var tooLarge string maxMTU := i.l.MaxMessageSize() - 50 if mtu > maxMTU { tooLarge = fmt.Sprintf(" (too large, capped at %v)", maxMTU) mtu = maxMTU } // Update the MTU, getting the prior MTU. old := i.tun.mtu.Swap(int32(mtu)) // If the MTU changed, we should update the buffer size in the packet reader. if int(old) != mtu { go func() { i.tun.mtuc <- mtu }() i.log.Debugf("MTU updated: %v%s", mtu, tooLarge) } } // If the interface is going up, we should update our state. if event&tun.EventUp != 0 { i.log.Debug("Interface up requested") i.Up() } // If the interface is going down, we should update our state. if event&tun.EventDown != 0 { i.log.Debug("Interface down requested") i.Down() } } i.log.Debug("event worker - stopped") } // Read packets from the tun device, and process them. func (i *Interface) packetReader() { // When stopping, we want to ensure this packet reader stopped before we release the interface. i.state.stopping.Add(1) defer func() { i.log.Debug("TUN packet reader - stopped") i.state.stopping.Done() }() // Setup packet parsers. var ip4 layers.IPv4 ip4Parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip4) ip4Parser.IgnoreUnsupported = true var ip6 layers.IPv6 ip6Parser := gopacket.NewDecodingLayerParser(layers.LayerTypeIPv4, &ip6) ip6Parser.IgnoreUnsupported = true decoded := []gopacket.LayerType{} // Setup buffer with current MTU value. i.log.Debug("TUN packet reader - started") buf := make([]byte, i.MTU()) // Start reading from the tun device. for { n, err := i.tun.device.Read(buf) // If packet received data, parse it. if n > 1 { packet := buf[:n] switch packet[0] >> 4 { case 4: // Parse IPv4 packet. decoded = nil err := ip4Parser.DecodeLayers(packet, &decoded) if err == nil { // Parse the IP address for the destination. addr, ok := netip.AddrFromSlice(ip4.DstIP) if !ok { continue } // Find the MAC address for the IP. mac, err := i.GetMacFor(addr) if err != nil { i.log.Error(err) continue } // Get the UDP destination for the MAC address. dst := i.GetDestinationFor(mac) if dst == nil { continue } // Setup ethernet layer. eth := layers.Ethernet{ SrcMAC: i.tun.mac, DstMAC: mac, EthernetType: layers.EthernetTypeIPv4, } // Setup upper layer masquerade. masq := Masquerade{ MData: ip4.Payload, MLayerType: ip4.Protocol.LayerType(), } // Send data to destination. i.SendLayers(dst, ð, &ip4, &masq) } case 6: // Pase IPv6 packet. decoded = nil err := ip6Parser.DecodeLayers(packet, &decoded) if err == nil { // Parse the IP address for the destination. addr, ok := netip.AddrFromSlice(ip6.DstIP) if !ok { continue } // Find the MAC address for the IP. mac, err := i.GetMacFor(addr) if err != nil { i.log.Error(err) continue } // Get the UDP destination for the MAC address. dst := i.GetDestinationFor(mac) if dst == nil { continue } // Setup ethernet layer. eth := layers.Ethernet{ SrcMAC: i.tun.mac, DstMAC: mac, EthernetType: layers.EthernetTypeIPv6, } // Setup upper layer masquerade. masq := Masquerade{ MData: ip6.Payload, MLayerType: ip6.NextLayerType(), } // Send data to destination. i.SendLayers(dst, ð, &ip6, &masq) } } } // If we received an error, check if we should stop. if err != nil { // If the error is relating to segments, just continue. if errors.Is(err, tun.ErrTooManySegments) { i.log.Debugf("dropped some packets from multi-segment read: %v", err) continue } // If this device isn't closed, we should close it for other errors. if !i.IsClosed() { // Log error if its not an closed error. if !errors.Is(err, os.ErrClosed) { i.log.Errorf("failed to read packet from TUN device: %v", err) } // Close this interface. go i.Close() } // Stop the packet reader here. return } // If the MTU has a change request, make a new buffer. select { case newMTU, ok := <-i.tun.mtuc: if ok { buf = make([]byte, newMTU) } default: } } }