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"
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 {
29
// make sure to unexpose the gvproxy ports when an error happens
31
if err := r.unexposeMachinePorts(ctr.config.PortMappings); err != nil {
32
logrus.Errorf("failed to free gvproxy machine ports: %v", err)
36
if ctr.config.NetMode.IsSlirp4netns() {
37
return nil, r.setupSlirp4netns(ctr, ctrNS)
39
if ctr.config.NetMode.IsPasta() {
40
return nil, r.setupPasta(ctr, ctrNS)
42
networks, err := ctr.networks()
46
// All networks have been removed from the container.
47
// This is effectively forcing net=none.
48
if len(networks) == 0 {
52
netOpts := ctr.getNetworkOptions(networks)
53
netStatus, err := r.setUpNetwork(ctrNS, netOpts)
58
// do not forget to tear down the netns when a later error happened.
60
if err := r.teardownNetworkBackend(ctrNS, netOpts); err != nil {
61
logrus.Warnf("failed to teardown network after failed setup: %v", err)
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)
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()
84
return "", nil, fmt.Errorf("creating network namespace for container %s: %w", ctr.ID(), err)
88
if err := netns.UnmountNS(ctrNS.Path()); err != nil {
89
logrus.Errorf("Unmounting partially created network namespace for container %s: %v", ctr.ID(), err)
91
if err := ctrNS.Close(); err != nil {
92
logrus.Errorf("Closing partially created network namespace for container %s: %v", ctr.ID(), err)
97
logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID())
99
var networkStatus map[string]types.StatusBlock
100
networkStatus, err = r.configureNetNS(ctr, ctrNS.Path())
101
return ctrNS.Path(), networkStatus, err
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)
108
b := make([]byte, 16)
110
if _, err := rand.Reader.Read(b); err != nil {
111
return fmt.Errorf("failed to generate random netns name: %w", err)
113
nsPath, err := netns.GetNSRunDir()
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:]))
119
if err := os.MkdirAll(filepath.Dir(nsPath), 0711); err != nil {
123
mountPointFd, err := os.Create(nsPath)
127
if err := mountPointFd.Close(); err != nil {
131
if err := unix.Mount(nsProcess, nsPath, "none", unix.MS_BIND, ""); err != nil {
132
return fmt.Errorf("cannot mount %s: %w", nsPath, err)
135
networkStatus, err := r.configureNetNS(ctr, nsPath)
137
// Assign NetNS attributes to container
138
ctr.state.NetNS = nsPath
139
ctr.state.NetworkStatus = networkStatus
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)
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)
155
// First unmount the namespace
156
if err := netns.UnmountNS(ctr.state.NetNS); err != nil {
158
logrus.Error(prevErr)
160
return fmt.Errorf("unmounting network namespace for container %s: %w", ctr.ID(), err)
168
func getContainerNetNS(ctr *Container) (string, *Container, error) {
169
if ctr.state.NetNS != "" {
170
return ctr.state.NetNS, nil, nil
172
if ctr.config.NetNsCtr != "" {
173
c, err := ctr.runtime.GetContainer(ctr.config.NetNsCtr)
177
if err = c.syncContainer(); err != nil {
180
netNs, c2, err := getContainerNetNS(c)
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)
193
netNSPath, _, netPathErr := getContainerNetNS(ctr)
194
if netPathErr != nil {
195
return nil, netPathErr
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
203
err := ns.WithNetNSPath(netNSPath, func(_ ns.NetNS) error {
204
links, err := netlink.LinkList()
206
return fmt.Errorf("retrieving all network interfaces: %w", err)
208
for _, link := range links {
209
attributes := link.Attrs()
210
if attributes.Flags&net.FlagLoopback != 0 {
214
if attributes.Statistics != nil {
215
perNetworkStats[attributes.Name] = getNetStatsFromNetlinkStats(attributes.Statistics)
220
return perNetworkStats, err
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,
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
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()
253
routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
258
for _, route := range routes {
260
if route.Dst == nil {
264
result.Interfaces = make(map[string]types.NetInterface)
265
for _, iface := range ifaces {
266
if iface.Flags&net.FlagLoopback != 0 {
269
addrs, err := iface.Addrs()
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() {
282
subnet := types.NetAddress{
287
if ipnet.Contains(gateway) {
288
subnet.Gateway = gateway
290
subnets = append(subnets, subnet)
293
result.Interfaces[iface.Name] = types.NetInterface{
295
MacAddress: types.HardwareAddr(iface.HardwareAddr),