engine: mac: more ipv6 gm
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -91,6 +92,17 @@ func (r *sourceMACResolver) Resolve(ip net.IP) net.HardwareAddr {
|
|||||||
if mac := r.ndpByIP[ipKey]; len(mac) != 0 {
|
if mac := r.ndpByIP[ipKey]; len(mac) != 0 {
|
||||||
return append(net.HardwareAddr(nil), mac...)
|
return append(net.HardwareAddr(nil), mac...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// On-demand IPv6 neighbor lookup via route-netlink as a last fast path.
|
||||||
|
if ip.To4() == nil {
|
||||||
|
if mac, ok := lookupNeighborMACNetlink(ip); ok {
|
||||||
|
out := append(net.HardwareAddr(nil), mac...)
|
||||||
|
r.mu.Lock()
|
||||||
|
r.ndpByIP[ipKey] = append(net.HardwareAddr(nil), mac...)
|
||||||
|
r.mu.Unlock()
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,6 +182,10 @@ func (r *sourceMACResolver) refreshNDPCache(now time.Time) {
|
|||||||
// Fallback for environments without /proc/net/ndisc_cache.
|
// Fallback for environments without /proc/net/ndisc_cache.
|
||||||
m = readIPv6NeighNetlink()
|
m = readIPv6NeighNetlink()
|
||||||
}
|
}
|
||||||
|
if len(m) == 0 {
|
||||||
|
// Last-resort fallback for environments where route-netlink dumps are restricted.
|
||||||
|
m = readIPv6NeighCommand()
|
||||||
|
}
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
r.ndpByIP = m
|
r.ndpByIP = m
|
||||||
r.lastNDPRefresh = now
|
r.lastNDPRefresh = now
|
||||||
@@ -258,6 +274,100 @@ func readIPv6NeighNetlink() map[string]net.HardwareAddr {
|
|||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func lookupNeighborMACNetlink(target net.IP) (net.HardwareAddr, bool) {
|
||||||
|
const (
|
||||||
|
ndMsgLen = 12
|
||||||
|
ndaDst = 1
|
||||||
|
ndaLLAddr = 2
|
||||||
|
)
|
||||||
|
if target == nil || target.To4() != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
target16 := target.To16()
|
||||||
|
if target16 == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := netlink.Dial(unix.NETLINK_ROUTE, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
req := make([]byte, ndMsgLen)
|
||||||
|
req[0] = unix.AF_INET6
|
||||||
|
msgs, err := conn.Execute(netlink.Message{
|
||||||
|
Header: netlink.Header{
|
||||||
|
Type: unix.RTM_GETNEIGH,
|
||||||
|
Flags: netlink.Request | netlink.Dump,
|
||||||
|
},
|
||||||
|
Data: req,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, msg := range msgs {
|
||||||
|
if msg.Header.Type != unix.RTM_NEWNEIGH || len(msg.Data) < ndMsgLen || msg.Data[0] != unix.AF_INET6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
attrs, err := netlink.UnmarshalAttributes(msg.Data[ndMsgLen:])
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var dstIP net.IP
|
||||||
|
var mac net.HardwareAddr
|
||||||
|
for _, a := range attrs {
|
||||||
|
switch a.Type {
|
||||||
|
case ndaDst:
|
||||||
|
if len(a.Data) == net.IPv6len {
|
||||||
|
dstIP = net.IP(append([]byte(nil), a.Data...))
|
||||||
|
}
|
||||||
|
case ndaLLAddr:
|
||||||
|
if len(a.Data) >= 6 {
|
||||||
|
candidate := append(net.HardwareAddr(nil), a.Data...)
|
||||||
|
if candidate.String() != "00:00:00:00:00:00" {
|
||||||
|
mac = candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if dstIP != nil && mac != nil && dstIP.Equal(target16) {
|
||||||
|
return mac, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func readIPv6NeighCommand() map[string]net.HardwareAddr {
|
||||||
|
commands := [][]string{
|
||||||
|
{"ip", "-6", "neigh", "show"},
|
||||||
|
{"/sbin/ip", "-6", "neigh", "show"},
|
||||||
|
{"/usr/sbin/ip", "-6", "neigh", "show"},
|
||||||
|
{"busybox", "ip", "-6", "neigh", "show"},
|
||||||
|
{"/bin/busybox", "ip", "-6", "neigh", "show"},
|
||||||
|
}
|
||||||
|
for _, cmd := range commands {
|
||||||
|
out, err := exec.Command(cmd[0], cmd[1:]...).Output()
|
||||||
|
if err != nil || len(out) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m := make(map[string]net.HardwareAddr)
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
ip, mac, ok := parseNeighborLine(line)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m[ip] = mac
|
||||||
|
}
|
||||||
|
if len(m) != 0 {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map[string]net.HardwareAddr{}
|
||||||
|
}
|
||||||
|
|
||||||
func parseNeighborLine(line string) (string, net.HardwareAddr, bool) {
|
func parseNeighborLine(line string) (string, net.HardwareAddr, bool) {
|
||||||
fields := strings.Fields(line)
|
fields := strings.Fields(line)
|
||||||
if len(fields) < 2 {
|
if len(fields) < 2 {
|
||||||
|
|||||||
Reference in New Issue
Block a user