diff --git a/pkg/test/dockerutil/container.go b/pkg/test/dockerutil/container.go index ae5b03941..d32c9d305 100644 --- a/pkg/test/dockerutil/container.go +++ b/pkg/test/dockerutil/container.go @@ -105,6 +105,9 @@ type RunOpts struct { // Links is the list of containers to be connected to the container. Links []string + + // Sysctls is the list of Namespaced sysctls used for the container. + Sysctls map[string]string } func makeContainer(ctx context.Context, logger testutil.Logger, runtime string) *Container { @@ -275,6 +278,7 @@ func (c *Container) hostConfig(r RunOpts) *container.HostConfig { Privileged: r.Privileged, ReadonlyRootfs: r.ReadOnly, NetworkMode: container.NetworkMode(r.NetworkMode), + Sysctls: r.Sysctls, Resources: container.Resources{ Memory: int64(r.Memory), // In bytes. CpusetCpus: r.CpusetCpus, diff --git a/runsc/boot/loader.go b/runsc/boot/loader.go index f78dfcb7b..90d2d3582 100644 --- a/runsc/boot/loader.go +++ b/runsc/boot/loader.go @@ -647,6 +647,11 @@ func (l *Loader) run() error { // Delay host network configuration to this point because network namespace // is configured after the loader is created and before Run() is called. log.Debugf("Configuring host network") + + // Apply any net-related sysctls to the host kernel in our + // network namespace now. + specutils.ApplySysctls(l.root.spec, "net.") + s := l.k.RootNetworkNamespace().Stack().(*hostinet.Stack) if err := s.Configure(l.root.conf.EnableRaw); err != nil { return err diff --git a/runsc/specutils/specutils.go b/runsc/specutils/specutils.go index 703c27385..9f2f3c957 100644 --- a/runsc/specutils/specutils.go +++ b/runsc/specutils/specutils.go @@ -698,3 +698,24 @@ func ResolveEnvs(envs ...[]string) ([]string, error) { func FaqErrorMsg(anchor, msg string) string { return fmt.Sprintf("%s; see https://gvisor.dev/faq#%s for more details", msg, anchor) } + +// ApplySysctls applies the sysctls from the spec with the given prefix to the +// host kernel. Falures are not fatal, but a warning is logged. +func ApplySysctls(spec *specs.Spec, prefix string) { + if spec.Linux == nil { + return + } + for k, v := range spec.Linux.Sysctl { + if !strings.HasPrefix(k, prefix) { + continue + } + // Turn the sysctl name into corresponding proc file. We will + // write to the proc file directly, rather than rely on the + // sysctl binary which might not exist in our filesystem + pFile := path.Join("/proc/sys", strings.ReplaceAll(k, ".", "/")) + log.Infof("Applying sysctl name=%q value=%q", k, v) + if err := os.WriteFile(pFile, []byte(v), 0644); err != nil { + log.Warningf("Failed to set sysctl name=%q (%s) value=%q: %v", k, pFile, v, err) + } + } +} diff --git a/test/e2e/integration_test.go b/test/e2e/integration_test.go index 60b94bbf0..4e89005cc 100644 --- a/test/e2e/integration_test.go +++ b/test/e2e/integration_test.go @@ -510,18 +510,27 @@ func TestPing4Loopback(t *testing.T) { // This test ensures we can enable ipv6 on loopback and run ping6 without // errors. func TestPing6Loopback(t *testing.T) { - if testutil.IsRunningWithHostNet() { - // TODO(gvisor.dev/issue/5011): support ICMP sockets in hostnet and enable - // this test. - t.Skip("hostnet only supports TCP/UDP sockets, so ping6 is not supported.") - } + // We must set sysctl net.ipv6.conf.all.disable_ipv6=0 to enable ipv6 + // inside Docker. Sometimes that's still not enough to get the loopback + // device, so the test tries to add it with 'ip' tool, requiring + // CAP_NET_ADMIN. + ctx := context.Background() + d := dockerutil.MakeContainer(ctx, t) + defer d.CleanUp(ctx) - // The CAP_NET_ADMIN capability is required to use the `ip` utility, which - // we use to enable ipv6 on loopback. - // - // By default, ipv6 loopback is not enabled by runsc, because docker does - // not assign an ipv6 address to the test container. - runIntegrationTest(t, []string{"NET_ADMIN"}, "./ping6.sh") + opts := dockerutil.RunOpts{ + Image: "basic/integrationtest", + WorkDir: "/root", + CapAdd: []string{"NET_ADMIN"}, + Sysctls: map[string]string{ + "net.ipv6.conf.all.disable_ipv6": "0", + }, + } + if got, err := d.Run(ctx, opts, "./ping6.sh"); err != nil { + t.Fatalf("docker run failed: %v", err) + } else if got != "" { + t.Errorf("test failed:\n%s", got) + } } // This test checks that the owner of the sticky directory can delete files