Skip to content
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

Fix ping/trace in local AS #16

Merged
merged 2 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
[#15](https://github.com/tzaeschke/phtree-cpp/pull/15)

### Fixed
- Fixed: SCMP problem when pinging local AS.
[#16](https://github.com/tzaeschke/phtree-cpp/pull/16),
- Fixed SCMP timeout and error handling (IOExceptions + SCMP errors).
[#13](https://github.com/tzaeschke/phtree-cpp/pull/13)
- CI (only) failures on JDK 8. [#10](https://github.com/tzaeschke/phtree-cpp/pull/10)
Expand Down
1 change: 1 addition & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
- BUG: Ping to iaOVGU causes 3 CS requests that have type UP,CORE,CORE....?
- FIX: Ask why requesting an UP segment effectively returns a DOWN segment
(it needs to be reversed + the SegID needs to be XORed)
- Why are Java pings 8 bytes shorter than scionproto pings? -> local AS
- Docs:
Add docs for setting up an environment
https://github.com/marcfrei/scion-time#setting-up-a-scion-test-environment
Expand Down
13 changes: 9 additions & 4 deletions src/main/java/org/scion/RequestPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ private Daemon.Path protoPath() {

@Override
public InetSocketAddress getFirstHopAddress() throws UnknownHostException {
if (getRawPath() == null || getRawPath().length == 0) {
// local AS
InetAddress addr = InetAddress.getByAddress(getDestinationAddress());
return new InetSocketAddress(addr, getDestinationPort());
}
return getFirstHopAddress(pathProtoc);
}

Expand Down Expand Up @@ -175,13 +180,13 @@ public EpicAuths getEpicAuths() {

public enum LinkType {
/** Unspecified link type. */
LINK_TYPE_UNSPECIFIED, // = 0;
LINK_TYPE_UNSPECIFIED, // = 0
/** Direct physical connection. */
LINK_TYPE_DIRECT, // = 1;
LINK_TYPE_DIRECT, // = 1
/** Connection with local routing/switching. */
LINK_TYPE_MULTI_HOP, // = 2;
LINK_TYPE_MULTI_HOP, // = 2
/** Connection overlayed over publicly routed Internet. */
LINK_TYPE_OPEN_NET, // = 3;
LINK_TYPE_OPEN_NET, // = 3
}

public static class EpicAuths {
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/org/scion/ScmpChannel.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ public void setTimeOut(int milliSeconds) {
this.timeOutMs = milliSeconds;
}

public int getTimeOut() {
return this.timeOutMs;
}

@Override
public void close() throws IOException {
channel.close();
Expand Down
5 changes: 4 additions & 1 deletion src/main/java/org/scion/internal/PathHeaderParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import java.util.ArrayList;

public class PathHeaderParser {
public PathHeaderParser() {}
private PathHeaderParser() {}

private static int getSegmentCount(byte[] raw) {
ByteBuffer data = ByteBuffer.wrap(raw);
Expand Down Expand Up @@ -51,6 +51,9 @@ public Node(int id, boolean constDirFlag, int posHopFlags, int hopFlags) {

public static ArrayList<Node> getTraceNodes(byte[] raw) {
ArrayList<Node> nodes = new ArrayList<>();
if (raw == null || raw.length == 0) {
return nodes;
}

ByteBuffer data = ByteBuffer.wrap(raw);
int i0 = data.getInt();
Expand Down
20 changes: 15 additions & 5 deletions src/main/java/org/scion/internal/ScionHeaderParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,11 @@ public static ResponsePath extractResponsePath(

// raw path
byte[] path = new byte[hdrLenBytes - data.position()];
data.get(path);
reversePathInPlace(ByteBuffer.wrap(path));
if (path.length > 0) {
// raw path may be empty for local AS
data.get(path);
reversePathInPlace(ByteBuffer.wrap(path));
}

// get remote port from UDP overlay
data.position(hdrLenBytes);
Expand Down Expand Up @@ -200,8 +203,9 @@ public static String validate(ByteBuffer data) {
+ (hdrLenBytes + payLoadLen);
}
int pathType = readInt(i2, 0, 8);
if (pathType != 1) {
return PRE + "Invalid path type: expected 1, got " + pathType;
if (pathType != 1 && pathType != 0) {
// Validation against path length happens further down.
return PRE + "Invalid path type: expected 0 or 1, got " + pathType;
}
int dt = readInt(i2, 8, 2);
int dl = readInt(i2, 10, 2);
Expand Down Expand Up @@ -258,6 +262,12 @@ public static String validate(ByteBuffer data) {
// raw path
byte[] path = new byte[start + hdrLenBytes - data.position()];
data.get(path);
if (path.length == 0 && pathType != 0) {
return PRE + "Path is empty but path type is: " + pathType;
}
if (path.length > 0 && pathType != 1) {
return PRE + "Path is not empty but path type is: " + pathType;
}
// TODO validate path

if (nextHeader == InternalConstants.HdrTypes.UDP.code()) {
Expand Down Expand Up @@ -304,7 +314,7 @@ public static void write(
i1 = writeInt(i1, 8, 8, newHdrLen); // HdrLen = bytes/4
i1 = writeInt(i1, 16, 16, userPacketLength); // PayloadLen (+ overlay!)
data.putInt(i1);
i2 = writeInt(i2, 0, 8, 1); // PathType : SCION = 1
i2 = writeInt(i2, 0, 8, pathHeaderLength > 0 ? 1 : 0); // PathType : SCION = 1
i2 = writeInt(i2, 8, 2, 0); // DT
i2 = writeInt(i2, 10, 2, dl); // DL
i2 = writeInt(i2, 12, 2, 0); // ST
Expand Down
31 changes: 18 additions & 13 deletions src/test/java/org/scion/api/SCMPTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.scion.*;
import org.scion.demo.inspector.ScionPacketInspector;
Expand Down Expand Up @@ -100,7 +99,6 @@ void echo() throws IOException {
}

@Test
@Disabled
void echo_localAS() throws IOException {
testEcho(this::getPathToLocalAS);
}
Expand Down Expand Up @@ -129,11 +127,11 @@ void echo_timeout() throws IOException {
try (ScmpChannel channel = Scmp.createChannel(getPathTo112())) {
channel.setScmpErrorListener(scmpMessage -> fail(scmpMessage.getTypeCode().getText()));
channel.setOption(ScionSocketOptions.SN_API_THROW_PARSER_FAILURE, true);
channel.setTimeOut(1_000);
channel.setTimeOut(100);
MockNetwork.dropNextPackets(1);
Scmp.EchoMessage result1 = channel.sendEchoRequest(42, ByteBuffer.allocate(0));
assertTrue(result1.isTimedOut());
assertEquals(1_000 * 1_000_000, result1.getNanoSeconds());
assertEquals(100 * 1_000_000, result1.getNanoSeconds());
assertEquals(42, result1.getSequenceNumber());

// try again
Expand Down Expand Up @@ -183,22 +181,20 @@ void echo_SCMP_error() throws IOException {

@Test
void traceroute() throws IOException {
testTraceroute(this::getPathTo112);
testTraceroute(this::getPathTo112, 2);
}

@Test
@Disabled
void traceroute_localAS() throws IOException {
testTraceroute(this::getPathToLocalAS);
testTraceroute(this::getPathToLocalAS, 0);
}

private void testTraceroute(Supplier<RequestPath> path) throws IOException {
private void testTraceroute(Supplier<RequestPath> path, int nHops) throws IOException {
MockNetwork.startTiny();
try (ScmpChannel channel = Scmp.createChannel(path.get())) {
channel.setScmpErrorListener(scmpMessage -> fail(scmpMessage.getTypeCode().getText()));
channel.setOption(ScionSocketOptions.SN_API_THROW_PARSER_FAILURE, true);
Collection<Scmp.TracerouteMessage> results = channel.sendTracerouteRequest();
channel.setTimeOut(Integer.MAX_VALUE); // TODO ??

int n = 0;
for (Scmp.TracerouteMessage result : results) {
Expand All @@ -217,7 +213,7 @@ private void testTraceroute(Supplier<RequestPath> path) throws IOException {
}
}

assertEquals(2, results.size());
assertEquals(nHops, results.size());
} finally {
MockNetwork.stopTiny();
}
Expand All @@ -229,13 +225,16 @@ void traceroute_timeout() throws IOException {
try (ScmpChannel channel = Scmp.createChannel(getPathTo112())) {
channel.setScmpErrorListener(scmpMessage -> fail(scmpMessage.getTypeCode().getText()));
channel.setOption(ScionSocketOptions.SN_API_THROW_PARSER_FAILURE, true);
assertEquals(1000, channel.getTimeOut());
channel.setTimeOut(100);
assertEquals(100, channel.getTimeOut());
MockNetwork.dropNextPackets(1);
Collection<Scmp.TracerouteMessage> results1 = channel.sendTracerouteRequest();
assertEquals(1, results1.size());
for (Scmp.TracerouteMessage result : results1) {
assertTrue(result.isTimedOut());
assertEquals(Scmp.ScmpTypeCode.TYPE_130, result.getTypeCode());
assertEquals(1_000 * 1_000_000, result.getNanoSeconds());
assertEquals(100 * 1_000_000, result.getNanoSeconds());
}

// retry
Expand Down Expand Up @@ -291,7 +290,13 @@ private RequestPath getPathTo112() {
private RequestPath getPathToLocalAS() {
ScionService service = Scion.defaultService();
long dstIA = service.getLocalIsdAs();
List<RequestPath> paths = service.getPaths(dstIA, new byte[] {0, 0, 0, 0}, 12345);
return paths.get(0);
try {
InetAddress addr = InetAddress.getByName(MockNetwork.BORDER_ROUTER_HOST);
int port = MockNetwork.BORDER_ROUTER_PORT1;
List<RequestPath> paths = service.getPaths(dstIA, new InetSocketAddress(addr, port));
return paths.get(0);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
}
42 changes: 35 additions & 7 deletions src/test/java/org/scion/api/ScionServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
import static org.junit.jupiter.api.Assertions.*;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.scion.*;
Expand All @@ -34,12 +34,6 @@ public class ScionServiceTest {
private static final String SCION_TXT = "\"scion=1-ff00:0:110,127.0.0.1\"";
private static final int DEFAULT_PORT = MockDaemon.DEFAULT_PORT;

@BeforeAll
public static void beforeAll() {
// System.setProperty(
// PackageVisibilityHelper.DEBUG_PROPERTY_DNS_MOCK, SCION_HOST + "=" + SCION_TXT);
}

@AfterAll
public static void afterAll() {
System.clearProperty(PackageVisibilityHelper.DEBUG_PROPERTY_DNS_MOCK);
Expand Down Expand Up @@ -165,6 +159,40 @@ void getPaths() throws IOException {
}
}

@Test
void getPaths_localAS() throws IOException {
InetSocketAddress dstAddress = new InetSocketAddress("::1", 12345);
MockDaemon.createAndStartDefault();
try {
// String daemonAddr = "127.0.0.12:30255"; // from 110-topo
List<RequestPath> paths;
long dstIA = ScionUtil.parseIA("1-ff00:0:110");
try (Scion.CloseableService client =
Scion.newServiceWithDaemon(MockDaemon.DEFAULT_ADDRESS_STR)) {
paths = client.getPaths(dstIA, dstAddress);
} catch (IOException e) {
throw new RuntimeException(e);
}

// Paths found: 1
// Path: exp=1708596832 / 2024-02-22T10:13:52Z mtu=1472
// Path: first hop =
// raw: []
// raw: {}
assertEquals(1, paths.size());
RequestPath path = paths.get(0);
InetAddress addr = InetAddress.getByAddress(path.getDestinationAddress());
InetSocketAddress sAddr = new InetSocketAddress(addr, path.getDestinationPort());
assertEquals(sAddr, path.getFirstHopAddress());
assertEquals(dstIA, path.getDestinationIsdAs());

// get local AS, get PATH
assertEquals(2, MockDaemon.getAndResetCallCount());
} finally {
MockDaemon.closeDefault();
}
}

@Test
void getScionAddress() throws IOException {
// Test that DNS injection via properties works
Expand Down
Loading