Files
podman/libpod/networking_freebsd.go
Matt Heon 72f1617fac Bump Go module to v5
Moving from Go module v4 to v5 prepares us for public releases.

Move done using gomove [1] as with the v3 and v4 moves.

[1] https://github.com/KSubedi/gomove

Signed-off-by: Matt Heon <mheon@redhat.com>
2024-02-08 09:35:39 -05:00

275 lines
8.4 KiB
Go

//go:build !remote
package libpod
import (
"crypto/rand"
jdec "encoding/json"
"errors"
"fmt"
"net"
"os/exec"
"path/filepath"
"github.com/containers/buildah/pkg/jail"
"github.com/containers/common/libnetwork/types"
"github.com/containers/podman/v5/libpod/define"
"github.com/containers/storage/pkg/lockfile"
"github.com/sirupsen/logrus"
)
type Netstat struct {
Statistics NetstatInterface `json:"statistics"`
}
type NetstatInterface struct {
Interface []NetstatAddress `json:"interface"`
}
type NetstatAddress struct {
Name string `json:"name"`
Flags string `json:"flags"`
Mtu int `json:"mtu"`
Network string `json:"network"`
Address string `json:"address"`
ReceivedPackets uint64 `json:"received-packets"`
ReceivedBytes uint64 `json:"received-bytes"`
ReceivedErrors uint64 `json:"received-errors"`
SentPackets uint64 `json:"sent-packets"`
SentBytes uint64 `json:"sent-bytes"`
SentErrors uint64 `json:"send-errors"`
DroppedPackets uint64 `json:"dropped-packets"`
Collisions uint64 `json:"collisions"`
}
type RootlessNetNS struct {
dir string
Lock *lockfile.LockFile
}
// getPath will join the given path to the rootless netns dir
func (r *RootlessNetNS) getPath(path string) string {
return filepath.Join(r.dir, path)
}
// Do - run the given function in the rootless netns.
// It does not lock the rootlessCNI lock, the caller
// should only lock when needed, e.g. for network operations.
func (r *RootlessNetNS) Do(toRun func() error) error {
return errors.New("not supported on freebsd")
}
// Cleanup the rootless network namespace if needed.
// It checks if we have running containers with the bridge network mode.
// Cleanup() expects that r.Lock is locked
func (r *RootlessNetNS) Cleanup(runtime *Runtime) error {
return errors.New("not supported on freebsd")
}
// GetRootlessNetNs returns the rootless netns object. If create is set to true
// the rootless network namespace will be created if it does not already exist.
// If called as root it returns always nil.
// On success the returned RootlessCNI lock is locked and must be unlocked by the caller.
func (r *Runtime) GetRootlessNetNs(new bool) (*RootlessNetNS, error) {
return nil, nil
}
func getSlirp4netnsIP(subnet *net.IPNet) (*net.IP, error) {
return nil, errors.New("not implemented GetSlirp4netnsIP")
}
// This is called after the container's jail is created but before its
// started. We can use this to initialise the container's vnet when we don't
// have a separate vnet jail (which is the case in FreeBSD 13.3 and later).
func (r *Runtime) setupNetNS(ctr *Container) error {
networkStatus, err := r.configureNetNS(ctr, ctr.ID())
ctr.state.NetNS = ctr.ID()
ctr.state.NetworkStatus = networkStatus
return err
}
// Create and configure a new network namespace for a container
func (r *Runtime) configureNetNS(ctr *Container, ctrNS string) (status map[string]types.StatusBlock, rerr error) {
if err := r.exposeMachinePorts(ctr.config.PortMappings); err != nil {
return nil, err
}
defer func() {
// make sure to unexpose the gvproxy ports when an error happens
if rerr != nil {
if err := r.unexposeMachinePorts(ctr.config.PortMappings); err != nil {
logrus.Errorf("failed to free gvproxy machine ports: %v", err)
}
}
}()
networks, err := ctr.networks()
if err != nil {
return nil, err
}
// All networks have been removed from the container.
// This is effectively forcing net=none.
if len(networks) == 0 {
return nil, nil
}
netOpts := ctr.getNetworkOptions(networks)
netStatus, err := r.setUpNetwork(ctrNS, netOpts)
if err != nil {
return nil, err
}
return netStatus, err
}
// Create and configure a new network namespace for a container
func (r *Runtime) createNetNS(ctr *Container) (n string, q map[string]types.StatusBlock, retErr error) {
b := make([]byte, 16)
_, err := rand.Reader.Read(b)
if err != nil {
return "", nil, fmt.Errorf("failed to generate random vnet name: %v", err)
}
netns := fmt.Sprintf("vnet-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
jconf := jail.NewConfig()
jconf.Set("name", netns)
jconf.Set("vnet", jail.NEW)
jconf.Set("children.max", 1)
jconf.Set("persist", true)
jconf.Set("enforce_statfs", 0)
jconf.Set("devfs_ruleset", 4)
jconf.Set("allow.raw_sockets", true)
jconf.Set("allow.chflags", true)
jconf.Set("securelevel", -1)
j, err := jail.Create(jconf)
if err != nil {
return "", nil, fmt.Errorf("Failed to create vnet jail %s for container %s: %w", netns, ctr.ID(), err)
}
logrus.Debugf("Created vnet jail %s for container %s", netns, ctr.ID())
var networkStatus map[string]types.StatusBlock
networkStatus, err = r.configureNetNS(ctr, netns)
if err != nil {
jconf := jail.NewConfig()
jconf.Set("persist", false)
if err := j.Set(jconf); err != nil {
// Log this error and return the error from configureNetNS
logrus.Errorf("failed to destroy vnet jail %s: %w", netns, err)
}
}
return netns, networkStatus, err
}
// Tear down a network namespace, undoing all state associated with it.
func (r *Runtime) teardownNetNS(ctr *Container) error {
if err := r.unexposeMachinePorts(ctr.config.PortMappings); err != nil {
// do not return an error otherwise we would prevent network cleanup
logrus.Errorf("failed to free gvproxy machine ports: %v", err)
}
if err := r.teardownNetwork(ctr); err != nil {
return err
}
if ctr.state.NetNS != "" {
// If PostConfigureNetNS is false, then we are running with a
// separate vnet jail so we need to clean that up now.
if !ctr.config.PostConfigureNetNS {
// Rather than destroying the jail immediately, reset the
// persist flag so that it will live until the container is
// done.
netjail, err := jail.FindByName(ctr.state.NetNS)
if err != nil {
return fmt.Errorf("finding network jail %s: %w", ctr.state.NetNS, err)
}
jconf := jail.NewConfig()
jconf.Set("persist", false)
if err := netjail.Set(jconf); err != nil {
return fmt.Errorf("releasing network jail %s: %w", ctr.state.NetNS, err)
}
}
ctr.state.NetNS = ""
}
return nil
}
// TODO (5.0): return the statistics per network interface
// This would allow better compat with docker.
func getContainerNetIO(ctr *Container) (map[string]define.ContainerNetworkStats, error) {
if ctr.state.NetNS == "" {
// If NetNS is nil, it was set as none, and no netNS
// was set up this is a valid state and thus return no
// error, nor any statistics
return nil, nil
}
// First try running 'netstat -j' - this lets us retrieve stats from
// containers which don't have a separate vnet jail.
cmd := exec.Command("netstat", "-j", ctr.state.NetNS, "-bi", "--libxo", "json")
out, err := cmd.Output()
if err != nil {
// Fall back to using jexec so that this still works on 13.2
// which does not have the -j flag.
cmd := exec.Command("jexec", ctr.state.NetNS, "netstat", "-bi", "--libxo", "json")
out, err = cmd.Output()
}
if err != nil {
return nil, fmt.Errorf("failed to read network stats: %v", err)
}
stats := Netstat{}
if err := jdec.Unmarshal(out, &stats); err != nil {
return nil, err
}
res := make(map[string]define.ContainerNetworkStats)
// Sum all the interface stats - in practice only Tx/TxBytes are needed
for _, ifaddr := range stats.Statistics.Interface {
// Each interface has two records, one for link-layer which has
// an MTU field and one for IP which doesn't. We only want the
// link-layer stats.
//
// It's not clear if we should include loopback stats here but
// if we move to per-interface stats in future, this can be
// reported separately.
if ifaddr.Mtu > 0 {
linkStats := define.ContainerNetworkStats{
RxPackets: ifaddr.ReceivedPackets,
TxPackets: ifaddr.SentPackets,
RxBytes: ifaddr.ReceivedBytes,
TxBytes: ifaddr.SentBytes,
RxErrors: ifaddr.ReceivedErrors,
TxErrors: ifaddr.SentErrors,
RxDropped: ifaddr.DroppedPackets,
}
res[ifaddr.Name] = linkStats
}
}
return res, nil
}
func (c *Container) joinedNetworkNSPath() (string, bool) {
return c.state.NetNS, false
}
func (c *Container) inspectJoinedNetworkNS(networkns string) (q types.StatusBlock, retErr error) {
// TODO: extract interface information from the vnet jail
return types.StatusBlock{}, nil
}
func (c *Container) reloadRootlessRLKPortMapping() error {
return errors.New("unsupported (*Container).reloadRootlessRLKPortMapping")
}
func (c *Container) setupRootlessNetwork() error {
return nil
}
func getPastaIP(state *ContainerState) (net.IP, error) {
return nil, fmt.Errorf("pasta networking is Linux only")
}