blob: 91ad3888f9393a8d8b5bddc3408d67b628bdbfe6 [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package arc
import (
"context"
"os"
"os/exec"
"time"
"chromiumos/tast/errors"
"chromiumos/tast/local/arc"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/testexec"
"chromiumos/tast/local/upstart"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: MultiNetworking,
Desc: "Verifies guest network setup upon physical interface change",
Contacts: []string{"taoyl@google.com", "cros-networking@google.com"},
Attr: []string{"group:mainline", "informational"},
SoftwareDeps: []string{"android_p", "chrome"},
// TODO(yusukes): Change the timeout back to 4 min when we revert arc.go's BootTimeout to 120s.
Timeout: 5 * time.Minute,
})
}
func MultiNetworking(ctx context.Context, s *testing.State) {
const (
testNetnsName = "test"
ifName = "eth99"
peerIFName = "peer99"
brIFName = "arc_eth99"
vethIFName = "veth_eth99"
networkInitializationPollTimeout = 10 * time.Second // The time to wait for patchpaneld to set up virtual network after physical network changes.
configurationPollTimeout = 1 * time.Second // The time to wait for remaining configurations after detecting virtual network
)
startARC := func() {
cr, err := chrome.New(ctx, chrome.ARCEnabled())
if err != nil {
s.Fatal("Failed to connect to Chrome: ", err)
}
defer cr.Close(ctx)
a, err := arc.New(ctx, s.OutDir())
if err != nil {
s.Fatal("Failed to start ARC: ", err)
}
defer a.Close()
}
startARC()
s.Log("Testing multinet behavior on device addition")
// Create a virtual interface and verify that corresponding data path is set up.
// Use a ethernet name template so patchpaneld treats it as an ethernet interface.
if err := testexec.CommandContext(ctx, "/bin/ip", "netns", "add", testNetnsName).Run(testexec.DumpLogOnError); err != nil {
// Ignore failure here for potential netns already exists case. If it's a legitimate failure it will fail at next step.
s.Logf("Failed to create test netns %s: %s", testNetnsName, err)
}
defer testexec.CommandContext(ctx, "/bin/ip", "netns", "delete", testNetnsName).Run()
if err := testexec.CommandContext(ctx, "/bin/ip", "link", "add", ifName, "type", "veth", "peer", "name", peerIFName, "netns", testNetnsName).Run(testexec.DumpLogOnError); err != nil {
s.Fatalf("Failed to create test interface %s: %s", ifName, err)
}
defer testexec.CommandContext(ctx, "/bin/ip", "link", "delete", ifName).Run()
verifyDeviceAdded := func() {
// Verify bridge and veth created correctly and veth moved to ARC netns.
testing.Poll(ctx, func(ctx context.Context) error {
if _, err := os.Stat("/sys/class/net/" + brIFName); os.IsNotExist(err) {
return errors.Wrapf(err, "bridge %s was not created", brIFName)
}
return nil
}, &testing.PollOptions{Timeout: networkInitializationPollTimeout})
testing.Poll(ctx, func(ctx context.Context) error {
if _, err := os.Stat("/sys/class/net/" + vethIFName); os.IsNotExist(err) {
return errors.Wrapf(err, "veth interface %s was not created", vethIFName)
}
return nil
}, &testing.PollOptions{Timeout: configurationPollTimeout})
testing.Poll(ctx, func(ctx context.Context) error {
if err := arc.BootstrapCommand(ctx, "/system/bin/ip", "link", "show", ifName).Run(); err != nil {
return errors.Wrapf(err, "failed verifying interface %s in ARC (%s)", ifName, err)
}
return nil
}, &testing.PollOptions{Timeout: configurationPollTimeout})
// Verify forwarding rule set up correctly.
if err := testexec.CommandContext(ctx, "/sbin/iptables", "-C", "FORWARD", "-i", ifName, "-o", brIFName, "-j", "ACCEPT", "-w").
Run(testexec.DumpLogOnError); err != nil {
s.Fatalf("Cannot verify iptables -A FORWARD -i %s -o %s -j ACCEPT -w rule: %s", ifName, brIFName, err)
}
if err := testexec.CommandContext(ctx, "/sbin/ip6tables", "-C", "FORWARD", "-i", ifName, "-o", brIFName, "-j", "ACCEPT", "-w").
Run(testexec.DumpLogOnError); err != nil {
s.Fatalf("Cannot verify ip6tables -A FORWARD -i %s -o %s -j ACCEPT -w rule: %s", ifName, brIFName, err)
}
if err := testexec.CommandContext(ctx, "/sbin/ip6tables", "-C", "FORWARD", "-i", brIFName, "-o", ifName, "-j", "ACCEPT", "-w").
Run(testexec.DumpLogOnError); err != nil {
s.Fatalf("Cannot verify ip6tables -A FORWARD -i %s -o %s -j ACCEPT -w rule: %s", brIFName, ifName, err)
}
}
verifyDeviceAdded()
s.Log("Testing multinet behavior on ARC restart")
// Log out to ensure the container is down.
upstart.RestartJob(ctx, "ui")
if err := upstart.WaitForJobStatus(ctx, "patchpanel", upstart.StartGoal, upstart.RunningState, upstart.RejectWrongGoal, 30*time.Second); err != nil {
s.Fatal("patchpanel job failed to start: ", err)
}
// Restart ARC.
startARC()
verifyDeviceAdded()
s.Log("Testing multinet behavior on device deletion")
// Remove test device
if err := testexec.CommandContext(ctx, "/bin/ip", "link", "delete", ifName).Run(testexec.DumpLogOnError); err != nil {
s.Fatalf("Failed to delete test interface %s: %s", ifName, err)
}
// Verify bridge and veth removed correctly.
testing.Poll(ctx, func(ctx context.Context) error {
if _, err := os.Stat("/sys/class/net/" + brIFName); err == nil {
return errors.Errorf("bridge %s was not removed", brIFName)
}
return nil
}, &testing.PollOptions{Timeout: networkInitializationPollTimeout})
testing.Poll(ctx, func(ctx context.Context) error {
if _, err := os.Stat("/sys/class/net/" + vethIFName); err == nil {
return errors.Errorf("veth interface %s was not removed", vethIFName)
}
return nil
}, &testing.PollOptions{Timeout: configurationPollTimeout})
// Verify iptables forwarding rule removed correctly.
if err := testexec.CommandContext(ctx, "/sbin/iptables", "-C", "FORWARD", "-i", ifName, "-o", brIFName, "-j", "ACCEPT", "-w").
Run(testexec.DumpLogOnError); err == nil {
s.Errorf("iptables -A FORWARD -i %s -o %s -j ACCEPT -w rule is not removed", ifName, brIFName)
} else if _, ok := err.(*exec.ExitError); !ok {
s.Error("iptables -C returned unexpected error: ", err)
}
if err := testexec.CommandContext(ctx, "/sbin/ip6tables", "-C", "FORWARD", "-i", ifName, "-o", brIFName, "-j", "ACCEPT", "-w").
Run(testexec.DumpLogOnError); err == nil {
s.Errorf("ip6tables -A FORWARD -i %s -o %s -j ACCEPT -w rule is not removed", ifName, brIFName)
} else if _, ok := err.(*exec.ExitError); !ok {
s.Error("ip6tables -C returned unexpected error: ", err)
}
if err := testexec.CommandContext(ctx, "/sbin/ip6tables", "-C", "FORWARD", "-i", brIFName, "-o", ifName, "-j", "ACCEPT", "-w").
Run(testexec.DumpLogOnError); err == nil {
s.Errorf("ip6tables -A FORWARD -i %s -o %s -j ACCEPT -w rule is not removed", brIFName, ifName)
} else if _, ok := err.(*exec.ExitError); !ok {
s.Error("ip6tables -C returned unexpected error: ", err)
}
}