mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-11 17:47:20 +10:00
Add Android support for MAC and hostname rule items
This commit is contained in:
@@ -5,9 +5,19 @@ import (
|
||||
"net/netip"
|
||||
)
|
||||
|
||||
type NeighborEntry struct {
|
||||
Address netip.Addr
|
||||
MACAddress net.HardwareAddr
|
||||
Hostname string
|
||||
}
|
||||
|
||||
type NeighborResolver interface {
|
||||
LookupMAC(address netip.Addr) (net.HardwareAddr, bool)
|
||||
LookupHostname(address netip.Addr) (string, bool)
|
||||
Start() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
type NeighborUpdateListener interface {
|
||||
UpdateNeighborTable(entries []NeighborEntry)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,10 @@ type PlatformInterface interface {
|
||||
|
||||
UsePlatformNotification() bool
|
||||
SendNotification(notification *Notification) error
|
||||
|
||||
UsePlatformNeighborResolver() bool
|
||||
StartNeighborMonitor(listener NeighborUpdateListener) error
|
||||
CloseNeighborMonitor(listener NeighborUpdateListener) error
|
||||
}
|
||||
|
||||
type FindConnectionOwnerRequest struct {
|
||||
|
||||
@@ -144,6 +144,18 @@ func (s *platformInterfaceStub) SendNotification(notification *adapter.Notificat
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) UsePlatformNeighborResolver() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) StartNeighborMonitor(listener adapter.NeighborUpdateListener) error {
|
||||
return os.ErrInvalid
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) CloseNeighborMonitor(listener adapter.NeighborUpdateListener) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *platformInterfaceStub) UsePlatformLocalDNSTransport() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
135
experimental/libbox/neighbor.go
Normal file
135
experimental/libbox/neighbor.go
Normal file
@@ -0,0 +1,135 @@
|
||||
//go:build linux
|
||||
|
||||
package libbox
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/route"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/mdlayher/netlink"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type NeighborEntry struct {
|
||||
Address string
|
||||
MACAddress string
|
||||
Hostname string
|
||||
}
|
||||
|
||||
type NeighborEntryIterator interface {
|
||||
Next() *NeighborEntry
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type NeighborSubscription struct {
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func SubscribeNeighborTable(listener NeighborUpdateListener) (*NeighborSubscription, error) {
|
||||
entries, err := route.ReadNeighborEntries()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "initial neighbor dump")
|
||||
}
|
||||
table := make(map[netip.Addr]net.HardwareAddr)
|
||||
for _, entry := range entries {
|
||||
table[entry.Address] = entry.MACAddress
|
||||
}
|
||||
listener.UpdateNeighborTable(tableToIterator(table))
|
||||
connection, err := netlink.Dial(unix.NETLINK_ROUTE, &netlink.Config{
|
||||
Groups: 1 << (unix.RTNLGRP_NEIGH - 1),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "subscribe neighbor updates")
|
||||
}
|
||||
subscription := &NeighborSubscription{
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
go subscription.loop(listener, connection, table)
|
||||
return subscription, nil
|
||||
}
|
||||
|
||||
func (s *NeighborSubscription) Close() {
|
||||
close(s.done)
|
||||
}
|
||||
|
||||
func (s *NeighborSubscription) loop(listener NeighborUpdateListener, connection *netlink.Conn, table map[netip.Addr]net.HardwareAddr) {
|
||||
defer connection.Close()
|
||||
for {
|
||||
select {
|
||||
case <-s.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
err := connection.SetReadDeadline(time.Now().Add(3 * time.Second))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
messages, err := connection.Receive()
|
||||
if err != nil {
|
||||
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
||||
continue
|
||||
}
|
||||
select {
|
||||
case <-s.done:
|
||||
return
|
||||
default:
|
||||
}
|
||||
continue
|
||||
}
|
||||
changed := false
|
||||
for _, message := range messages {
|
||||
address, mac, isDelete, ok := route.ParseNeighborMessage(message)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if isDelete {
|
||||
if _, exists := table[address]; exists {
|
||||
delete(table, address)
|
||||
changed = true
|
||||
}
|
||||
} else {
|
||||
existing, exists := table[address]
|
||||
if !exists || !slices.Equal(existing, mac) {
|
||||
table[address] = mac
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
listener.UpdateNeighborTable(tableToIterator(table))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tableToIterator(table map[netip.Addr]net.HardwareAddr) NeighborEntryIterator {
|
||||
entries := make([]*NeighborEntry, 0, len(table))
|
||||
for address, mac := range table {
|
||||
entries = append(entries, &NeighborEntry{
|
||||
Address: address.String(),
|
||||
MACAddress: mac.String(),
|
||||
})
|
||||
}
|
||||
return &neighborEntryIterator{entries}
|
||||
}
|
||||
|
||||
type neighborEntryIterator struct {
|
||||
entries []*NeighborEntry
|
||||
}
|
||||
|
||||
func (i *neighborEntryIterator) HasNext() bool {
|
||||
return len(i.entries) > 0
|
||||
}
|
||||
|
||||
func (i *neighborEntryIterator) Next() *NeighborEntry {
|
||||
if len(i.entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
entry := i.entries[0]
|
||||
i.entries = i.entries[1:]
|
||||
return entry
|
||||
}
|
||||
24
experimental/libbox/neighbor_stub.go
Normal file
24
experimental/libbox/neighbor_stub.go
Normal file
@@ -0,0 +1,24 @@
|
||||
//go:build !linux
|
||||
|
||||
package libbox
|
||||
|
||||
import "os"
|
||||
|
||||
type NeighborEntry struct {
|
||||
Address string
|
||||
MACAddress string
|
||||
Hostname string
|
||||
}
|
||||
|
||||
type NeighborEntryIterator interface {
|
||||
Next() *NeighborEntry
|
||||
HasNext() bool
|
||||
}
|
||||
|
||||
type NeighborSubscription struct{}
|
||||
|
||||
func SubscribeNeighborTable(listener NeighborUpdateListener) (*NeighborSubscription, error) {
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (s *NeighborSubscription) Close() {}
|
||||
@@ -21,6 +21,12 @@ type PlatformInterface interface {
|
||||
SystemCertificates() StringIterator
|
||||
ClearDNSCache()
|
||||
SendNotification(notification *Notification) error
|
||||
StartNeighborMonitor(listener NeighborUpdateListener) error
|
||||
CloseNeighborMonitor(listener NeighborUpdateListener) error
|
||||
}
|
||||
|
||||
type NeighborUpdateListener interface {
|
||||
UpdateNeighborTable(entries NeighborEntryIterator)
|
||||
}
|
||||
|
||||
type ConnectionOwner struct {
|
||||
|
||||
@@ -220,6 +220,43 @@ func (w *platformInterfaceWrapper) SendNotification(notification *adapter.Notifi
|
||||
return w.iif.SendNotification((*Notification)(notification))
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) UsePlatformNeighborResolver() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) StartNeighborMonitor(listener adapter.NeighborUpdateListener) error {
|
||||
return w.iif.StartNeighborMonitor(&neighborUpdateListenerWrapper{listener: listener})
|
||||
}
|
||||
|
||||
func (w *platformInterfaceWrapper) CloseNeighborMonitor(listener adapter.NeighborUpdateListener) error {
|
||||
return w.iif.CloseNeighborMonitor(nil)
|
||||
}
|
||||
|
||||
type neighborUpdateListenerWrapper struct {
|
||||
listener adapter.NeighborUpdateListener
|
||||
}
|
||||
|
||||
func (w *neighborUpdateListenerWrapper) UpdateNeighborTable(entries NeighborEntryIterator) {
|
||||
var result []adapter.NeighborEntry
|
||||
for entries.HasNext() {
|
||||
entry := entries.Next()
|
||||
address, err := netip.ParseAddr(entry.Address)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
macAddress, err := net.ParseMAC(entry.MACAddress)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
result = append(result, adapter.NeighborEntry{
|
||||
Address: address,
|
||||
MACAddress: macAddress,
|
||||
Hostname: entry.Hostname,
|
||||
})
|
||||
}
|
||||
w.listener.UpdateNeighborTable(result)
|
||||
}
|
||||
|
||||
func AvailablePort(startPort int32) (int32, error) {
|
||||
for port := int(startPort); ; port++ {
|
||||
if port > 65535 {
|
||||
|
||||
@@ -4,7 +4,6 @@ package route
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"net/netip"
|
||||
@@ -204,43 +203,17 @@ func (r *neighborResolver) subscribeNeighborUpdates() {
|
||||
continue
|
||||
}
|
||||
for _, message := range messages {
|
||||
switch message.Header.Type {
|
||||
case unix.RTM_NEWNEIGH:
|
||||
var neighMessage rtnetlink.NeighMessage
|
||||
unmarshalErr := neighMessage.UnmarshalBinary(message.Data)
|
||||
if unmarshalErr != nil {
|
||||
continue
|
||||
}
|
||||
if neighMessage.Attributes == nil {
|
||||
continue
|
||||
}
|
||||
if neighMessage.Attributes.LLAddress == nil || len(neighMessage.Attributes.Address) == 0 {
|
||||
continue
|
||||
}
|
||||
address, ok := netip.AddrFromSlice(neighMessage.Attributes.Address)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
r.access.Lock()
|
||||
r.neighborIPToMAC[address] = slices.Clone(neighMessage.Attributes.LLAddress)
|
||||
r.access.Unlock()
|
||||
case unix.RTM_DELNEIGH:
|
||||
var neighMessage rtnetlink.NeighMessage
|
||||
unmarshalErr := neighMessage.UnmarshalBinary(message.Data)
|
||||
if unmarshalErr != nil {
|
||||
continue
|
||||
}
|
||||
if neighMessage.Attributes == nil || len(neighMessage.Attributes.Address) == 0 {
|
||||
continue
|
||||
}
|
||||
address, ok := netip.AddrFromSlice(neighMessage.Attributes.Address)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
r.access.Lock()
|
||||
delete(r.neighborIPToMAC, address)
|
||||
r.access.Unlock()
|
||||
address, mac, isDelete, ok := ParseNeighborMessage(message)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
r.access.Lock()
|
||||
if isDelete {
|
||||
delete(r.neighborIPToMAC, address)
|
||||
} else {
|
||||
r.neighborIPToMAC[address] = mac
|
||||
}
|
||||
r.access.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -554,43 +527,3 @@ func (r *neighborResolver) parseKeaCSV6(file *os.File, ipToMAC map[netip.Addr]ne
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func extractMACFromDUID(duid []byte) (net.HardwareAddr, bool) {
|
||||
if len(duid) < 4 {
|
||||
return nil, false
|
||||
}
|
||||
duidType := binary.BigEndian.Uint16(duid[0:2])
|
||||
hwType := binary.BigEndian.Uint16(duid[2:4])
|
||||
if hwType != 1 {
|
||||
return nil, false
|
||||
}
|
||||
switch duidType {
|
||||
case 1:
|
||||
if len(duid) < 14 {
|
||||
return nil, false
|
||||
}
|
||||
return net.HardwareAddr(slices.Clone(duid[8:14])), true
|
||||
case 3:
|
||||
if len(duid) < 10 {
|
||||
return nil, false
|
||||
}
|
||||
return net.HardwareAddr(slices.Clone(duid[4:10])), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func extractMACFromEUI64(address netip.Addr) (net.HardwareAddr, bool) {
|
||||
if !address.Is6() {
|
||||
return nil, false
|
||||
}
|
||||
b := address.As16()
|
||||
if b[11] != 0xff || b[12] != 0xfe {
|
||||
return nil, false
|
||||
}
|
||||
return net.HardwareAddr{b[8] ^ 0x02, b[9], b[10], b[13], b[14], b[15]}, true
|
||||
}
|
||||
|
||||
func parseDUID(s string) ([]byte, error) {
|
||||
cleaned := strings.ReplaceAll(s, ":", "")
|
||||
return hex.DecodeString(cleaned)
|
||||
}
|
||||
|
||||
50
route/neighbor_resolver_parse.go
Normal file
50
route/neighbor_resolver_parse.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func extractMACFromDUID(duid []byte) (net.HardwareAddr, bool) {
|
||||
if len(duid) < 4 {
|
||||
return nil, false
|
||||
}
|
||||
duidType := binary.BigEndian.Uint16(duid[0:2])
|
||||
hwType := binary.BigEndian.Uint16(duid[2:4])
|
||||
if hwType != 1 {
|
||||
return nil, false
|
||||
}
|
||||
switch duidType {
|
||||
case 1:
|
||||
if len(duid) < 14 {
|
||||
return nil, false
|
||||
}
|
||||
return net.HardwareAddr(slices.Clone(duid[8:14])), true
|
||||
case 3:
|
||||
if len(duid) < 10 {
|
||||
return nil, false
|
||||
}
|
||||
return net.HardwareAddr(slices.Clone(duid[4:10])), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func extractMACFromEUI64(address netip.Addr) (net.HardwareAddr, bool) {
|
||||
if !address.Is6() {
|
||||
return nil, false
|
||||
}
|
||||
b := address.As16()
|
||||
if b[11] != 0xff || b[12] != 0xfe {
|
||||
return nil, false
|
||||
}
|
||||
return net.HardwareAddr{b[8] ^ 0x02, b[9], b[10], b[13], b[14], b[15]}, true
|
||||
}
|
||||
|
||||
func parseDUID(s string) ([]byte, error) {
|
||||
cleaned := strings.ReplaceAll(s, ":", "")
|
||||
return hex.DecodeString(cleaned)
|
||||
}
|
||||
84
route/neighbor_resolver_platform.go
Normal file
84
route/neighbor_resolver_platform.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package route
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"sync"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing/common/logger"
|
||||
)
|
||||
|
||||
type platformNeighborResolver struct {
|
||||
logger logger.ContextLogger
|
||||
platform adapter.PlatformInterface
|
||||
access sync.RWMutex
|
||||
ipToMAC map[netip.Addr]net.HardwareAddr
|
||||
ipToHostname map[netip.Addr]string
|
||||
macToHostname map[string]string
|
||||
}
|
||||
|
||||
func newPlatformNeighborResolver(resolverLogger logger.ContextLogger, platform adapter.PlatformInterface) adapter.NeighborResolver {
|
||||
return &platformNeighborResolver{
|
||||
logger: resolverLogger,
|
||||
platform: platform,
|
||||
ipToMAC: make(map[netip.Addr]net.HardwareAddr),
|
||||
ipToHostname: make(map[netip.Addr]string),
|
||||
macToHostname: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *platformNeighborResolver) Start() error {
|
||||
return r.platform.StartNeighborMonitor(r)
|
||||
}
|
||||
|
||||
func (r *platformNeighborResolver) Close() error {
|
||||
return r.platform.CloseNeighborMonitor(r)
|
||||
}
|
||||
|
||||
func (r *platformNeighborResolver) LookupMAC(address netip.Addr) (net.HardwareAddr, bool) {
|
||||
r.access.RLock()
|
||||
defer r.access.RUnlock()
|
||||
mac, found := r.ipToMAC[address]
|
||||
if found {
|
||||
return mac, true
|
||||
}
|
||||
return extractMACFromEUI64(address)
|
||||
}
|
||||
|
||||
func (r *platformNeighborResolver) LookupHostname(address netip.Addr) (string, bool) {
|
||||
r.access.RLock()
|
||||
defer r.access.RUnlock()
|
||||
hostname, found := r.ipToHostname[address]
|
||||
if found {
|
||||
return hostname, true
|
||||
}
|
||||
mac, found := r.ipToMAC[address]
|
||||
if !found {
|
||||
mac, found = extractMACFromEUI64(address)
|
||||
}
|
||||
if !found {
|
||||
return "", false
|
||||
}
|
||||
hostname, found = r.macToHostname[mac.String()]
|
||||
return hostname, found
|
||||
}
|
||||
|
||||
func (r *platformNeighborResolver) UpdateNeighborTable(entries []adapter.NeighborEntry) {
|
||||
ipToMAC := make(map[netip.Addr]net.HardwareAddr)
|
||||
ipToHostname := make(map[netip.Addr]string)
|
||||
macToHostname := make(map[string]string)
|
||||
for _, entry := range entries {
|
||||
ipToMAC[entry.Address] = entry.MACAddress
|
||||
if entry.Hostname != "" {
|
||||
ipToHostname[entry.Address] = entry.Hostname
|
||||
macToHostname[entry.MACAddress.String()] = entry.Hostname
|
||||
}
|
||||
}
|
||||
r.access.Lock()
|
||||
r.ipToMAC = ipToMAC
|
||||
r.ipToHostname = ipToHostname
|
||||
r.macToHostname = macToHostname
|
||||
r.access.Unlock()
|
||||
r.logger.Info("updated neighbor table: ", len(entries), " entries")
|
||||
}
|
||||
68
route/neighbor_table_linux.go
Normal file
68
route/neighbor_table_linux.go
Normal file
@@ -0,0 +1,68 @@
|
||||
//go:build linux
|
||||
|
||||
package route
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/netip"
|
||||
"slices"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/jsimonetti/rtnetlink"
|
||||
"github.com/mdlayher/netlink"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func ReadNeighborEntries() ([]adapter.NeighborEntry, error) {
|
||||
connection, err := rtnetlink.Dial(nil)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "dial rtnetlink")
|
||||
}
|
||||
defer connection.Close()
|
||||
neighbors, err := connection.Neigh.List()
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "list neighbors")
|
||||
}
|
||||
var entries []adapter.NeighborEntry
|
||||
for _, neighbor := range neighbors {
|
||||
if neighbor.Attributes == nil {
|
||||
continue
|
||||
}
|
||||
if neighbor.Attributes.LLAddress == nil || len(neighbor.Attributes.Address) == 0 {
|
||||
continue
|
||||
}
|
||||
address, ok := netip.AddrFromSlice(neighbor.Attributes.Address)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
entries = append(entries, adapter.NeighborEntry{
|
||||
Address: address,
|
||||
MACAddress: slices.Clone(neighbor.Attributes.LLAddress),
|
||||
})
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func ParseNeighborMessage(message netlink.Message) (address netip.Addr, macAddress net.HardwareAddr, isDelete bool, ok bool) {
|
||||
var neighMessage rtnetlink.NeighMessage
|
||||
err := neighMessage.UnmarshalBinary(message.Data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if neighMessage.Attributes == nil || len(neighMessage.Attributes.Address) == 0 {
|
||||
return
|
||||
}
|
||||
address, ok = netip.AddrFromSlice(neighMessage.Attributes.Address)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
isDelete = message.Header.Type == unix.RTM_DELNEIGH
|
||||
if !isDelete && neighMessage.Attributes.LLAddress == nil {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
macAddress = slices.Clone(neighMessage.Attributes.LLAddress)
|
||||
return
|
||||
}
|
||||
@@ -159,21 +159,34 @@ func (r *Router) Start(stage adapter.StartStage) error {
|
||||
}
|
||||
r.needFindNeighbor = needFindNeighbor
|
||||
if needFindNeighbor {
|
||||
monitor.Start("initialize neighbor resolver")
|
||||
resolver, err := newNeighborResolver(r.logger, r.leaseFiles)
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
if err != os.ErrInvalid {
|
||||
r.logger.Warn(E.Cause(err, "create neighbor resolver"))
|
||||
}
|
||||
} else {
|
||||
err = resolver.Start()
|
||||
if r.platformInterface != nil && r.platformInterface.UsePlatformNeighborResolver() {
|
||||
monitor.Start("initialize neighbor resolver")
|
||||
resolver := newPlatformNeighborResolver(r.logger, r.platformInterface)
|
||||
err := resolver.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
r.logger.Warn(E.Cause(err, "start neighbor resolver"))
|
||||
r.logger.Error(E.Cause(err, "start neighbor resolver"))
|
||||
} else {
|
||||
r.neighborResolver = resolver
|
||||
}
|
||||
}
|
||||
if r.neighborResolver == nil {
|
||||
monitor.Start("initialize neighbor resolver")
|
||||
resolver, err := newNeighborResolver(r.logger, r.leaseFiles)
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
if err != os.ErrInvalid {
|
||||
r.logger.Error(E.Cause(err, "create neighbor resolver"))
|
||||
}
|
||||
} else {
|
||||
err = resolver.Start()
|
||||
if err != nil {
|
||||
r.logger.Error(E.Cause(err, "start neighbor resolver"))
|
||||
} else {
|
||||
r.neighborResolver = resolver
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case adapter.StartStatePostStart:
|
||||
for i, rule := range r.rules {
|
||||
|
||||
Reference in New Issue
Block a user