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