File: netlink.go

package info (click to toggle)
incus 6.0.5-8
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 26,092 kB
  • sloc: sh: 16,313; ansic: 3,121; python: 457; makefile: 337; ruby: 51; sql: 50; lisp: 6
file content (99 lines) | stat: -rw-r--r-- 2,545 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//go:build linux

package linux

import (
	"fmt"
	"net"
	"syscall"
	"unsafe"
)

// NetlinkInterface returns a net.Interface extended to also contain its addresses.
type NetlinkInterface struct {
	net.Interface

	Addresses []net.Addr
}

// NetlinkInterfaces performs a RTM_GETADDR call to get both.
func NetlinkInterfaces() ([]NetlinkInterface, error) {
	// Grab the interface list.
	ifaces, err := net.Interfaces()
	if err != nil {
		return nil, err
	}

	// Initialize result slice.
	netlinkIfaces := make([]NetlinkInterface, 0, len(ifaces))
	for _, iface := range ifaces {
		netlinkIfaces = append(netlinkIfaces, NetlinkInterface{iface, make([]net.Addr, 0)})
	}

	// Turn it into a map.
	ifaceMap := make(map[int]*NetlinkInterface, len(ifaces))
	for k, v := range netlinkIfaces {
		ifaceMap[v.Index] = &netlinkIfaces[k] //nolint:typecheck
	}

	// Make the netlink call.
	rib, err := syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_UNSPEC)
	if err != nil {
		return nil, fmt.Errorf("Failed to query RTM_GETADDR: %v", err)
	}

	messages, err := syscall.ParseNetlinkMessage(rib)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse RTM_GETADDR: %v", err)
	}

	for _, m := range messages {
		if m.Header.Type == syscall.RTM_NEWADDR {
			addrMessage := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0]))

			addrAttrs, err := syscall.ParseNetlinkRouteAttr(&m)
			if err != nil {
				return nil, fmt.Errorf("Failed to parse route attribute: %v", err)
			}

			ifi, ok := ifaceMap[int(addrMessage.Index)]
			if ok {
				ifi.Addresses = append(ifi.Addresses, newAddr(addrMessage, addrAttrs))
			}
		}
	}

	return netlinkIfaces, nil
}

// Variation of function of the same name from within Go source.
func newAddr(ifam *syscall.IfAddrmsg, attrs []syscall.NetlinkRouteAttr) net.Addr {
	var ipPointToPoint bool

	// Seems like we need to make sure whether the IP interface
	// stack consists of IP point-to-point numbered or unnumbered
	// addressing.
	for _, a := range attrs {
		if a.Attr.Type == syscall.IFA_LOCAL {
			ipPointToPoint = true
			break
		}
	}

	for _, a := range attrs {
		if ipPointToPoint && a.Attr.Type == syscall.IFA_ADDRESS {
			continue
		}

		switch ifam.Family {
		case syscall.AF_INET:
			return &net.IPNet{IP: net.IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]), Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv4len)}
		case syscall.AF_INET6:
			ifa := &net.IPNet{IP: make(net.IP, net.IPv6len), Mask: net.CIDRMask(int(ifam.Prefixlen), 8*net.IPv6len)}
			copy(ifa.IP, a.Value[:])
			return ifa
		}
	}

	return nil
}