diff --git a/CHANGELOG.md b/CHANGELOG.md index cee7369..de23e8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Update to JPAN 0.7.0 and improve SCMP error handling. [#18](https://github.com/netsec-ethz/jpan-cli/pull/18) +- Adapt to JPAN 0.7.0 and improve SCMP error handling. + [#14](https://github.com/netsec-ethz/jpan-cli/pull/14) + [#19](https://github.com/netsec-ethz/jpan-cli/pull/19) ## [0.2.2] - 2026-03-27 diff --git a/src/main/java/org/scion/cli/Cli.java b/src/main/java/org/scion/cli/Cli.java index fddd712..c8ab16d 100644 --- a/src/main/java/org/scion/cli/Cli.java +++ b/src/main/java/org/scion/cli/Cli.java @@ -26,7 +26,7 @@ public class Cli { - private static final String VERSION = "0.2.2 (using JPAN 0.6.1)"; + private static final String VERSION = "0.3.0-SNAPSHOT (using JPAN 0.7.0)"; public static void main(String... args) { System.setProperty(org.slf4j.simple.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "ERROR"); diff --git a/src/main/java/org/scion/cli/Ping.java b/src/main/java/org/scion/cli/Ping.java index a223797..57c76d3 100644 --- a/src/main/java/org/scion/cli/Ping.java +++ b/src/main/java/org/scion/cli/Ping.java @@ -20,7 +20,6 @@ import java.net.*; import java.nio.ByteBuffer; import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; import org.scion.cli.util.Errors; import org.scion.cli.util.ExitCodeException; import org.scion.cli.util.Prober; @@ -72,7 +71,7 @@ public void run(String... args) throws IOException { dstAddress.getIsdAs(), dstAddress.getInetAddress(), Constants.SCMP_PORT); } if (paths.isEmpty()) { - String src = ScionUtil.toStringIA(service.getLocalIsdAs()); + String src = localIsdAses(service); String dst = ScionUtil.toStringIA(dstAddress.getIsdAs()); throw new ExitCodeException(2, "No path found from " + src + " to " + dst); } @@ -158,7 +157,7 @@ private void run(List paths) throws IOException { } } if (newPaths.isEmpty()) { - String src = ScionUtil.toStringIA(Scion.defaultService().getLocalIsdAs()); + String src = localIsdAses(Scion.defaultService()); String dst = ScionUtil.toStringIA(dstAddress.getIsdAs()); throw new ExitCodeException(2, "No path found from " + src + " to " + dst); } @@ -167,6 +166,10 @@ private void run(List paths) throws IOException { path = paths.get(0); } + send(path); + } + + private void send(Path path) throws IOException { ByteBuffer data = ByteBuffer.allocate(payloadSize); for (int i = 0; i < payloadSize; i++) { data.put((byte) i); @@ -184,14 +187,12 @@ private void run(List paths) throws IOException { if (localPort != null) { builder.setLocalPort(localPort); } - // No concurrency, but must be final - AtomicBoolean scmpError = new AtomicBoolean(false); try (ScmpSender sender = builder.build()) { sender.setTimeOut(timeoutMs); - sender.setScmpErrorListener( + sender.setScmpErrorHandler( e -> { println("SCMP Error: " + e.getTypeCode().getText()); - scmpError.set(true); + return false; }); println("Listening on port " + sender.getLocalAddress().getPort() + " ..."); println("Resolved local address: "); @@ -199,7 +200,6 @@ private void run(List paths) throws IOException { printPath(path); for (int i = 0; i < count; i++) { - // TODO this may throw an IOException if external interface is down try { Scmp.EchoMessage msg = sender.sendEchoRequest(path, data); if (i == 0) { @@ -220,11 +220,7 @@ private void run(List paths) throws IOException { Util.sleep(intervalMs); } } catch (IOException e) { - if (scmpError.getAndSet(false)) { - // Ignore, we already reported the error - continue; - } - throw e; + println("Error: " + e.getMessage()); } } } @@ -241,7 +237,7 @@ private static void printPath(Path path) { String nl = System.lineSeparator(); String sb = "Using path:" + nl + " Hops: " + ScionUtil.toStringPath(path.getMetadata()); sb += " MTU: " + path.getMetadata().getMtu(); - sb += " NextHop: " + path.getMetadata().getInterface().getAddress() + nl; + sb += " NextHop: " + path.getMetadata().getLocalInterface().getAddress() + nl; println(sb); } diff --git a/src/main/java/org/scion/cli/Showpaths.java b/src/main/java/org/scion/cli/Showpaths.java index 4c062ff..1db8909 100644 --- a/src/main/java/org/scion/cli/Showpaths.java +++ b/src/main/java/org/scion/cli/Showpaths.java @@ -135,7 +135,7 @@ public void run() throws IOException { new InetSocketAddress(InetAddress.getLoopbackAddress(), 12345); List paths = service.getPaths(isdAs, destinationAddress); if (paths.isEmpty()) { - String src = ScionUtil.toStringIA(service.getLocalIsdAs()); + String src = localIsdAses(service); String dst = ScionUtil.toStringIA(isdAs); throw new ExitCodeException(1, "No path found from " + src + " to " + dst); } @@ -217,7 +217,7 @@ private static String toStringExpiry(PathMetadata meta) { private static String toStringLatency(PathMetadata meta) { int latencyMs = 0; boolean latencyComplete = true; - for (int l : meta.getLatencyList()) { + for (int l : meta.getLatencies()) { if (l >= 0) { latencyMs += l; } else { @@ -234,7 +234,7 @@ private static String toStringLatency(PathMetadata meta) { private static String toStringBandwidth(PathMetadata meta) { long bw = Long.MAX_VALUE; boolean bwComplete = true; - for (long l : meta.getBandwidthList()) { + for (long l : meta.getBandwidths()) { if (l > 0) { bw = Math.min(bw, l); } else { @@ -251,7 +251,7 @@ private static String toStringBandwidth(PathMetadata meta) { private static String toStringGeo(PathMetadata meta) { StringBuilder s = new StringBuilder("["); - for (PathMetadata.GeoCoordinates g : meta.getGeoList()) { + for (PathMetadata.GeoCoordinates g : meta.getGeoCoordinates()) { if (s.length() > 1) { s.append(" > "); } @@ -269,7 +269,7 @@ private static String toStringGeo(PathMetadata meta) { private static String toStringLinkType(PathMetadata meta) { StringBuilder s = new StringBuilder("["); - for (PathMetadata.LinkType lt : meta.getLinkTypeList()) { + for (PathMetadata.LinkType lt : meta.getLinkTypes()) { if (s.length() > 1) { s.append(", "); } @@ -312,12 +312,12 @@ private static String toStringEPIC(PathMetadata meta) { private static String toStringNotes(PathMetadata meta) { StringBuilder s = new StringBuilder("["); int i = 0; - for (String note : meta.getNotesList()) { + for (String note : meta.getNotes()) { if (note != null && !note.isEmpty()) { if (s.length() > 1) { s.append(", "); } - long isdAs = meta.getInterfacesList().get(Math.max(0, i * 2 - 1)).getIsdAs(); + long isdAs = meta.getInterfaces().get(Math.max(0, i * 2 - 1)).getIsdAs(); s.append(ScionUtil.toStringIA(isdAs)); s.append(": \"").append(note).append("\""); } diff --git a/src/main/java/org/scion/cli/Traceroute.java b/src/main/java/org/scion/cli/Traceroute.java index ebdfb26..a4650fd 100644 --- a/src/main/java/org/scion/cli/Traceroute.java +++ b/src/main/java/org/scion/cli/Traceroute.java @@ -19,7 +19,6 @@ import java.io.*; import java.net.*; import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; import org.scion.cli.util.Errors; import org.scion.cli.util.ExitCodeException; import org.scion.cli.util.Prober; @@ -34,7 +33,7 @@ public class Traceroute { private Integer localPort; private boolean startShim = true; - private long localIsdAs = 0; + private Long localIsdAs; private InetAddress localIP = null; private ScionAddress dstAddress; private String dstUrl; @@ -130,9 +129,8 @@ public void run() throws IOException { } if (paths.isEmpty()) { - String src = ScionUtil.toStringIA(service.getLocalIsdAs()); String dst = ScionUtil.toStringIA(dstAddress.getIsdAs()); - throw new ExitCodeException(2, "No path found from " + src + " to " + dst); + throw new ExitCodeException(2, "No path found from " + localIsdAses(service) + " to " + dst); } Path path; @@ -145,7 +143,7 @@ public void run() throws IOException { } } if (newPaths.isEmpty()) { - String src = ScionUtil.toStringIA(Scion.defaultService().getLocalIsdAs()); + String src = localIsdAses(service); String dst = ScionUtil.toStringIA(dstAddress.getIsdAs()); throw new ExitCodeException(2, "No path found from " + src + " to " + dst); } @@ -154,6 +152,10 @@ public void run() throws IOException { path = paths.get(0); } + send(path); + } + + private void send(Path path) throws IOException { String localAddress; try (ScionDatagramChannel channel = ScionDatagramChannel.open()) { channel.connect(path); @@ -166,20 +168,18 @@ public void run() throws IOException { builder.setLocalPort(localPort); } // No concurrency, but must be final - AtomicBoolean scmpError = new AtomicBoolean(false); try (ScmpSender sender = builder.build()) { sender.setTimeOut(timeoutMs); - sender.setScmpErrorListener( + sender.setScmpErrorHandler( e -> { println("SCMP Error: " + e.getTypeCode().getText()); - scmpError.set(true); + return false; }); println("Listening on port " + sender.getLocalAddress().getPort() + " ..."); println("Resolved local address: "); println(" " + localAddress); printPath(path); - // TODO this may throw an IOException if external interface is down List results = sender.sendTracerouteRequest(path); int nTimeouts = 0; for (Scmp.TracerouteMessage msg : results) { @@ -201,24 +201,23 @@ public void run() throws IOException { } throw new ExitCodeException(1, "Number of timeouts: " + nTimeouts + msg); } - } catch (IOException e) { - if (scmpError.getAndSet(false)) { - // Ignore, we already reported the error - return; - } - throw e; } } private static void printPath(Path path) { String nl = System.lineSeparator(); - StringBuilder sb = new StringBuilder(); // sb.append("Actual local address:").append(nl); // sb.append(" ").append(channel.getLocalAddress().getAddress().getHostAddress()).append(nl); - sb.append("Using path:").append(nl); - sb.append(" Hops: ").append(ScionUtil.toStringPath(path.getMetadata())); - sb.append(" MTU: ").append(path.getMetadata().getMtu()); - sb.append(" NextHop: ").append(path.getMetadata().getInterface().getAddress()).append(nl); - println(sb.toString()); + String sb = + "Using path:" + + nl + + " Hops: " + + ScionUtil.toStringPath(path.getMetadata()) + + " MTU: " + + path.getMetadata().getMtu() + + " NextHop: " + + path.getMetadata().getLocalInterface().getAddress() + + nl; + println(sb); } } diff --git a/src/main/java/org/scion/cli/util/Prober.java b/src/main/java/org/scion/cli/util/Prober.java index 3b6ba99..c837d7b 100644 --- a/src/main/java/org/scion/cli/util/Prober.java +++ b/src/main/java/org/scion/cli/util/Prober.java @@ -109,10 +109,8 @@ public void onError(Scmp.ErrorMessage msg) { m2 = ScmpParser.consume(ByteBuffer.wrap(msg.getCause()), (ResponsePath) msg.getPath()); Scmp.TypeCode tc = m2.getTypeCode(); if (tc == Scmp.TypeCode.TYPE_128 || tc == Scmp.TypeCode.TYPE_130) { - if (m2 instanceof Scmp.TimedMessage) { - int sn = ((Scmp.TimedMessage) m2).getSequenceNumber(); - result.put(sn, Status.SCMP); - } + int sn = ((Scmp.TimedMessage) m2).getSequenceNumber(); + result.put(sn, Status.SCMP); } } catch (RuntimeException e) { Util.println("Could not decode Scmp Error (" + msg.getTypeCode() + "): " + e.getMessage()); diff --git a/src/main/java/org/scion/cli/util/Util.java b/src/main/java/org/scion/cli/util/Util.java index 589751d..a8aeb52 100644 --- a/src/main/java/org/scion/cli/util/Util.java +++ b/src/main/java/org/scion/cli/util/Util.java @@ -92,6 +92,17 @@ public static double round(double d, int nDigits) { return Math.round(d * div) / div; } + public static String localIsdAses(ScionService service) { + if (service.getLocalIsdAses().isEmpty()) { + return "[]"; + } + StringBuilder ret = new StringBuilder("["); + for (long ia : service.getLocalIsdAses()) { + ret.append(ScionUtil.toStringIA(ia)).append(","); + } + return ret.substring(0, ret.length() - 1) + "]"; + } + public static void prepareShim(boolean startShim, Integer port) { if (startShim && port != null && port.equals(Constants.SCMP_PORT)) { throw new ExitCodeException(