podman

Форк
0
234 строки · 6.4 Кб
1
// Copyright 2015-2017 CNI authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
package ns
16

17
import (
18
	"fmt"
19
	"os"
20
	"runtime"
21
	"sync"
22
	"syscall"
23

24
	"golang.org/x/sys/unix"
25
)
26

27
// Returns an object representing the current OS thread's network namespace
28
func GetCurrentNS() (NetNS, error) {
29
	// Lock the thread in case other goroutine executes in it and changes its
30
	// network namespace after getCurrentThreadNetNSPath(), otherwise it might
31
	// return an unexpected network namespace.
32
	runtime.LockOSThread()
33
	defer runtime.UnlockOSThread()
34
	return GetNS(getCurrentThreadNetNSPath())
35
}
36

37
func getCurrentThreadNetNSPath() string {
38
	// /proc/self/ns/net returns the namespace of the main thread, not
39
	// of whatever thread this goroutine is running on.  Make sure we
40
	// use the thread's net namespace since the thread is switching around
41
	return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
42
}
43

44
func (ns *netNS) Close() error {
45
	if err := ns.errorIfClosed(); err != nil {
46
		return err
47
	}
48

49
	if err := ns.file.Close(); err != nil {
50
		return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err)
51
	}
52
	ns.closed = true
53

54
	return nil
55
}
56

57
func (ns *netNS) Set() error {
58
	if err := ns.errorIfClosed(); err != nil {
59
		return err
60
	}
61

62
	if err := unix.Setns(int(ns.Fd()), unix.CLONE_NEWNET); err != nil {
63
		return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err)
64
	}
65

66
	return nil
67
}
68

69
type NetNS interface {
70
	// Executes the passed closure in this object's network namespace,
71
	// attempting to restore the original namespace before returning.
72
	// However, since each OS thread can have a different network namespace,
73
	// and Go's thread scheduling is highly variable, callers cannot
74
	// guarantee any specific namespace is set unless operations that
75
	// require that namespace are wrapped with Do().  Also, no code called
76
	// from Do() should call runtime.UnlockOSThread(), or the risk
77
	// of executing code in an incorrect namespace will be greater.  See
78
	// https://github.com/golang/go/wiki/LockOSThread for further details.
79
	Do(toRun func(NetNS) error) error
80

81
	// Sets the current network namespace to this object's network namespace.
82
	// Note that since Go's thread scheduling is highly variable, callers
83
	// cannot guarantee the requested namespace will be the current namespace
84
	// after this function is called; to ensure this wrap operations that
85
	// require the namespace with Do() instead.
86
	Set() error
87

88
	// Returns the filesystem path representing this object's network namespace
89
	Path() string
90

91
	// Returns a file descriptor representing this object's network namespace
92
	Fd() uintptr
93

94
	// Cleans up this instance of the network namespace; if this instance
95
	// is the last user the namespace will be destroyed
96
	Close() error
97
}
98

99
type netNS struct {
100
	file   *os.File
101
	closed bool
102
}
103

104
// netNS implements the NetNS interface
105
var _ NetNS = &netNS{}
106

107
const (
108
	// https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h
109
	NSFS_MAGIC   = unix.NSFS_MAGIC
110
	PROCFS_MAGIC = unix.PROC_SUPER_MAGIC
111
)
112

113
type NSPathNotExistErr struct{ msg string }
114

115
func (e NSPathNotExistErr) Error() string { return e.msg }
116

117
type NSPathNotNSErr struct{ msg string }
118

119
func (e NSPathNotNSErr) Error() string { return e.msg }
120

121
func IsNSorErr(nspath string) error {
122
	stat := syscall.Statfs_t{}
123
	if err := syscall.Statfs(nspath, &stat); err != nil {
124
		if os.IsNotExist(err) {
125
			err = NSPathNotExistErr{msg: fmt.Sprintf("failed to Statfs %q: %v", nspath, err)}
126
		} else {
127
			err = fmt.Errorf("failed to Statfs %q: %v", nspath, err)
128
		}
129
		return err
130
	}
131

132
	switch stat.Type {
133
	case PROCFS_MAGIC, NSFS_MAGIC:
134
		return nil
135
	default:
136
		return NSPathNotNSErr{msg: fmt.Sprintf("unknown FS magic on %q: %x", nspath, stat.Type)}
137
	}
138
}
139

140
// Returns an object representing the namespace referred to by @path
141
func GetNS(nspath string) (NetNS, error) {
142
	err := IsNSorErr(nspath)
143
	if err != nil {
144
		return nil, err
145
	}
146

147
	fd, err := os.Open(nspath)
148
	if err != nil {
149
		return nil, err
150
	}
151

152
	return &netNS{file: fd}, nil
153
}
154

155
func (ns *netNS) Path() string {
156
	return ns.file.Name()
157
}
158

159
func (ns *netNS) Fd() uintptr {
160
	return ns.file.Fd()
161
}
162

163
func (ns *netNS) errorIfClosed() error {
164
	if ns.closed {
165
		return fmt.Errorf("%q has already been closed", ns.file.Name())
166
	}
167
	return nil
168
}
169

170
func (ns *netNS) Do(toRun func(NetNS) error) error {
171
	if err := ns.errorIfClosed(); err != nil {
172
		return err
173
	}
174

175
	containedCall := func(hostNS NetNS) error {
176
		threadNS, err := GetCurrentNS()
177
		if err != nil {
178
			return fmt.Errorf("failed to open current netns: %v", err)
179
		}
180
		defer threadNS.Close()
181

182
		// switch to target namespace
183
		if err = ns.Set(); err != nil {
184
			return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
185
		}
186
		defer func() {
187
			err := threadNS.Set() // switch back
188
			if err == nil {
189
				// Unlock the current thread only when we successfully switched back
190
				// to the original namespace; otherwise leave the thread locked which
191
				// will force the runtime to scrap the current thread, that is maybe
192
				// not as optimal but at least always safe to do.
193
				runtime.UnlockOSThread()
194
			}
195
		}()
196

197
		return toRun(hostNS)
198
	}
199

200
	// save a handle to current network namespace
201
	hostNS, err := GetCurrentNS()
202
	if err != nil {
203
		return fmt.Errorf("Failed to open current namespace: %v", err)
204
	}
205
	defer hostNS.Close()
206

207
	var wg sync.WaitGroup
208
	wg.Add(1)
209

210
	// Start the callback in a new green thread so that if we later fail
211
	// to switch the namespace back to the original one, we can safely
212
	// leave the thread locked to die without a risk of the current thread
213
	// left lingering with incorrect namespace.
214
	var innerError error
215
	go func() {
216
		defer wg.Done()
217
		runtime.LockOSThread()
218
		innerError = containedCall(hostNS)
219
	}()
220
	wg.Wait()
221

222
	return innerError
223
}
224

225
// WithNetNSPath executes the passed closure under the given network
226
// namespace, restoring the original namespace afterwards.
227
func WithNetNSPath(nspath string, toRun func(NetNS) error) error {
228
	ns, err := GetNS(nspath)
229
	if err != nil {
230
		return err
231
	}
232
	defer ns.Close()
233
	return ns.Do(toRun)
234
}
235

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.