mirror of
https://github.com/containers/podman.git
synced 2025-05-21 00:56:36 +08:00

this should represent the last major changes to get darwin to **compile**. again, the purpose here is to get darwin to compile so that we can eventually implement a ci task that would protect against regressions for darwin compilation. i have left the manual darwin compilation largely static still and in fact now only interject (manually) two build tags to assist with the build. trevor king has great ideas on how to make this better and i will defer final implementation of those to him. Signed-off-by: baude <bbaude@redhat.com> Closes: #1047 Approved by: rhatdan
264 lines
8.1 KiB
Go
264 lines
8.1 KiB
Go
// +build linux
|
|
|
|
package libpod
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
|
|
cnitypes "github.com/containernetworking/cni/pkg/types/current"
|
|
"github.com/containernetworking/plugins/pkg/ns"
|
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
|
"github.com/pkg/errors"
|
|
"github.com/projectatomic/libpod/pkg/inspect"
|
|
"github.com/projectatomic/libpod/utils"
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/vishvananda/netlink"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// Get an OCICNI network config
|
|
func getPodNetwork(id, name, nsPath string, ports []ocicni.PortMapping) ocicni.PodNetwork {
|
|
return ocicni.PodNetwork{
|
|
Name: name,
|
|
Namespace: name, // TODO is there something else we should put here? We don't know about Kube namespaces
|
|
ID: id,
|
|
NetNS: nsPath,
|
|
PortMappings: ports,
|
|
}
|
|
}
|
|
|
|
// Create and configure a new network namespace for a container
|
|
func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (err error) {
|
|
podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ctrNS.Path(), ctr.config.PortMappings)
|
|
|
|
result, err := r.netPlugin.SetUpPod(podNetwork)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error configuring network namespace for container %s", ctr.ID())
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
if err2 := r.netPlugin.TearDownPod(podNetwork); err2 != nil {
|
|
logrus.Errorf("Error tearing down partially created network namespace for container %s: %v", ctr.ID(), err2)
|
|
}
|
|
}
|
|
}()
|
|
|
|
logrus.Debugf("Response from CNI plugins: %v", result.String())
|
|
|
|
resultStruct, err := cnitypes.GetResult(result)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error parsing result from CNI plugins")
|
|
}
|
|
|
|
ctr.state.NetNS = ctrNS
|
|
ctr.state.IPs = resultStruct.IPs
|
|
ctr.state.Routes = resultStruct.Routes
|
|
ctr.state.Interfaces = resultStruct.Interfaces
|
|
|
|
// We need to temporarily use iptables to allow the container
|
|
// to resolve DNS until this issue is fixed upstream.
|
|
// https://github.com/containernetworking/plugins/pull/75
|
|
if resultStruct.IPs != nil {
|
|
for _, ip := range resultStruct.IPs {
|
|
if ip.Address.IP.To4() != nil {
|
|
iptablesDNS("-I", ip.Address.IP.String())
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Create and configure a new network namespace for a container
|
|
func (r *Runtime) createNetNS(ctr *Container) (err error) {
|
|
ctrNS, err := ns.NewNS()
|
|
if err != nil {
|
|
return errors.Wrapf(err, "error creating network namespace for container %s", ctr.ID())
|
|
}
|
|
defer func() {
|
|
if err != nil {
|
|
if err2 := ctrNS.Close(); err2 != nil {
|
|
logrus.Errorf("Error closing partially created network namespace for container %s: %v", ctr.ID(), err2)
|
|
}
|
|
}
|
|
}()
|
|
|
|
logrus.Debugf("Made network namespace at %s for container %s", ctrNS.Path(), ctr.ID())
|
|
return r.configureNetNS(ctr, ctrNS)
|
|
}
|
|
|
|
// Configure the network namespace using the container process
|
|
func (r *Runtime) setupNetNS(ctr *Container) (err error) {
|
|
nsProcess := fmt.Sprintf("/proc/%d/ns/net", ctr.state.PID)
|
|
|
|
b := make([]byte, 16)
|
|
|
|
if _, err := rand.Reader.Read(b); err != nil {
|
|
return errors.Wrapf(err, "failed to generate random netns name")
|
|
}
|
|
|
|
nsPath := fmt.Sprintf("/var/run/netns/cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
|
|
|
if err := os.MkdirAll(filepath.Dir(nsPath), 0711); err != nil {
|
|
return errors.Wrapf(err, "cannot create %s", filepath.Dir(nsPath))
|
|
}
|
|
|
|
mountPointFd, err := os.Create(nsPath)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "cannot open %s", nsPath)
|
|
}
|
|
mountPointFd.Close()
|
|
|
|
if err := unix.Mount(nsProcess, nsPath, "none", unix.MS_BIND, ""); err != nil {
|
|
return errors.Wrapf(err, "cannot mount %s", nsPath)
|
|
}
|
|
|
|
netNS, err := ns.GetNS(nsPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return r.configureNetNS(ctr, netNS)
|
|
}
|
|
|
|
// iptablesDNS accepts an arg (-I|-D) and IP address of the container and then
|
|
// generates an iptables command to either add or subtract the needed rule
|
|
func iptablesDNS(arg, ip string) error {
|
|
iptablesCmd := []string{"-t", "filter", arg, "FORWARD", "-s", ip, "!", "-o", ip, "-j", "ACCEPT"}
|
|
logrus.Debug("Running iptables command: ", strings.Join(iptablesCmd, " "))
|
|
_, err := utils.ExecCmd("iptables", iptablesCmd...)
|
|
if err != nil {
|
|
logrus.Error(err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Join an existing network namespace
|
|
func joinNetNS(path string) (ns.NetNS, error) {
|
|
ns, err := ns.GetNS(path)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error retrieving network namespace at %s", path)
|
|
}
|
|
|
|
return ns, nil
|
|
}
|
|
|
|
// Get a container's IP address
|
|
func (r *Runtime) getContainerIP(ctr *Container) (net.IP, error) {
|
|
if ctr.state.NetNS == nil {
|
|
return nil, errors.Wrapf(ErrInvalidArg, "container %s has no network namespace, cannot get IP", ctr.ID())
|
|
}
|
|
|
|
podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.PortMappings)
|
|
|
|
ipStr, err := r.netPlugin.GetPodNetworkStatus(podNetwork)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "error retrieving network status of container %s", ctr.ID())
|
|
}
|
|
|
|
ip := net.ParseIP(ipStr)
|
|
if ip == nil {
|
|
return nil, errors.Wrapf(ErrInternal, "error parsing IP address %s for container %s", ipStr, ctr.ID())
|
|
}
|
|
|
|
return ip, nil
|
|
}
|
|
|
|
// Tear down a network namespace
|
|
func (r *Runtime) teardownNetNS(ctr *Container) error {
|
|
if ctr.state.NetNS == nil {
|
|
// The container has no network namespace, we're set
|
|
return nil
|
|
}
|
|
|
|
// Because we are using iptables to allow the container to resolve DNS
|
|
// on per IP address, we also need to try to remove the iptables rule
|
|
// on cleanup. Remove when https://github.com/containernetworking/plugins/pull/75
|
|
// is merged.
|
|
for _, ip := range ctr.state.IPs {
|
|
if ip.Address.IP.To4() != nil {
|
|
iptablesDNS("-D", ip.Address.IP.String())
|
|
}
|
|
}
|
|
|
|
logrus.Debugf("Tearing down network namespace at %s for container %s", ctr.state.NetNS.Path(), ctr.ID())
|
|
|
|
podNetwork := getPodNetwork(ctr.ID(), ctr.Name(), ctr.state.NetNS.Path(), ctr.config.PortMappings)
|
|
|
|
// The network may have already been torn down, so don't fail here, just log
|
|
if err := r.netPlugin.TearDownPod(podNetwork); err != nil {
|
|
logrus.Errorf("Failed to tear down network namespace for container %s: %v", ctr.ID(), err)
|
|
}
|
|
|
|
nsPath := ctr.state.NetNS.Path()
|
|
|
|
if err := ctr.state.NetNS.Close(); err != nil {
|
|
return errors.Wrapf(err, "error closing network namespace for container %s", ctr.ID())
|
|
}
|
|
|
|
// We need to unconditionally try to unmount/remove the namespace
|
|
// because we may be in a separate process from the one that created the
|
|
// namespace, and Close() will only do that if it is the same process.
|
|
if err := unix.Unmount(nsPath, unix.MNT_DETACH); err != nil {
|
|
if err != syscall.EINVAL && err != syscall.ENOENT {
|
|
return errors.Wrapf(err, "error unmounting network namespace %s for container %s", nsPath, ctr.ID())
|
|
}
|
|
}
|
|
if err := os.RemoveAll(nsPath); err != nil && !os.IsNotExist(err) {
|
|
return errors.Wrapf(err, "error removing network namespace %s for container %s", nsPath, ctr.ID())
|
|
}
|
|
|
|
ctr.state.NetNS = nil
|
|
|
|
return nil
|
|
}
|
|
|
|
func getContainerNetIO(ctr *Container) (*netlink.LinkStatistics, error) {
|
|
var netStats *netlink.LinkStatistics
|
|
err := ns.WithNetNSPath(ctr.state.NetNS.Path(), func(_ ns.NetNS) error {
|
|
link, err := netlink.LinkByName(ocicni.DefaultInterfaceName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
netStats = link.Attrs().Statistics
|
|
return nil
|
|
})
|
|
return netStats, err
|
|
}
|
|
|
|
func (c *Container) getContainerNetworkInfo(data *inspect.ContainerInspectData) *inspect.ContainerInspectData {
|
|
if c.state.NetNS != nil {
|
|
// Go through our IP addresses
|
|
for _, ctrIP := range c.state.IPs {
|
|
ipWithMask := ctrIP.Address.String()
|
|
splitIP := strings.Split(ipWithMask, "/")
|
|
mask, _ := strconv.Atoi(splitIP[1])
|
|
if ctrIP.Version == "4" {
|
|
data.NetworkSettings.IPAddress = splitIP[0]
|
|
data.NetworkSettings.IPPrefixLen = mask
|
|
data.NetworkSettings.Gateway = ctrIP.Gateway.String()
|
|
} else {
|
|
data.NetworkSettings.GlobalIPv6Address = splitIP[0]
|
|
data.NetworkSettings.GlobalIPv6PrefixLen = mask
|
|
data.NetworkSettings.IPv6Gateway = ctrIP.Gateway.String()
|
|
}
|
|
}
|
|
|
|
// Set network namespace path
|
|
data.NetworkSettings.SandboxKey = c.state.NetNS.Path()
|
|
|
|
// Set MAC address of interface linked with network namespace path
|
|
for _, i := range c.state.Interfaces {
|
|
if i.Sandbox == data.NetworkSettings.SandboxKey {
|
|
data.NetworkSettings.MacAddress = i.Mac
|
|
}
|
|
}
|
|
}
|
|
return data
|
|
}
|