podman

Форк
0
/
networking_linux.go 
301 строка · 8.6 Кб
1
//go:build !remote
2

3
package libpod
4

5
import (
6
	"crypto/rand"
7
	"fmt"
8
	"net"
9
	"os"
10
	"path/filepath"
11

12
	"github.com/containernetworking/plugins/pkg/ns"
13
	"github.com/containers/common/libnetwork/types"
14
	"github.com/containers/common/pkg/netns"
15
	"github.com/containers/podman/v5/libpod/define"
16
	"github.com/containers/podman/v5/pkg/rootless"
17
	"github.com/opencontainers/runtime-spec/specs-go"
18
	"github.com/sirupsen/logrus"
19
	"github.com/vishvananda/netlink"
20
	"golang.org/x/sys/unix"
21
)
22

23
// Create and configure a new network namespace for a container
24
func (r *Runtime) configureNetNS(ctr *Container, ctrNS string) (status map[string]types.StatusBlock, rerr error) {
25
	if err := r.exposeMachinePorts(ctr.config.PortMappings); err != nil {
26
		return nil, err
27
	}
28
	defer func() {
29
		// make sure to unexpose the gvproxy ports when an error happens
30
		if rerr != nil {
31
			if err := r.unexposeMachinePorts(ctr.config.PortMappings); err != nil {
32
				logrus.Errorf("failed to free gvproxy machine ports: %v", err)
33
			}
34
		}
35
	}()
36
	if ctr.config.NetMode.IsSlirp4netns() {
37
		return nil, r.setupSlirp4netns(ctr, ctrNS)
38
	}
39
	if ctr.config.NetMode.IsPasta() {
40
		return nil, r.setupPasta(ctr, ctrNS)
41
	}
42
	networks, err := ctr.networks()
43
	if err != nil {
44
		return nil, err
45
	}
46
	// All networks have been removed from the container.
47
	// This is effectively forcing net=none.
48
	if len(networks) == 0 {
49
		return nil, nil
50
	}
51

52
	netOpts := ctr.getNetworkOptions(networks)
53
	netStatus, err := r.setUpNetwork(ctrNS, netOpts)
54
	if err != nil {
55
		return nil, err
56
	}
57
	defer func() {
58
		// do not forget to tear down the netns when a later error happened.
59
		if rerr != nil {
60
			if err := r.teardownNetworkBackend(ctrNS, netOpts); err != nil {
61
				logrus.Warnf("failed to teardown network after failed setup: %v", err)
62
			}
63
		}
64
	}()
65

66
	// set up rootless port forwarder when rootless with ports and the network status is empty,
67
	// if this is called from network reload the network status will not be empty and we should
68
	// not set up port because they are still active
69
	if rootless.IsRootless() && len(ctr.config.PortMappings) > 0 && ctr.getNetworkStatus() == nil {
70
		// set up port forwarder for rootless netns
71
		// TODO: support slirp4netns port forwarder as well
72
		// make sure to fix this in container.handleRestartPolicy() as well
73
		// Important we have to call this after r.setUpNetwork() so that
74
		// we can use the proper netStatus
75
		err = r.setupRootlessPortMappingViaRLK(ctr, ctrNS, netStatus)
76
	}
77
	return netStatus, err
78
}
79

80
// Create and configure a new network namespace for a container
81
func (r *Runtime) createNetNS(ctr *Container) (n string, q map[string]types.StatusBlock, retErr error) {
82
	ctrNS, err := netns.NewNS()
83
	if err != nil {
84
		return "", nil, fmt.Errorf("creating network namespace for container %s: %w", ctr.ID(), err)
85
	}
86
	defer func() {
87
		if retErr != nil {
88
			if err := netns.UnmountNS(ctrNS.Path()); err != nil {
89
				logrus.Errorf("Unmounting partially created network namespace for container %s: %v", ctr.ID(), err)
90
			}
91
			if err := ctrNS.Close(); err != nil {
92
				logrus.Errorf("Closing partially created network namespace for container %s: %v", ctr.ID(), err)
93
			}
94
		}
95
	}()
96

97
	logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID())
98

99
	var networkStatus map[string]types.StatusBlock
100
	networkStatus, err = r.configureNetNS(ctr, ctrNS.Path())
101
	return ctrNS.Path(), networkStatus, err
102
}
103

