engine: mac: use ndp for ipv6
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -12,6 +13,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
ifaceCacheTTL = 30 * time.Second
|
ifaceCacheTTL = 30 * time.Second
|
||||||
arpCacheTTL = 10 * time.Second
|
arpCacheTTL = 10 * time.Second
|
||||||
|
ndpCacheTTL = 10 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
type sourceMACResolver struct {
|
type sourceMACResolver struct {
|
||||||
@@ -22,12 +24,16 @@ type sourceMACResolver struct {
|
|||||||
|
|
||||||
lastARPRefresh time.Time
|
lastARPRefresh time.Time
|
||||||
arpByIP map[string]net.HardwareAddr
|
arpByIP map[string]net.HardwareAddr
|
||||||
|
|
||||||
|
lastNDPRefresh time.Time
|
||||||
|
ndpByIP map[string]net.HardwareAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSourceMACResolver() *sourceMACResolver {
|
func newSourceMACResolver() *sourceMACResolver {
|
||||||
return &sourceMACResolver{
|
return &sourceMACResolver{
|
||||||
ifaceByIP: make(map[string]net.HardwareAddr),
|
ifaceByIP: make(map[string]net.HardwareAddr),
|
||||||
arpByIP: make(map[string]net.HardwareAddr),
|
arpByIP: make(map[string]net.HardwareAddr),
|
||||||
|
ndpByIP: make(map[string]net.HardwareAddr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +50,7 @@ func (r *sourceMACResolver) Resolve(ip net.IP) net.HardwareAddr {
|
|||||||
r.mu.RLock()
|
r.mu.RLock()
|
||||||
ifaceRefreshDue := now.Sub(r.lastIfaceRefresh) > ifaceCacheTTL
|
ifaceRefreshDue := now.Sub(r.lastIfaceRefresh) > ifaceCacheTTL
|
||||||
arpRefreshDue := now.Sub(r.lastARPRefresh) > arpCacheTTL
|
arpRefreshDue := now.Sub(r.lastARPRefresh) > arpCacheTTL
|
||||||
|
ndpRefreshDue := now.Sub(r.lastNDPRefresh) > ndpCacheTTL
|
||||||
if mac := r.ifaceByIP[ipKey]; len(mac) != 0 {
|
if mac := r.ifaceByIP[ipKey]; len(mac) != 0 {
|
||||||
out := append(net.HardwareAddr(nil), mac...)
|
out := append(net.HardwareAddr(nil), mac...)
|
||||||
r.mu.RUnlock()
|
r.mu.RUnlock()
|
||||||
@@ -54,6 +61,11 @@ func (r *sourceMACResolver) Resolve(ip net.IP) net.HardwareAddr {
|
|||||||
r.mu.RUnlock()
|
r.mu.RUnlock()
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
if mac := r.ndpByIP[ipKey]; len(mac) != 0 && !ndpRefreshDue {
|
||||||
|
out := append(net.HardwareAddr(nil), mac...)
|
||||||
|
r.mu.RUnlock()
|
||||||
|
return out
|
||||||
|
}
|
||||||
r.mu.RUnlock()
|
r.mu.RUnlock()
|
||||||
|
|
||||||
if ifaceRefreshDue {
|
if ifaceRefreshDue {
|
||||||
@@ -62,6 +74,9 @@ func (r *sourceMACResolver) Resolve(ip net.IP) net.HardwareAddr {
|
|||||||
if arpRefreshDue {
|
if arpRefreshDue {
|
||||||
r.refreshARPCache(now)
|
r.refreshARPCache(now)
|
||||||
}
|
}
|
||||||
|
if ndpRefreshDue {
|
||||||
|
r.refreshNDPCache(now)
|
||||||
|
}
|
||||||
|
|
||||||
r.mu.RLock()
|
r.mu.RLock()
|
||||||
defer r.mu.RUnlock()
|
defer r.mu.RUnlock()
|
||||||
@@ -71,6 +86,9 @@ func (r *sourceMACResolver) Resolve(ip net.IP) net.HardwareAddr {
|
|||||||
if mac := r.arpByIP[ipKey]; len(mac) != 0 {
|
if mac := r.arpByIP[ipKey]; len(mac) != 0 {
|
||||||
return append(net.HardwareAddr(nil), mac...)
|
return append(net.HardwareAddr(nil), mac...)
|
||||||
}
|
}
|
||||||
|
if mac := r.ndpByIP[ipKey]; len(mac) != 0 {
|
||||||
|
return append(net.HardwareAddr(nil), mac...)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,3 +161,78 @@ func (r *sourceMACResolver) refreshARPCache(now time.Time) {
|
|||||||
r.lastARPRefresh = now
|
r.lastARPRefresh = now
|
||||||
r.mu.Unlock()
|
r.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *sourceMACResolver) refreshNDPCache(now time.Time) {
|
||||||
|
m, ok := readNeighborCacheFile("/proc/net/ndisc_cache")
|
||||||
|
if !ok {
|
||||||
|
// Fallback for environments without /proc/net/ndisc_cache.
|
||||||
|
m = readIPv6NeighCommand()
|
||||||
|
}
|
||||||
|
r.mu.Lock()
|
||||||
|
r.ndpByIP = m
|
||||||
|
r.lastNDPRefresh = now
|
||||||
|
r.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func readNeighborCacheFile(path string) (map[string]net.HardwareAddr, bool) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
m := make(map[string]net.HardwareAddr)
|
||||||
|
scanner := bufio.NewScanner(f)
|
||||||
|
for scanner.Scan() {
|
||||||
|
ip, mac, ok := parseNeighborLine(scanner.Text())
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[ip] = mac
|
||||||
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return m, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func readIPv6NeighCommand() map[string]net.HardwareAddr {
|
||||||
|
m := make(map[string]net.HardwareAddr)
|
||||||
|
out, err := exec.Command("ip", "-6", "neigh", "show").Output()
|
||||||
|
if err != nil {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
ip, mac, ok := parseNeighborLine(line)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[ip] = mac
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNeighborLine(line string) (string, net.HardwareAddr, bool) {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < 2 {
|
||||||
|
return "", nil, false
|
||||||
|
}
|
||||||
|
var ipStr string
|
||||||
|
var mac net.HardwareAddr
|
||||||
|
for _, f := range fields {
|
||||||
|
if ip := net.ParseIP(f); ip != nil {
|
||||||
|
ipStr = ip.String()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if parsedMAC, err := net.ParseMAC(f); err == nil {
|
||||||
|
// Ignore unresolved/bogus entries.
|
||||||
|
if parsedMAC.String() != "00:00:00:00:00:00" {
|
||||||
|
mac = append(net.HardwareAddr(nil), parsedMAC...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ipStr == "" || len(mac) == 0 {
|
||||||
|
return "", nil, false
|
||||||
|
}
|
||||||
|
return ipStr, mac, true
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user