-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Description
Background
At present if we want to get all interface and associated Addresses we write the following
ifaceByName := make(map[string]net.Interface)
addrByName := make(map[string][]net.IP)
interfaces, _ := net.Interfaces()
for i := 0; i < len(interfaces); i++ {
intf := interfaces[i]
addrByName[intf.Name] = intf.Addrs()
}
I would like a function that returns this entire information.
Why is it needed ?
intf.Addrs()
call is inefficient because it results in getting the entire RIB syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
every time. The size of the RIB is proportional to the number of interfaces/Addrs. If on a router you have 1000s of interfaces this O(n^2)
behaviour becomes untenable.
When number of interfaces is small this problem does not show up.
details
The flamegraphs show excessive time spent making syscall and repeatedly parsing messages.
I also ran call count using bpftrace
/usr/local/bin/bpftrace -p ${prog-pid}\
-e 'uprobe:/usr/local/bin/prog:*vmsysmon.*,uprobe:/usr/local/bin/prog:net.int*,uprobe:/usr/local/bin/prog:syscall.* { @[func] = count(); }'
@[prog.(*IfMon).calcStats]: 5922
@[net.interfaceAddrTable]: 6169
@[syscall.Close]: 6318
@[syscall.RawSyscall.abi0]: 10287
@[syscall.Syscall.abi0]: 10583
@[syscall.(*SockaddrNetlink).sockaddr]: 12320
@[syscall.ParseNetlinkRouteAttr]: 16582
@[syscall.recvfrom]: 398880
@[syscall.anyToSockaddr]: 506789
@[syscall.Syscall6.abi0]: 568181
@[syscall.Recvfrom]: 622379
@[syscall.ParseNetlinkMessage]: 655167
calcStats
function results in
After I made the change to fetch the entire RIB and associate addresses in a single loop it makes significant change in the call counts and times.
@[syscall.Syscall.abi0]: 2265
@[syscall.recvfrom]: 3330
@[syscall.Recvfrom]: 3334
@[syscall.ParseNetlinkMessage]: 3335
@[syscall.anyToSockaddr]: 3347
@[syscall.Syscall6.abi0]: 3482
@[aviatrix.com/vmsysmon.(*IfMon).calcStats]: 5912
@[syscall.ParseNetlinkRouteAttr]: 18326
Proposed API
Option 1
// InterfaceAddrsWithIndex returns a map from interface index to a list of
// associated unicast addresses.
func InterfaceAddrsWithIndex()(map[int][]Addr, error) {
return nil, nil
}
Keeps the flavor of existing InterfaceAddrs
API.
Typical use.
func testListInterfaceDetails() {
interfaces, _ := Interfaces()
addrsMap, _ := InterfaceAddrsWithIndex()
for i := 0; i < len(interfaces); i++ {
intf := interfaces[i]
if addrs, found := addrsMap[intf.Index]; found {
fmt.Printf("%v: %v\n", intf, addrs)
}
}
}
Option 2
Treat all the information above as a single DB and return it. More optimizations are possible with this API without changing the API signature.
// IntfRecord keeps interface and associated Addresses.
type IntfRecord struct {
Intf Interface
Addrs []Addr
}
type IntfDB map[int]IntfRecord
// InterfaceDB returns a map from interface index to an IntfRecord.
// This is the most efficient way feth the entire Interface database.
func InterfaceDB() (IntfDB, error) {
return nil, nil
}
Metadata
Metadata
Assignees
Labels
Type
Projects
Status