104
// Configure the network namespace using the container process
105
func (r *Runtime) setupNetNS(ctr *Container) error {
106
	nsProcess := fmt.Sprintf("/proc/%d/ns/net", ctr.state.PID)
107

108
	b := make([]byte, 16)
109

110
	if _, err := rand.Reader.Read(b); err != nil {
111
		return fmt.Errorf("failed to generate random netns name: %w", err)
112
	}
113
	nsPath, err := netns.GetNSRunDir()
114
	if err != nil {
115
		return err
116
	}
117
	nsPath = filepath.Join(nsPath, fmt.Sprintf("netns-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]))
118

119
	if err := os.MkdirAll(filepath.Dir(nsPath), 0711); err != nil {
120
		return err
121
	}
122

123
	mountPointFd, err := os.Create(nsPath)
124
	if err != nil {
125
		return err
126
	}
127
	if err := mountPointFd.Close(); err != nil {
128
		return err
129
	}
130

131
	if err := unix.Mount(nsProcess, nsPath, "none", unix.MS_BIND, ""); err != nil {
132
		return fmt.Errorf("cannot mount %s: %w", nsPath, err)
133
	}
134

135
	networkStatus, err := r.configureNetNS(ctr, nsPath)
136

137
	// Assign NetNS attributes to container
138
	ctr.state.NetNS = nsPath
139
	ctr.state.NetworkStatus = networkStatus
140
	return err
141
}
142

143
// Tear down a network namespace, undoing all state associated with it.
144
func (r *Runtime) teardownNetNS(ctr *Container) error {
145
	if err := r.unexposeMachinePorts(ctr.config.PortMappings); err != nil {
146
		// do not return an error otherwise we would prevent network cleanup
147
		logrus.Errorf("failed to free gvproxy machine ports: %v", err)
148
	}
149

150
	// Do not check the error here, we want to always umount the netns
151
	// This will ensure that the container interface will be deleted
152
	// even when there is a CNI or netavark bug.
153
	prevErr := r.teardownNetwork(ctr)
154

155
	// First unmount the namespace
156
	if err := netns.UnmountNS(ctr.state.NetNS); err != nil {
157
		if prevErr != nil {
158
			logrus.Error(prevErr)
159
		}
160
		return fmt.Errorf("unmounting network namespace for container %s: %w", ctr.ID(), err)
161
	}
162

163
	ctr.state.NetNS = ""
164

165
	return prevErr
166
}
167

168
func getContainerNetNS(ctr *Container) (string, *Container, error) {
169
	if ctr.state.NetNS != "" {
170
		return ctr.state.NetNS, nil, nil
171
	}
172
	if ctr.config.NetNsCtr != "" {
173
		c, err := ctr.runtime.GetContainer(ctr.config.NetNsCtr)
174
		if err != nil {
175
			return "", nil, err
176
		}
177
		if err = c.syncContainer(); err != nil {
178
			return "", c, err
179
		}
180
		netNs, c2, err := getContainerNetNS(c)
181
		if c2 != nil {
182
			c = c2
183
		}
184
		return netNs, c, err
185
	}
186
	return "", nil, nil
187
}
188

189
// Returns a map of interface name to statistics for that interface.
190
func getContainerNetIO(ctr *Container) (map[string]define.ContainerNetworkStats, error) {
191
	perNetworkStats := make(map[string]define.ContainerNetworkStats)
192

193
	netNSPath, _, netPathErr := getContainerNetNS(ctr)
194
	if netPathErr != nil {
195
		return nil, netPathErr
196
	}
197
	if netNSPath == "" {
198
		// If netNSPath is empty, it was set as none, and no netNS was set up
199
		// this is a valid state and thus return no error, nor any statistics
200
		return nil, nil
201
	}
202

203
	err := ns.WithNetNSPath(netNSPath, func(_ ns.NetNS) error {
204
		links, err := netlink.LinkList()
205
		if err != nil {
206
			return fmt.Errorf("retrieving all network interfaces: %w", err)
207
		}
208
		for _, link := range links {
209
			attributes := link.Attrs()
210
			if attributes.Flags&net.FlagLoopback != 0 {
211
				continue
212
			}
213

214
			if attributes.Statistics != nil {
215
				perNetworkStats[attributes.Name] = getNetStatsFromNetlinkStats(attributes.Statistics)
216
			}
217
		}
218
		return nil
219
	})
220
	return perNetworkStats, err
221
}
222

223
func getNetStatsFromNetlinkStats(stats *netlink.LinkStatistics) define.ContainerNetworkStats {
224
	return define.ContainerNetworkStats{
225
		RxBytes:   stats.RxBytes,
226
		RxDropped: stats.RxDropped,
227
		RxErrors:  stats.RxErrors,
228
		RxPackets: stats.RxPackets,
229
		TxBytes:   stats.TxBytes,
230
		TxDropped: stats.TxDropped,
231
		TxErrors:  stats.TxErrors,
232
		TxPackets: stats.TxPackets,
233
	}
234
}
235

236
// joinedNetworkNSPath returns netns path and bool if netns was set
237
func (c *Container) joinedNetworkNSPath() (string, bool) {
238
	for _, namespace := range c.config.Spec.Linux.Namespaces {
239
		if namespace.Type == specs.NetworkNamespace {
240
			return namespace.Path, true
241
		}
242
	}
243
	return "", false
244
}
245

246
func (c *Container) inspectJoinedNetworkNS(networkns string) (q types.StatusBlock, retErr error) {
247
	var result types.StatusBlock
248
	err := ns.WithNetNSPath(networkns, func(_ ns.NetNS) error {
249
		ifaces, err := net.Interfaces()
250
		if err != nil {
251
			return err
252
		}
253
		routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
254
		if err != nil {
255
			return err
256
		}
257
		var gateway net.IP
258
		for _, route := range routes {
259
			// default gateway
260
			if route.Dst == nil {
261
				gateway = route.Gw
262
			}
263
		}
264
		result.Interfaces = make(map[string]types.NetInterface)
265
		for _, iface := range ifaces {
266
			if iface.Flags&net.FlagLoopback != 0 {
267
				continue
268
			}
269
			addrs, err := iface.Addrs()
270
			if err != nil {
271
				continue
272
			}
273
			if len(addrs) == 0 {
274
				continue
275
			}
276
			subnets := make([]types.NetAddress, 0, len(addrs))
277
			for _, address := range addrs {
278
				if ipnet, ok := address.(*net.IPNet); ok {
279
					if ipnet.IP.IsLinkLocalMulticast() || ipnet.IP.IsLinkLocalUnicast() {
280
						continue
281
					}
282
					subnet := types.NetAddress{
283
						IPNet: types.IPNet{
284
							IPNet: *ipnet,
285
						},
286
					}
287
					if ipnet.Contains(gateway) {
288
						subnet.Gateway = gateway
289
					}
290
					subnets = append(subnets, subnet)
291
				}
292
			}
293
			result.Interfaces[iface.Name] = types.NetInterface{
294
				Subnets:    subnets,
295
				MacAddress: types.HardwareAddr(iface.HardwareAddr),
296
			}
297
		}
298
		return nil
299
	})
300
	return result, err
301
}
302

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

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

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

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