- 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