-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat network steps #173
base: main
Are you sure you want to change the base?
Feat network steps #173
Changes from 13 commits
ed486dd
2ba72d0
a243a74
eac15c6
eccbd3a
f39d33a
434e077
011b09a
f88cece
7ccc866
fbd002d
b539e9e
5b319ff
12ea59f
47ddba6
43a1dfc
bdb94d6
bc552b6
ba75c37
74f7203
b00884a
b5155b3
7159dcc
52a765c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package com.aws.greengrass.testing.features; | ||
|
||
import com.aws.greengrass.testing.api.device.Device; | ||
import com.aws.greengrass.testing.api.model.PillboxContext; | ||
import com.aws.greengrass.testing.platform.Platform; | ||
import com.aws.greengrass.testing.platform.PlatformResolver; | ||
import io.cucumber.guice.ScenarioScoped; | ||
import io.cucumber.java.en.Given; | ||
import io.cucumber.java.en.When; | ||
|
||
import java.io.IOException; | ||
import javax.inject.Inject; | ||
|
||
/** | ||
* Checks the connectivity for platfroms. | ||
* | ||
* @throws IOException {throws IOException} | ||
* @throws InterruptedException {throws IInterruptedException} | ||
*/ | ||
@ScenarioScoped | ||
public class CommanConnectivitySteps { | ||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private final Platform platform; | ||
|
||
@Inject | ||
@SuppressWarnings("MissingJavadocMethod") | ||
public CommanConnectivitySteps(Platform platform) { | ||
this.platform = platform; | ||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
/** | ||
* Checks the connectivity for platfroms. | ||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* @param connectivity checks platfrom connectivity | ||
* @throws IOException {throws IOException} | ||
* @throws InterruptedException {throws IInterruptedException} | ||
*/ | ||
@Given("device network connectivity is {word}") | ||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@When("I set device network connectivity to {word}") | ||
public void setDeviceNetwork(final String connectivity) throws IOException, InterruptedException { | ||
if ("offline".equalsIgnoreCase(connectivity)) { | ||
platform.getNetworkUtils().disconnectNetwork(); | ||
} else { | ||
platform.getNetworkUtils().recoverNetwork(); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package com.aws.greengrass.testing.features; | ||
|
||
import com.aws.greengrass.testing.platform.Platform; | ||
import io.cucumber.guice.ScenarioScoped; | ||
import io.cucumber.java.After; | ||
import io.cucumber.java.en.Given; | ||
import io.cucumber.java.en.When; | ||
|
||
import java.io.IOException; | ||
import javax.inject.Inject; | ||
|
||
/** | ||
* Checks the connectivity for platfroms. | ||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* @throws IOException {throws IOException} | ||
* @throws InterruptedException {throws IInterruptedException} | ||
*/ | ||
@ScenarioScoped | ||
public class CommonConnectivitySteps { | ||
private final Platform platform; | ||
|
||
@Inject | ||
@SuppressWarnings("MissingJavadocMethod") | ||
public CommonConnectivitySteps(Platform platform) { | ||
this.platform = platform; | ||
} | ||
|
||
/** | ||
* Checks the connectivity for platfroms. | ||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* | ||
* @param connectivity checks platfrom connectivity | ||
* @throws IOException {throws IOException} | ||
* @throws InterruptedException {throws IInterruptedException} | ||
*/ | ||
@Given("device network connectivity is {word}") | ||
@When("I set device network connectivity to {word}") | ||
public void setDeviceNetwork(final String connectivity) throws IOException, InterruptedException { | ||
if ("offline".equalsIgnoreCase(connectivity)) { | ||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
platform.getNetworkUtils().disconnectNetwork(); | ||
} else { | ||
platform.getNetworkUtils().recoverNetwork(); | ||
} | ||
} | ||
|
||
@After | ||
public void afterEachScenario()throws IOException, InterruptedException { | ||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
platform.getNetworkUtils().recoverNetwork(); | ||
} | ||
|
||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package com.aws.greengrass.testing.platform; | ||
|
||
import java.io.IOException; | ||
import java.io.UnsupportedEncodingException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public abstract class NetworkUtils { | ||
protected static final String[] MQTT_PORTS = {"8883", "443"}; | ||
// 8888 and 8889 are used by the squid proxy which runs on a remote DUT | ||
// and need to disable access to test offline proxy scenarios | ||
protected static final String[] NETWORK_PORTS = {"443", "8888", "8889"}; | ||
protected static final int[] GG_UPSTREAM_PORTS = {8883, 8443, 443}; | ||
protected static final int SSH_PORT = 22; | ||
protected final List<Integer> blockedPorts = new ArrayList<>(); | ||
|
||
public abstract void disconnectNetwork() throws InterruptedException, IOException; | ||
|
||
public abstract void recoverNetwork() throws InterruptedException, IOException; | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package com.aws.greengrass.testing.platform.linux; | ||
|
||
import com.aws.greengrass.testing.platform.NetworkUtils; | ||
import software.amazon.awssdk.utils.IoUtils; | ||
|
||
import java.io.IOException; | ||
import java.net.NetworkInterface; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.Collections; | ||
import java.util.Enumeration; | ||
import java.util.List; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
|
||
public class NetworkUtilsLinux extends NetworkUtils { | ||
private static final String ENABLE_OPTION = "--insert"; | ||
private static final String DISABLE_OPTION = "--delete"; | ||
private static final String APPEND_OPTION = "-A"; | ||
private static final String IPTABLE_COMMAND_BLOCK_INGRESS_STR = | ||
"sudo iptables %s INPUT -p tcp --sport %s -j REJECT"; | ||
private static final String IPTABLE_COMMAND_STR = "sudo iptables %s OUTPUT -p tcp --dport %s -j REJECT && " | ||
+ "sudo iptables %s INPUT -p tcp --sport %s -j REJECT"; | ||
private static final String IPTABLES_DROP_DPORT_EXTERNAL_ONLY_COMMAND_STR = | ||
"sudo iptables %s INPUT -p tcp -s localhost --dport %s -j ACCEPT && " | ||
+ | ||
"sudo iptables %s INPUT -p tcp --dport %s -j DROP && " | ||
+ | ||
"sudo iptables %s OUTPUT -p tcp -d localhost --dport %s -j ACCEPT && " | ||
+ | ||
"sudo iptables %s OUTPUT -p tcp --dport %s -j DROP"; | ||
private static final String IPTABLE_SAFELIST_COMMAND_STR | ||
= "sudo iptables %s OUTPUT -p tcp -d %s --dport %d -j ACCEPT && " | ||
+ | ||
"sudo iptables %s INPUT -p tcp -s %s --sport %d -j ACCEPT"; | ||
private static final String GET_IPTABLES_RULES = "sudo iptables -S"; | ||
|
||
// The string we are looking for to verify that there is an iptables rule to reject a port | ||
// We only need to look for sport because sport only gets created if dport is successful | ||
private static final String IPTABLES_RULE = "-m tcp --sport %s -j REJECT"; | ||
|
||
private static final AtomicBoolean bandwidthSetup = new AtomicBoolean(false); | ||
|
||
|
||
private void modifyMqttConnection(String action) throws IOException, InterruptedException { | ||
for (String port : MQTT_PORTS) { | ||
new ProcessBuilder().command( | ||
"sh", "-c", String.format(IPTABLES_DROP_DPORT_EXTERNAL_ONLY_COMMAND_STR, | ||
action, port, action, port, action, port, action, port) | ||
).start().waitFor(2, TimeUnit.SECONDS); | ||
} | ||
} | ||
|
||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private void filterPortOnInterface(String iface, int port) throws IOException, InterruptedException { | ||
// Filtering SSH traffic impacts test execution, so we explicitly disallow it | ||
if (port == SSH_PORT) { | ||
return; | ||
} | ||
List<String> filterSourcePortCommand = Stream.of("sudo", "tc", "filter", "add", "dev", | ||
iface, "parent", "1:", "protocol", "ip", "prio", "1", "u32", "match", | ||
"ip", "sport", Integer.toString(port), "0xffff", "flowid", "1:2").collect(Collectors.toList()); | ||
executeCommand(filterSourcePortCommand); | ||
|
||
List<String> filterDestPortCommand = Stream.of("sudo", "tc", "filter", "add", "dev", iface, | ||
"parent", "1:", "protocol", "ip", "prio", "1", "u32", "match", | ||
"ip", "dport", Integer.toString(port), "0xffff", "flowid", "1:2").collect(Collectors.toList()); | ||
executeCommand(filterDestPortCommand); | ||
} | ||
|
||
private void deleteRootNetemQdiscOnInterface() throws InterruptedException, IOException { | ||
Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces(); | ||
for (NetworkInterface netint : Collections.list(nets)) { | ||
if (netint.isPointToPoint() || netint.isLoopback()) { | ||
continue; | ||
} | ||
executeCommand(Stream.of("sudo", "tc", "qdisc", "del", "dev", netint.getName(), "root") | ||
.collect(Collectors.toList())); | ||
} | ||
} | ||
|
||
private void createRootNetemQdiscOnInterface(String iface, int netemRateKbps) | ||
throws InterruptedException, IOException { | ||
// TODO: Add support for setting packet loss and delay | ||
int netemDelayMs = 750; | ||
List<String> addQdiscCommand = Stream.of("sudo", "tc", "qdisc", "add", "dev", iface, "root", "handle", | ||
"1:", "prio", "bands", "2", "priomap", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", | ||
"0", "0", "0", "0", "0").collect(Collectors.toList()); | ||
executeCommand(addQdiscCommand); | ||
|
||
List<String> netemCommand = | ||
Stream.of("sudo", "tc", "qdisc", "add", "dev", iface, "parent", "1:2", "netem", "delay", | ||
String.format("%dms", netemDelayMs), "rate", String.format("%dkbit", netemRateKbps)) | ||
.collect(Collectors.toList()); | ||
executeCommand(netemCommand); | ||
} | ||
|
||
private String executeCommand(List<String> command) throws IOException, InterruptedException { | ||
Process proc = new ProcessBuilder().command(command).start(); | ||
proc.waitFor(2, TimeUnit.SECONDS); | ||
if (proc.exitValue() != 0) { | ||
throw new IOException("CLI command " + command + " failed with error " | ||
+ new String(IoUtils.toByteArray(proc.getErrorStream()), StandardCharsets.UTF_8)); | ||
} | ||
return new String(IoUtils.toByteArray(proc.getInputStream()), StandardCharsets.UTF_8); | ||
} | ||
|
||
@Override | ||
public void disconnectNetwork() throws InterruptedException, IOException { | ||
interfacepolicy(IPTABLE_COMMAND_STR, ENABLE_OPTION, "connection-loss", NETWORK_PORTS); | ||
} | ||
|
||
@Override | ||
public void recoverNetwork() throws InterruptedException, IOException { | ||
interfacepolicy(IPTABLE_COMMAND_STR, DISABLE_OPTION, "connection-recover", NETWORK_PORTS); | ||
if (bandwidthSetup.get()) { | ||
Nelsonochoam marked this conversation as resolved.
Show resolved
Hide resolved
|
||
deleteRootNetemQdiscOnInterface(); | ||
bandwidthSetup.set(false); | ||
} | ||
} | ||
|
||
private void interfacepolicy(String iptableCommandString, String option, String eventName, String... ports) | ||
throws InterruptedException, | ||
jaiszeen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
IOException { | ||
for (String port : ports) { | ||
new ProcessBuilder().command("sh", "-c", String.format(iptableCommandString, option, port, option, port)) | ||
.start().waitFor(2, TimeUnit.SECONDS); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -124,7 +124,7 @@ | |
<dependency> | ||
<groupId>com.google.guava</groupId> | ||
<artifactId>guava</artifactId> | ||
<version>31.1-jre</version> | ||
<version>23.0</version> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this updated? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mmm I don't know why this shows up as changed but we have to make sure this has the previous value. We don't want to update this dependencies if it is not needed. |
||
</dependency> | ||
<dependency> | ||
<groupId>org.immutables</groupId> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this dependency removed?