Skip to content

proposal: net: fetch all interfaces with associated addresses in a single call  #53660

@mandarjog

Description

@mandarjog

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

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Incoming

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions