-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
netns.go
152 lines (123 loc) · 3.52 KB
/
netns.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
//go:build linux
package kernel
import (
"errors"
"fmt"
"os"
"path"
"runtime"
"syscall"
"github.com/vishvananda/netns"
"golang.org/x/sys/unix"
"github.com/DataDog/datadog-agent/pkg/util/log"
)
// WithRootNS executes a function within root network namespace and then switch back
// to the previous namespace. If the thread is already in the root network namespace,
// the function is executed without calling SYS_SETNS.
func WithRootNS(procRoot string, fn func() error) error {
rootNS, err := GetRootNetNamespace(procRoot)
if err != nil {
return err
}
defer rootNS.Close()
return WithNS(rootNS, fn)
}
// WithNS executes the given function in the given network namespace, and then
// switches back to the previous namespace.
func WithNS(ns netns.NsHandle, fn func() error) error {
if ns == netns.None() {
return fn()
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
prevNS, err := netns.Get()
if err != nil {
return err
}
defer prevNS.Close()
if ns.Equal(prevNS) {
return fn()
}
if err := netns.Set(ns); err != nil {
return err
}
fnErr := fn()
nsErr := netns.Set(prevNS)
if fnErr != nil {
return fnErr
}
return nsErr
}
// GetNetNamespaces returns a list of network namespaces on the machine. The caller
// is responsible for calling Close() on each of the returned NsHandle's.
func GetNetNamespaces(procRoot string) ([]netns.NsHandle, error) {
var nss []netns.NsHandle
seen := make(map[string]interface{})
err := WithAllProcs(procRoot, func(pid int) error {
ns, err := netns.GetFromPath(path.Join(procRoot, fmt.Sprintf("%d/ns/net", pid)))
if err != nil {
if !errors.Is(err, os.ErrNotExist) && !errors.Is(err, unix.ENOENT) {
log.Errorf("error while reading %s: %s", path.Join(procRoot, fmt.Sprintf("%d/ns/net", pid)), err)
}
return nil
}
uid := ns.UniqueId()
if _, ok := seen[uid]; ok {
ns.Close()
return nil
}
seen[uid] = struct{}{}
nss = append(nss, ns)
return nil
})
if err != nil {
// close all the accumulated ns handles
for _, ns := range nss {
ns.Close()
}
return nil, err
}
return nss, nil
}
// GetCurrentIno returns the ino number for the current network namespace
func GetCurrentIno() (uint32, error) {
curNS, err := netns.Get()
if err != nil {
return 0, err
}
defer curNS.Close()
return GetInoForNs(curNS)
}
// GetRootNetNamespace gets the root network namespace
func GetRootNetNamespace(procRoot string) (netns.NsHandle, error) {
return GetNetNamespaceFromPid(procRoot, 1)
}
// GetNetNamespaceFromPid gets the network namespace for a given `pid`
func GetNetNamespaceFromPid(procRoot string, pid int) (netns.NsHandle, error) {
return netns.GetFromPath(path.Join(procRoot, fmt.Sprintf("%d/ns/net", pid)))
}
// GetNetNsInoFromPid gets the network namespace inode number for the given
// `pid`
func GetNetNsInoFromPid(procRoot string, pid int) (uint32, error) {
ns, err := GetNetNamespaceFromPid(procRoot, pid)
if err != nil {
return 0, err
}
defer ns.Close()
return GetInoForNs(ns)
}
// GetInoForNs gets the inode number for the given network namespace
func GetInoForNs(ns netns.NsHandle) (uint32, error) {
if ns.Equal(netns.None()) {
return 0, fmt.Errorf("net ns is none")
}
var s syscall.Stat_t
if err := syscall.Fstat(int(ns), &s); err != nil {
return 0, err
}
return uint32(s.Ino), nil
}