For fast datapath, the router core will only be invoked upon datapath misses. And it needs to yield routing decisions in a form that can be used to configure the in-kernel datapath. So here we make the router core yield FlowOps, which represent operations which actually do something with a packet. The router core obtains these in turn from the Bridge or the Overlay, depending on what it wants to do with a packet. For fast datapath, it's critical that the router makes decisions based only on the required packet header information (as the same header information will be used when configuring the ODP flow rules). So we pass only that information into the router core, in the form of PacketKeys. The overall effect is to curry the router core: Rather than taking a packet and doing something with it, it takes packet header information and returns a function that does something with a packet.
99 lines
2.0 KiB
Go
99 lines
2.0 KiB
Go
package router
|
|
|
|
import (
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type MacCacheEntry struct {
|
|
lastSeen time.Time
|
|
peer *Peer
|
|
}
|
|
|
|
type MacCache struct {
|
|
sync.RWMutex
|
|
table map[uint64]*MacCacheEntry
|
|
maxAge time.Duration
|
|
expiryTimer *time.Timer
|
|
onExpiry func(net.HardwareAddr, *Peer)
|
|
}
|
|
|
|
func NewMacCache(maxAge time.Duration, onExpiry func(net.HardwareAddr, *Peer)) *MacCache {
|
|
cache := &MacCache{
|
|
table: make(map[uint64]*MacCacheEntry),
|
|
maxAge: maxAge,
|
|
onExpiry: onExpiry}
|
|
cache.setExpiryTimer()
|
|
return cache
|
|
}
|
|
|
|
func (cache *MacCache) Enter(mac net.HardwareAddr, peer *Peer) bool {
|
|
key := macint(mac)
|
|
now := time.Now()
|
|
cache.RLock()
|
|
entry, found := cache.table[key]
|
|
if found && entry.peer == peer && now.Before(entry.lastSeen.Add(cache.maxAge/10)) {
|
|
cache.RUnlock()
|
|
return false
|
|
}
|
|
cache.RUnlock()
|
|
cache.Lock()
|
|
defer cache.Unlock()
|
|
entry, found = cache.table[key]
|
|
if !found {
|
|
cache.table[key] = &MacCacheEntry{lastSeen: now, peer: peer}
|
|
return true
|
|
}
|
|
if entry.peer != peer {
|
|
entry.lastSeen = now
|
|
entry.peer = peer
|
|
return true
|
|
}
|
|
if now.After(entry.lastSeen.Add(cache.maxAge / 10)) {
|
|
entry.lastSeen = now
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (cache *MacCache) Lookup(mac net.HardwareAddr) *Peer {
|
|
key := macint(mac)
|
|
cache.RLock()
|
|
defer cache.RUnlock()
|
|
entry, found := cache.table[key]
|
|
if !found {
|
|
return nil
|
|
}
|
|
return entry.peer
|
|
}
|
|
|
|
func (cache *MacCache) Delete(peer *Peer) bool {
|
|
found := false
|
|
cache.Lock()
|
|
defer cache.Unlock()
|
|
for key, entry := range cache.table {
|
|
if entry.peer == peer {
|
|
delete(cache.table, key)
|
|
found = true
|
|
}
|
|
}
|
|
return found
|
|
}
|
|
|
|
func (cache *MacCache) setExpiryTimer() {
|
|
cache.expiryTimer = time.AfterFunc(cache.maxAge/10, func() { cache.expire() })
|
|
}
|
|
|
|
func (cache *MacCache) expire() {
|
|
now := time.Now()
|
|
cache.Lock()
|
|
defer cache.Unlock()
|
|
for key, entry := range cache.table {
|
|
if now.After(entry.lastSeen.Add(cache.maxAge)) {
|
|
delete(cache.table, key)
|
|
cache.onExpiry(intmac(key), entry.peer)
|
|
}
|
|
}
|
|
cache.setExpiryTimer()
|
|
}
|