diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/container_api.go | 26 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 13 | ||||
-rw-r--r-- | libpod/container_internal_unsupported.go | 4 | ||||
-rw-r--r-- | libpod/networking_linux.go | 85 | ||||
-rw-r--r-- | libpod/networking_unsupported.go | 9 |
5 files changed, 132 insertions, 5 deletions
diff --git a/libpod/container_api.go b/libpod/container_api.go index 6a7ddc421..1b33f16b4 100644 --- a/libpod/container_api.go +++ b/libpod/container_api.go @@ -639,6 +639,32 @@ func (c *Container) Sync() error { return nil } +// ReloadNetwork reconfigures the container's network. +// Technically speaking, it will tear down and then reconfigure the container's +// network namespace, which will result in all firewall rules being recreated. +// It is mostly intended to be used in cases where the system firewall has been +// reloaded, and existing rules have been wiped out. It is expected that some +// downtime will result, as the rules are destroyed as part of this process. +// At present, this only works on root containers; it may be expanded to restart +// slirp4netns in the future to work with rootless containers as well. +// Requires that the container must be running or created. +func (c *Container) ReloadNetwork() error { + if !c.batched { + c.lock.Lock() + defer c.lock.Unlock() + + if err := c.syncContainer(); err != nil { + return err + } + } + + if !c.ensureState(define.ContainerStateCreated, define.ContainerStateRunning) { + return errors.Wrapf(define.ErrCtrStateInvalid, "cannot reload network unless container network has been configured") + } + + return c.reloadNetwork() +} + // Refresh is DEPRECATED and REMOVED. func (c *Container) Refresh(ctx context.Context) error { // This has been deprecated for a long while, and is in the process of diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 56575c195..a9a789ad8 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -230,6 +230,19 @@ func (c *Container) cleanupNetwork() error { return nil } +// reloadNetwork reloads the network for the given container, recreating +// firewall rules. +func (c *Container) reloadNetwork() error { + result, err := c.runtime.reloadContainerNetwork(c) + if err != nil { + return err + } + + c.state.NetworkStatus = result + + return c.save() +} + func (c *Container) getUserOverrides() *lookup.Overrides { var hasPasswdFile, hasGroupFile bool overrides := lookup.Overrides{} diff --git a/libpod/container_internal_unsupported.go b/libpod/container_internal_unsupported.go index c22e9a4a4..7f6fc9ec9 100644 --- a/libpod/container_internal_unsupported.go +++ b/libpod/container_internal_unsupported.go @@ -50,6 +50,10 @@ func (c *Container) cleanupOverlayMounts() error { return nil } +func (c *Container) reloadNetwork() error { + return define.ErrNotImplemented +} + func (c *Container) getUserOverrides() *lookup.Overrides { return nil } diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 15e470c80..c7db95eb3 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -13,6 +13,7 @@ import ( "os" "os/exec" "path/filepath" + "regexp" "sort" "strings" "syscall" @@ -740,8 +741,9 @@ func (r *Runtime) closeNetNS(ctr *Container) error { return nil } -// Tear down a network namespace, undoing all state associated with it. -func (r *Runtime) teardownNetNS(ctr *Container) error { +// Tear down a container's CNI network configuration, but do not tear down the +// namespace itself. +func (r *Runtime) teardownCNI(ctr *Container) error { if ctr.state.NetNS == nil { // The container has no network namespace, we're set return nil @@ -780,6 +782,19 @@ func (r *Runtime) teardownNetNS(ctr *Container) error { return errors.Wrapf(err, "error tearing down CNI namespace configuration for container %s", ctr.ID()) } } + return nil +} + +// Tear down a network namespace, undoing all state associated with it. +func (r *Runtime) teardownNetNS(ctr *Container) error { + if err := r.teardownCNI(ctr); err != nil { + return err + } + + networks, _, err := ctr.networks() + if err != nil { + return err + } // CNI-in-slirp4netns if rootless.IsRootless() && len(networks) != 0 { @@ -820,6 +835,68 @@ func getContainerNetNS(ctr *Container) (string, error) { return "", nil } +// Reload only works with containers with a configured network. +// It will tear down, and then reconfigure, the network of the container. +// This is mainly used when a reload of firewall rules wipes out existing +// firewall configuration. +// Efforts will be made to preserve MAC and IP addresses, but this only works if +// the container only joined a single CNI network, and was only assigned a +// single MAC or IP. +// Only works on root containers at present, though in the future we could +// extend this to stop + restart slirp4netns +func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, error) { + if ctr.state.NetNS == nil { + return nil, errors.Wrapf(define.ErrCtrStateInvalid, "container %s network is not configured, refusing to reload", ctr.ID()) + } + if rootless.IsRootless() || ctr.config.NetMode.IsSlirp4netns() { + return nil, errors.Wrapf(define.ErrRootless, "network reload only supported for root containers") + } + + logrus.Infof("Going to reload container %s network", ctr.ID()) + + var requestedIP net.IP + var requestedMAC net.HardwareAddr + // Set requested IP and MAC address, if possible. + if len(ctr.state.NetworkStatus) == 1 { + result := ctr.state.NetworkStatus[0] + if len(result.IPs) == 1 { + resIP := result.IPs[0] + + requestedIP = resIP.Address.IP + ctr.requestedIP = requestedIP + logrus.Debugf("Going to preserve container %s IP address %s", ctr.ID(), ctr.requestedIP.String()) + + if resIP.Interface != nil && *resIP.Interface < len(result.Interfaces) && *resIP.Interface >= 0 { + var err error + requestedMAC, err = net.ParseMAC(result.Interfaces[*resIP.Interface].Mac) + if err != nil { + return nil, errors.Wrapf(err, "error parsing container %s MAC address %s", ctr.ID(), result.Interfaces[*resIP.Interface].Mac) + } + ctr.requestedMAC = requestedMAC + logrus.Debugf("Going to preserve container %s MAC address %s", ctr.ID(), ctr.requestedMAC.String()) + } + } + } + + err := r.teardownCNI(ctr) + if err != nil { + // teardownCNI will error if the iptables rules do not exists and this is the case after + // a firewall reload. The purpose of network reload is to recreate the rules if they do + // not exists so we should not log this specific error as error. This would confuse users otherwise. + b, rerr := regexp.MatchString("Couldn't load target `CNI-[a-f0-9]{24}':No such file or directory", err.Error()) + if rerr == nil && !b { + logrus.Error(err) + } else { + logrus.Info(err) + } + } + + // teardownCNI will clean the requested IP and MAC so we need to set them again + ctr.requestedIP = requestedIP + ctr.requestedMAC = requestedMAC + return r.configureNetNS(ctr, ctr.state.NetNS) +} + func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) { var netStats *netlink.LinkStatistics // rootless v2 cannot seem to resolve its network connection to @@ -983,12 +1060,12 @@ func resultToBasicNetworkConfig(result *cnitypes.Result) (define.InspectBasicNet config.IPAddress = ctrIP.Address.IP.String() config.IPPrefixLen = size config.Gateway = ctrIP.Gateway.String() - if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface > 0 { + if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 { config.MacAddress = result.Interfaces[*ctrIP.Interface].Mac } case ctrIP.Version == "4" && config.IPAddress != "": config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, ctrIP.Address.String()) - if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface > 0 { + if ctrIP.Interface != nil && *ctrIP.Interface < len(result.Interfaces) && *ctrIP.Interface >= 0 { config.AdditionalMacAddresses = append(config.AdditionalMacAddresses, result.Interfaces[*ctrIP.Interface].Mac) } case ctrIP.Version == "6" && config.IPAddress == "": diff --git a/libpod/networking_unsupported.go b/libpod/networking_unsupported.go index 76bb01424..9e5c4adde 100644 --- a/libpod/networking_unsupported.go +++ b/libpod/networking_unsupported.go @@ -2,7 +2,10 @@ package libpod -import "github.com/containers/podman/v2/libpod/define" +import ( + cnitypes "github.com/containernetworking/cni/pkg/types/current" + "github.com/containers/podman/v2/libpod/define" +) func (r *Runtime) setupRootlessNetNS(ctr *Container) error { return define.ErrNotImplemented @@ -28,6 +31,10 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e return nil, define.ErrNotImplemented } +func (r *Runtime) reloadContainerNetwork(ctr *Container) ([]*cnitypes.Result, error) { + return nil, define.ErrNotImplemented +} + func getCNINetworksDir() (string, error) { return "", define.ErrNotImplemented } |