diff --git a/CHANGELOG.md b/CHANGELOG.md index f84de48..d1c976b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. -- Nothing yet +### Added + +- Added command line `--help` and several options to multi-ping. + [#20](https://github.com/netsec-ethz/scion-java-multiping/pull/20) + ## [0.5.0] - 2025-09-09 ### Added diff --git a/README.md b/README.md index e4b418f..ae1fb62 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,15 @@ java -jar scion-multiping-0.5.0-executable.jar [tool-command] Some tools require configuration files, see below in the tool description sections. See also the troubleshooting section below in case of issues. +# Help + +To get command line help, the tool can be executed with: + +``` +java -jar scion-multiping-0.5.0-executable.jar --help +``` + + # Download Assignments The tool @@ -34,7 +43,9 @@ Note: the `isd-as-assignments.csv` output file can be directly used as input fil The tool parses [Anapayas ISD/AS assignment website](https://docs.anapaya.net/en/latest/resources/isd-as-assignments/), -identifies the shortest path to each AS and sends a traceroute to each AS. +identifies the shortest or fastest path to each AS and sends a traceroute to each AS. +By default is will use the fastest path (determined by a traceroute over all paths), but it can be +configured to use the shortest path (`--shortest`). It reports the number of paths to each AS as well as the shortest path with latency, remote IP, hop count and remote IP. diff --git a/src/main/java/org/scion/multiping/Main.java b/src/main/java/org/scion/multiping/Main.java index b1fa470..9128fce 100644 --- a/src/main/java/org/scion/multiping/Main.java +++ b/src/main/java/org/scion/multiping/Main.java @@ -22,18 +22,21 @@ public class Main { public static void main(String[] args) throws IOException { - if (args.length != 1) { - printUsage(); - System.exit(1); - } + checkArgs(args, 1, Integer.MAX_VALUE); String mode = args[0].toLowerCase(Locale.ROOT); String[] newArgs = Arrays.copyOfRange(args, 1, args.length); switch (mode) { case "download-assignments": { + checkArgs(args, 1, 1); DownloadAssignments.main(newArgs); return; } + case "help": + { + printHelp(args.length == 1 ? "" : args[1]); + return; + } case "ping-all": { PingAll.main(newArgs); @@ -41,11 +44,13 @@ public static void main(String[] args) throws IOException { } case "ping-repeat": { + checkArgs(args, 1, 1); PingRepeat.main(newArgs); return; } case "ping-responder": { + checkArgs(args, 1, 1); PingResponder.main(newArgs); return; } @@ -55,6 +60,33 @@ public static void main(String[] args) throws IOException { } } + private static void checkArgs(String[] args, int minArgs, int maxArgs) { + if (args.length < minArgs || args.length > maxArgs) { + Util.println("Invalid number of arguments."); + printUsage(); + System.exit(1); + } + } + + private static void printHelp(String mode) { + switch (mode) { + case "download-assignments": + printUsageDownloadAssignments(); + return; + case "ping-all": + printUsagePingAll(); + return; + case "ping-repeat": + printUsagePingRepeat(); + return; + case "ping-responder": + printUsagePingResponder(); + return; + default: + printUsage(); + } + } + private static void printUsage() { Util.println("Usage: scion-multiping [MODE]"); Util.println("where MODE is one of: "); @@ -65,6 +97,53 @@ private static void printUsage() { " - `ping-repeat` for repeatedly probing (traceroute) multiple paths to multiple ASes."); Util.println( " - `ping-responder` for starting a server that responds to incoming echo requests."); + Util.println(" - `help [MODE]` for getting more help for a given mode."); + Util.println(""); + } + + private static void printUsageDownloadAssignments() { + Util.println("Usage: scion-multiping download-assignments"); + Util.println(); + Util.println( + " This tool downloads a list of known ISD/AS assignments and saves it to a file."); + Util.println(" The output file is called `isd-as-assignments.csv`."); + Util.println(""); + } + + static void printUsagePingAll() { + Util.println( + "Usage: ping-all [--help] [--fastest|--shortest|--shortest_echo|--fastest_sync] [--port ] [--shim]"); + Util.println(" --help Show this help message."); + Util.println(" --fastest Use fastest path with SCMP traceroute (default)."); + Util.println( + " The fastest path is determined by running a single traceroute on all path."); + // Util.println(" --fastest_sync Use fastest path with SCMP traceroute (synchronous)"); + Util.println(" --shortest Use shortest path (fewest hops) with SCMP traceroute."); + // Util.println(" --shortest_echo Use shortest path with SCMP echo"); + Util.println( + " --port Use specified local port (default " + PingAll.localPort + ")."); + Util.println(" --shim Start with SHIM enabled (default disabled)."); + Util.println(""); + } + + private static void printUsagePingRepeat() { + Util.println("Usage: scion-multiping ping-repeat"); + Util.println(); + Util.println( + " This tool is used for repeatedly probing (traceroute) multiple paths to multiple ASes."); + Util.println(" The destination ASes are read from a file `isd-as-assignments.csv`."); + Util.println(" Other configuration options can be defined in `ping-repeat-config.json`."); + Util.println(" Results are written to a CSV file `ping-results.csv`."); + Util.println(" See README.md for more information."); + Util.println(""); + } + + private static void printUsagePingResponder() { + Util.println("Usage: scion-multiping ping-responder"); + Util.println(); + Util.println(" This command starts a server that responds to incoming echo requests."); + Util.println(" It takes a configuration file `ping-responder-config.json` as input."); + Util.println(" See README.md for more information."); Util.println(""); } } diff --git a/src/main/java/org/scion/multiping/PingAll.java b/src/main/java/org/scion/multiping/PingAll.java index 741321e..c33db41 100644 --- a/src/main/java/org/scion/multiping/PingAll.java +++ b/src/main/java/org/scion/multiping/PingAll.java @@ -52,8 +52,8 @@ public class PingAll { private static final boolean SHOW_ONLY_ICMP = false; private static final Config config = new Config(); - private static final int LOCAL_PORT = 30041; - private static final boolean STOP_SHIM = true; + static int localPort = 30041; + private static boolean startShim = false; static { config.tryICMP = false; @@ -88,21 +88,71 @@ enum Policy { this.service = service; } - public static void main(String[] args) throws IOException { + public static void main(String[] argsArray) throws IOException { PRINT = true; - // System.setProperty(Constants.PROPERTY_DNS_SEARCH_DOMAINS, "ethz.ch."); - System.setProperty(Constants.PROPERTY_SHIM, STOP_SHIM ? "false" : "true"); // disable SHIM + + Policy policy = parseArgs(argsArray); + + System.setProperty(Constants.PROPERTY_SHIM, startShim ? "true" : "false"); // disable SHIM println("Settings:"); - println(" Path policy = " + DEFAULT_POLICY); + println(" Path policy = " + policy); println(" ICMP=" + config.tryICMP); println(" printOnlyICMP=" + SHOW_ONLY_ICMP); - PingAll pingAll = new PingAll(DEFAULT_POLICY, ScionProvider.defaultProvider(LOCAL_PORT)); + PingAll pingAll = new PingAll(policy, ScionProvider.defaultProvider(localPort)); pingAll.run(); pingAll.summary.prettyPrint(); } + private static Policy parseArgs(String[] argsArray) { + List args = new ArrayList<>(Arrays.asList(argsArray)); + Policy policy = DEFAULT_POLICY; + while (!args.isEmpty()) { + switch (args.get(0)) { + case "--fastest": + policy = Policy.FASTEST_TR_ASYNC; + break; + case "--shortest": + policy = Policy.SHORTEST_TR; + break; + case "--shortest_echo": + policy = Policy.SHORTEST_ECHO; + break; + case "--fastest_sync": + policy = Policy.FASTEST_TR; + break; + case "--help": + Main.printUsagePingAll(); + System.exit(0); + case "--shim": + startShim = true; + break; + case "--port": + if (args.size() < 2) { + Util.println("Error: --port requires a port number"); + Main.printUsagePingAll(); + System.exit(1); + } + try { + localPort = Integer.parseInt(args.get(1)); + } catch (NumberFormatException e) { + Util.println("Error: Invalid port number: " + args.get(1)); + Main.printUsagePingAll(); + System.exit(1); + } + args.remove(1); + break; + default: + Util.println("Unknown option: " + args.get(0)); + Main.printUsagePingAll(); + System.exit(1); + } + args.remove(0); + } + return policy; + } + ResultSummary run() throws IOException { List allASes = service.getIsdAsEntries(); // remove entry for local AS