Repository: go
Issue: proposal: net: fetch all interfaces with associated addresses in a single call 
Labels: Proposal

## Background
At present if we want to get all interface and associated Addresses we write the following 

```go

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`

```javascript
/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.

```javascript
@[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
```go
// 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.
```go
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.
```go
// 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
}
```