/
util_linux.go
138 lines (120 loc) · 3.57 KB
/
util_linux.go
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//go:build linux
// +build linux
package ocicni
import (
"errors"
"fmt"
"net"
"os/exec"
"strings"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/vishvananda/netlink"
)
const defaultNamespaceEnterCommandName = "nsenter"
type nsManager struct {
nsenterPath string
}
func (nsm *nsManager) init() error {
var err error
nsm.nsenterPath, err = exec.LookPath(defaultNamespaceEnterCommandName)
return err
}
func getContainerDetails(nsm *nsManager, netnsPath, interfaceName, addrType string) (*net.IPNet, *net.HardwareAddr, error) {
// Try to retrieve ip inside container network namespace
output, err := exec.Command(nsm.nsenterPath, "--net="+netnsPath, "-F", "--",
"ip", "-o", addrType, "addr", "show", "dev", interfaceName, "scope", "global").CombinedOutput()
if err != nil {
return nil, nil, fmt.Errorf("unexpected command output %s with error: %w", output, err)
}
lines := strings.Split(string(output), "\n")
if len(lines) < 1 {
return nil, nil, fmt.Errorf("unexpected command output %s", output)
}
fields := strings.Fields(lines[0])
if len(fields) < 4 {
return nil, nil, fmt.Errorf("unexpected address output %s ", lines[0])
}
ip, ipNet, err := net.ParseCIDR(fields[3])
if err != nil {
return nil, nil, fmt.Errorf("CNI failed to parse ip from output %s due to %w", output, err)
}
if ip.To4() == nil {
ipNet.IP = ip
} else {
ipNet.IP = ip.To4()
}
// Try to retrieve MAC inside container network namespace
output, err = exec.Command(nsm.nsenterPath, "--net="+netnsPath, "-F", "--",
"ip", "link", "show", "dev", interfaceName).CombinedOutput()
if err != nil {
return nil, nil, fmt.Errorf("unexpected 'ip link' command output %s with error: %w", output, err)
}
lines = strings.Split(string(output), "\n")
if len(lines) < 2 {
return nil, nil, fmt.Errorf("unexpected 'ip link' command output %s", output)
}
fields = strings.Fields(lines[1])
if len(fields) < 4 {
return nil, nil, fmt.Errorf("unexpected link output %s ", lines[0])
}
mac, err := net.ParseMAC(fields[1])
if err != nil {
return nil, nil, fmt.Errorf("failed to parse MAC from output %s due to %w", output, err)
}
return ipNet, &mac, nil
}
func bringUpLoopback(netns string) error {
if err := ns.WithNetNSPath(netns, func(_ ns.NetNS) error {
link, err := netlink.LinkByName(loIfname)
if err == nil {
err = netlink.LinkSetUp(link)
}
if err != nil {
return err
}
v4Addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
if err != nil {
return err
}
if len(v4Addrs) != 0 {
// sanity check that this is a loopback address
for _, addr := range v4Addrs {
if !addr.IP.IsLoopback() {
return fmt.Errorf("loopback interface found with non-loopback address %q", addr.IP)
}
}
}
v6Addrs, err := netlink.AddrList(link, netlink.FAMILY_V6)
if err != nil {
return err
}
if len(v6Addrs) != 0 {
// sanity check that this is a loopback address
for _, addr := range v6Addrs {
if !addr.IP.IsLoopback() {
return fmt.Errorf("loopback interface found with non-loopback address %q", addr.IP)
}
}
}
return nil
}); err != nil {
return fmt.Errorf("error adding loopback interface: %w", err)
}
return nil
}
func checkLoopback(netns string) error {
// Make sure loopback interface is up
if err := ns.WithNetNSPath(netns, func(_ ns.NetNS) error {
link, err := netlink.LinkByName(loIfname)
if err != nil {
return err
}
if link.Attrs().Flags&net.FlagUp != net.FlagUp {
return errors.New("loopback interface is down")
}
return nil
}); err != nil {
return fmt.Errorf("error checking loopback interface: %w", err)
}
return nil
}