diff --git a/CHANGELOG.md b/CHANGELOG.md index 73725d778..9141dc59e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,15 +7,16 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added - Code coverage. [#11](https://github.com/tzaeschke/phtree-cpp/pull/11) -- SCMP error handling - +- SCMP error handling: change paths in case of broken links or routers. + ### Changed - BREAKING CHANGE: Changed maven artifactId to "client" [#9](https://github.com/tzaeschke/phtree-cpp/pull/9) - BREAKING CHANGE: SCMP refactoring, renamed several SCMP related classes. [#14](https://github.com/tzaeschke/phtree-cpp/pull/14), [#15](https://github.com/tzaeschke/phtree-cpp/pull/15), - [#17](https://github.com/tzaeschke/phtree-cpp/pull/17) + [#17](https://github.com/tzaeschke/phtree-cpp/pull/17), + [#19](https://github.com/tzaeschke/phtree-cpp/pull/19) - BREAKING CHANGE:`ScionService` instances created via `Scion.newXYZ` will not be considered by `Scion.defaultService()`. Also, `DatagramChannel.getService()` does not create a service if none exists. diff --git a/TODO.md b/TODO.md index 6d9d87bda..8dd6d10b7 100644 --- a/TODO.md +++ b/TODO.md @@ -72,16 +72,22 @@ ## Plan ### 0.1.0 -- SCMP error handling (only error, not info) - - BUG: Ping to iaOVGU causes 3 CS requests that have type UP,CORE,CORE....? +- Review + clean up + +### 0.2.0 +- SCMP errors handling (above) + - Especially for type ยจ5: External Interface Down" and "6: Internal Connectivity Down" + Problem: we need to receive() or read() to actually receive SCMP errors. + We could do this concurrently (actually, this would probably block writes), + or we do this onyl if the user calls read/receive. We can then store the failure info + (path + AS/IP/IF of failure location). During next send/write, we compare the + path against this failure and try to find a better one. If no better path is found + we can just drop the packet (default; consistent with UDP behavior) or throw an error. + Also: The list of broken paths should be cleaned up once the path is expired (or earlier?). +- SCION-Proto questions: - 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 -Discuss required for 0.1.0: -- SCMP errors handling (above) - - Especially for expired paths / revoked paths / broken paths? - -### 0.2.0 - Segments: - Sorting by weight (see graph.go:195) - Consider peering diff --git a/src/main/java/org/scion/AbstractDatagramChannel.java b/src/main/java/org/scion/AbstractDatagramChannel.java index 88743f05c..e6f6e0748 100644 --- a/src/main/java/org/scion/AbstractDatagramChannel.java +++ b/src/main/java/org/scion/AbstractDatagramChannel.java @@ -231,7 +231,7 @@ protected InternalConstants.HdrTypes receiveExtensionHeader( } protected void receiveScmp(ByteBuffer buffer, Path path) { - Scmp.ScmpType type = ScmpParser.extractType(buffer); + Scmp.Type type = ScmpParser.extractType(buffer); Scmp.Message msg = Scmp.createMessage(type, path); ScmpParser.consume(buffer, msg); checkListeners(msg); diff --git a/src/main/java/org/scion/Scmp.java b/src/main/java/org/scion/Scmp.java index b2d46b74d..1ab0fe04a 100644 --- a/src/main/java/org/scion/Scmp.java +++ b/src/main/java/org/scion/Scmp.java @@ -35,7 +35,7 @@ static E parse(Class e, int code) { } } - public enum ScmpType implements ParseEnum { + public enum Type implements ParseEnum { // SCMP error messages: ERROR_1(1, "Destination Unreachable"), ERROR_2(2, "Packet Too Big"), @@ -59,13 +59,13 @@ public enum ScmpType implements ParseEnum { final int id; final String text; - ScmpType(int id, String text) { + Type(int id, String text) { this.id = id; this.text = text; } - public static ScmpType parse(int id) { - return ParseEnum.parse(ScmpType.class, id); + public static Type parse(int id) { + return ParseEnum.parse(Type.class, id); } @Override @@ -83,7 +83,7 @@ public String toString() { } } - public enum ScmpTypeCode implements ParseEnum { + public enum TypeCode implements ParseEnum { // SCMP type 1 messages: TYPE_1_CODE_0(1, 0, "No route to destination"), TYPE_1_CODE_1(1, 1, "Communication administratively denied"), @@ -132,16 +132,16 @@ public enum ScmpTypeCode implements ParseEnum { final int id; final String text; - ScmpTypeCode(int type, int code, String text) { + TypeCode(int type, int code, String text) { this.type = type; this.id = code; this.text = text; } - public static ScmpTypeCode parse(int type, int code) { - ScmpTypeCode[] values = ScmpTypeCode.class.getEnumConstants(); + public static TypeCode parse(int type, int code) { + TypeCode[] values = TypeCode.class.getEnumConstants(); for (int i = 0; i < values.length; i++) { - ScmpTypeCode pe = values[i]; + TypeCode pe = values[i]; if (pe.id() == code && pe.type == type) { return pe; } @@ -167,7 +167,7 @@ public String getText() { } public boolean isError() { - return type >= ScmpType.ERROR_1.id && type <= ScmpType.ERROR_127.id; + return type >= Type.ERROR_1.id && type <= Type.ERROR_127.id; } @Override @@ -177,13 +177,13 @@ public String toString() { } public static class Message { - private ScmpTypeCode typeCode; + private TypeCode typeCode; private int identifier; private int sequenceNumber; private Path path; /** DO NOT USE! */ - public Message(ScmpTypeCode typeCode, int identifier, int sequenceNumber, Path path) { + public Message(TypeCode typeCode, int identifier, int sequenceNumber, Path path) { this.typeCode = typeCode; this.identifier = identifier; this.sequenceNumber = sequenceNumber; @@ -198,7 +198,7 @@ public int getSequenceNumber() { return sequenceNumber; } - public ScmpTypeCode getTypeCode() { + public TypeCode getTypeCode() { return typeCode; } @@ -210,7 +210,7 @@ public void setPath(Path path) { this.path = path; } - public void setMessageArgs(ScmpTypeCode sc, int identifier, int sequenceNumber) { + public void setMessageArgs(TypeCode sc, int identifier, int sequenceNumber) { this.typeCode = sc; this.identifier = identifier; this.sequenceNumber = sequenceNumber; @@ -221,7 +221,7 @@ public abstract static class TimedMessage extends Message { private long nanoSeconds; private boolean timedOut = false; - private TimedMessage(ScmpTypeCode typeCode, int identifier, int sequenceNumber, Path path) { + private TimedMessage(TypeCode typeCode, int identifier, int sequenceNumber, Path path) { super(typeCode, identifier, sequenceNumber, path); } @@ -248,19 +248,19 @@ public static class EchoMessage extends TimedMessage { private int sizeReceived; private EchoMessage( - ScmpTypeCode typeCode, int identifier, int sequenceNumber, Path path, byte[] data) { + TypeCode typeCode, int identifier, int sequenceNumber, Path path, byte[] data) { super(typeCode, identifier, sequenceNumber, path); this.data = data; } public static EchoMessage createEmpty(Path path) { - return new EchoMessage(ScmpTypeCode.TYPE_128, -1, -1, path, null); + return new EchoMessage(TypeCode.TYPE_128, -1, -1, path, null); } public static EchoMessage createRequest(int sequenceNumber, Path path, ByteBuffer payload) { byte[] data = new byte[payload.remaining()]; payload.get(data); - return new EchoMessage(ScmpTypeCode.TYPE_128, -1, sequenceNumber, path, data); + return new EchoMessage(TypeCode.TYPE_128, -1, sequenceNumber, path, data); } public byte[] getData() { @@ -293,26 +293,20 @@ public static class TracerouteMessage extends TimedMessage { private long isdAs; private long ifID; - private TracerouteMessage( - ScmpTypeCode typeCode, int identifier, int sequenceNumber, Path path) { + private TracerouteMessage(TypeCode typeCode, int identifier, int sequenceNumber, Path path) { this(typeCode, identifier, sequenceNumber, 0, 0, path); } public static TracerouteMessage createEmpty(Path path) { - return new TracerouteMessage(ScmpTypeCode.TYPE_130, -1, -1, path); + return new TracerouteMessage(TypeCode.TYPE_130, -1, -1, path); } public static TracerouteMessage createRequest(int sequenceNumber, Path path) { - return new TracerouteMessage(ScmpTypeCode.TYPE_130, -1, sequenceNumber, path); + return new TracerouteMessage(TypeCode.TYPE_130, -1, sequenceNumber, path); } public TracerouteMessage( - ScmpTypeCode typeCode, - int identifier, - int sequenceNumber, - long isdAs, - long ifID, - Path path) { + TypeCode typeCode, int identifier, int sequenceNumber, long isdAs, long ifID, Path path) { super(typeCode, identifier, sequenceNumber, path); this.isdAs = isdAs; this.ifID = ifID; @@ -340,7 +334,7 @@ public void setTracerouteArgs(long isdAs, long ifID) { } } - static Scmp.Message createMessage(Scmp.ScmpType type, Path path) { + static Scmp.Message createMessage(Type type, Path path) { switch (type) { case INFO_128: case INFO_129: @@ -371,6 +365,19 @@ public static ScmpChannel createChannel(RequestPath path) throws IOException { * @return New SCMP channel */ public static ScmpChannel createChannel(RequestPath path, int listeningPort) throws IOException { - return new ScmpChannel(path, listeningPort); + return new ScmpChannel(Scion.defaultService(), path, listeningPort); + } + + /** + * Create a channel for sending SCMP requests. + * + * @param service the ScionService instance + * @param path Path to destination + * @param listeningPort Local port to listen for SCMP requests. + * @return New SCMP channel + */ + public static ScmpChannel createChannel(ScionService service, RequestPath path, int listeningPort) + throws IOException { + return new ScmpChannel(service, path, listeningPort); } } diff --git a/src/main/java/org/scion/ScmpChannel.java b/src/main/java/org/scion/ScmpChannel.java index edfd6c6b8..6b0a0e477 100644 --- a/src/main/java/org/scion/ScmpChannel.java +++ b/src/main/java/org/scion/ScmpChannel.java @@ -37,11 +37,11 @@ public class ScmpChannel implements AutoCloseable { private final InternalChannel channel; ScmpChannel(RequestPath path) throws IOException { - this(path, 12345); + this(Scion.defaultService(), path, 12345); } - ScmpChannel(RequestPath path, int port) throws IOException { - channel = new InternalChannel(Scion.defaultService(), path, port); + ScmpChannel(ScionService service, RequestPath path, int port) throws IOException { + channel = new InternalChannel(service, path, port); } /** @@ -57,7 +57,7 @@ public class ScmpChannel implements AutoCloseable { public Scmp.EchoMessage sendEchoRequest(int sequenceNumber, ByteBuffer data) throws IOException { RequestPath path = (RequestPath) channel.getCurrentPath(); Scmp.EchoMessage request = Scmp.EchoMessage.createRequest(sequenceNumber, path, data); - sendScmpRequest(() -> channel.sendEchoRequest(request), Scmp.ScmpTypeCode.TYPE_129); + sendScmpRequest(() -> channel.sendEchoRequest(request), Scmp.TypeCode.TYPE_129); return request; } @@ -78,8 +78,7 @@ public Collection sendTracerouteRequest() throws IOExcep Scmp.TracerouteMessage request = Scmp.TracerouteMessage.createRequest(i, path); requests.add(request); PathHeaderParser.Node node = nodes.get(i); - sendScmpRequest( - () -> channel.sendTracerouteRequest(request, node), Scmp.ScmpTypeCode.TYPE_131); + sendScmpRequest(() -> channel.sendTracerouteRequest(request, node), Scmp.TypeCode.TYPE_131); if (request.isTimedOut()) { break; } @@ -87,8 +86,8 @@ public Collection sendTracerouteRequest() throws IOExcep return requests; } - private void sendScmpRequest( - IOCallable sender, Scmp.ScmpTypeCode expectedTypeCode) throws IOException { + private void sendScmpRequest(IOCallable sender, Scmp.TypeCode expectedTypeCode) + throws IOException { long sendNanos = System.nanoTime(); Scmp.TimedMessage result = sender.call(); long nanos = System.nanoTime() - sendNanos; diff --git a/src/main/java/org/scion/internal/ScmpParser.java b/src/main/java/org/scion/internal/ScmpParser.java index 35bf0bc00..baae9ba56 100644 --- a/src/main/java/org/scion/internal/ScmpParser.java +++ b/src/main/java/org/scion/internal/ScmpParser.java @@ -16,9 +16,9 @@ import static org.scion.Scmp.EchoMessage; import static org.scion.Scmp.Message; -import static org.scion.Scmp.ScmpType; -import static org.scion.Scmp.ScmpTypeCode; import static org.scion.Scmp.TracerouteMessage; +import static org.scion.Scmp.Type; +import static org.scion.Scmp.TypeCode; import java.nio.ByteBuffer; @@ -36,7 +36,7 @@ public static void buildExtensionHeader(ByteBuffer buffer, InternalConstants.Hdr public static void buildScmpPing( ByteBuffer buffer, int identifier, int sequenceNumber, byte[] data) { - buffer.put(ByteUtil.toByte(ScmpType.INFO_128.id())); + buffer.put(ByteUtil.toByte(Type.INFO_128.id())); buffer.put(ByteUtil.toByte(0)); buffer.putShort((short) 0); // TODO checksum buffer.putShort((short) identifier); // unsigned @@ -46,7 +46,7 @@ public static void buildScmpPing( public static void buildScmpPing( ByteBuffer buffer, int identifier, int sequenceNumber, ByteBuffer data) { - buffer.put(ByteUtil.toByte(ScmpType.INFO_128.id())); + buffer.put(ByteUtil.toByte(Type.INFO_128.id())); buffer.put(ByteUtil.toByte(0)); buffer.putShort((short) 0); // TODO checksum buffer.putShort((short) identifier); // unsigned @@ -55,7 +55,7 @@ public static void buildScmpPing( } public static void buildScmpTraceroute(ByteBuffer buffer, int identifier, int sequenceNumber) { - buffer.put(ByteUtil.toByte(ScmpType.INFO_130.id())); + buffer.put(ByteUtil.toByte(Type.INFO_130.id())); buffer.put(ByteUtil.toByte(0)); buffer.putShort((short) 0); // TODO checksum buffer.putShort((short) identifier); // unsigned @@ -75,9 +75,9 @@ public static void buildScmpTraceroute(ByteBuffer buffer, int identifier, int se buffer.putLong(0); } - public static ScmpType extractType(ByteBuffer data) { + public static Type extractType(ByteBuffer data) { // Avoid changing the position! - return ScmpType.parse(ByteUtil.toUnsigned(data.get(data.position()))); + return Type.parse(ByteUtil.toUnsigned(data.get(data.position()))); } /** @@ -93,8 +93,8 @@ public static void consume(ByteBuffer data, Message holder) { data.getShort(); // checksum // TODO validate checksum - ScmpType st = ScmpType.parse(type); - ScmpTypeCode sc = ScmpTypeCode.parse(type, code); + Type st = Type.parse(type); + TypeCode sc = TypeCode.parse(type, code); int short1 = ByteUtil.toUnsigned(data.getShort()); int short2 = ByteUtil.toUnsigned(data.getShort()); holder.setMessageArgs(sc, short1, short2); diff --git a/src/main/java/org/scion/internal/Segments.java b/src/main/java/org/scion/internal/Segments.java index 3e6b366a4..ac6791821 100644 --- a/src/main/java/org/scion/internal/Segments.java +++ b/src/main/java/org/scion/internal/Segments.java @@ -176,12 +176,6 @@ private static Daemon.Path buildPath( raw.putInt(i0); // info fields - if (hopCount0 == 0) { - // TODO - // This can probably happen if we start in a CORE AS! - // E.g. only coreSegments or only downSegments or core+down - throw new UnsupportedOperationException(); - } long[] endingASes = new long[2]; boolean reversed0 = isReversed(seg0, brLookup.getLocalIsdAs(), endingASes); writeInfoField(raw, info0, reversed0); @@ -223,7 +217,7 @@ private static Daemon.Path buildPath( path.setExpiration(Timestamp.newBuilder().setSeconds(minExp).build()); path.setMtu(minMtu.v); - // TODO implement this + // TODO where do we get these? // segUp.getSegmentInfo(); // path.setLatency(); // path.setInternalHops(); @@ -260,7 +254,6 @@ private static void writeInfoField( ByteBuffer raw, Seg.SegmentInformation info, boolean reversed) { int inf0 = ((reversed ? 0 : 1) << 24) | info.getSegmentId(); raw.putInt(inf0); - // TODO in the daemon's path, all segments have the same timestamp.... raw.putInt(ByteUtil.toInt(info.getTimestamp())); } @@ -276,7 +269,6 @@ private static void writeHopFields( for (int i = 0; i < n; i++) { int pos = reversed ? (n - i - 1) : i; Seg.ASEntrySignedBody body = getBody(pathSegment.getAsEntriesList().get(pos)); - Seg.HopEntry hopEntry = body.getHopEntry(); Seg.HopField hopField = body.getHopEntry().getHopField(); raw.put((byte) 0); @@ -292,10 +284,6 @@ private static void writeHopFields( raw.put(bytePosSegID + 1, ByteUtil.toByte(raw.get(bytePosSegID + 1) ^ mac.byteAt(1))); } minExp.v = Math.min(minExp.v, hopField.getExpTime()); - // TODO implement for "reversed"? - // if (i < n - 1) { // TODO correct? The last one always appear to be 0 - // minMtu.v = Math.min(minMtu.v, hopEntry.getIngressMtu()); - // } minMtu.v = Math.min(minMtu.v, body.getMtu()); boolean addInterfaces = (reversed && pos > 0) || (!reversed && pos < n - 1); @@ -303,13 +291,6 @@ private static void writeHopFields( Daemon.PathInterface.Builder pib = Daemon.PathInterface.newBuilder(); pib.setId(reversed ? hopField.getIngress() : hopField.getEgress()); path.addInterfaces(pib.setIsdAs(body.getIsdAs()).build()); - // System.out.println( - // "IF-0: " - // + hopField.getIngress() - // + " / " - // + hopField.getEgress() - // + " --> " - // + path.getInterfaces(path.getInterfacesCount() - 1).getId()); Daemon.PathInterface.Builder pib2 = Daemon.PathInterface.newBuilder(); int pos2 = reversed ? pos - 1 : pos + 1; @@ -317,13 +298,6 @@ private static void writeHopFields( Seg.HopField hopField2 = body2.getHopEntry().getHopField(); pib2.setId(reversed ? hopField2.getEgress() : hopField2.getIngress()); path.addInterfaces(pib2.setIsdAs(body2.getIsdAs()).build()); - // System.out.println( - // "IF-2: " - // + hopField2.getIngress() - // + " / " - // + hopField2.getEgress() - // + " --> " - // + path.getInterfaces(path.getInterfacesCount() - 1).getId()); } } } diff --git a/src/test/java/org/scion/PackageVisibilityHelper.java b/src/test/java/org/scion/PackageVisibilityHelper.java index e2c3c103e..0b0a67189 100644 --- a/src/test/java/org/scion/PackageVisibilityHelper.java +++ b/src/test/java/org/scion/PackageVisibilityHelper.java @@ -46,7 +46,7 @@ public static InternalConstants.HdrTypes getNextHdr(ByteBuffer packet) { return ScionHeaderParser.extractNextHeader(packet); } - public static Scmp.Message createMessage(Scmp.ScmpType type, Path path) { + public static Scmp.Message createMessage(Scmp.Type type, Path path) { return Scmp.createMessage(type, path); } diff --git a/src/test/java/org/scion/api/DatagramChannelErrorHandlingTest.java b/src/test/java/org/scion/api/DatagramChannelErrorHandlingTest.java index 6feb1d0a7..915b1fc6b 100644 --- a/src/test/java/org/scion/api/DatagramChannelErrorHandlingTest.java +++ b/src/test/java/org/scion/api/DatagramChannelErrorHandlingTest.java @@ -19,70 +19,56 @@ import java.io.IOException; import java.net.*; import java.nio.ByteBuffer; -import java.nio.charset.Charset; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.scion.*; -import org.scion.testutil.PingPongHelper; +import org.scion.testutil.ExamplePacket; +import org.scion.testutil.MockDatagramChannel; +import org.scion.testutil.MockNetwork; -/** Test path switching (changing first hop) on DatagramChannel. */ +/** Test path switching on DatagramChannel in case of network problems. */ class DatagramChannelErrorHandlingTest { - private final PathPolicy alternatingPolicy = - new PathPolicy() { - private int count = 0; - - @Override - public RequestPath filter(List paths) { - return paths.get(count++ % 2); - } - }; - @AfterAll public static void afterAll() { // Defensive clean up ScionService.closeDefault(); } + @Disabled @Test void testDummyChannel() throws IOException { - // java.nio.channels.DatagramChannel channel2 = new TestChannel(); - // DatagramChannel channel = null; - // try { - // channel = Scion.defaultService().openChannel(channel2); - // } finally { - // channel.close(); - // } - } - - @Test - void test() { - // PingPongHelper.Server serverFn = PingPongHelper::defaultServer; - // PingPongHelper.Client clientFn = this::client; - // PingPongHelper pph = new PingPongHelper(1, 2, 10); - // pph.runPingPong(serverFn, clientFn, false); - // assertEquals(2 * 10, MockNetwork.getForwardCount(0)); - // assertEquals(2 * 10, MockNetwork.getForwardCount(1)); - // assertEquals(2 * 2 * 10, MockNetwork.getAndResetForwardCount()); - } - - private void client(DatagramChannel channel, Path serverAddress, int id) throws IOException { - String message = PingPongHelper.MSG + "-" + id; - ByteBuffer sendBuf = ByteBuffer.wrap(message.getBytes()); - - // Use a path policy that alternates between 1st and 2nd path - // -> setPathPolicy() sets a new path! - channel.setPathPolicy(alternatingPolicy); - channel.write(sendBuf); + MockDatagramChannel mock = MockDatagramChannel.open(); + MockNetwork.startTiny(); + InetSocketAddress dstAddr = new InetSocketAddress("127.0.0.1", 12345); + try (DatagramChannel channel = Scion.defaultService().openChannel()) { + AtomicInteger scmpReceived = new AtomicInteger(); + channel.setScmpErrorListener( + message -> { + scmpReceived.incrementAndGet(); + System.out.println("msg: " + message.getTypeCode()); + throw new IllegalArgumentException(); + }); + List paths = Scion.defaultService().getPaths(ExamplePacket.DST_IA, dstAddr); + assertEquals(2, paths.size()); + RequestPath path0 = paths.get(0); + RequestPath path1 = paths.get(0); + channel.connect(path0); + channel.write(ByteBuffer.allocate(0)); + assertEquals(path0, channel.getCurrentPath()); - // System.out.println("CLIENT: Receiving ... (" + channel.getLocalAddress() + ")"); - ByteBuffer response = ByteBuffer.allocate(512); - int len = channel.read(response); - assertEquals(message.length(), len); + // TODO Use mock instead of daemon? + MockNetwork.returnScmpErrorOnNextPacket(Scmp.TypeCode.TYPE_5); + channel.write(ByteBuffer.allocate(0)); + assertEquals(path0, channel.getCurrentPath()); + assertEquals(1, scmpReceived.get()); + // mock.setSendCallback((byteBuffer,path) -> {}); - response.flip(); - String pong = Charset.defaultCharset().decode(response).toString(); - assertEquals(message, pong); + } finally { + mock.close(); + } } } diff --git a/src/test/java/org/scion/api/SCMPTest.java b/src/test/java/org/scion/api/SCMPTest.java index 34797b3bb..de790d870 100644 --- a/src/test/java/org/scion/api/SCMPTest.java +++ b/src/test/java/org/scion/api/SCMPTest.java @@ -83,7 +83,7 @@ public static void afterAll() { @Test void echo_error_invalidMAC() throws IOException { ScionPacketInspector spi = ScionPacketInspector.readPacket(ByteBuffer.wrap(PING_ERROR_4_51_HK)); - assertEquals(Scmp.ScmpTypeCode.TYPE_4_CODE_51, spi.getScmpHeader().getCode()); + assertEquals(Scmp.TypeCode.TYPE_4_CODE_51, spi.getScmpHeader().getCode()); // Test that error can be parsed without throwing an exception ByteBuffer buffer = ByteBuffer.wrap(PING_ERROR_4_51_HK); @@ -111,7 +111,7 @@ private void testEcho(Supplier path) throws IOException { byte[] data = new byte[] {1, 2, 3, 4, 5}; Scmp.EchoMessage result = channel.sendEchoRequest(42, ByteBuffer.wrap(data)); assertEquals(42, result.getSequenceNumber()); - assertEquals(Scmp.ScmpTypeCode.TYPE_129, result.getTypeCode()); + assertEquals(Scmp.TypeCode.TYPE_129, result.getTypeCode()); assertTrue(result.getNanoSeconds() > 0); assertTrue(result.getNanoSeconds() < 10_000_000); // 10 ms assertArrayEquals(data, result.getData()); @@ -136,7 +136,7 @@ void echo_timeout() throws IOException { // try again Scmp.EchoMessage result2 = channel.sendEchoRequest(43, ByteBuffer.allocate(0)); - assertEquals(Scmp.ScmpTypeCode.TYPE_129, result2.getTypeCode()); + assertEquals(Scmp.TypeCode.TYPE_129, result2.getTypeCode()); assertFalse(result2.isTimedOut()); assertTrue(result2.getNanoSeconds() > 0); assertTrue(result2.getNanoSeconds() < 10_000_000); // 10 ms @@ -168,12 +168,12 @@ void echo_SCMP_error() throws IOException { channel.setScmpErrorListener(scmpMessage -> listenerWasTriggered.set(true)); channel.setOption(ScionSocketOptions.SN_API_THROW_PARSER_FAILURE, true); // Router will return SCMP error - MockNetwork.returnScmpErrorOnNextPacket(Scmp.ScmpTypeCode.TYPE_1_CODE_0); + MockNetwork.returnScmpErrorOnNextPacket(Scmp.TypeCode.TYPE_1_CODE_0); Throwable t = assertThrows( IOException.class, () -> channel.sendEchoRequest(42, ByteBuffer.allocate(0))); assertTrue(listenerWasTriggered.get()); - assertTrue(t.getMessage().contains(Scmp.ScmpTypeCode.TYPE_1_CODE_0.getText())); + assertTrue(t.getMessage().contains(Scmp.TypeCode.TYPE_1_CODE_0.getText())); } finally { MockNetwork.stopTiny(); } @@ -199,7 +199,7 @@ private void testTraceroute(Supplier path, int nHops) throws IOExce int n = 0; for (Scmp.TracerouteMessage result : results) { assertEquals(n++, result.getSequenceNumber()); - assertEquals(Scmp.ScmpTypeCode.TYPE_131, result.getTypeCode()); + assertEquals(Scmp.TypeCode.TYPE_131, result.getTypeCode()); assertTrue(result.getNanoSeconds() > 0); assertTrue(result.getNanoSeconds() < 10_000_000); // 10 ms assertFalse(result.isTimedOut()); @@ -233,7 +233,7 @@ void traceroute_timeout() throws IOException { assertEquals(1, results1.size()); for (Scmp.TracerouteMessage result : results1) { assertTrue(result.isTimedOut()); - assertEquals(Scmp.ScmpTypeCode.TYPE_130, result.getTypeCode()); + assertEquals(Scmp.TypeCode.TYPE_130, result.getTypeCode()); assertEquals(100 * 1_000_000, result.getNanoSeconds()); } @@ -242,7 +242,7 @@ void traceroute_timeout() throws IOException { assertEquals(2, results2.size()); for (Scmp.TracerouteMessage result : results2) { assertFalse(result.isTimedOut()); - assertEquals(Scmp.ScmpTypeCode.TYPE_131, result.getTypeCode()); + assertEquals(Scmp.TypeCode.TYPE_131, result.getTypeCode()); } } finally { MockNetwork.stopTiny(); @@ -271,10 +271,10 @@ void traceroute_SCMP_error() throws IOException { channel.setScmpErrorListener(scmpMessage -> listenerWasTriggered.set(true)); channel.setOption(ScionSocketOptions.SN_API_THROW_PARSER_FAILURE, true); // Router will return SCMP error - MockNetwork.returnScmpErrorOnNextPacket(Scmp.ScmpTypeCode.TYPE_1_CODE_0); + MockNetwork.returnScmpErrorOnNextPacket(Scmp.TypeCode.TYPE_1_CODE_0); Throwable t = assertThrows(IOException.class, channel::sendTracerouteRequest); assertTrue(listenerWasTriggered.get()); - assertTrue(t.getMessage().contains(Scmp.ScmpTypeCode.TYPE_1_CODE_0.getText())); + assertTrue(t.getMessage().contains(Scmp.TypeCode.TYPE_1_CODE_0.getText())); } finally { MockNetwork.stopTiny(); } diff --git a/src/test/java/org/scion/demo/ScmpEchoDemo.java b/src/test/java/org/scion/demo/ScmpEchoDemo.java index adb6f5a97..90f264db4 100644 --- a/src/test/java/org/scion/demo/ScmpEchoDemo.java +++ b/src/test/java/org/scion/demo/ScmpEchoDemo.java @@ -77,8 +77,13 @@ public static void main(String[] args) throws IOException, InterruptedException } case MINIMAL_PROTO: { - Scion.newServiceWithTopologyFile("topologies/minimal/ASff00_0_1111/topology.json"); - // Scion.newServiceWithDaemon(DemoConstants.daemon1111_minimal); + System.setProperty( + Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, + "topologies/minimal/ASff00_0_1111/topology.json"); + // System.setProperty(Constants.PROPERTY_DAEMON_HOST, + // toHost(DemoConstants.daemon1111_minimal)); + // System.setProperty(Constants.PROPERTY_DAEMON_PORT, + // toPort(DemoConstants.daemon1111_minimal)); ScmpEchoDemo demo = new ScmpEchoDemo(); demo.runDemo(DemoConstants.ia211, serviceIP); // demo.runDemo(DemoConstants.ia111, toAddr(DemoConstants.daemon111_minimal)); @@ -87,8 +92,8 @@ public static void main(String[] args) throws IOException, InterruptedException } case PRODUCTION: { - Scion.newServiceWithDNS("ethz.ch"); - // Scion.newServiceWithBootstrapServer("129.132.121.175:8041"); + System.setProperty(Constants.PROPERTY_BOOTSTRAP_NAPTR_NAME, "ethz.ch"); + // System.setProperty(Constants.PROPERTY_BOOTSTRAP_HOST, "129.132.121.175:8041"); // Port must be 30041 for networks that expect a dispatcher ScmpEchoDemo demo = new ScmpEchoDemo(30041); demo.runDemo(DemoConstants.iaOVGU, serviceIP); @@ -100,8 +105,7 @@ public static void main(String[] args) throws IOException, InterruptedException } private void runDemo(long dstIA, InetSocketAddress dstAddress) throws IOException { - ScionService service = Scion.defaultService(); - List paths = service.getPaths(dstIA, dstAddress); + List paths = Scion.defaultService().getPaths(dstIA, dstAddress); RequestPath path = paths.get(0); ByteBuffer data = ByteBuffer.allocate(0); @@ -160,4 +164,14 @@ private static InetSocketAddress toAddr(String addrString) throws UnknownHostExc InetAddress addr = InetAddress.getByName(addrString.substring(0, posColon)); return new InetSocketAddress(addr, 30041); } + + private static String toHost(String addrString) { + int posColon = addrString.indexOf(':'); + return addrString.substring(0, posColon); + } + + private static String toPort(String addrString) { + int posColon = addrString.indexOf(':'); + return addrString.substring(posColon + 1); + } } diff --git a/src/test/java/org/scion/demo/ScmpTracerouteDemo.java b/src/test/java/org/scion/demo/ScmpTracerouteDemo.java index 121a424ae..3dcf5e696 100644 --- a/src/test/java/org/scion/demo/ScmpTracerouteDemo.java +++ b/src/test/java/org/scion/demo/ScmpTracerouteDemo.java @@ -57,24 +57,32 @@ public static void main(String[] args) throws IOException, InterruptedException } case TINY_PROTO: { - // Scion.newServiceWithTopologyFile("topologies/scionproto-tiny-110.json"); - Scion.newServiceWithDaemon(DemoConstants.daemon110_tiny); + // System.setProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, + // "topologies/scionproto-tiny-110.json"); + System.setProperty( + Constants.PROPERTY_DAEMON_HOST, toHost(DemoConstants.daemon1111_minimal)); + System.setProperty( + Constants.PROPERTY_DAEMON_PORT, toPort(DemoConstants.daemon1111_minimal)); ScmpTracerouteDemo demo = new ScmpTracerouteDemo(); demo.runDemo(DemoConstants.ia110); break; } case MINIMAL_PROTO: { - // Scion.newServiceWithTopologyFile("topologies/minimal/ASff00_0_111/topology.json"); - Scion.newServiceWithDaemon(DemoConstants.daemon1111_minimal); + // System.setProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, + // "topologies/minimal/ASff00_0_1111/topology.json"); + System.setProperty( + Constants.PROPERTY_DAEMON_HOST, toHost(DemoConstants.daemon1111_minimal)); + System.setProperty( + Constants.PROPERTY_DAEMON_PORT, toPort(DemoConstants.daemon1111_minimal)); ScmpTracerouteDemo demo = new ScmpTracerouteDemo(); demo.runDemo(DemoConstants.ia211); break; } case PRODUCTION: { - Scion.newServiceWithDNS("inf.ethz.ch"); - // Scion.newServiceWithBootstrapServer("129.132.121.175:8041"); + System.setProperty(Constants.PROPERTY_BOOTSTRAP_NAPTR_NAME, "ethz.ch"); + // System.setProperty(Constants.PROPERTY_BOOTSTRAP_HOST, "129.132.121.175:8041"); // Port must be 30041 for networks that expect a dispatcher ScmpTracerouteDemo demo = new ScmpTracerouteDemo(30041); demo.runDemo(DemoConstants.iaAnapayaHK); @@ -117,4 +125,14 @@ private static void println(String msg) { System.out.println(msg); } } + + private static String toHost(String addrString) { + int posColon = addrString.indexOf(':'); + return addrString.substring(0, posColon); + } + + private static String toPort(String addrString) { + int posColon = addrString.indexOf(':'); + return addrString.substring(posColon + 1); + } } diff --git a/src/test/java/org/scion/demo/inspector/ScionPacketInspector.java b/src/test/java/org/scion/demo/inspector/ScionPacketInspector.java index 4f5654b2d..e7c6dac47 100644 --- a/src/test/java/org/scion/demo/inspector/ScionPacketInspector.java +++ b/src/test/java/org/scion/demo/inspector/ScionPacketInspector.java @@ -166,16 +166,16 @@ public void writePacket(ByteBuffer newData, byte[] userData) { } public void writePacketSCMP(ByteBuffer newData) { - Scmp.ScmpType type = scmpHeader.getType(); - EnumSet errors = + Scmp.Type type = scmpHeader.getType(); + EnumSet errors = EnumSet.of( - Scmp.ScmpType.ERROR_1, - Scmp.ScmpType.ERROR_2, - Scmp.ScmpType.ERROR_3, - Scmp.ScmpType.ERROR_4, - Scmp.ScmpType.ERROR_5, - Scmp.ScmpType.ERROR_6); - if (type == Scmp.ScmpType.INFO_128 || type == Scmp.ScmpType.INFO_129) { + Scmp.Type.ERROR_1, + Scmp.Type.ERROR_2, + Scmp.Type.ERROR_3, + Scmp.Type.ERROR_4, + Scmp.Type.ERROR_5, + Scmp.Type.ERROR_6); + if (type == Scmp.Type.INFO_128 || type == Scmp.Type.INFO_129) { scionHeader.write( newData, scmpHeader.getUserData().length + 8, @@ -184,7 +184,7 @@ public void writePacketSCMP(ByteBuffer newData) { InternalConstants.HdrTypes.SCMP); pathHeaderScion.write(newData); scmpHeader.writeEcho(newData); - } else if (type == Scmp.ScmpType.INFO_130 || type == Scmp.ScmpType.INFO_131) { + } else if (type == Scmp.Type.INFO_130 || type == Scmp.Type.INFO_131) { scionHeader.write( newData, 24, diff --git a/src/test/java/org/scion/demo/inspector/ScmpHeader.java b/src/test/java/org/scion/demo/inspector/ScmpHeader.java index 12013933f..20add4533 100644 --- a/src/test/java/org/scion/demo/inspector/ScmpHeader.java +++ b/src/test/java/org/scion/demo/inspector/ScmpHeader.java @@ -38,7 +38,7 @@ public void read(ByteBuffer data) { code = readInt(i0, 8, 8); checksum = readInt(i0, 16, 16); - Scmp.ScmpType st = Scmp.ScmpType.parse(type); + Scmp.Type st = Scmp.Type.parse(type); short1 = org.scion.internal.ByteUtil.toUnsigned(data.getShort()); short2 = org.scion.internal.ByteUtil.toUnsigned(data.getShort()); switch (st) { @@ -109,15 +109,15 @@ public String toString() { return "ScionSCMPHeader{" + "type=" + type + ", code=" + code + ", checksum=" + checksum + '}'; } - public Scmp.ScmpType getType() { - return Scmp.ScmpType.parse(type); + public Scmp.Type getType() { + return Scmp.Type.parse(type); } - public Scmp.ScmpTypeCode getCode() { - return Scmp.ScmpTypeCode.parse(type, code); + public Scmp.TypeCode getCode() { + return Scmp.TypeCode.parse(type, code); } - public void setCode(Scmp.ScmpTypeCode code) { + public void setCode(Scmp.TypeCode code) { this.code = code.code(); this.type = code.type(); } diff --git a/src/test/java/org/scion/demo/inspector/ScmpInspectorTest.java b/src/test/java/org/scion/demo/inspector/ScmpInspectorTest.java index d341a6d0d..3b38ad326 100644 --- a/src/test/java/org/scion/demo/inspector/ScmpInspectorTest.java +++ b/src/test/java/org/scion/demo/inspector/ScmpInspectorTest.java @@ -93,8 +93,8 @@ void testScmpError_WrongPacketSize() throws IOException { ByteBuffer data = ByteBuffer.wrap(SCMP_PKT_SIZE).asReadOnlyBuffer(); ScionPacketInspector spi = ScionPacketInspector.readPacket(data); ScmpHeader hdr = spi.getScmpHeader(); - assertEquals(Scmp.ScmpTypeCode.TYPE_4_CODE_19, hdr.getCode()); - assertEquals(Scmp.ScmpType.ERROR_4, hdr.getType()); + assertEquals(Scmp.TypeCode.TYPE_4_CODE_19, hdr.getCode()); + assertEquals(Scmp.Type.ERROR_4, hdr.getType()); } @Test @@ -102,8 +102,8 @@ void testScmpError_WrongPacketSize2() throws IOException { ByteBuffer data = ByteBuffer.wrap(SCMP_PACKET_SIZE2).asReadOnlyBuffer(); ScionPacketInspector spi = ScionPacketInspector.readPacket(data); ScmpHeader hdr = spi.getScmpHeader(); - assertEquals(Scmp.ScmpTypeCode.TYPE_4_CODE_19, hdr.getCode()); - assertEquals(Scmp.ScmpType.ERROR_4, hdr.getType()); + assertEquals(Scmp.TypeCode.TYPE_4_CODE_19, hdr.getCode()); + assertEquals(Scmp.Type.ERROR_4, hdr.getType()); } @Test @@ -111,7 +111,7 @@ void testScmpError_WrongSrcIsdAs() throws IOException { ByteBuffer data = ByteBuffer.wrap(WRONG_SRC_ISD_AS).asReadOnlyBuffer(); ScionPacketInspector spi = ScionPacketInspector.readPacket(data); ScmpHeader hdr = spi.getScmpHeader(); - assertEquals(Scmp.ScmpTypeCode.TYPE_4_CODE_33, hdr.getCode()); - assertEquals(Scmp.ScmpType.ERROR_4, hdr.getType()); + assertEquals(Scmp.TypeCode.TYPE_4_CODE_33, hdr.getCode()); + assertEquals(Scmp.Type.ERROR_4, hdr.getType()); } } diff --git a/src/test/java/org/scion/testutil/MockDatagramChannel.java b/src/test/java/org/scion/testutil/MockDatagramChannel.java index 4615b7b74..8d1433a53 100644 --- a/src/test/java/org/scion/testutil/MockDatagramChannel.java +++ b/src/test/java/org/scion/testutil/MockDatagramChannel.java @@ -20,7 +20,9 @@ import java.nio.channels.MembershipKey; import java.nio.channels.spi.SelectorProvider; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Function; +import org.scion.Path; import org.scion.ScionSocketOptions; public class MockDatagramChannel extends java.nio.channels.DatagramChannel { @@ -35,6 +37,11 @@ public class MockDatagramChannel extends java.nio.channels.DatagramChannel { throw new UnsupportedOperationException(); }; + private BiConsumer sendCallback = + (byteBuffer, path) -> { + throw new UnsupportedOperationException(); + }; + public static MockDatagramChannel open() throws IOException { return new MockDatagramChannel(); } @@ -47,6 +54,10 @@ public void setReceiveCallback(Function cb) { receiveCallback = cb; } + public void setSendCallback(BiConsumer cb) { + sendCallback = cb; + } + @Override public java.nio.channels.DatagramChannel bind(SocketAddress socketAddress) throws IOException { bindAddress = socketAddress; diff --git a/src/test/java/org/scion/testutil/MockNetwork.java b/src/test/java/org/scion/testutil/MockNetwork.java index b84bf621c..f24114685 100644 --- a/src/test/java/org/scion/testutil/MockNetwork.java +++ b/src/test/java/org/scion/testutil/MockNetwork.java @@ -54,7 +54,7 @@ public class MockNetwork { static final AtomicInteger nForwardTotal = new AtomicInteger(); static final AtomicIntegerArray nForwards = new AtomicIntegerArray(20); static final AtomicInteger dropNextPackets = new AtomicInteger(); - static final AtomicReference scmpErrorOnNextPacket = new AtomicReference<>(); + static final AtomicReference scmpErrorOnNextPacket = new AtomicReference<>(); public static final int BORDER_ROUTER_PORT1 = 30555; private static final int BORDER_ROUTER_PORT2 = 30556; private static final Logger logger = LoggerFactory.getLogger(MockNetwork.class.getName()); @@ -170,7 +170,7 @@ public static void dropNextPackets(int n) { dropNextPackets.set(n); } - public static void returnScmpErrorOnNextPacket(Scmp.ScmpTypeCode scmpTypeCode) { + public static void returnScmpErrorOnNextPacket(Scmp.TypeCode scmpTypeCode) { scmpErrorOnNextPacket.set(scmpTypeCode); } @@ -314,7 +314,7 @@ private void handleScmp( buffer.position(ScionHeaderParser.extractHeaderLength(buffer)); ResponsePath path = PackageVisibilityHelper.getResponsePath(buffer, (InetSocketAddress) srcAddress); - Scmp.ScmpType type = ScmpParser.extractType(buffer); + Scmp.Type type = ScmpParser.extractType(buffer); Scmp.Message scmpMsg = PackageVisibilityHelper.createMessage(type, path); ScmpParser.consume(buffer, scmpMsg); logger.info( @@ -325,7 +325,7 @@ private void handleScmp( // This is very basic: // - we always answer regardless of whether we are actually the destination. // - We do not invert path / addresses - sendScmp(Scmp.ScmpTypeCode.TYPE_129, buffer, srcAddress, incoming); + sendScmp(Scmp.TypeCode.TYPE_129, buffer, srcAddress, incoming); } else if (scmpMsg instanceof Scmp.TracerouteMessage) { answerTraceRoute(buffer, srcAddress, incoming); } else { @@ -345,7 +345,7 @@ private void handleScmp( } private void sendScmp( - Scmp.ScmpTypeCode type, ByteBuffer buffer, SocketAddress srcAddress, DatagramChannel channel) + Scmp.TypeCode type, ByteBuffer buffer, SocketAddress srcAddress, DatagramChannel channel) throws IOException { // send back! buffer.rewind(); @@ -368,7 +368,7 @@ private void answerTraceRoute( ScionPacketInspector spi = ScionPacketInspector.readPacket(buffer); spi.reversePath(); ScmpHeader scmpHeader = spi.getScmpHeader(); - scmpHeader.setCode(Scmp.ScmpTypeCode.TYPE_131); + scmpHeader.setCode(Scmp.TypeCode.TYPE_131); PathHeaderScion phs = spi.getPathHeaderScion(); for (int i = 0; i < phs.getHopCount(); i++) { HopField hf = phs.getHopField(i);