mirror of https://github.com/containers/podman.git
Merge pull request #16141 from sbrivio-rh/pasta
Add pasta networking mode
This commit is contained in:
commit
aaa88f4554
|
@ -95,7 +95,7 @@ func DefineNetFlags(cmd *cobra.Command) {
|
|||
}
|
||||
|
||||
// NetFlagsToNetOptions parses the network flags for the given cmd.
|
||||
func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*entities.NetOptions, error) {
|
||||
func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet, pastaNetworkNameExists bool) (*entities.NetOptions, error) {
|
||||
var (
|
||||
err error
|
||||
)
|
||||
|
@ -192,7 +192,7 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*enti
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ns, networks, options, err := specgen.ParseNetworkFlag(network)
|
||||
ns, networks, options, err := specgen.ParseNetworkFlag(network, pastaNetworkNameExists)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -105,8 +105,15 @@ func init() {
|
|||
|
||||
func commonFlags(cmd *cobra.Command) error {
|
||||
var err error
|
||||
|
||||
report, err := registry.ContainerEngine().NetworkExists(registry.Context(), "pasta")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pastaNetworkNameExists := report.Value
|
||||
|
||||
flags := cmd.Flags()
|
||||
cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags)
|
||||
cliVals.Net, err = common.NetFlagsToNetOptions(nil, *flags, pastaNetworkNameExists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -130,12 +130,18 @@ func create(cmd *cobra.Command, args []string) error {
|
|||
createOptions.Infra = false
|
||||
}
|
||||
|
||||
report, err := registry.ContainerEngine().NetworkExists(registry.Context(), "pasta")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pastaNetworkNameExists := report.Value
|
||||
|
||||
if !createOptions.Infra {
|
||||
if cmd.Flag("no-hosts").Changed {
|
||||
return fmt.Errorf("cannot specify --no-hosts without an infra container")
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags)
|
||||
createOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, pastaNetworkNameExists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -152,7 +158,7 @@ func create(cmd *cobra.Command, args []string) error {
|
|||
} else {
|
||||
// reassign certain options for lbpod api, these need to be populated in spec
|
||||
flags := cmd.Flags()
|
||||
infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags)
|
||||
infraOptions.Net, err = common.NetFlagsToNetOptions(nil, *flags, pastaNetworkNameExists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -35,3 +35,42 @@ Valid _mode_ values are:
|
|||
- **port_handler=rootlesskit**: Use rootlesskit for port forwarding. Default.
|
||||
Note: Rootlesskit changes the source IP address of incoming packets to an IP address in the container network namespace, usually `10.0.2.100`. If your application requires the real source IP address, e.g. web server logs, use the slirp4netns port handler. The rootlesskit port handler is also used for rootless containers when connected to user-defined networks.
|
||||
- **port_handler=slirp4netns**: Use the slirp4netns port forwarding, it is slower than rootlesskit but preserves the correct source IP address. This port handler cannot be used for user-defined networks.
|
||||
|
||||
- **pasta[:OPTIONS,...]**: use **pasta**(1) to create a user-mode networking
|
||||
stack. \
|
||||
This is only supported in rootless mode. \
|
||||
By default, IPv4 and IPv6 addresses and routes, as well as the pod interface
|
||||
name, are copied from the host. If port forwarding isn't configured, ports
|
||||
will be forwarded dynamically as services are bound on either side (init
|
||||
namespace or container namespace). Port forwarding preserves the original
|
||||
source IP address. Options described in pasta(1) can be specified as
|
||||
comma-separated arguments. \
|
||||
In terms of pasta(1) options, **--config-net** is given by default, in
|
||||
order to configure networking when the container is started, and
|
||||
**--no-map-gw** is also assumed by default, to avoid direct access from
|
||||
container to host using the gateway address. The latter can be overridden
|
||||
by passing **--map-gw** in the pasta-specific options (despite not being an
|
||||
actual pasta(1) option). \
|
||||
Also, **-t none** and **-u none** are passed if, respectively, no TCP or
|
||||
UDP port forwarding from host to container is configured, to disable
|
||||
automatic port forwarding based on bound ports. Similarly, **-T none** and
|
||||
**-U none** are given to disable the same functionality from container to
|
||||
host. \
|
||||
Some examples:
|
||||
- **pasta:--map-gw**: Allow the container to directly reach the host using the
|
||||
gateway address.
|
||||
- **pasta:--mtu,1500**: Specify a 1500 bytes MTU for the _tap_ interface in
|
||||
the container.
|
||||
- **pasta:--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,-m,1500,--no-ndp,--no-dhcpv6,--no-dhcp**,
|
||||
equivalent to default slirp4netns(1) options: disable IPv6, assign
|
||||
`10.0.2.0/24` to the `tap0` interface in the container, with gateway
|
||||
`10.0.2.3`, enable DNS forwarder reachable at `10.0.2.3`, set MTU to 1500
|
||||
bytes, disable NDP, DHCPv6 and DHCP support.
|
||||
- **pasta:-I,tap0,--ipv4-only,-a,10.0.2.0,-n,24,-g,10.0.2.2,--dns-forward,10.0.2.3,--no-ndp,--no-dhcpv6,--no-dhcp**,
|
||||
equivalent to default slirp4netns(1) options with Podman overrides: same as
|
||||
above, but leave the MTU to 65520 bytes
|
||||
- **pasta:-t,auto,-u,auto,-T,auto,-U,auto**: enable automatic port forwarding
|
||||
based on observed bound ports from both host and container sides
|
||||
- **pasta:-T,5201**: enable forwarding of TCP port 5201 from container to
|
||||
host, using the loopback interface instead of the tap interface for improved
|
||||
performance
|
||||
|
|
|
@ -491,8 +491,11 @@ In order for users to run rootless, there must be an entry for their username in
|
|||
|
||||
Rootless Podman works better if the fuse-overlayfs and slirp4netns packages are installed.
|
||||
The fuse-overlayfs package provides a userspace overlay storage driver, otherwise users need to use
|
||||
the vfs storage driver, which is diskspace expensive and does not perform well. slirp4netns is
|
||||
required for VPN, without it containers need to be run with the --network=host flag.
|
||||
the vfs storage driver, which can be disk space expensive and less performant
|
||||
than other drivers.
|
||||
|
||||
To enable VPN on the container, slirp4netns or pasta needs to be specified;
|
||||
without either, containers need to be run with the --network=host flag.
|
||||
|
||||
## ENVIRONMENT
|
||||
|
||||
|
@ -541,7 +544,7 @@ page.
|
|||
NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`.
|
||||
|
||||
## SEE ALSO
|
||||
**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
|
||||
**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[pasta(1)](https://passt.top/builds/latest/web/passt.1.html)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
|
||||
|
||||
## HISTORY
|
||||
October 2017, converted from Docker documentation to Podman by Dan Walsh for Podman `<dwalsh@redhat.com>`
|
||||
|
|
|
@ -219,6 +219,8 @@ $ podman pod create --network slirp4netns:outbound_addr=127.0.0.1,allow_host_loo
|
|||
|
||||
$ podman pod create --network slirp4netns:cidr=192.168.0.0/24
|
||||
|
||||
$ podman pod create --network pasta
|
||||
|
||||
$ podman pod create --network net1:ip=10.89.1.5 --network net2:ip=10.89.10.10
|
||||
```
|
||||
|
||||
|
|
|
@ -837,8 +837,11 @@ In order for users to run rootless, there must be an entry for their username in
|
|||
|
||||
Rootless Podman works better if the fuse-overlayfs and slirp4netns packages are installed.
|
||||
The **fuse-overlayfs** package provides a userspace overlay storage driver, otherwise users need to use
|
||||
the **vfs** storage driver, which is diskspace expensive and does not perform well. slirp4netns is
|
||||
required for VPN, without it containers need to be run with the **--network=host** flag.
|
||||
the **vfs** storage driver, which can be disk space expensive and less
|
||||
performant than other drivers.
|
||||
|
||||
To enable VPN on the container, slirp4netns or pasta needs to be specified;
|
||||
without either, containers need to be run with the --network=host flag.
|
||||
|
||||
## ENVIRONMENT
|
||||
|
||||
|
@ -885,7 +888,7 @@ page.
|
|||
NOTE: Use the environment variable `TMPDIR` to change the temporary storage location of downloaded container images. Podman defaults to use `/var/tmp`.
|
||||
|
||||
## SEE ALSO
|
||||
**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
|
||||
**[podman(1)](podman.1.md)**, **[podman-save(1)](podman-save.1.md)**, **[podman-ps(1)](podman-ps.1.md)**, **[podman-attach(1)](podman-attach.1.md)**, **[podman-pod-create(1)](podman-pod-create.1.md)**, **[podman-port(1)](podman-port.1.md)**, **[podman-start(1)](podman-start.1.md)**, **[podman-kill(1)](podman-kill.1.md)**, **[podman-stop(1)](podman-stop.1.md)**, **[podman-generate-systemd(1)](podman-generate-systemd.1.md)**, **[podman-rm(1)](podman-rm.1.md)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[systemd.unit(5)](https://www.freedesktop.org/software/systemd/man/systemd.unit.html)**, **[setsebool(8)](https://man7.org/linux/man-pages/man8/setsebool.8.html)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[pasta(1)](https://passt.top/builds/latest/web/passt.1.html)**, **[fuse-overlayfs(1)](https://github.com/containers/fuse-overlayfs/blob/main/fuse-overlayfs.1.md)**, **proc(5)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**, **personality(2)**
|
||||
|
||||
## HISTORY
|
||||
September 2018, updated by Kunal Kushwaha `<kushwaha_kunal_v7@lab.ntt.co.jp>`
|
||||
|
|
|
@ -88,7 +88,7 @@ Set libpod namespace. Namespaces are used to separate groups of containers and p
|
|||
When namespace is set, created containers and pods will join the given namespace, and only containers and pods in the given namespace will be visible to Podman.
|
||||
|
||||
#### **--network-cmd-path**=*path*
|
||||
Path to the command binary to use for setting up a network. It is currently only used for setting up a slirp4netns network. If "" is used then the binary is looked up using the $PATH environment variable.
|
||||
Path to the command binary to use for setting up a network. It is currently only used for setting up a slirp4netns(1) or pasta(1) network. If "" is used then the binary is looked up using the $PATH environment variable.
|
||||
|
||||
#### **--network-config-dir**=*directory*
|
||||
|
||||
|
@ -422,7 +422,9 @@ See the `subuid(5)` and `subgid(5)` man pages for more information.
|
|||
|
||||
Images are pulled under `XDG_DATA_HOME` when specified, otherwise in the home directory of the user under `.local/share/containers/storage`.
|
||||
|
||||
Currently the slirp4netns package is required to be installed to create a network device, otherwise rootless containers need to run in the network namespace of the host.
|
||||
Currently slirp4netns or pasta is required to be installed to create a network
|
||||
device, otherwise rootless containers need to run in the network namespace of
|
||||
the host.
|
||||
|
||||
In certain environments like HPC (High Performance Computing), users cannot take advantage of the additional UIDs and GIDs from the /etc/subuid and /etc/subgid systems. However, in this environment, rootless Podman can operate with a single UID. To make this work, set the `ignore_chown_errors` option in the /etc/containers/storage.conf or in ~/.config/containers/storage.conf files. This option tells Podman when pulling an image to ignore chown errors when attempting to change a file in a container image to match the non-root UID in the image. This means all files get saved as the user's UID. Note this could cause issues when running the container.
|
||||
|
||||
|
@ -435,7 +437,7 @@ The Network File System (NFS) and other distributed file systems (for example: L
|
|||
For more information, please refer to the [Podman Troubleshooting Page](https://github.com/containers/podman/blob/main/troubleshooting.md).
|
||||
|
||||
## SEE ALSO
|
||||
**[containers-mounts.conf(5)](https://github.com/containers/common/blob/main/docs/containers-mounts.conf.5.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.1.md)**, **oci-hooks(5)**, **[containers-policy.json(5)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**
|
||||
**[containers-mounts.conf(5)](https://github.com/containers/common/blob/main/docs/containers-mounts.conf.5.md)**, **[containers.conf(5)](https://github.com/containers/common/blob/main/docs/containers.conf.5.md)**, **[containers-registries.conf(5)](https://github.com/containers/image/blob/main/docs/containers-registries.conf.5.md)**, **[containers-storage.conf(5)](https://github.com/containers/storage/blob/main/docs/containers-storage.conf.5.md)**, **[buildah(1)](https://github.com/containers/buildah/blob/main/docs/buildah.1.md)**, **oci-hooks(5)**, **[containers-policy.json(5)](https://github.com/containers/image/blob/main/docs/containers-policy.json.5.md)**, **[crun(1)](https://github.com/containers/crun/blob/main/crun.1.md)**, **[runc(8)](https://github.com/opencontainers/runc/blob/master/man/runc.8.md)**, **[subuid(5)](https://www.unix.com/man-page/linux/5/subuid)**, **[subgid(5)](https://www.unix.com/man-page/linux/5/subgid)**, **[slirp4netns(1)](https://github.com/rootless-containers/slirp4netns/blob/master/slirp4netns.1.md)**, **[pasta(1)](https://passt.top/builds/latest/web/passt.1.html)**, **[conmon(8)](https://github.com/containers/conmon/blob/main/docs/conmon.8.md)**
|
||||
|
||||
## HISTORY
|
||||
Dec 2016, Originally compiled by Dan Walsh <dwalsh@redhat.com>
|
||||
|
|
|
@ -619,6 +619,8 @@ sub _is_valid_external_link {
|
|||
return 1 if $link eq "https://www.freedesktop.org/software/systemd/man/$base.html";
|
||||
}
|
||||
|
||||
return 1 if $link eq "https://passt.top/builds/latest/web/passt.1.html";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -133,7 +133,8 @@ func (r *Runtime) teardownCNI(ctr *Container) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if !ctr.config.NetMode.IsSlirp4netns() && len(networks) > 0 {
|
||||
if !ctr.config.NetMode.IsSlirp4netns() &&
|
||||
!ctr.config.NetMode.IsPasta() && len(networks) > 0 {
|
||||
netOpts := ctr.getNetworkOptions(networks)
|
||||
return r.teardownNetwork(ctr.state.NetNS.Path(), netOpts)
|
||||
}
|
||||
|
|
|
@ -563,6 +563,9 @@ func (r *Runtime) configureNetNS(ctr *Container, ctrNS ns.NetNS) (status map[str
|
|||
if ctr.config.NetMode.IsSlirp4netns() {
|
||||
return nil, r.setupSlirp4netns(ctr, ctrNS)
|
||||
}
|
||||
if ctr.config.NetMode.IsPasta() {
|
||||
return nil, r.setupPasta(ctr, ctrNS)
|
||||
}
|
||||
networks, err := ctr.networks()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// networking_pasta_linux.go - Start pasta(1) for user-mode connectivity
|
||||
//
|
||||
// Copyright (c) 2022 Red Hat GmbH
|
||||
// Author: Stefano Brivio <sbrivio@redhat.com>
|
||||
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func (r *Runtime) setupPasta(ctr *Container, netns ns.NetNS) error {
|
||||
var NoTCPInitPorts = true
|
||||
var NoUDPInitPorts = true
|
||||
var NoTCPNamespacePorts = true
|
||||
var NoUDPNamespacePorts = true
|
||||
var NoMapGW = true
|
||||
|
||||
path, err := r.config.FindHelperBinary("pasta", true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find pasta, the network namespace can't be configured: %w", err)
|
||||
}
|
||||
|
||||
cmdArgs := []string{}
|
||||
cmdArgs = append(cmdArgs, "--config-net")
|
||||
|
||||
for _, i := range ctr.convertPortMappings() {
|
||||
protocols := strings.Split(i.Protocol, ",")
|
||||
for _, protocol := range protocols {
|
||||
var addr string
|
||||
|
||||
if i.HostIP != "" {
|
||||
addr = fmt.Sprintf("%s/", i.HostIP)
|
||||
}
|
||||
|
||||
switch protocol {
|
||||
case "tcp":
|
||||
cmdArgs = append(cmdArgs, "-t")
|
||||
case "udp":
|
||||
cmdArgs = append(cmdArgs, "-u")
|
||||
case "default":
|
||||
return fmt.Errorf("can't forward protocol: %s", protocol)
|
||||
}
|
||||
|
||||
arg := fmt.Sprintf("%s%d-%d:%d-%d", addr,
|
||||
i.HostPort,
|
||||
i.HostPort+i.Range-1,
|
||||
i.ContainerPort,
|
||||
i.ContainerPort+i.Range-1)
|
||||
cmdArgs = append(cmdArgs, arg)
|
||||
}
|
||||
}
|
||||
|
||||
cmdArgs = append(cmdArgs, ctr.config.NetworkOptions["pasta"]...)
|
||||
|
||||
for i, opt := range cmdArgs {
|
||||
switch opt {
|
||||
case "-t", "--tcp-ports":
|
||||
NoTCPInitPorts = false
|
||||
case "-u", "--udp-ports":
|
||||
NoUDPInitPorts = false
|
||||
case "-T", "--tcp-ns":
|
||||
NoTCPNamespacePorts = false
|
||||
case "-U", "--udp-ns":
|
||||
NoUDPNamespacePorts = false
|
||||
case "--map-gw":
|
||||
NoMapGW = false
|
||||
// not an actual pasta(1) option
|
||||
cmdArgs = append(cmdArgs[:i], cmdArgs[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
if NoTCPInitPorts {
|
||||
cmdArgs = append(cmdArgs, "-t", "none")
|
||||
}
|
||||
if NoUDPInitPorts {
|
||||
cmdArgs = append(cmdArgs, "-u", "none")
|
||||
}
|
||||
if NoTCPNamespacePorts {
|
||||
cmdArgs = append(cmdArgs, "-T", "none")
|
||||
}
|
||||
if NoUDPNamespacePorts {
|
||||
cmdArgs = append(cmdArgs, "-U", "none")
|
||||
}
|
||||
if NoMapGW {
|
||||
cmdArgs = append(cmdArgs, "--no-map-gw")
|
||||
}
|
||||
|
||||
cmdArgs = append(cmdArgs, "--netns", netns.Path())
|
||||
|
||||
logrus.Debugf("pasta arguments: %s", strings.Join(cmdArgs, " "))
|
||||
|
||||
// pasta forks once ready, and quits once we delete the target namespace
|
||||
_, err = exec.Command(path, cmdArgs...).Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start pasta:\n%s",
|
||||
err.(*exec.ExitError).Stderr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -267,7 +267,8 @@ func cliOpts(cc handlers.CreateContainerConfig, rtc *config.Config) (*entities.C
|
|||
if netmode == "" || netmode == "default" {
|
||||
netmode = "bridge"
|
||||
}
|
||||
nsmode, networks, netOpts, err := specgen.ParseNetworkFlag([]string{netmode})
|
||||
|
||||
nsmode, networks, netOpts, err := specgen.ParseNetworkFlag([]string{netmode}, false)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
|
@ -139,6 +139,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o
|
|||
}
|
||||
|
||||
func (ic *ContainerEngine) NetworkCreate(ctx context.Context, network types.Network) (*types.Network, error) {
|
||||
// TODO (5.0): Stop accepting "pasta" as value here
|
||||
if util.StringInSlice(network.Name, []string{"none", "host", "bridge", "private", "slirp4netns", "container", "ns"}) {
|
||||
return nil, fmt.Errorf("cannot create network with name %q because it conflicts with a valid network mode", network.Name)
|
||||
}
|
||||
|
|
|
@ -375,7 +375,14 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||
}
|
||||
|
||||
if len(options.Networks) > 0 {
|
||||
ns, networks, netOpts, err := specgen.ParseNetworkFlag(options.Networks)
|
||||
var pastaNetworkNameExists bool
|
||||
|
||||
_, err := ic.Libpod.Network().NetworkInspect("pasta")
|
||||
if err == nil {
|
||||
pastaNetworkNameExists = true
|
||||
}
|
||||
|
||||
ns, networks, netOpts, err := specgen.ParseNetworkFlag(options.Networks, pastaNetworkNameExists)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ const (
|
|||
privateType = "private"
|
||||
shareableType = "shareable"
|
||||
slirpType = "slirp4netns"
|
||||
pastaType = "pasta"
|
||||
)
|
||||
|
||||
// KeepIDUserNsOptions defines how to keepIDmatically create a user namespace.
|
||||
|
@ -440,6 +441,11 @@ func (n NetworkMode) IsSlirp4netns() bool {
|
|||
return n == slirpType || strings.HasPrefix(string(n), slirpType+":")
|
||||
}
|
||||
|
||||
// IsPasta indicates if we are running a rootless network stack using pasta
|
||||
func (n NetworkMode) IsPasta() bool {
|
||||
return n == pastaType || strings.HasPrefix(string(n), pastaType+":")
|
||||
}
|
||||
|
||||
// IsNS indicates a network namespace passed in by path (ns:<path>)
|
||||
func (n NetworkMode) IsNS() bool {
|
||||
return strings.HasPrefix(string(n), nsType)
|
||||
|
@ -461,5 +467,5 @@ func (n NetworkMode) IsPod() bool {
|
|||
|
||||
// IsUserDefined indicates user-created network
|
||||
func (n NetworkMode) IsUserDefined() bool {
|
||||
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() && !n.IsSlirp4netns() && !n.IsNS()
|
||||
return !n.IsDefault() && !n.IsBridge() && !n.IsHost() && !n.IsNone() && !n.IsContainer() && !n.IsSlirp4netns() && !n.IsPasta() && !n.IsNS()
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ func GetDefaultNamespaceMode(nsType string, cfg *config.Config, pod *libpod.Pod)
|
|||
case "cgroup":
|
||||
return specgen.ParseCgroupNamespace(cfg.Containers.CgroupNS)
|
||||
case "net":
|
||||
ns, _, _, err := specgen.ParseNetworkFlag(nil)
|
||||
ns, _, _, err := specgen.ParseNetworkFlag(nil, false)
|
||||
return ns, err
|
||||
}
|
||||
|
||||
|
@ -300,6 +300,13 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.
|
|||
val = fmt.Sprintf("slirp4netns:%s", s.NetNS.Value)
|
||||
}
|
||||
toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil))
|
||||
case specgen.Pasta:
|
||||
portMappings, expose, err := createPortMappings(s, imageData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val := "pasta"
|
||||
toReturn = append(toReturn, libpod.WithNetNS(portMappings, expose, postConfigureNetNS, val, nil))
|
||||
case specgen.Bridge, specgen.Private, specgen.Default:
|
||||
portMappings, expose, err := createPortMappings(s, imageData)
|
||||
if err != nil {
|
||||
|
|
|
@ -38,7 +38,7 @@ func canMountSys(isRootless, isNewUserns bool, s *specgen.SpecGenerator) bool {
|
|||
}
|
||||
if isNewUserns {
|
||||
switch s.NetNS.NSMode {
|
||||
case specgen.Slirp, specgen.Private, specgen.NoNetwork, specgen.Bridge:
|
||||
case specgen.Slirp, specgen.Pasta, specgen.Private, specgen.NoNetwork, specgen.Bridge:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
|
|
@ -195,11 +195,16 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) {
|
|||
p.InfraContainerSpec.NetworkOptions = p.NetworkOptions
|
||||
p.InfraContainerSpec.NetNS.NSMode = specgen.Slirp
|
||||
}
|
||||
case specgen.Pasta:
|
||||
logrus.Debugf("Pod will use pasta")
|
||||
if p.InfraContainerSpec.NetNS.NSMode != specgen.Host {
|
||||
p.InfraContainerSpec.NetworkOptions = p.NetworkOptions
|
||||
p.InfraContainerSpec.NetNS.NSMode = specgen.Pasta
|
||||
}
|
||||
case specgen.Path:
|
||||
logrus.Debugf("Pod will use namespace path networking")
|
||||
p.InfraContainerSpec.NetNS.NSMode = specgen.Path
|
||||
p.InfraContainerSpec.NetNS.Value = p.PodNetworkConfig.NetNS.Value
|
||||
|
||||
case specgen.NoNetwork:
|
||||
logrus.Debugf("Pod will not use networking")
|
||||
if len(p.InfraContainerSpec.PortMappings) > 0 ||
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
cutil "github.com/containers/common/pkg/util"
|
||||
"github.com/containers/podman/v4/libpod/define"
|
||||
"github.com/containers/podman/v4/pkg/namespaces"
|
||||
"github.com/containers/podman/v4/pkg/rootless"
|
||||
"github.com/containers/podman/v4/pkg/util"
|
||||
"github.com/containers/storage"
|
||||
spec "github.com/opencontainers/runtime-spec/specs-go"
|
||||
|
@ -52,6 +53,9 @@ const (
|
|||
// be used.
|
||||
// Only used with the network namespace, invalid otherwise.
|
||||
Slirp NamespaceMode = "slirp4netns"
|
||||
// Pasta indicates that a pasta network stack should be used.
|
||||
// Only used with the network namespace, invalid otherwise.
|
||||
Pasta NamespaceMode = "pasta"
|
||||
// KeepId indicates a user namespace to keep the owner uid inside
|
||||
// of the namespace itself.
|
||||
// Only used with the user namespace, invalid otherwise.
|
||||
|
@ -156,6 +160,11 @@ func validateNetNS(n *Namespace) error {
|
|||
switch n.NSMode {
|
||||
case Slirp:
|
||||
break
|
||||
case Pasta:
|
||||
if rootless.IsRootless() {
|
||||
break
|
||||
}
|
||||
return fmt.Errorf("pasta networking is only supported for rootless mode")
|
||||
case "", Default, Host, Path, FromContainer, FromPod, Private, NoNetwork, Bridge:
|
||||
break
|
||||
default:
|
||||
|
@ -197,7 +206,7 @@ func (n *Namespace) validate() error {
|
|||
switch n.NSMode {
|
||||
case "", Default, Host, Path, FromContainer, FromPod, Private:
|
||||
// Valid, do nothing
|
||||
case NoNetwork, Bridge, Slirp:
|
||||
case NoNetwork, Bridge, Slirp, Pasta:
|
||||
return errors.New("cannot use network modes with non-network namespace")
|
||||
default:
|
||||
return fmt.Errorf("invalid namespace type %s specified", n.NSMode)
|
||||
|
@ -329,7 +338,8 @@ func ParseUserNamespace(ns string) (Namespace, error) {
|
|||
|
||||
// ParseNetworkFlag parses a network string slice into the network options
|
||||
// If the input is nil or empty it will use the default setting from containers.conf
|
||||
func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetworkOptions, map[string][]string, error) {
|
||||
// TODO (5.0): Drop pastaNetworkNameExists
|
||||
func ParseNetworkFlag(networks []string, pastaNetworkNameExists bool) (Namespace, map[string]types.PerNetworkOptions, map[string][]string, error) {
|
||||
var networkOptions map[string][]string
|
||||
// by default we try to use the containers.conf setting
|
||||
// if we get at least one value use this instead
|
||||
|
@ -385,6 +395,22 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork
|
|||
}
|
||||
toReturn.NSMode = FromContainer
|
||||
toReturn.Value = split[1]
|
||||
case ns == string(Pasta), strings.HasPrefix(ns, string(Pasta)+":"):
|
||||
var parts []string
|
||||
|
||||
if pastaNetworkNameExists {
|
||||
goto nextCase
|
||||
}
|
||||
|
||||
parts = strings.SplitN(ns, ":", 2)
|
||||
if len(parts) > 1 {
|
||||
networkOptions = make(map[string][]string)
|
||||
networkOptions[parts[0]] = strings.Split(parts[1], ",")
|
||||
}
|
||||
toReturn.NSMode = Pasta
|
||||
break
|
||||
nextCase:
|
||||
fallthrough
|
||||
default:
|
||||
// we should have a normal network
|
||||
parts := strings.SplitN(ns, ":", 2)
|
||||
|
@ -419,6 +445,7 @@ func ParseNetworkFlag(networks []string) (Namespace, map[string]types.PerNetwork
|
|||
if parts[0] == "" {
|
||||
return toReturn, nil, nil, fmt.Errorf("network name cannot be empty: %w", define.ErrInvalidArg)
|
||||
}
|
||||
// TODO (5.0): Don't accept string(Pasta) here once we drop pastaNetworkNameExists
|
||||
if cutil.StringInSlice(parts[0], []string{string(Bridge), string(Slirp), string(FromPod), string(NoNetwork),
|
||||
string(Default), string(Private), string(Path), string(FromContainer), string(Host)}) {
|
||||
return toReturn, nil, nil, fmt.Errorf("can only set extra network names, selected mode %s conflicts with bridge: %w", parts[0], define.ErrInvalidArg)
|
||||
|
|
|
@ -241,7 +241,7 @@ func TestParseNetworkFlag(t *testing.T) {
|
|||
for _, tt := range tests {
|
||||
tt := tt
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, got1, got2, err := ParseNetworkFlag(tt.args)
|
||||
got, got1, got2, err := ParseNetworkFlag(tt.args, false)
|
||||
if tt.err != "" {
|
||||
assert.EqualError(t, err, tt.err, tt.name)
|
||||
} else {
|
||||
|
|
|
@ -65,9 +65,9 @@ func (p *PodSpecGenerator) Validate() error {
|
|||
return exclusivePodOptions("NoInfra", "NoManageResolvConf")
|
||||
}
|
||||
}
|
||||
if p.NetNS.NSMode != "" && p.NetNS.NSMode != Bridge && p.NetNS.NSMode != Slirp && p.NetNS.NSMode != Default {
|
||||
if p.NetNS.NSMode != "" && p.NetNS.NSMode != Bridge && p.NetNS.NSMode != Slirp && p.NetNS.NSMode != Pasta && p.NetNS.NSMode != Default {
|
||||
if len(p.PortMappings) > 0 {
|
||||
return errors.New("PortMappings can only be used with Bridge or slirp4netns networking")
|
||||
return errors.New("PortMappings can only be used with Bridge, slirp4netns, or pasta networking")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ type PodNetworkConfig struct {
|
|||
// PortMappings is a set of ports to map into the infra container.
|
||||
// As, by default, containers share their network with the infra
|
||||
// container, this will forward the ports to the entire pod.
|
||||
// Only available if NetNS is set to Bridge or Slirp.
|
||||
// Only available if NetNS is set to Bridge, Slirp, or Pasta.
|
||||
// Optional.
|
||||
PortMappings []types.PortMapping `json:"portmappings,omitempty"`
|
||||
// Map of networks names to ids the container should join to.
|
||||
|
|
|
@ -421,7 +421,7 @@ type ContainerNetworkConfig struct {
|
|||
// Mandatory.
|
||||
NetNS Namespace `json:"netns,omitempty"`
|
||||
// PortBindings is a set of ports to map into the container.
|
||||
// Only available if NetNS is set to bridge or slirp.
|
||||
// Only available if NetNS is set to bridge, slirp, or pasta.
|
||||
// Optional.
|
||||
PortMappings []nettypes.PortMapping `json:"portmappings,omitempty"`
|
||||
// PublishExposedPorts will publish ports specified in the image to
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
# Override standard setup! We don't yet trust podman-images or podman-rm
|
||||
function setup() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
@test "podman run - basic tests" {
|
||||
rand=$(random_string 30)
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
# Custom helpers for this test only. These just save us having to duplicate
|
||||
# the same thing four times (two tests, each with -i and stdin).
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
###############################################################################
|
||||
# BEGIN one-time envariable setup
|
||||
|
@ -343,7 +344,7 @@ function _test_skopeo_credential_sharing() {
|
|||
fi
|
||||
|
||||
# Make sure socket is closed
|
||||
if ! port_is_free $PODMAN_LOGIN_REGISTRY_PORT; then
|
||||
if tcp_port_probe $PODMAN_LOGIN_REGISTRY_PORT; then
|
||||
die "Socket still seems open"
|
||||
fi
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
LOOPDEVICE=
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
load helpers
|
||||
load helpers.systemd
|
||||
load helpers.network
|
||||
|
||||
SERVICE_NAME="podman-service-$(random_string)"
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
SERVICE_NAME="podman_test_$(random_string)"
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
# This will be set if we start a local service
|
||||
_SERVICE_PID=
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
@test "podman network - basic tests" {
|
||||
heading="NETWORK *ID *NAME *DRIVER"
|
||||
|
|
|
@ -0,0 +1,683 @@
|
|||
#!/usr/bin/env bats -*- bats -*-
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Networking with pasta(1)
|
||||
#
|
||||
# Copyright (c) 2022 Red Hat GmbH
|
||||
# Author: Stefano Brivio <sbrivio@redhat.com>
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
function setup() {
|
||||
basic_setup
|
||||
skip_if_not_rootless "pasta networking only available in rootless mode"
|
||||
skip_if_no_pasta "pasta not found: install pasta(1) to run these tests"
|
||||
|
||||
XFER_FILE="${PODMAN_TMPDIR}/pasta.bin"
|
||||
}
|
||||
|
||||
# pasta_test_do() - Run tests involving clients and servers
|
||||
# $1: IP version: 4 or 6
|
||||
# $2: Interface type: "tap" or "loopback"
|
||||
# $3: Protocol: "tcp" or "udp"
|
||||
# $4: Size of port range, 1 for tests over a single port
|
||||
# $5: Delta for remapped destination ports in guest
|
||||
# $6: How specifically pasta will bind: "port", "address", or "interface"
|
||||
# $7: Bytes to transfer, with multiplicative suffixes as supported by dd(1)
|
||||
function pasta_test_do() {
|
||||
local ip_ver="${1}"
|
||||
local iftype="${2}"
|
||||
local proto="${3}"
|
||||
local range="${4}"
|
||||
local delta="${5}"
|
||||
local bind_type="${6}"
|
||||
local bytes="${7}"
|
||||
|
||||
# Calculate and set addresses,
|
||||
if [ ${ip_ver} -eq 4 ]; then
|
||||
skip_if_no_ipv4 "IPv4 not routable on the host"
|
||||
if [ ${iftype} = "loopback" ]; then
|
||||
local addr="127.0.0.1"
|
||||
else
|
||||
local addr="$(ipv4_get_addr_global)"
|
||||
fi
|
||||
elif [ ${ip_ver} -eq 6 ]; then
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
if [ ${iftype} = "loopback" ]; then
|
||||
local addr="::1"
|
||||
else
|
||||
local addr="$(ipv6_get_addr_global)"
|
||||
fi
|
||||
else
|
||||
skip "Unsupported IP version"
|
||||
fi
|
||||
|
||||
# interface names,
|
||||
if [ ${iftype} = "loopback" ]; then
|
||||
local ifname="lo"
|
||||
else
|
||||
local ifname="$(ether_get_name)"
|
||||
fi
|
||||
|
||||
# ports,
|
||||
if [ ${range} -gt 1 ]; then
|
||||
local port="$(random_free_port_range ${range} ${addr} ${proto})"
|
||||
local xport="$((${port%-*} + delta))-$((${port#*-} + delta))"
|
||||
local seq="$(echo ${port} | tr '-' ' ')"
|
||||
local xseq="$(echo ${xport} | tr '-' ' ')"
|
||||
else
|
||||
local port=$(random_free_port "" ${address} ${proto})
|
||||
local xport="$((port + port_delta))"
|
||||
local seq="${port} ${port}"
|
||||
local xseq="${xport} ${xport}"
|
||||
fi
|
||||
|
||||
local proto_upper="$(echo "${proto}" | tr [:lower:] [:upper:])"
|
||||
|
||||
# socat options for first <address> in server ("LISTEN" address types),
|
||||
local bind="${proto_upper}${ip_ver}-LISTEN:\${port}"
|
||||
# For IPv6 via tap, we can pick either link-local or global unicast
|
||||
if [ ${ip_ver} -eq 4 ] || [ ${iftype} = "loopback" ]; then
|
||||
bind="${bind},bind=[${addr}]"
|
||||
fi
|
||||
if [ "${proto}" = "udp" ]; then
|
||||
bind="${bind},null-eof"
|
||||
fi
|
||||
|
||||
# socat options for second <address> in server ("STDOUT" or "EXEC"),
|
||||
if [ "${bytes}" = "1" ]; then
|
||||
recv="STDOUT"
|
||||
else
|
||||
recv="EXEC:md5sum"
|
||||
fi
|
||||
|
||||
# socat options for first <address> in client ("OPEN" or "EXEC"),
|
||||
if [ "${bytes}" = "1" ]; then
|
||||
send="EXEC:printf x"
|
||||
else
|
||||
send="OPEN:${XFER_FILE}"
|
||||
fi
|
||||
|
||||
# and port forwarding configuration for Podman and pasta.
|
||||
#
|
||||
# TODO: Use Podman options once/if
|
||||
# https://github.com/containers/podman/issues/14425 is solved
|
||||
case ${bind_type} in
|
||||
"interface")
|
||||
local pasta_spec=":--${proto}-ports,${addr}%${ifname}/${port}:${xport}"
|
||||
local podman_spec=
|
||||
;;
|
||||
"address")
|
||||
local pasta_spec=
|
||||
local podman_spec="[${addr}]:${port}:${xport}/${proto}"
|
||||
;;
|
||||
*)
|
||||
local pasta_spec=
|
||||
local podman_spec="[${addr}]:${port}:${xport}/${proto}"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Fill in file for data transfer tests, and expected output strings
|
||||
if [ "${bytes}" != "1" ]; then
|
||||
dd if=/dev/urandom bs=${bytes} count=1 of="${XFER_FILE}"
|
||||
local expect="$(cat "${XFER_FILE}" | md5sum)"
|
||||
else
|
||||
local expect="$(for i in $(seq ${seq}); do printf "x"; done)"
|
||||
fi
|
||||
|
||||
# Set retry/initial delay for client to connect
|
||||
local delay=1
|
||||
if [ ${ip_ver} -eq 6 ] && [ "${addr}" != "::1" ]; then
|
||||
# Duplicate Address Detection on link-local
|
||||
delay=3
|
||||
elif [ "${proto}" = "udp" ]; then
|
||||
# With Podman up, and socat still starting, UDP clients send to nowhere
|
||||
delay=2
|
||||
fi
|
||||
|
||||
# Now actually run the test: client,
|
||||
for one_port in $(seq ${seq}); do
|
||||
local connect="${proto_upper}${ip_ver}:[${addr}]:${one_port}"
|
||||
[ "${proto}" = "udp" ] && connect="${connect},shut-null"
|
||||
|
||||
(while sleep ${delay} && ! socat -u "${send}" "${connect}"; do :
|
||||
done) &
|
||||
done
|
||||
|
||||
# and server,
|
||||
run_podman run --net=pasta"${pasta_spec}" -p "${podman_spec}" "${IMAGE}" \
|
||||
sh -c 'for port in $(seq '"${xseq}"'); do '\
|
||||
' socat -u '"${bind}"' '"${recv}"' & '\
|
||||
' done; wait'
|
||||
|
||||
# which should give us the expected output back.
|
||||
assert "${output}" = "${expect}" "Mismatch between data sent and received"
|
||||
}
|
||||
|
||||
function teardown() {
|
||||
rm -f "${XFER_FILE}"
|
||||
}
|
||||
|
||||
### Addresses ##################################################################
|
||||
|
||||
@test "podman networking with pasta(1) - IPv4 default address assignment" {
|
||||
skip_if_no_ipv4 "IPv4 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta $IMAGE ip -j -4 address show
|
||||
|
||||
local container_address="$(ipv4_get_addr_global "${output}")"
|
||||
local host_address="$(ipv4_get_addr_global)"
|
||||
|
||||
assert "${container_address}" = "${host_address}" \
|
||||
"Container address not matching host"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - IPv4 address assignment" {
|
||||
skip_if_no_ipv4 "IPv4 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta:-a,192.0.2.1 $IMAGE ip -j -4 address show
|
||||
|
||||
local container_address="$(ipv4_get_addr_global "${output}")"
|
||||
|
||||
assert "${container_address}" = "192.0.2.1" \
|
||||
"Container address not matching configured value"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - No IPv4" {
|
||||
skip_if_no_ipv4 "IPv4 not routable on the host"
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta:-6 $IMAGE ip -j -4 address show
|
||||
|
||||
local container_address="$(ipv4_get_addr_global "${output}")"
|
||||
|
||||
assert "${container_address}" = "null" \
|
||||
"Container has IPv4 global address with IPv4 disabled"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - IPv6 default address assignment" {
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta $IMAGE ip -j -6 address show
|
||||
|
||||
local container_address="$(ipv6_get_addr_global "${output}")"
|
||||
local host_address="$(ipv6_get_addr_global)"
|
||||
|
||||
assert "${container_address}" = "${host_address}" \
|
||||
"Container address not matching host"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - IPv6 address assignment" {
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta:-a,2001:db8::1 $IMAGE ip -j -6 address show
|
||||
|
||||
local container_address="$(ipv6_get_addr_global "${output}")"
|
||||
|
||||
assert "${container_address}" = "2001:db8::1" \
|
||||
"Container address not matching configured value"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - No IPv6" {
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
skip_if_no_ipv4 "IPv4 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta:-4 $IMAGE ip -j -6 address show
|
||||
|
||||
local container_address="$(ipv6_get_addr_global "${output}")"
|
||||
|
||||
assert "${container_address}" = "null" \
|
||||
"Container has IPv6 global address with IPv6 disabled"
|
||||
}
|
||||
|
||||
### Routes #####################################################################
|
||||
|
||||
@test "podman networking with pasta(1) - IPv4 default route" {
|
||||
skip_if_no_ipv4 "IPv4 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta $IMAGE ip -j -4 route show
|
||||
|
||||
local container_route="$(ipv4_get_route_default "${output}")"
|
||||
local host_route="$(ipv4_get_route_default)"
|
||||
|
||||
assert "${container_route}" = "${host_route}" \
|
||||
"Container route not matching host"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - IPv4 default route assignment" {
|
||||
skip_if_no_ipv4 "IPv4 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta:-a,192.0.2.2,-g,192.0.2.1 $IMAGE \
|
||||
ip -j -4 route show
|
||||
|
||||
local container_route="$(ipv4_get_route_default "${output}")"
|
||||
|
||||
assert "${container_route}" = "192.0.2.1" \
|
||||
"Container route not matching configured value"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - IPv6 default route" {
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta $IMAGE ip -j -6 route show
|
||||
|
||||
local container_route="$(ipv6_get_route_default "${output}")"
|
||||
local host_route="$(ipv6_get_route_default)"
|
||||
|
||||
assert "${container_route}" = "${host_route}" \
|
||||
"Container route not matching host"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - IPv6 default route assignment" {
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta:-a,2001:db8::2,-g,2001:db8::1 $IMAGE \
|
||||
ip -j -6 route show
|
||||
|
||||
local container_route="$(ipv6_get_route_default "${output}")"
|
||||
|
||||
assert "${container_route}" = "2001:db8::1" \
|
||||
"Container route not matching configured value"
|
||||
}
|
||||
|
||||
### Interfaces #################################################################
|
||||
|
||||
@test "podman networking with pasta(1) - Default MTU" {
|
||||
run_podman run --net=pasta $IMAGE ip -j link show
|
||||
|
||||
container_tap_mtu="$(ether_get_mtu "${output}")"
|
||||
|
||||
assert "${container_tap_mtu}" = "65520" \
|
||||
"Container's default MTU not 65220 bytes by default"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - MTU assignment" {
|
||||
run_podman run --net=pasta:-m,1280 $IMAGE ip -j link show
|
||||
|
||||
container_tap_mtu="$(ether_get_mtu "${output}")"
|
||||
|
||||
assert "${container_tap_mtu}" = "1280" \
|
||||
"Container's default MTU not matching configured 1280 bytes"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Loopback interface state" {
|
||||
run_podman run --net=pasta $IMAGE ip -j link show
|
||||
|
||||
local jq_expr='.[] | select(.link_type == "loopback").flags | '\
|
||||
' contains(["UP"])'
|
||||
|
||||
container_loopback_up="$(printf '%s' "${output}" | jq -rM "${jq_expr}")"
|
||||
|
||||
assert "${container_loopback_up}" = "true" \
|
||||
"Container's loopback interface not up"
|
||||
}
|
||||
|
||||
### DNS ########################################################################
|
||||
|
||||
@test "podman networking with pasta(1) - External resolver, IPv4" {
|
||||
skip_if_no_ipv4 "IPv4 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta $IMAGE nslookup 127.0.0.1 || :
|
||||
|
||||
assert "$output" =~ "1.0.0.127.in-addr.arpa" \
|
||||
"127.0.0.1 not resolved"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - External resolver, IPv6" {
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
|
||||
run_podman run --net=pasta $IMAGE nslookup ::1 || :
|
||||
|
||||
assert "$output" =~ "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" \
|
||||
"::1 not resolved"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Local forwarder, IPv4" {
|
||||
skip_if_no_ipv4 "IPv4 not routable on the host"
|
||||
|
||||
run_podman run --dns 198.51.100.1 \
|
||||
--net=pasta:--dns-forward,198.51.100.1 $IMAGE nslookup 127.0.0.1
|
||||
|
||||
assert "$output" =~ "1.0.0.127.in-addr.arpa" \
|
||||
"127.0.0.1 not resolved to 1.0.0.127.in-addr.arpa"
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Local forwarder, IPv6" {
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
|
||||
# TODO: Two issues here:
|
||||
skip "Currently unsupported"
|
||||
# run_podman run --dns 2001:db8::1 \
|
||||
# --net=pasta:--dns-forward,2001:db8::1 $IMAGE nslookup ::1
|
||||
#
|
||||
# 1. With this, Podman writes "nameserver 2001:db8::1" to
|
||||
# /etc/resolv.conf, without zone, and the query originates from ::1.
|
||||
# Passing:
|
||||
# --dns "2001:db8::2%eth0"
|
||||
# results in:
|
||||
# Error: 2001:db8::2%eth0 is not an ip address
|
||||
# Fix the issue in Podman once we figure out 2. below.
|
||||
#
|
||||
#
|
||||
# run_podman run --dns 2001:db8::1 \
|
||||
# --net=pasta:--dns-forward,2001:db8::1 $IMAGE \
|
||||
# sh -c 'echo 2001:db8::1%eth0 >/etc/resolv.conf; nslookup ::1'
|
||||
#
|
||||
# 2. This fixes the source address of the query, but the answer is
|
||||
# discarded. Figure out if it's an issue in Busybox, in musl, if we
|
||||
# should just include a full-fledged resolver in the test image, etc.
|
||||
}
|
||||
|
||||
### TCP/IPv4 Port Forwarding ###################################################
|
||||
|
||||
@test "podman networking with pasta(1) - Single TCP port forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap tcp 1 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Single TCP port forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback tcp 1 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP port range forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap tcp 3 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP port range forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback tcp 3 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Translated TCP port forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap tcp 1 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Translated TCP port forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback tcp 1 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP translated port range forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap tcp 3 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP translated port range forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback tcp 3 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Address-bound TCP port forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap tcp 1 0 "address" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Address-bound TCP port forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback tcp 1 0 "address" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Interface-bound TCP port forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap tcp 1 0 "interface" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Interface-bound TCP port forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback tcp 1 0 "interface" 1
|
||||
}
|
||||
|
||||
### TCP/IPv6 Port Forwarding ###################################################
|
||||
|
||||
@test "podman networking with pasta(1) - Single TCP port forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap tcp 1 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Single TCP port forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback tcp 1 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP port range forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap tcp 3 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP port range forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback tcp 3 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Translated TCP port forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap tcp 1 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Translated TCP port forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback tcp 1 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP translated port range forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap tcp 3 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP translated port range forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback tcp 3 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Address-bound TCP port forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap tcp 1 0 "address" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Address-bound TCP port forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback tcp 1 0 "address" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Interface-bound TCP port forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap tcp 1 0 "interface" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Interface-bound TCP port forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback tcp 1 0 "interface" 1
|
||||
}
|
||||
|
||||
### UDP/IPv4 Port Forwarding ###################################################
|
||||
|
||||
@test "podman networking with pasta(1) - Single UDP port forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap udp 1 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Single UDP port forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback udp 1 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP port range forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap udp 3 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP port range forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback udp 3 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Translated UDP port forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap udp 1 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Translated UDP port forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback udp 1 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP translated port range forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap udp 3 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP translated port range forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback udp 3 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Address-bound UDP port forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap udp 1 0 "address" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Address-bound UDP port forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback udp 1 0 "address" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Interface-bound UDP port forwarding, IPv4, tap" {
|
||||
pasta_test_do 4 tap udp 1 0 "interface" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Interface-bound UDP port forwarding, IPv4, loopback" {
|
||||
pasta_test_do 4 loopback udp 1 0 "interface" 1
|
||||
}
|
||||
|
||||
### UDP/IPv6 Port Forwarding ###################################################
|
||||
|
||||
@test "podman networking with pasta(1) - Single UDP port forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap udp 1 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Single UDP port forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback udp 1 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP port range forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap udp 3 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP port range forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback udp 3 0 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Translated UDP port forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap udp 1 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Translated UDP port forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback udp 1 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP translated port range forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap udp 3 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP translated port range forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback udp 3 1 "port" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Address-bound UDP port forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap udp 1 0 "address" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Address-bound UDP port forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback udp 1 0 "address" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Interface-bound UDP port forwarding, IPv6, tap" {
|
||||
pasta_test_do 6 tap udp 1 0 "interface" 1
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - Interface-bound UDP port forwarding, IPv6, loopback" {
|
||||
pasta_test_do 6 loopback udp 1 0 "interface" 1
|
||||
}
|
||||
|
||||
### TCP/IPv4 transfer ##########################################################
|
||||
|
||||
@test "podman networking with pasta(1) - TCP/IPv4 small transfer, tap" {
|
||||
pasta_test_do 4 tap tcp 1 0 "port" 2k
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP/IPv4 small transfer, loopback" {
|
||||
pasta_test_do 4 loopback tcp 1 0 "port" 2k
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP/IPv4 large transfer, tap" {
|
||||
pasta_test_do 4 tap tcp 1 0 "port" 10M
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP/IPv4 large transfer, loopback" {
|
||||
pasta_test_do 4 loopback tcp 1 0 "port" 10M
|
||||
}
|
||||
|
||||
### TCP/IPv6 transfer ##########################################################
|
||||
|
||||
@test "podman networking with pasta(1) - TCP/IPv6 small transfer, tap" {
|
||||
pasta_test_do 6 tap tcp 1 0 "port" 2k
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP/IPv6 small transfer, loopback" {
|
||||
pasta_test_do 6 loopback tcp 1 0 "port" 2k
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP/IPv6 large transfer, tap" {
|
||||
pasta_test_do 6 tap tcp 1 0 "port" 10M
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - TCP/IPv6 large transfer, loopback" {
|
||||
pasta_test_do 6 loopback tcp 1 0 "port" 10M
|
||||
}
|
||||
|
||||
### UDP/IPv4 transfer ##########################################################
|
||||
|
||||
@test "podman networking with pasta(1) - UDP/IPv4 small transfer, tap" {
|
||||
pasta_test_do 4 tap udp 1 0 "port" 2k
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP/IPv4 small transfer, loopback" {
|
||||
pasta_test_do 4 loopback udp 1 0 "port" 2k
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP/IPv4 large transfer, tap" {
|
||||
pasta_test_do 4 tap udp 1 0 "port" $(($(cat /proc/sys/net/core/wmem_default) / 4))
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP/IPv4 large transfer, loopback" {
|
||||
pasta_test_do 4 loopback udp 1 0 "port" $(($(cat /proc/sys/net/core/wmem_default) / 4))
|
||||
}
|
||||
|
||||
### UDP/IPv6 transfer ##########################################################
|
||||
|
||||
@test "podman networking with pasta(1) - UDP/IPv6 small transfer, tap" {
|
||||
pasta_test_do 6 tap udp 1 0 "port" 2k
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP/IPv6 small transfer, loopback" {
|
||||
pasta_test_do 6 loopback udp 1 0 "port" 2k
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP/IPv6 large transfer, tap" {
|
||||
pasta_test_do 6 tap udp 1 0 "port" $(($(cat /proc/sys/net/core/wmem_default) / 4))
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - UDP/IPv6 large transfer, loopback" {
|
||||
pasta_test_do 6 loopback udp 1 0 "port" $(($(cat /proc/sys/net/core/wmem_default) / 4))
|
||||
}
|
||||
|
||||
### ICMP, ICMPv6 ###############################################################
|
||||
|
||||
@test "podman networking with pasta(1) - ICMP echo request" {
|
||||
skip_if_no_ipv4 "IPv6 not routable on the host"
|
||||
|
||||
local minuid=$(cut -f1 /proc/sys/net/ipv4/ping_group_range)
|
||||
local maxuid=$(cut -f2 /proc/sys/net/ipv4/ping_group_range)
|
||||
|
||||
if [ $(id -u) -lt ${minuid} ] || [ $(id -u) -gt ${maxuid} ]; then
|
||||
skip "ICMP echo sockets not available for this UID"
|
||||
fi
|
||||
|
||||
run_podman run --net=pasta $IMAGE \
|
||||
sh -c 'ping -c3 -W1 sed -nr "s/^nameserver[ ]{1,}([^.]*).(.*)/\1.\2/p" /etc/resolv.conf | head -1'
|
||||
}
|
||||
|
||||
@test "podman networking with pasta(1) - ICMPv6 echo request" {
|
||||
skip_if_no_ipv6 "IPv6 not routable on the host"
|
||||
|
||||
local minuid=$(cut -f1 /proc/sys/net/ipv4/ping_group_range)
|
||||
local maxuid=$(cut -f2 /proc/sys/net/ipv4/ping_group_range)
|
||||
|
||||
if [ $(id -u) -lt ${minuid} ] || [ $(id -u) -gt ${maxuid} ]; then
|
||||
skip "ICMPv6 echo sockets not available for this UID"
|
||||
fi
|
||||
|
||||
run_podman run --net=pasta $IMAGE ping -6 -c3 -W1 \
|
||||
sh -c 'ping -c3 -W1 sed -nr "s/^nameserver[ ]{1,}([^:]*):(.*)/\1:\2/p" /etc/resolv.conf | head -1'
|
||||
}
|
||||
|
||||
### Lifecycle ##################################################################
|
||||
|
||||
@test "podman networking with pasta(1) - pasta(1) quits when the namespace is gone" {
|
||||
local pidfile="${PODMAN_TMPDIR}/pasta.pid"
|
||||
|
||||
run_podman run "--net=pasta:--pid,${pidfile}" $IMAGE true
|
||||
sleep 1
|
||||
! ps -p $(cat "${pidfile}") && rm "${pidfile}"
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
CHECKED_ROOTLESS=
|
||||
function setup() {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
# This is a long ugly way to clean up pods and remove the pause image
|
||||
function teardown() {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#
|
||||
|
||||
load helpers
|
||||
load helpers.network
|
||||
|
||||
# Override standard setup! We don't yet trust podman-images or podman-rm
|
||||
function setup() {
|
||||
|
|
|
@ -279,72 +279,6 @@ function wait_for_ready {
|
|||
wait_for_output 'READY' "$@"
|
||||
}
|
||||
|
||||
######################
|
||||
# random_free_port # Pick an available port within a specified range
|
||||
######################
|
||||
function random_free_port() {
|
||||
local range=${1:-5000-5999}
|
||||
|
||||
local port
|
||||
for port in $(shuf -i ${range}); do
|
||||
if port_is_free $port; then
|
||||
echo $port
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
die "Could not find open port in range $range"
|
||||
}
|
||||
|
||||
function random_free_port_range() {
|
||||
local size=${1?Usage: random_free_port_range SIZE (as in, number of ports)}
|
||||
|
||||
local maxtries=10
|
||||
while [[ $maxtries -gt 0 ]]; do
|
||||
local firstport=$(random_free_port)
|
||||
local lastport=
|
||||
for i in $(seq 1 $((size - 1))); do
|
||||
lastport=$((firstport + i))
|
||||
if ! port_is_free $lastport; then
|
||||
echo "# port $lastport is in use; trying another." >&3
|
||||
lastport=
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ -n "$lastport" ]]; then
|
||||
echo "$firstport-$lastport"
|
||||
return
|
||||
fi
|
||||
|
||||
maxtries=$((maxtries - 1))
|
||||
done
|
||||
|
||||
die "Could not find free port range with size $size"
|
||||
}
|
||||
|
||||
function port_is_free() {
|
||||
local port=${1?Usage: port_is_free PORT}
|
||||
! { exec {unused_fd}<> /dev/tcp/127.0.0.1/$port; } &>/dev/null
|
||||
}
|
||||
|
||||
###################
|
||||
# wait_for_port # Returns once port is available on host
|
||||
###################
|
||||
function wait_for_port() {
|
||||
local host=$1 # Probably "localhost"
|
||||
local port=$2 # Numeric port
|
||||
local _timeout=${3:-5} # Optional; default to 5 seconds
|
||||
|
||||
# Wait
|
||||
while [ $_timeout -gt 0 ]; do
|
||||
{ exec {unused_fd}<> /dev/tcp/$host/$port; } &>/dev/null && return
|
||||
sleep 1
|
||||
_timeout=$(( $_timeout - 1 ))
|
||||
done
|
||||
|
||||
die "Timed out waiting for $host:$port"
|
||||
}
|
||||
|
||||
###################
|
||||
# wait_for_file # Returns once file is available on host
|
||||
###################
|
||||
|
@ -503,7 +437,6 @@ function skip_if_no_ssh() {
|
|||
fi
|
||||
}
|
||||
|
||||
|
||||
######################
|
||||
# skip_if_rootless # ...with an optional message
|
||||
######################
|
||||
|
@ -821,36 +754,6 @@ function random_string() {
|
|||
head /dev/urandom | tr -dc a-zA-Z0-9 | head -c$length
|
||||
}
|
||||
|
||||
|
||||
###########################
|
||||
# random_rfc1918_subnet #
|
||||
###########################
|
||||
#
|
||||
# Use the class B set, because much of our CI environment (Google, RH)
|
||||
# already uses up much of the class A, and it's really hard to test
|
||||
# if a block is in use.
|
||||
#
|
||||
# This returns THREE OCTETS! It is up to our caller to append .0/24, .255, &c.
|
||||
#
|
||||
function random_rfc1918_subnet() {
|
||||
local retries=1024
|
||||
|
||||
while [ "$retries" -gt 0 ];do
|
||||
local cidr=172.$(( 16 + $RANDOM % 16 )).$(( $RANDOM & 255 ))
|
||||
|
||||
in_use=$(ip route list | fgrep $cidr)
|
||||
if [ -z "$in_use" ]; then
|
||||
echo "$cidr"
|
||||
return
|
||||
fi
|
||||
|
||||
retries=$(( retries - 1 ))
|
||||
done
|
||||
|
||||
die "Could not find a random not-in-use rfc1918 subnet"
|
||||
}
|
||||
|
||||
|
||||
#########################
|
||||
# find_exec_pid_files # Returns nothing or exec_pid hash files
|
||||
#########################
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
# -*- bash -*-
|
||||
|
||||
|
||||
### Feature Checks #############################################################
|
||||
|
||||
# has_ipv4() - Check if one default route is available for IPv4
|
||||
function has_ipv4() {
|
||||
[ -n "$(ip -j -4 route show | jq -rM '.[] | select(.dst == "default")')" ]
|
||||
}
|
||||
|
||||
# has_ipv6() - Check if one default route is available for IPv6
|
||||
function has_ipv6() {
|
||||
[ -n "$(ip -j -6 route show | jq -rM '.[] | select(.dst == "default")')" ]
|
||||
}
|
||||
|
||||
# skip_if_no_ipv4() - Skip current test if IPv4 traffic can't be routed
|
||||
# $1: Optional message to display
|
||||
function skip_if_no_ipv4() {
|
||||
if ! has_ipv4; then
|
||||
local msg=$(_add_label_if_missing "$1" "IPv4")
|
||||
skip "${msg:-not applicable with no routable IPv4}"
|
||||
fi
|
||||
}
|
||||
|
||||
# skip_if_no_ipv6() - Skip current test if IPv6 traffic can't be routed
|
||||
# $1: Optional message to display
|
||||
function skip_if_no_ipv6() {
|
||||
if ! has_ipv6; then
|
||||
local msg=$(_add_label_if_missing "$1" "IPv6")
|
||||
skip "${msg:-not applicable with no routable IPv6}"
|
||||
fi
|
||||
}
|
||||
|
||||
# has_pasta() - Check if the pasta(1) command is available
|
||||
function has_pasta() {
|
||||
command -v pasta >/dev/null
|
||||
}
|
||||
|
||||
# skip_if_no_pasta() - Skip current test if pasta(1) is not available
|
||||
# $1: Optional message to display
|
||||
function skip_if_no_pasta() {
|
||||
if ! has_pasta; then
|
||||
local msg=$(_add_label_if_missing "$1" "pasta")
|
||||
skip "${msg:-not applicable with no pasta binary}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
### procfs access ##############################################################
|
||||
|
||||
# ipv6_to_procfs() - RFC 5952 IPv6 address text representation to procfs format
|
||||
# $1: Address in any notation described by RFC 5952
|
||||
function ipv6_to_procfs() {
|
||||
local addr="${1}"
|
||||
|
||||
# Add leading zero if missing
|
||||
case ${addr} in
|
||||
"::"*) addr=0"${addr}" ;;
|
||||
esac
|
||||
|
||||
# Double colon can mean any number of all-zero fields. Expand to fill
|
||||
# as many colons as are missing. (This will not be a valid IPv6 form,
|
||||
# but we don't need it for long). E.g., 0::1 -> 0:::::::1
|
||||
case ${addr} in
|
||||
*"::"*)
|
||||
# All the colons in the address
|
||||
local colons
|
||||
colons=$(tr -dc : <<<$addr)
|
||||
# subtract those from a string of eight colons; this gives us
|
||||
# a string of two to six colons...
|
||||
local pad
|
||||
pad=$(sed -e "s/$colons//" <<<":::::::")
|
||||
# ...which we then inject in place of the double colon.
|
||||
addr=$(sed -e "s/::/::$pad/" <<<$addr)
|
||||
;;
|
||||
esac
|
||||
|
||||
# Print as a contiguous string of zero-filled 16-bit words
|
||||
# (The additional ":" below is needed because 'read -d x' actually
|
||||
# means "x is a TERMINATOR, not a delimiter")
|
||||
local group
|
||||
while read -d : group; do
|
||||
printf "%04X" "0x${group:-0}"
|
||||
done <<<"${addr}:"
|
||||
}
|
||||
|
||||
# __ipv4_to_procfs() - Print bytes in hexadecimal notation reversing arguments
|
||||
# $@: IPv4 address as separate bytes
|
||||
function __ipv4_to_procfs() {
|
||||
printf "%02X%02X%02X%02X" ${4} ${3} ${2} ${1}
|
||||
}
|
||||
|
||||
# ipv4_to_procfs() - IPv4 address representation to big-endian procfs format
|
||||
# $1: Text representation of IPv4 address
|
||||
function ipv4_to_procfs() {
|
||||
IFS='.' __ipv4_to_procfs ${1}
|
||||
}
|
||||
|
||||
|
||||
### Addresses, Routes, Links ###################################################
|
||||
|
||||
# ipv4_get_addr_global() - Print first global IPv4 address reported by netlink
|
||||
# $1: Optional output of 'ip -j -4 address show' from a different context
|
||||
function ipv4_get_addr_global() {
|
||||
local expr='[.[].addr_info[] | select(.scope=="global")] | .[0].local'
|
||||
echo "${1:-$(ip -j -4 address show)}" | jq -rM "${expr}"
|
||||
}
|
||||
|
||||
# ipv6_get_addr_global() - Print first global IPv6 address reported by netlink
|
||||
# $1: Optional output of 'ip -j -6 address show' from a different context
|
||||
function ipv6_get_addr_global() {
|
||||
local expr='[.[].addr_info[] | select(.scope=="global")] | .[0].local'
|
||||
echo "${1:-$(ip -j -6 address show)}" | jq -rM "${expr}"
|
||||
}
|
||||
|
||||
# random_rfc1918_subnet() - Pseudorandom unused subnet in 172.16/12 prefix
|
||||
#
|
||||
# Use the class B set, because much of our CI environment (Google, RH)
|
||||
# already uses up much of the class A, and it's really hard to test
|
||||
# if a block is in use.
|
||||
#
|
||||
# This returns THREE OCTETS! It is up to our caller to append .0/24, .255, &c.
|
||||
#
|
||||
function random_rfc1918_subnet() {
|
||||
local retries=1024
|
||||
|
||||
while [ "$retries" -gt 0 ];do
|
||||
local cidr=172.$(( 16 + $RANDOM % 16 )).$(( $RANDOM & 255 ))
|
||||
|
||||
in_use=$(ip route list | fgrep $cidr)
|
||||
if [ -z "$in_use" ]; then
|
||||
echo "$cidr"
|
||||
return
|
||||
fi
|
||||
|
||||
retries=$(( retries - 1 ))
|
||||
done
|
||||
|
||||
die "Could not find a random not-in-use rfc1918 subnet"
|
||||
}
|
||||
|
||||
# ipv4_get_route_default() - Print first default IPv4 route reported by netlink
|
||||
# $1: Optional output of 'ip -j -4 route show' from a different context
|
||||
function ipv4_get_route_default() {
|
||||
local jq_expr='[.[] | select(.dst == "default").gateway] | .[0]'
|
||||
echo "${1:-$(ip -j -4 route show)}" | jq -rM "${jq_expr}"
|
||||
}
|
||||
|
||||
# ipv6_get_route_default() - Print first default IPv6 route reported by netlink
|
||||
# $1: Optional output of 'ip -j -6 route show' from a different context
|
||||
function ipv6_get_route_default() {
|
||||
local jq_expr='[.[] | select(.dst == "default").gateway] | .[0]'
|
||||
echo "${1:-$(ip -j -6 route show)}" | jq -rM "${jq_expr}"
|
||||
}
|
||||
|
||||
# ether_get_mtu() - Get MTU of first Ethernet-like link
|
||||
# $1: Optional output of 'ip -j link show' from a different context
|
||||
function ether_get_mtu() {
|
||||
local jq_expr='[.[] | select(.link_type == "ether").mtu] | .[0]'
|
||||
echo "${1:-$(ip -j link show)}" | jq -rM "${jq_expr}"
|
||||
}
|
||||
|
||||
# ether_get_name() - Get name of first Ethernet-like interface
|
||||
# $1: Optional output of 'ip -j link show' from a different context
|
||||
function ether_get_name() {
|
||||
local jq_expr='[.[] | select(.link_type == "ether").ifname] | .[0]'
|
||||
echo "${1:-$(ip -j link show)}" | jq -rM "${jq_expr}"
|
||||
}
|
||||
|
||||
|
||||
### Ports and Ranges ###########################################################
|
||||
|
||||
# random_free_port() - Get unbound port with pseudorandom number
|
||||
# $1: Optional, dash-separated interval, [5000, 5999] by default
|
||||
# $2: Optional binding address, any IPv4 address by default
|
||||
# $3: Optional protocol, tcp or udp
|
||||
function random_free_port() {
|
||||
local range=${1:-5000-5999}
|
||||
local address=${2:-0.0.0.0}
|
||||
local protocol=${3:-tcp}
|
||||
|
||||
local port
|
||||
for port in $(shuf -i ${range}); do
|
||||
if port_is_free $port $address $protocol; then
|
||||
echo $port
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
die "Could not find open port in range $range"
|
||||
}
|
||||
|
||||
# random_free_port_range() - Get range of unbound ports with pseudorandom start
|
||||
# $1: Size of range (i.e. number of ports)
|
||||
# $2: Optional binding address, any IPv4 address by default
|
||||
# $3: Optional protocol, tcp or udp
|
||||
function random_free_port_range() {
|
||||
local size=${1?Usage: random_free_port_range SIZE [ADDRESS [tcp|udp]]}
|
||||
local address=${2:-0.0.0.0}
|
||||
local protocol=${3:-tcp}
|
||||
|
||||
local maxtries=10
|
||||
while [[ $maxtries -gt 0 ]]; do
|
||||
local firstport=$(random_free_port)
|
||||
local lastport=
|
||||
for i in $(seq 1 $((size - 1))); do
|
||||
lastport=$((firstport + i))
|
||||
if ! port_is_free $lastport $address $protocol; then
|
||||
echo "# port $lastport is in use; trying another." >&3
|
||||
lastport=
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ -n "$lastport" ]]; then
|
||||
echo "$firstport-$lastport"
|
||||
return
|
||||
fi
|
||||
|
||||
maxtries=$((maxtries - 1))
|
||||
done
|
||||
|
||||
die "Could not find free port range with size $size"
|
||||
}
|
||||
|
||||
# port_is_bound() - Check if TCP or UDP port is bound for a given address
|
||||
# $1: Port number
|
||||
# $2: Optional protocol, or optional IPv4 or IPv6 address, default: tcp
|
||||
# $3: Optional IPv4 or IPv6 address, or optional protocol, default: any
|
||||
function port_is_bound() {
|
||||
local port=${1?Usage: port_is_bound PORT [tcp|udp] [ADDRESS]}
|
||||
|
||||
if [ "${2}" = "tcp" ] || [ "${2}" = "udp" ]; then
|
||||
local address="${3}"
|
||||
local proto="${2}"
|
||||
elif [ "${3}" = "tcp" ] || [ "${3}" = "udp" ]; then
|
||||
local address="${2}"
|
||||
local proto="${3}"
|
||||
else
|
||||
local address="${2}" # Might be empty
|
||||
local proto="tcp"
|
||||
fi
|
||||
|
||||
port=$(printf %04X ${port})
|
||||
case "${address}" in
|
||||
*":"*)
|
||||
grep -e "^[^:]*: $(ipv6_to_procfs "${address}"):${port} .*" \
|
||||
-e "^[^:]*: $(ipv6_to_procfs "::0"):${port} .*" \
|
||||
-q "/proc/net/${proto}6"
|
||||
;;
|
||||
*"."*)
|
||||
grep -e "^[^:]*: $(ipv4_to_procfs "${address}"):${port}" \
|
||||
-e "^[^:]*: $(ipv4_to_procfs "0.0.0.0"):${port}" \
|
||||
-q "/proc/net/${proto}"
|
||||
;;
|
||||
*)
|
||||
# No address: check both IPv4 and IPv6, for any bound address
|
||||
grep "^[^:]*: [^:]*:${port} .*" -q "/proc/net/${proto}6" || \
|
||||
grep "^[^:]*: [^:]*:${port} .*" -q "/proc/net/${proto}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# port_is_free() - Check if TCP or UDP port is free to bind for a given address
|
||||
# $1: Port number
|
||||
# $2: Optional protocol, or optional IPv4 or IPv6 address, default: tcp
|
||||
# $3: Optional IPv4 or IPv6 address, or optional protocol, default: any
|
||||
function port_is_free() {
|
||||
! port_is_bound ${@}
|
||||
}
|
||||
|
||||
# wait_for_port() - Return once port is available on the host
|
||||
# $1: Host or address to check for possible binding
|
||||
# $2: Port number
|
||||
# $3: Optional timeout, 5 seconds if not given
|
||||
function wait_for_port() {
|
||||
local host=$1
|
||||
local port=$2
|
||||
local _timeout=${3:-5}
|
||||
|
||||
# Wait
|
||||
while [ $_timeout -gt 0 ]; do
|
||||
port_is_free ${port} "${host}" && return
|
||||
sleep 1
|
||||
_timeout=$(( $_timeout - 1 ))
|
||||
done
|
||||
|
||||
die "Timed out waiting for $host:$port"
|
||||
}
|
||||
|
||||
# tcp_port_probe() - Check if a TCP port has an active listener
|
||||
# $1: Port number
|
||||
# $2: Optional address, 0.0.0.0 by default
|
||||
function tcp_port_probe() {
|
||||
local address="${2:-0.0.0.0}"
|
||||
|
||||
: | nc "${address}" "${1}"
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
#
|
||||
|
||||
source "$(dirname $0)"/helpers.bash
|
||||
source "$(dirname $0)"/helpers.network.bash
|
||||
|
||||
die() {
|
||||
echo "$(basename $0): $*" >&2
|
||||
|
@ -224,5 +225,22 @@ check_result "$found" "16700" "random_free_port"
|
|||
|
||||
# END random_free_port
|
||||
###############################################################################
|
||||
# BEGIN ipv6_to_procfs
|
||||
|
||||
# Table of IPv6 short forms and their procfs equivalents. For readability,
|
||||
# spaces separate each 16-bit word. Spaces are removed when testing.
|
||||
table="
|
||||
2b06::1 | 2B06 0000 0000 0000 0000 0000 0000 0001
|
||||
::1 | 0000 0000 0000 0000 0000 0000 0000 0001
|
||||
0::1 | 0000 0000 0000 0000 0000 0000 0000 0001
|
||||
"
|
||||
|
||||
while read shortform expect; do
|
||||
actual=$(ipv6_to_procfs $shortform)
|
||||
check_result "$actual" "${expect// }" "ipv6_to_procfs $shortform"
|
||||
done < <(parse_table "$table")
|
||||
|
||||
# END ipv6_to_procfs
|
||||
###############################################################################
|
||||
|
||||
exit $rc
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# -*- bash -*-
|
||||
|
||||
load "../system/helpers"
|
||||
load "../system/helpers.network" # For random_free_port()
|
||||
|
||||
setup() {
|
||||
:
|
||||
|
|
Loading…
Reference in New Issue