From 8c86d7449227a04f5ca5435f918bda6ddf3b36a4 Mon Sep 17 00:00:00 2001 From: tzaeschke Date: Mon, 11 Nov 2024 16:51:14 +0100 Subject: [PATCH 1/8] squash-4 --- CHANGELOG.md | 18 +++ TODO.md | 3 - .../scion/jpan/AbstractDatagramChannel.java | 35 ++++- src/main/java/org/scion/jpan/Constants.java | 6 + src/main/java/org/scion/jpan/PathPolicy.java | 4 +- .../java/org/scion/jpan/ScionService.java | 31 +++- .../java/org/scion/jpan/ScmpSenderAsync.java | 21 +-- .../scion/jpan/internal/LocalTopology.java | 82 +++++++++- .../java/org/scion/jpan/internal/Shim.java | 12 +- .../scion/protobuf/daemon/v1/daemon.proto | 13 +- .../DatagramChannelApiConcurrencyTest.java | 4 +- .../api/DatagramChannelApiServerTest.java | 2 + .../jpan/api/DatagramChannelApiTest.java | 1 + .../api/DatagramSocketApiConcurrencyTest.java | 2 +- .../java/org/scion/jpan/api/ScionTest.java | 141 +++++++++++++++++- .../org/scion/jpan/api/ScmpChannelTest.java | 14 +- .../org/scion/jpan/api/ScmpResponderTest.java | 10 ++ .../scion/jpan/api/ScmpSenderAsyncTest.java | 17 ++- .../org/scion/jpan/api/ScmpSenderTest.java | 14 +- .../org/scion/jpan/demo/ScmpEchoDemo.java | 53 +++---- .../scion/jpan/demo/ScmpTracerouteDemo.java | 61 ++++---- .../org/scion/jpan/demo/ShowpathsDemo.java | 26 +--- .../org/scion/jpan/internal/ShimTest.java | 33 ++-- .../java/org/scion/jpan/testutil/AsInfo.java | 15 ++ .../org/scion/jpan/testutil/JUnitSetUp.java | 18 ++- .../scion/jpan/testutil/JsonFileParser.java | 29 ++++ .../jpan/testutil/MockBootstrapServer.java | 4 + .../scion/jpan/testutil/MockBorderRouter.java | 4 + .../org/scion/jpan/testutil/MockDaemon.java | 53 +++++-- .../org/scion/jpan/testutil/MockNetwork.java | 32 +++- .../scion/jpan/testutil/MockScmpHandler.java | 3 +- .../jpan/testutil/PingPongChannelHelper.java | 26 +++- .../jpan/testutil/PingPongHelperBase.java | 3 + .../jpan/testutil/PingPongSocketHelper.java | 5 +- src/test/resources/topologies/README.txt | 13 ++ .../topologies/dispatcher-port-range-all.json | 46 ++++++ .../topologies/dispatcher-port-range-bad.json | 46 ++++++ .../dispatcher-port-range-empty.json | 46 ++++++ .../dispatcher-port-range-none.json | 45 ++++++ .../topologies/dispatcher-port-range.json | 46 ++++++ .../minimal/ASff00_0_110/topology.json | 3 +- .../minimal/ASff00_0_111/topology.json | 3 +- .../minimal/ASff00_0_1111/topology.json | 3 +- .../minimal/ASff00_0_1112/topology.json | 3 +- .../minimal/ASff00_0_112/topology.json | 3 +- .../minimal/ASff00_0_1121/topology.json | 3 +- .../minimal/ASff00_0_120/topology.json | 3 +- .../minimal/ASff00_0_121/topology.json | 3 +- .../minimal/ASff00_0_210/topology.json | 3 +- .../minimal/ASff00_0_211/topology.json | 3 +- .../ASff00_0_110/topology.json | 3 +- .../ASff00_0_111/topology.json | 3 +- .../ASff00_0_112/topology.json | 3 +- .../ASff00_0_120/topology.json | 3 +- .../ASff00_0_121/topology.json | 3 +- .../ASff00_0_122/topology.json | 3 +- .../ASff00_0_130/topology.json | 3 +- .../ASff00_0_131/topology.json | 3 +- .../ASff00_0_132/topology.json | 3 +- .../ASff00_0_133/topology.json | 3 +- .../ASff00_0_210/topology.json | 3 +- .../ASff00_0_211/topology.json | 3 +- .../ASff00_0_212/topology.json | 3 +- .../ASff00_0_220/topology.json | 3 +- .../ASff00_0_221/topology.json | 3 +- .../ASff00_0_222/topology.json | 3 +- .../topologies/scionproto-tiny/README.txt | 7 - .../scionproto-tiny/topology-110.json | 2 +- .../scionproto-tiny/topology-111.json | 2 +- .../scionproto-tiny/topology-112.json | 2 +- .../topologies/topology-scionproto-0.10.json | 62 ++++++++ .../topology-scionproto-0.11.json | 1 + 72 files changed, 956 insertions(+), 235 deletions(-) create mode 100644 src/test/resources/topologies/README.txt create mode 100644 src/test/resources/topologies/dispatcher-port-range-all.json create mode 100644 src/test/resources/topologies/dispatcher-port-range-bad.json create mode 100644 src/test/resources/topologies/dispatcher-port-range-empty.json create mode 100644 src/test/resources/topologies/dispatcher-port-range-none.json create mode 100644 src/test/resources/topologies/dispatcher-port-range.json delete mode 100644 src/test/resources/topologies/scionproto-tiny/README.txt create mode 100644 src/test/resources/topologies/topology-scionproto-0.10.json rename src/test/resources/topologies/{scionproto-tiny => }/topology-scionproto-0.11.json (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a7ea268f..cca536591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### TODO for 0.4.0 +- USe topo port range for connections in local AS! - Fix demos to return an "int" that can be tested! - ShimTest: TODO the serverService(null) should be removed once SHIM becomes the default - Bootstrap JPAN with reverse lookup to get search-domain --> Francois - Fix showpaths to show "127.0.0.81:31038" i.o. "/192.168.53.20" +- Bootstrap to find local search domain with reverse lookup, ask Francois! +- Cache local address, see AbstractChannel:600 + srcAddress = getOrCreateService().getExternalIP(path.getFirstHopAddress()); +- MockDaemon: read properties from topofile, see TODO +- Cache paths +- Server should get firstHop from topofile or daemon, not from packet IP! + -> BRs may use different ports for in/outgoing traffic. + -> Thios would also solve the server/SHIM problem, see TODO.md - Fix @Disabled tests - Support topofile port range - Upgrade all JUnit topo files to post 0.11 with new format (including port range) @@ -30,6 +39,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add a SHIM, required for #130 (topo file port range support). [#130](https://github.com/scionproto-contrib/jpan/pull/130) - ManagedThread test helper. [#136](https://github.com/scionproto-contrib/jpan/pull/136) +- Support for `dispatched_ports` in topo files + [#130](https://github.com/scionproto-contrib/jpan/pull/130) + +TODO: +- Test demos with JUNIT topo and with scioproto topo +- FIX: if no ephemeral ports are available, just use ANY port but enforce 30041 as return port. +- Deprecate configureRemoteDispatcher() +- Remove workaround from sendRaw() that checks for SHIM. Currently required for tests.... + -> Do this in separate PR? ### Changed - Buildified PingPong test helper. [#132](https://github.com/scionproto-contrib/jpan/pull/132) diff --git a/TODO.md b/TODO.md index 03dc2873b..74a221f2a 100644 --- a/TODO.md +++ b/TODO.md @@ -33,9 +33,6 @@ - Implement interfaces from nio.DatagramChannel - Look into Selectors: https://www.baeldung.com/java-nio-selector - Consider subclassing DatagramChannel directly. -- DISPATCHER migration: - - Daemon supposedly provides information about dispatcher. Double check updated proto files - - Parse topofiles with port range information -> indicates DISPATCHER presence - Consider SHIM support. SHIM is a compatibility component that supports old border-router software (requiring a fixed port on the client, unless the client is listening on this very port). When SHIM is used, we cannot diff --git a/src/main/java/org/scion/jpan/AbstractDatagramChannel.java b/src/main/java/org/scion/jpan/AbstractDatagramChannel.java index 7c528b410..f1e0bd202 100644 --- a/src/main/java/org/scion/jpan/AbstractDatagramChannel.java +++ b/src/main/java/org/scion/jpan/AbstractDatagramChannel.java @@ -132,10 +132,26 @@ public C bind(InetSocketAddress address) throws IOException { } } - private void ensureBound() throws IOException { + protected void ensureBound() throws IOException { synchronized (stateLock) { if (localAddress == null) { - bind(null); + LocalTopology.DispatcherPortRange ports = getService().getLocalPortRange(); + if (ports.hasPortRange()) { + // This is a bit ugly, we iterate through all ports to find a free one. + int min = ports.getPortMin(); + int max = ports.getPortMax(); + for (int port = min; port <= max; port++) { + try { + bind(new InetSocketAddress(port)); + return; + } catch (IOException e) { + // ignore and try next port + } + } + throw new IOException("No free port found in SCION port range: " + min + "-" + max); + } else { + bind(null); + } } } } @@ -286,6 +302,14 @@ public C connect(Path path) throws IOException { synchronized (stateLock) { checkConnected(false); ensureBound(); + if (localAddress.isAnyLocalAddress()) { + // Do we really need this? + // - It ensures that after connect we have a proper local address for getLocalAddress(), + // this is what connect() should do. + // - It allows us to have an ANY address underneath which could help with interface + // switching. + localAddress = getService().getExternalIP(path.getFirstHopAddress()); + } updateConnection((RequestPath) path, false); return (C) this; } @@ -412,6 +436,13 @@ public void setOverrideSourceAddress(InetSocketAddress address) { } protected int sendRaw(ByteBuffer buffer, Path path) throws IOException { + // For inter-AS connections we need to send directly to the destination address. + // We also need to respect the port range and use 30041 when applicable. + if (getService() != null && path.getRawPath().length == 0) { + InetSocketAddress remoteHostIP = path.getFirstHopAddress(); + remoteHostIP = getService().getLocalPortRange().mapToLocalPort(remoteHostIP); + return channel.send(buffer, remoteHostIP); + } if (cfgRemoteDispatcher && path.getRawPath().length == 0) { InetAddress remoteHostIP = path.getFirstHopAddress().getAddress(); return channel.send(buffer, new InetSocketAddress(remoteHostIP, Constants.DISPATCHER_PORT)); diff --git a/src/main/java/org/scion/jpan/Constants.java b/src/main/java/org/scion/jpan/Constants.java index 5dc35c628..814739416 100644 --- a/src/main/java/org/scion/jpan/Constants.java +++ b/src/main/java/org/scion/jpan/Constants.java @@ -90,6 +90,12 @@ public final class Constants { public static final String ENV_DNS_SEARCH_DOMAINS = "SCION_DNS_SEARCH_DOMAINS"; + /** Run the SHIM (or not). */ + public static final String PROPERTY_SHIM = "org.scion.shim"; + + public static final String ENV_SHIM = "SCION_SHIM"; + public static final boolean DEFAULT_SHIM = true; + /** * Non-public property that allows specifying DNS TXT entries for debugging. Example with two * entries: server1.com="scion=1-ff00:0:110,127.0.0.1";server2.ch="scion=1-ff00:0:112,::1" diff --git a/src/main/java/org/scion/jpan/PathPolicy.java b/src/main/java/org/scion/jpan/PathPolicy.java index 2c852d77e..2316876a7 100644 --- a/src/main/java/org/scion/jpan/PathPolicy.java +++ b/src/main/java/org/scion/jpan/PathPolicy.java @@ -78,7 +78,7 @@ public Path filter(List paths) { private boolean checkPath(Path path) { for (PathMetadata.PathInterface pif : path.getMetadata().getInterfacesList()) { - int isd = (int) (pif.getIsdAs() >>> 48); + int isd = ScionUtil.extractIsd(pif.getIsdAs()); if (!allowedIsds.contains(isd)) { return false; } @@ -104,7 +104,7 @@ public Path filter(List paths) { private boolean checkPath(Path path) { for (PathMetadata.PathInterface pif : path.getMetadata().getInterfacesList()) { - int isd = (int) (pif.getIsdAs() >>> 48); + int isd = ScionUtil.extractIsd(pif.getIsdAs()); if (disallowedIsds.contains(isd)) { return false; } diff --git a/src/main/java/org/scion/jpan/ScionService.java b/src/main/java/org/scion/jpan/ScionService.java index 6512a613d..3990aa55b 100644 --- a/src/main/java/org/scion/jpan/ScionService.java +++ b/src/main/java/org/scion/jpan/ScionService.java @@ -30,6 +30,7 @@ import static org.scion.jpan.Constants.PROPERTY_DNS_SEARCH_DOMAINS; import static org.scion.jpan.Constants.PROPERTY_USE_OS_SEARCH_DOMAINS; +import com.google.protobuf.Empty; import io.grpc.*; import java.io.IOException; import java.net.InetAddress; @@ -39,7 +40,6 @@ import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Collectors; import org.scion.jpan.internal.*; import org.scion.jpan.proto.control_plane.SegmentLookupServiceGrpc; import org.scion.jpan.proto.daemon.Daemon; @@ -75,6 +75,7 @@ public class ScionService { private final ScionBootstrapper bootstrapper; private final DaemonServiceGrpc.DaemonServiceBlockingStub daemonStub; private final SegmentLookupServiceGrpc.SegmentLookupServiceBlockingStub segmentStub; + private LocalTopology.DispatcherPortRange portRange; private final boolean minimizeRequests; private final ManagedChannel channel; @@ -645,14 +646,28 @@ InetAddress getExternalIP(InetSocketAddress firstHopAddress) { } } - List getBorderRouterStrings() { - if (daemonStub != null) { - return getInterfaces().values().stream() - .map(i -> i.getAddress().getAddress()) - .collect(Collectors.toList()); - } else { - return bootstrapper.getLocalTopology().getBorderRouterAddresses(); + LocalTopology.DispatcherPortRange getLocalPortRange() { + if (portRange == null) { + if (bootstrapper != null) { + portRange = bootstrapper.getLocalTopology().getPortRange(); + } else if (daemonStub != null) { + // try daemon + Daemon.PortRangeResponse response; + try { + response = daemonStub.portRange(Empty.getDefaultInstance()); + portRange = + LocalTopology.DispatcherPortRange.create( + response.getDispatchedPortStart(), response.getDispatchedPortEnd()); + } catch (StatusRuntimeException e) { + LOG.warn("ERROR getting port range from daemon: {}", e.getMessage()); + // Daemon doesn't support port range. + portRange = LocalTopology.DispatcherPortRange.createEmpty(); + } + } else { + portRange = LocalTopology.DispatcherPortRange.createAll(); + } } + return portRange; } InetSocketAddress getBorderRouterAddress(int interfaceID) { diff --git a/src/main/java/org/scion/jpan/ScmpSenderAsync.java b/src/main/java/org/scion/jpan/ScmpSenderAsync.java index 162b79721..1cd8279a9 100644 --- a/src/main/java/org/scion/jpan/ScmpSenderAsync.java +++ b/src/main/java/org/scion/jpan/ScmpSenderAsync.java @@ -33,6 +33,7 @@ public class ScmpSenderAsync implements AutoCloseable { private static final Logger log = LoggerFactory.getLogger(ScmpSenderAsync.class); + private static final int PORT_NOT_SET = -1; private int timeOutMs = 1000; private final InternalChannel channel; private final AtomicInteger sequenceIDs = new AtomicInteger(0); @@ -215,12 +216,8 @@ protected InternalChannel( super.channel().configureBlocking(false); super.channel().register(selector, SelectionKey.OP_READ); - if (port == 0) { - if (Util.getJavaMajorVersion() >= 17) { - super.bind(null); - } else { - throw new IllegalArgumentException("With Java < 17, Please assign a local port >= 0"); - } + if (port == PORT_NOT_SET) { + ensureBound(); } else { // listen on ANY interface: 0.0.0.0 / [::] super.bind(new InetSocketAddress(port)); @@ -406,7 +403,7 @@ public void run() { public static class Builder { private ScionService service; - private int port = -1; + private int port = PORT_NOT_SET; private final ResponseHandler handler; private java.nio.channels.DatagramChannel channel = null; private Selector selector = null; @@ -417,9 +414,6 @@ private Builder(ResponseHandler handler) { public Builder setLocalPort(int localPort) { this.port = localPort; - if (port == 0 && Util.getJavaMajorVersion() < 17) { - log.warn("Using port 0 does likely not work with Java < 17"); - } return this; } @@ -443,13 +437,6 @@ public ScmpSenderAsync build() { try { channel = channel == null ? java.nio.channels.DatagramChannel.open() : channel; selector = selector == null ? Selector.open() : selector; - if (port == -1) { - if (Util.getJavaMajorVersion() >= 17) { - port = 0; - } else { - port = 51315; // Some random port - } - } return new ScmpSenderAsync(service, port, handler, channel, selector); } catch (IOException e) { throw new ScionRuntimeException(e); diff --git a/src/main/java/org/scion/jpan/internal/LocalTopology.java b/src/main/java/org/scion/jpan/internal/LocalTopology.java index 06140cdb0..2fd591a00 100644 --- a/src/main/java/org/scion/jpan/internal/LocalTopology.java +++ b/src/main/java/org/scion/jpan/internal/LocalTopology.java @@ -17,10 +17,12 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import org.scion.jpan.Constants; import org.scion.jpan.ScionRuntimeException; import org.scion.jpan.ScionUtil; @@ -33,6 +35,7 @@ public class LocalTopology { private String localIsdAs; private boolean isCoreAs; private int localMtu; + private DispatcherPortRange portRange; public static synchronized LocalTopology create(String topologyFile) { LocalTopology topo = new LocalTopology(); @@ -71,14 +74,6 @@ public String getBorderRouterAddress(int interfaceId) { throw new ScionRuntimeException("No router found with interface ID " + interfaceId); } - public List getBorderRouterAddresses() { - List result = new ArrayList<>(); - for (BorderRouter br : borderRouters) { - result.add(br.internalAddress); - } - return result; - } - public int getMtu() { return this.localMtu; } @@ -123,6 +118,12 @@ private void parseTopologyFile(String topologyFile) { discoveryServices.add(new ServiceNode(e.getKey(), ds.get("addr").getAsString())); } } + JsonElement dispatchedPorts = o.get("dispatched_ports"); + if (dispatchedPorts == null) { + portRange = DispatcherPortRange.createEmpty(); + } else { + portRange = parsePortRange(dispatchedPorts.getAsString()); + } JsonArray attr = safeGet(o, "attributes").getAsJsonArray(); for (int i = 0; i < attr.size(); i++) { if ("core".equals(attr.get(i).getAsString())) { @@ -132,6 +133,29 @@ private void parseTopologyFile(String topologyFile) { } } + private static DispatcherPortRange parsePortRange(String v) { + if ("-".equals(v)) { + return DispatcherPortRange.createEmpty(); + } else if ("all".equalsIgnoreCase(v)) { + return DispatcherPortRange.createAll(); + } else { + String[] sa = v.split("-"); + if (sa.length != 2) { + throw new ScionRuntimeException("Illegal expression in topo file dispatched_ports: " + v); + } + int portMin = Integer.parseInt(sa[0]); + int portMax = Integer.parseInt(sa[1]); + if (portMin < 1 || portMax < 1 || portMax > 65535 || portMin > portMax) { + throw new ScionRuntimeException("Illegal port values in topo file dispatched_ports: " + v); + } + return DispatcherPortRange.create(portMin, portMax); + } + } + + public DispatcherPortRange getPortRange() { + return portRange; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -231,4 +255,46 @@ public String toString() { return "{" + "name='" + name + '\'' + ", ipString='" + ipString + '\'' + '}'; } } + + public static class DispatcherPortRange { + private final int portMin; + private final int portMax; + + private DispatcherPortRange(int min, int max) { + portMin = min; + portMax = max; + } + + public static DispatcherPortRange create(int min, int max) { + return new DispatcherPortRange(min, max); + } + + public static DispatcherPortRange createAll() { + return new DispatcherPortRange(1, 65535); + } + + public static DispatcherPortRange createEmpty() { + return new DispatcherPortRange(-1, -2); + } + + public boolean hasPortRange() { + return portMin >= 1 && portMax <= 65535 && portMax >= portMin; + } + + public InetSocketAddress mapToLocalPort(InetSocketAddress address) { + if (address.getPort() == Constants.SCMP_PORT + || (address.getPort() >= portMin && address.getPort() <= portMax)) { + return address; + } + return new InetSocketAddress(address.getAddress(), Constants.DISPATCHER_PORT); + } + + public int getPortMin() { + return portMin; + } + + public int getPortMax() { + return portMax; + } + } } diff --git a/src/main/java/org/scion/jpan/internal/Shim.java b/src/main/java/org/scion/jpan/internal/Shim.java index c72ad8d3a..c2546526a 100644 --- a/src/main/java/org/scion/jpan/internal/Shim.java +++ b/src/main/java/org/scion/jpan/internal/Shim.java @@ -59,9 +59,10 @@ private Shim(ScionService service, int port) { public static void install(ScionService service) { synchronized (singleton) { if (singleton.get() == null) { - String flag = - ScionUtil.getPropertyOrEnv(DEBUG_PROPERTY_START_SHIM, DEBUG_ENV_START_SHIM, "true"); - if (flag.equalsIgnoreCase("true")) { + boolean flag = + ScionUtil.getPropertyOrEnv( + Constants.PROPERTY_SHIM, Constants.ENV_SHIM, Constants.DEFAULT_SHIM); + if (flag) { singleton.set(Shim.newBuilder(service).build()); singleton.get().start(); } @@ -105,12 +106,13 @@ private void start() { if (!scmpResponderBarrier.await(100, TimeUnit.MILLISECONDS)) { // ignore log.info("Could not start SHIM: {}", forwarder.getName()); + } else { + log.info("SHIM started."); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new ScionRuntimeException(e); } - log.info("SHIM started."); } private void forwardStarter() { @@ -148,7 +150,7 @@ public void close() throws IOException { public void forward(ByteBuffer buf, DatagramChannel channel) { buf.rewind(); try { - if (forwardCallback != null && forwardCallback.test(buf)) { + if (forwardCallback == null || forwardCallback.test(buf)) { InetSocketAddress dst = ScionHeaderParser.extractDestinationSocketAddress(buf); channel.send(buf, dst); } diff --git a/src/main/proto/scion/protobuf/daemon/v1/daemon.proto b/src/main/proto/scion/protobuf/daemon/v1/daemon.proto index fcd2c4358..4b8aecea6 100644 --- a/src/main/proto/scion/protobuf/daemon/v1/daemon.proto +++ b/src/main/proto/scion/protobuf/daemon/v1/daemon.proto @@ -21,6 +21,7 @@ package proto.daemon.v1; import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; +import "google/protobuf/empty.proto"; import "scion/protobuf/drkey/v1/drkey.proto"; service DaemonService { @@ -36,6 +37,8 @@ service DaemonService { rpc Services(ServicesRequest) returns (ServicesResponse) {} // Inform the SCION Daemon of a revocation. rpc NotifyInterfaceDown(NotifyInterfaceDownRequest) returns (NotifyInterfaceDownResponse) {} + // Returns the endhost portRange defined in the local AS. + rpc PortRange(google.protobuf.Empty) returns (PortRangeResponse) {} // DRKeyASHost returns a key that matches the request. rpc DRKeyASHost (DRKeyASHostRequest) returns (DRKeyASHostResponse) {} // DRKeyHostAS returns a key that matches the request. @@ -75,7 +78,8 @@ message Path { // Latency lists the latencies between any two consecutive interfaces. // Entry i describes the latency between interface i and i+1. // Consequently, there are N-1 entries for N interfaces. - // A 0-value indicates that the AS did not announce a latency for this hop. + // A negative value indicates that the AS did not announce a latency for + // this hop. repeated google.protobuf.Duration latency = 6; // Bandwidth lists the bandwidth between any two consecutive interfaces, in // Kbit/s. @@ -200,6 +204,13 @@ message NotifyInterfaceDownRequest { message NotifyInterfaceDownResponse {}; +message PortRangeResponse { + // The lowest port in the SCION/UDP dispatched port range. + uint32 dispatched_port_start = 1; + // The highest port in the SCION/UDP dispatched port range. + uint32 dispatched_port_end = 2; +} + message DRKeyHostASRequest{ // Point in time where requested key is valid. google.protobuf.Timestamp val_time = 1; diff --git a/src/test/java/org/scion/jpan/api/DatagramChannelApiConcurrencyTest.java b/src/test/java/org/scion/jpan/api/DatagramChannelApiConcurrencyTest.java index 256e73131..479b3efea 100644 --- a/src/test/java/org/scion/jpan/api/DatagramChannelApiConcurrencyTest.java +++ b/src/test/java/org/scion/jpan/api/DatagramChannelApiConcurrencyTest.java @@ -34,7 +34,7 @@ class DatagramChannelApiConcurrencyTest { - private static final int dummyPort = 44444; + private static final int dummyPort = 32000; private static final InetAddress dummyIPv4; private static final InetSocketAddress dummyAddress; @@ -143,7 +143,6 @@ private void concurrentReceive(Reader c1, Reader c2, Writer w1, boolean connect) synchronized (receiveCount) { receiveCount.wait(1000); } - assertEquals(2, receiveCount.get()); } catch (InterruptedException e) { throw new RuntimeException(e); } finally { @@ -151,6 +150,7 @@ private void concurrentReceive(Reader c1, Reader c2, Writer w1, boolean connect) r2.interrupt(); s1.interrupt(); } + assertEquals(2, receiveCount.get()); } } } diff --git a/src/test/java/org/scion/jpan/api/DatagramChannelApiServerTest.java b/src/test/java/org/scion/jpan/api/DatagramChannelApiServerTest.java index e0c84e298..63d75d63a 100644 --- a/src/test/java/org/scion/jpan/api/DatagramChannelApiServerTest.java +++ b/src/test/java/org/scion/jpan/api/DatagramChannelApiServerTest.java @@ -112,6 +112,7 @@ void send_withNullService() throws IOException { // check that send(Path) does not internally require a ScionService. ScionService.closeDefault(); try (ScionDatagramChannel channel = ScionDatagramChannel.open(null)) { + channel.bind(new InetSocketAddress("127.0.0.1", 12345)); assertNull(channel.getService()); ResponsePath path = PackageVisibilityHelper.createDummyResponsePath( @@ -139,6 +140,7 @@ void receive_withNullService() throws IOException { return addr; }); try (ScionDatagramChannel channel = ScionDatagramChannel.open(null, mock)) { + channel.bind(new InetSocketAddress("127.0.0.1", 12345)); assertNull(channel.getService()); ByteBuffer buffer = ByteBuffer.allocate(100); ScionSocketAddress responseAddress = channel.receive(buffer); diff --git a/src/test/java/org/scion/jpan/api/DatagramChannelApiTest.java b/src/test/java/org/scion/jpan/api/DatagramChannelApiTest.java index 1b6b5a83c..b7d24ceba 100644 --- a/src/test/java/org/scion/jpan/api/DatagramChannelApiTest.java +++ b/src/test/java/org/scion/jpan/api/DatagramChannelApiTest.java @@ -101,6 +101,7 @@ void getLocalAddress_withConnect() throws IOException { try (ScionDatagramChannel channel = ScionDatagramChannel.open()) { channel.connect(dummyAddress); InetSocketAddress local = channel.getLocalAddress(); + assertNotNull(local); assertFalse(local.getAddress().isAnyLocalAddress()); } } diff --git a/src/test/java/org/scion/jpan/api/DatagramSocketApiConcurrencyTest.java b/src/test/java/org/scion/jpan/api/DatagramSocketApiConcurrencyTest.java index 2b7d5a680..ce1d90b08 100644 --- a/src/test/java/org/scion/jpan/api/DatagramSocketApiConcurrencyTest.java +++ b/src/test/java/org/scion/jpan/api/DatagramSocketApiConcurrencyTest.java @@ -36,7 +36,7 @@ class DatagramSocketApiConcurrencyTest { - private static final int dummyPort = 44444; + private static final int dummyPort = 32000; private static final InetAddress dummyIPv4; private static final InetSocketAddress dummyAddress; diff --git a/src/test/java/org/scion/jpan/api/ScionTest.java b/src/test/java/org/scion/jpan/api/ScionTest.java index 362e42f6c..0bc97734d 100644 --- a/src/test/java/org/scion/jpan/api/ScionTest.java +++ b/src/test/java/org/scion/jpan/api/ScionTest.java @@ -213,8 +213,7 @@ void defaultService_bootstrapTopoFile_withoutDiscovery() { try { MockNetwork.startTiny(MockNetwork.Mode.AS_ONLY); // String file = "topologies/scionproto-tiny/topology-110.json" - String file = "topologies/no-discovery.json"; - System.setProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, file); + System.setProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, "topologies/no-discovery.json"); ScionService service = Scion.defaultService(); Path path = service.getPaths(dstIA, dstAddress).get(0); assertNotNull(path); @@ -224,6 +223,121 @@ void defaultService_bootstrapTopoFile_withoutDiscovery() { } } + @Test + void defaultService_bootstrapTopoFile_dispatcherPortRange_ignoredByExplicitPort() + throws IOException { + long dstIA = ScionUtil.parseIA("1-ff00:0:112"); + InetSocketAddress dstAddress = new InetSocketAddress("::1", 12345); + MockNetwork.startTiny(MockNetwork.Mode.AS_ONLY); + System.setProperty( + Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, "topologies/dispatcher-port-range.json"); + ScionService service = Scion.defaultService(); + Path path = service.getPaths(dstIA, dstAddress).get(0); + try (ScionDatagramChannel channel = ScionDatagramChannel.open()) { + channel.bind(new InetSocketAddress(12321)); + channel.connect(path); + assertEquals(12321, channel.getLocalAddress().getPort()); + } finally { + MockNetwork.stopTiny(); + } + } + + @Test + void defaultService_bootstrapTopoFile_dispatcherPortRange() throws IOException { + long dstIA = ScionUtil.parseIA("1-ff00:0:112"); + InetSocketAddress dstAddress = new InetSocketAddress("::1", 12345); + MockNetwork.startTiny(MockNetwork.Mode.AS_ONLY); + System.setProperty( + Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, "topologies/dispatcher-port-range.json"); + ScionService service = Scion.defaultService(); + Path path = service.getPaths(dstIA, dstAddress).get(0); + try (ScionDatagramChannel channel = ScionDatagramChannel.open()) { + channel.connect(path); + assertEquals(31000, channel.getLocalAddress().getPort()); + + try (ScionDatagramChannel channel2 = ScionDatagramChannel.open()) { + channel2.connect(path); + assertEquals(31001, channel2.getLocalAddress().getPort()); + } + } finally { + MockNetwork.stopTiny(); + } + } + + @Test + void defaultService_bootstrapTopoFile_dispatcherPortRange_EMPTY() throws IOException { + testDefaultService_bootstrapTopoFile_dispatcherPortRange( + "topologies/dispatcher-port-range-empty.json"); + } + + @Test + void defaultService_bootstrapTopoFile_dispatcherPortRange_NONE() throws IOException { + testDefaultService_bootstrapTopoFile_dispatcherPortRange( + "topologies/dispatcher-port-range-none.json"); + } + + private void testDefaultService_bootstrapTopoFile_dispatcherPortRange(String topoFile) + throws IOException { + long dstIA = ScionUtil.parseIA("1-ff00:0:112"); + InetSocketAddress dstAddress = new InetSocketAddress("::1", 12345); + MockNetwork.startTiny(MockNetwork.Mode.AS_ONLY); + System.setProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, topoFile); + ScionService service = Scion.defaultService(); + Path path = service.getPaths(dstIA, dstAddress).get(0); + // stop here to free up port 30041 for the SHIM + MockNetwork.stopTiny(); + try (ScionDatagramChannel channel = ScionDatagramChannel.open()) { + channel.connect(path); + // use ephemeral port + assertTrue( + 32768 <= channel.getLocalAddress().getPort(), + "port=" + channel.getLocalAddress().getPort()); + + try (ScionDatagramChannel channel2 = ScionDatagramChannel.open()) { + channel2.connect(path); + // use ephemeral port + assertTrue( + 32768 <= channel2.getLocalAddress().getPort(), + "port=" + channel2.getLocalAddress().getPort()); + assertNotEquals(channel.getLocalAddress().getPort(), channel2.getLocalAddress().getPort()); + } + } finally { + MockNetwork.stopTiny(); + } + } + + @Test + void defaultService_bootstrapTopoFile_dispatcherPortRange_ALL() throws IOException { + long dstIA = ScionUtil.parseIA("1-ff00:0:112"); + InetSocketAddress dstAddress = new InetSocketAddress("::1", 12345); + MockNetwork.startTiny(MockNetwork.Mode.AS_ONLY); + System.setProperty( + Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, "topologies/dispatcher-port-range-all.json"); + ScionService service = Scion.defaultService(); + Path path = service.getPaths(dstIA, dstAddress).get(0); + try (ScionDatagramChannel channel = ScionDatagramChannel.open()) { + channel.connect(path); + // just any port + assertNotNull(channel.getLocalAddress()); + + try (ScionDatagramChannel channel2 = ScionDatagramChannel.open()) { + channel2.connect(path); + assertNotNull(channel2.getLocalAddress()); + assertNotEquals(channel.getLocalAddress().getPort(), channel2.getLocalAddress().getPort()); + } + } finally { + MockNetwork.stopTiny(); + } + } + + @Test + void defaultService_bootstrapTopoFile_dispatcherPortRange_Illegal() { + System.setProperty( + Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, "topologies/dispatcher-port-range-bad.json"); + Throwable t = assertThrows(Throwable.class, Scion::defaultService); + assertTrue(t.getMessage().contains("Illegal port values in topo file")); + } + @Test void defaultService_bootstrapTopoFile_IOError_NoSuchFile() { System.setProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, TOPO_FILE + ".x"); @@ -392,14 +506,31 @@ void newServiceWithTopoFile() throws IOException { /** See Issue #72: ... */ @Test - void defaultService_bootstrapTopoFile_ScionProto_11() { + void defaultService_bootstrapTopoFile_ScionProto_0_10() { long dstIA = ScionUtil.parseIA("1-ff00:0:112"); InetSocketAddress dstAddress = new InetSocketAddress("::1", 12345); try { MockNetwork.startTiny(MockNetwork.Mode.BOOTSTRAP); System.setProperty( - Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, - "topologies/scionproto-tiny/topology-scionproto-0.11.json"); + Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, "topologies/topology-scionproto-0.10.json"); + ScionService service = Scion.defaultService(); + Path path = service.getPaths(dstIA, dstAddress).get(0); + assertNotNull(path); + assertEquals(0, MockDaemon.getAndResetCallCount()); // Daemon is not used! + } finally { + MockNetwork.stopTiny(); + } + } + + /** See Issue #72: ... */ + @Test + void defaultService_bootstrapTopoFile_ScionProto_0_11() { + long dstIA = ScionUtil.parseIA("1-ff00:0:112"); + InetSocketAddress dstAddress = new InetSocketAddress("::1", 12345); + MockNetwork.startTiny(MockNetwork.Mode.BOOTSTRAP); + try { + System.setProperty( + Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, "topologies/topology-scionproto-0.11.json"); ScionService service = Scion.defaultService(); Path path = service.getPaths(dstIA, dstAddress).get(0); assertNotNull(path); diff --git a/src/test/java/org/scion/jpan/api/ScmpChannelTest.java b/src/test/java/org/scion/jpan/api/ScmpChannelTest.java index 1cd55b1c2..2525f3455 100644 --- a/src/test/java/org/scion/jpan/api/ScmpChannelTest.java +++ b/src/test/java/org/scion/jpan/api/ScmpChannelTest.java @@ -26,11 +26,11 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; import java.util.function.Supplier; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.scion.jpan.*; import org.scion.jpan.demo.inspector.ScionPacketInspector; import org.scion.jpan.internal.ScionHeaderParser; +import org.scion.jpan.internal.Shim; import org.scion.jpan.testutil.MockNetwork; import org.scion.jpan.testutil.MockScmpHandler; @@ -78,10 +78,17 @@ public class ScmpChannelTest { 0, 0, 117, 89, }; + @BeforeEach + void beforeEach() { + System.setProperty(Constants.PROPERTY_SHIM, "false"); + Shim.uninstall(); + } + @AfterAll public static void afterAll() { // Defensive clean up ScionService.closeDefault(); + System.clearProperty(Constants.PROPERTY_SHIM); } @Test @@ -102,10 +109,11 @@ void echo() throws IOException { testEcho(this::getPathTo112); } + @Disabled @Test void echo_localAS_BR() throws IOException { testEcho(this::getPathToLocalAS_BR); - assertEquals(2, MockNetwork.getAndResetForwardCount()); + assertEquals(0, MockNetwork.getAndResetForwardCount()); assertEquals(1, MockScmpHandler.getAndResetAnswerTotal()); } diff --git a/src/test/java/org/scion/jpan/api/ScmpResponderTest.java b/src/test/java/org/scion/jpan/api/ScmpResponderTest.java index a4ca44e46..762a3a550 100644 --- a/src/test/java/org/scion/jpan/api/ScmpResponderTest.java +++ b/src/test/java/org/scion/jpan/api/ScmpResponderTest.java @@ -21,11 +21,14 @@ import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.function.Predicate; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.scion.jpan.*; +import org.scion.jpan.internal.Shim; import org.scion.jpan.testutil.ManagedThread; import org.scion.jpan.testutil.ManagedThreadNews; import org.scion.jpan.testutil.MockNetwork; @@ -34,10 +37,17 @@ public class ScmpResponderTest { private static final ConcurrentLinkedQueue errors = new ConcurrentLinkedQueue<>(); + @BeforeAll + public static void beforeAll() { + System.setProperty(Constants.PROPERTY_SHIM, "false"); + Shim.uninstall(); + } + @AfterAll public static void afterAll() { // Defensive clean up ScionService.closeDefault(); + System.clearProperty(Constants.PROPERTY_SHIM); } @AfterEach diff --git a/src/test/java/org/scion/jpan/api/ScmpSenderAsyncTest.java b/src/test/java/org/scion/jpan/api/ScmpSenderAsyncTest.java index 244742aef..bf6a717ff 100644 --- a/src/test/java/org/scion/jpan/api/ScmpSenderAsyncTest.java +++ b/src/test/java/org/scion/jpan/api/ScmpSenderAsyncTest.java @@ -27,12 +27,11 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.scion.jpan.*; import org.scion.jpan.demo.inspector.ScionPacketInspector; import org.scion.jpan.internal.ScionHeaderParser; +import org.scion.jpan.internal.Shim; import org.scion.jpan.testutil.MockDatagramChannel; import org.scion.jpan.testutil.MockNetwork; import org.scion.jpan.testutil.MockScmpHandler; @@ -83,10 +82,17 @@ public class ScmpSenderAsyncTest { private static final ConcurrentLinkedQueue errors = new ConcurrentLinkedQueue<>(); + @BeforeEach + void beforeEach() { + System.setProperty(Constants.PROPERTY_SHIM, "false"); + Shim.uninstall(); + } + @AfterAll public static void afterAll() { // Defensive clean up ScionService.closeDefault(); + System.clearProperty(Constants.PROPERTY_SHIM); } @AfterEach @@ -145,8 +151,10 @@ void sendEcho() throws IOException { @Test void sendEcho_localAS_BR() throws IOException { + // Q: does this test make sense? We send a ping to port 30555.... should that really work? int n = 5; testEcho(this::getPathToLocalAS_BR, n); + // These are sent to the daemon port 31004 which is in the unmapped port range. assertEquals(2 * n, MockNetwork.getAndResetForwardCount()); assertEquals(n, MockScmpHandler.getAndResetAnswerTotal()); } @@ -195,7 +203,8 @@ void sendEcho_async() throws IOException { void sendEcho_localAS_BR_async() throws IOException { int n = 25; testEchoAsync(this::getPathToLocalAS_BR, n); - assertEquals(2 * n, MockNetwork.getAndResetForwardCount()); + // These are sent to the daemon port 31004 which is in the unmapped port range. + assertEquals(2 * n, MockNetwork.getAndResetForwardCount()); // 1! assertEquals(n, MockScmpHandler.getAndResetAnswerTotal()); } diff --git a/src/test/java/org/scion/jpan/api/ScmpSenderTest.java b/src/test/java/org/scion/jpan/api/ScmpSenderTest.java index c84104782..f4b48e1a7 100644 --- a/src/test/java/org/scion/jpan/api/ScmpSenderTest.java +++ b/src/test/java/org/scion/jpan/api/ScmpSenderTest.java @@ -25,12 +25,11 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; import java.util.function.Supplier; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.scion.jpan.*; import org.scion.jpan.demo.inspector.ScionPacketInspector; import org.scion.jpan.demo.inspector.ScmpHeader; +import org.scion.jpan.internal.Shim; import org.scion.jpan.testutil.MockDatagramChannel; import org.scion.jpan.testutil.MockNetwork; import org.scion.jpan.testutil.MockScmpHandler; @@ -38,10 +37,17 @@ public class ScmpSenderTest { private static final ConcurrentLinkedQueue errors = new ConcurrentLinkedQueue<>(); + @BeforeEach + void beforeEach() { + System.setProperty(Constants.PROPERTY_SHIM, "false"); + Shim.uninstall(); + } + @AfterAll public static void afterAll() { // Defensive clean up ScionService.closeDefault(); + System.clearProperty(Constants.PROPERTY_SHIM); } @AfterEach @@ -82,7 +88,9 @@ void sendEcho() throws IOException { @Test void sendEcho_localAS_BR() throws IOException { + // Q: does this test make sense? We send a ping to port 30555.... should that really work? testEcho(this::getPathToLocalAS_BR); + // These are sent to the daemon port 31004 which is in the unmapped port range. assertEquals(2, MockNetwork.getAndResetForwardCount()); assertEquals(1, MockScmpHandler.getAndResetAnswerTotal()); } diff --git a/src/test/java/org/scion/jpan/demo/ScmpEchoDemo.java b/src/test/java/org/scion/jpan/demo/ScmpEchoDemo.java index 5d1c18585..589e0dcd4 100644 --- a/src/test/java/org/scion/jpan/demo/ScmpEchoDemo.java +++ b/src/test/java/org/scion/jpan/demo/ScmpEchoDemo.java @@ -19,7 +19,6 @@ import java.nio.ByteBuffer; import java.util.List; import org.scion.jpan.*; -import org.scion.jpan.testutil.MockDNS; import org.scion.jpan.testutil.MockScmpHandler; import org.scion.jpan.testutil.Scenario; @@ -41,7 +40,6 @@ public class ScmpEchoDemo { public static boolean PRINT = true; private static int REPEAT = 10; public static Network NETWORK = Network.PRODUCTION; - private final int localPort; public enum Network { JUNIT_MOCK_V4, // SCION Java JUnit mock network with local AS = 1-ff00:0:112 @@ -56,14 +54,6 @@ public static void init(boolean print, Network network, int repeat) { REPEAT = repeat; } - private ScmpEchoDemo() { - this(12345); - } - - private ScmpEchoDemo(int localPort) { - this.localPort = localPort; - } - public static void main(String[] args) throws IOException { try { run(); @@ -76,23 +66,21 @@ public static int run() throws IOException { switch (NETWORK) { case JUNIT_MOCK_V4: { + System.setProperty(Constants.PROPERTY_SHIM, "false"); // disable SHIM DemoTopology.configureMockV4(); - InetAddress remote = MockScmpHandler.getAddress().getAddress(); - MockDNS.install("1-ff00:0:112", "localhost", remote.toString()); - ScmpEchoDemo demo = new ScmpEchoDemo(); InetSocketAddress dst = MockScmpHandler.getAddress(); - int n = demo.runDemo(Scion.defaultService().getPaths(DemoConstants.ia112, dst).get(0)); + int n = runDemo(Scion.defaultService().getPaths(DemoConstants.ia112, dst).get(0)); DemoTopology.shutDown(); return n; } case JUNIT_MOCK_V6: { + System.setProperty(Constants.PROPERTY_SHIM, "false"); // disable SHIM DemoTopology.configureMockV6(); - ScmpEchoDemo demo = new ScmpEchoDemo(); MockScmpHandler.stop(); MockScmpHandler.start("[::1]"); // We need the SCMP handler running on IPv6 InetSocketAddress dst = MockScmpHandler.getAddress(); - int n = demo.runDemo(Scion.defaultService().getPaths(DemoConstants.ia112, dst).get(0)); + int n = runDemo(Scion.defaultService().getPaths(DemoConstants.ia112, dst).get(0)); DemoTopology.shutDown(); MockScmpHandler.stop(); return n; @@ -116,40 +104,41 @@ public static int run() throws IOException { System.setProperty(Constants.PROPERTY_DAEMON, scenario.getDaemon(srcIsdAs)); } - // Use a port from the dispatcher compatibility range - ScmpEchoDemo demo = new ScmpEchoDemo(32766); // Ping the dispatcher/shim. It listens on the same IP as the control service. InetAddress ip = scenario.getControlServer(dstIsdAs).getAddress(); // Get paths List paths = Scion.defaultService().getPaths(dstIsdAs, ip, Constants.SCMP_PORT); - return demo.runDemo(PathPolicy.MIN_HOPS.filter(paths)); + return runDemo(PathPolicy.MIN_HOPS.filter(paths)); } case PRODUCTION: { - ScmpEchoDemo demo = new ScmpEchoDemo(); ScionService service = Scion.defaultService(); - return demo.runDemo(service.lookupAndGetPath("ethz.ch", Constants.SCMP_PORT, null)); + return runDemo(service.lookupAndGetPath("ethz.ch", Constants.SCMP_PORT, null)); } } return -1; } - private int runDemo(Path path) throws IOException { + private static int runDemo(Path path) throws IOException { ByteBuffer data = ByteBuffer.allocate(0); - println("Listening on port " + localPort + " ..."); + String localAddress; try (ScionDatagramChannel channel = ScionDatagramChannel.open()) { channel.connect(path); - println("Resolved local address: "); - println(" " + channel.getLocalAddress().getAddress().getHostAddress()); + // We determine the address separately because SCMP will always have 0.0.0.0 as local address + localAddress = channel.getLocalAddress().getAddress().getHostAddress(); } - printPath(path); int n = 0; - try (ScmpSender scmpChannel = Scmp.newSenderBuilder().setLocalPort(localPort).build()) { + try (ScmpSender sender = Scmp.newSenderBuilder().build()) { + println("Listening on port " + sender.getLocalAddress().getPort() + " ..."); + println("Resolved local address: "); + println(" " + localAddress); + printPath(path); + for (int i = 0; i < REPEAT; i++) { - Scmp.EchoMessage msg = scmpChannel.sendEchoRequest(path, data); + Scmp.EchoMessage msg = sender.sendEchoRequest(path, data); if (i == 0) { printHeader(path.getRemoteSocketAddress(), data, msg); } @@ -177,7 +166,7 @@ private int runDemo(Path path) throws IOException { return n; } - private void printPath(Path path) { + 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(); @@ -185,9 +174,9 @@ private void printPath(Path path) { println(sb); } - private void printHeader(ScionSocketAddress dstAddress, ByteBuffer data, Scmp.EchoMessage msg) { - String sb = "PING " + ScionUtil.toStringIA(dstAddress.getIsdAs()) + ","; - sb += dstAddress.getHostString() + ":" + dstAddress.getPort() + " pld=" + data.remaining(); + private static void printHeader(ScionSocketAddress dst, ByteBuffer data, Scmp.EchoMessage msg) { + String sb = "PING " + ScionUtil.toStringIA(dst.getIsdAs()) + ","; + sb += dst.getHostString() + ":" + dst.getPort() + " pld=" + data.remaining(); sb += "B scion_pkt=" + msg.getSizeSent() + "B"; println(sb); } diff --git a/src/test/java/org/scion/jpan/demo/ScmpTracerouteDemo.java b/src/test/java/org/scion/jpan/demo/ScmpTracerouteDemo.java index 1e8322fd8..edcb7fab5 100644 --- a/src/test/java/org/scion/jpan/demo/ScmpTracerouteDemo.java +++ b/src/test/java/org/scion/jpan/demo/ScmpTracerouteDemo.java @@ -38,7 +38,6 @@ public class ScmpTracerouteDemo { public static boolean PRINT = true; public static Network NETWORK = Network.PRODUCTION; - private final int localPort; public enum Network { JUNIT_MOCK_V4, // SCION Java JUnit mock network with local AS = 1-ff00:0:112 @@ -52,14 +51,6 @@ public static void init(boolean print, ScmpTracerouteDemo.Network network) { NETWORK = network; } - public ScmpTracerouteDemo() { - this(12345); // Any port is fine unless we connect to a dispatcher network - } - - public ScmpTracerouteDemo(int localPort) { - this.localPort = localPort; - } - public static void main(String[] args) throws IOException { try { run(); @@ -72,50 +63,49 @@ public static int run() throws IOException { switch (NETWORK) { case JUNIT_MOCK_V4: { + System.setProperty(Constants.PROPERTY_SHIM, "false"); // disable SHIM DemoTopology.configureMockV4(); InetAddress remote = MockScmpHandler.getAddress().getAddress(); MockDNS.install("1-ff00:0:112", "localhost", remote.toString()); - ScmpTracerouteDemo demo = new ScmpTracerouteDemo(); - int n = demo.runDemo(DemoConstants.ia112); + int n = runDemo(DemoConstants.ia112); DemoTopology.shutDown(); return n; } case JUNIT_MOCK_V6: { + System.setProperty(Constants.PROPERTY_SHIM, "false"); // disable SHIM DemoTopology.configureMockV6(); MockDNS.install("1-ff00:0:112", "ip6-localhost", "::1"); MockScmpHandler.stop(); MockScmpHandler.start("[::1]"); // We need the SCMP handler running on IPv6 - ScmpTracerouteDemo demo = new ScmpTracerouteDemo(); - int n = demo.runDemo(DemoConstants.ia112); + int n = runDemo(DemoConstants.ia112); DemoTopology.shutDown(); MockScmpHandler.stop(); return n; } case SCION_PROTO: { + // Alternative: Use topo file i.o. daemon // System.setProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, // "topologies/minimal/ASff00_0_1111/topology.json"); System.setProperty(Constants.PROPERTY_DAEMON, DemoConstants.daemon1111_minimal); - // Use a port from the dispatcher compatibility range - ScmpTracerouteDemo demo = new ScmpTracerouteDemo(32766); - demo.runDemo(DemoConstants.ia211); - demo.runDemo(DemoConstants.ia111); - return demo.runDemo(DemoConstants.ia1111); + runDemo(DemoConstants.ia211); + runDemo(DemoConstants.ia111); + return runDemo(DemoConstants.ia1111); } case PRODUCTION: { - ScmpTracerouteDemo demo = new ScmpTracerouteDemo(); - return demo.runDemo(ScionUtil.parseIA("64-2:0:44")); // VEX - // demo.runDemo(ScionUtil.parseIA("66-2:0:10")); //singapore - // demo.runDemo(DemoConstants.iaAnapayaHK); - // demo.runDemo(DemoConstants.iaOVGU); + return runDemo(ScionUtil.parseIA("64-2:0:44")); // VEX + // runDemo(ScionUtil.parseIA("66-2:0:10")); //singapore + // runDemo(DemoConstants.iaAnapayaHK); + // runDemo(DemoConstants.iaOVGU); } + default: + return -1; } - return -1; } - private int runDemo(long destinationIA) throws IOException { + private static int runDemo(long destinationIA) throws IOException { ScionService service = Scion.defaultService(); // Dummy address. The traceroute will contact the control service IP instead. InetSocketAddress destinationAddress = @@ -128,18 +118,21 @@ private int runDemo(long destinationIA) throws IOException { } Path path = paths.get(0); - println("Listening on port " + localPort + " ..."); + String localAddress; try (ScionDatagramChannel channel = ScionDatagramChannel.open()) { channel.connect(path); - println("Resolved local address: "); - println(" " + channel.getLocalAddress().getAddress().getHostAddress()); + // We determine the address separately because SCMP will always have 0.0.0.0 as local address + localAddress = channel.getLocalAddress().getAddress().getHostAddress(); } - printPath(path); + try (ScmpSender sender = Scmp.newSenderBuilder().build()) { + println("Listening on port " + sender.getLocalAddress().getPort() + " ..."); + println("Resolved local address: "); + println(" " + localAddress); + printPath(path); - int n = 0; - try (ScmpSender scmpChannel = Scmp.newSenderBuilder().setLocalPort(localPort).build()) { - List results = scmpChannel.sendTracerouteRequest(path); + List results = sender.sendTracerouteRequest(path); + int n = 0; for (Scmp.TracerouteMessage msg : results) { if (!msg.isTimedOut()) { n++; @@ -152,11 +145,11 @@ private int runDemo(long destinationIA) throws IOException { out += " " + millis + "ms"; println(out); } + return n; } - return n; } - private void printPath(Path path) { + private static void printPath(Path path) { String nl = System.lineSeparator(); StringBuilder sb = new StringBuilder(); // sb.append("Actual local address:").append(nl); diff --git a/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java b/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java index 6919c1a6f..ab17511a6 100644 --- a/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java +++ b/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java @@ -38,7 +38,6 @@ public class ShowpathsDemo { public static boolean PRINT = true; public static Network NETWORK = Network.PRODUCTION; - private final int localPort; public enum Network { JUNIT_MOCK_V4, // SCION Java JUnit mock network with local AS = 1-ff00:0:112 @@ -52,14 +51,6 @@ public static void init(boolean print, ShowpathsDemo.Network network) { NETWORK = network; } - public ShowpathsDemo() { - this(12345); // Any port is fine unless we connect to a dispatcher network - } - - public ShowpathsDemo(int localPort) { - this.localPort = localPort; - } - public static void main(String[] args) throws IOException { try { run(); @@ -84,8 +75,7 @@ public static int run() throws IOException { { DemoTopology.configureMockV6(); MockDNS.install("1-ff00:0:112", "ip6-localhost", "::1"); - ShowpathsDemo demo = new ShowpathsDemo(); - int n = demo.runDemo(DemoConstants.ia110); + int n = runDemo(DemoConstants.ia110); DemoTopology.shutDown(); return n; } @@ -94,21 +84,20 @@ public static int run() throws IOException { // System.setProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, // "topologies/minimal/ASff00_0_1111/topology.json"); System.setProperty(Constants.PROPERTY_DAEMON, DemoConstants.daemon1111_minimal); - ShowpathsDemo demo = new ShowpathsDemo(); - return demo.runDemo(DemoConstants.ia211); + runDemo(DemoConstants.ia211); + break; } case PRODUCTION: { - // Local port must be 30041 for networks that expect a dispatcher - ShowpathsDemo demo = new ShowpathsDemo(Constants.DISPATCHER_PORT); - return demo.runDemo(DemoConstants.iaAnapayaHK); - // demo.runDemo(DemoConstants.iaOVGU); + runDemo(DemoConstants.iaAnapayaHK); + // runDemo(DemoConstants.iaOVGU); + break; } } return -1; } - private int runDemo(long destinationIA) throws IOException { + private static int runDemo(long destinationIA) throws IOException { ScionService service = Scion.defaultService(); // dummy address InetSocketAddress destinationAddress = @@ -120,7 +109,6 @@ private int runDemo(long destinationIA) throws IOException { throw new IOException("No path found from " + src + " to " + dst); } - println("Listening on port " + localPort + " ..."); println("Available paths to " + ScionUtil.toStringIA(destinationIA)); int id = 0; diff --git a/src/test/java/org/scion/jpan/internal/ShimTest.java b/src/test/java/org/scion/jpan/internal/ShimTest.java index f11ebfa9c..576ea0a67 100644 --- a/src/test/java/org/scion/jpan/internal/ShimTest.java +++ b/src/test/java/org/scion/jpan/internal/ShimTest.java @@ -135,35 +135,45 @@ private Path createDummyPath(int serverPort) { void testForwardingUDP() { PingPongChannelHelper.Server serverFn = this::server; PingPongChannelHelper.Client clientFn = this::client; - // we are circumventing the daemon! -> checkCounters(false) + // TODO remove this once we have server-side portRanges + // we are circumventing the daemon! -> checkCounters(false) // TODO the serverService(null) should be removed once SHIM becomes the default PingPongChannelHelper pph = PingPongChannelHelper.newBuilder(1, 2, 10).checkCounters(false).serverService(null).build(); pph.runPingPong(serverFn, clientFn); assertTrue(Shim.isInstalled()); assertEquals(2 * 2 * 10, shimForwardingCounter.getAndSet(0)); + // TODO should be 40 + assertEquals(1 * 2 * 10, MockNetwork.getAndResetForwardCount()); } - @Disabled @Test - void testForwardingUDP_LocalAS() { + void testForwardingUDP_LocalAS_remoteInsideRange() { + testForwardingUDP_LocalAS(31000, 0); + } + + @Test + void testForwardingUDP_LocalAS_remoteOutsideRange() { + testForwardingUDP_LocalAS(45678, 2 * 2 * 10); + } + + void testForwardingUDP_LocalAS(int port, int expectedShimCount) { PingPongChannelHelper.Server serverFn = this::server; PingPongChannelHelper.Client clientFn = this::client; + // we are circumventing the daemon! -> checkCounters(false) PingPongChannelHelper pph = PingPongChannelHelper.newBuilder(1, 2, 10) .checkCounters(false) .serverIsdAs(MockNetwork.TINY_CLIENT_ISD_AS) + .serverBindAddress(new InetSocketAddress("127.0.0.1", port)) .build(); pph.runPingPong(serverFn, clientFn); assertTrue(Shim.isInstalled()); - assertEquals(2 * 2 * 10, shimForwardingCounter.getAndSet(0)); + assertEquals(expectedShimCount, shimForwardingCounter.getAndSet(0)); + assertEquals(0, MockNetwork.getAndResetForwardCount()); } public void client(ScionDatagramChannel channel, Path serverAddress, int id) throws IOException { - // Stop the SCMP responder on 30041 - MockScmpHandler.stop(); - // Install the SHIM - Shim.install(Scion.defaultService()); assertTrue(Shim.isInstalled()); // Set callback Shim.setCallback( @@ -171,13 +181,6 @@ public void client(ScionDatagramChannel channel, Path serverAddress, int id) thr shimForwardingCounter.incrementAndGet(); return true; }); - // ensure that packets are sent to 30041 - channel.configureRemoteDispatcher(true); - - // overwrite path with a new path that stays in the local AS. - // We need to do that because the PingPong utility usually assumes separate ASes but that - // would not work with configureRemoteDispatcher(). - serverAddress = createDummyPath(serverAddress.getRemotePort()); // wait for server to start try { diff --git a/src/test/java/org/scion/jpan/testutil/AsInfo.java b/src/test/java/org/scion/jpan/testutil/AsInfo.java index 6228c4776..0d6ec19ef 100644 --- a/src/test/java/org/scion/jpan/testutil/AsInfo.java +++ b/src/test/java/org/scion/jpan/testutil/AsInfo.java @@ -14,22 +14,37 @@ package org.scion.jpan.testutil; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import org.scion.jpan.ScionRuntimeException; import org.scion.jpan.ScionUtil; +import org.scion.jpan.internal.LocalTopology; public class AsInfo { private long isdAs; private String controlServer; + private LocalTopology.DispatcherPortRange portRange; private final List borderRouters = new ArrayList<>(); void setIsdAs(long isdAs) { this.isdAs = isdAs; } + void setPortRange(LocalTopology.DispatcherPortRange portRange) { + this.portRange = portRange; + } + + LocalTopology.DispatcherPortRange getPortRange() { + return this.portRange; + } + + public InetSocketAddress mapDispatcherPorts(InetSocketAddress address) { + return portRange.mapToLocalPort(address); + } + void add(BorderRouter borderRouter) { borderRouters.add(borderRouter); } diff --git a/src/test/java/org/scion/jpan/testutil/JUnitSetUp.java b/src/test/java/org/scion/jpan/testutil/JUnitSetUp.java index a66f7c373..ce800b4ca 100644 --- a/src/test/java/org/scion/jpan/testutil/JUnitSetUp.java +++ b/src/test/java/org/scion/jpan/testutil/JUnitSetUp.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.net.ServerSocket; +import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; @@ -25,7 +26,10 @@ import org.scion.jpan.internal.Shim; public class JUnitSetUp - implements BeforeAllCallback, BeforeEachCallback, ExtensionContext.Store.CloseableResource { + implements BeforeAllCallback, + BeforeEachCallback, + AfterEachCallback, + ExtensionContext.Store.CloseableResource { private static boolean started = false; private static boolean failed = false; @@ -65,18 +69,22 @@ public void close() { @Override public void beforeEach(ExtensionContext context) { System.setProperty(Constants.PROPERTY_HOSTS_FILES, "....invalid-dummy-filename"); + if (failed) { + System.exit(1); + } + } + @Override + public void afterEach(ExtensionContext context) { Scion.closeDefault(); + Shim.uninstall(); System.clearProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE); System.clearProperty(Constants.PROPERTY_BOOTSTRAP_NAPTR_NAME); System.clearProperty(Constants.PROPERTY_BOOTSTRAP_HOST); System.clearProperty(Constants.PROPERTY_DAEMON); System.clearProperty(Constants.PROPERTY_DNS_SEARCH_DOMAINS); System.clearProperty(Constants.PROPERTY_HOSTS_FILES); + System.clearProperty(Constants.PROPERTY_SHIM); System.setProperty(Constants.PROPERTY_USE_OS_SEARCH_DOMAINS, "false"); - System.setProperty(Shim.DEBUG_PROPERTY_START_SHIM, "false"); - if (failed) { - System.exit(1); - } } } diff --git a/src/test/java/org/scion/jpan/testutil/JsonFileParser.java b/src/test/java/org/scion/jpan/testutil/JsonFileParser.java index 20c415647..2759d8cb5 100644 --- a/src/test/java/org/scion/jpan/testutil/JsonFileParser.java +++ b/src/test/java/org/scion/jpan/testutil/JsonFileParser.java @@ -28,6 +28,7 @@ import org.scion.jpan.ScionRuntimeException; import org.scion.jpan.ScionUtil; import org.scion.jpan.internal.IPHelper; +import org.scion.jpan.internal.LocalTopology; public class JsonFileParser { @@ -74,6 +75,12 @@ public static AsInfo parseTopologyFile(Path path) { JsonObject o = jsonTree.getAsJsonObject(); as.setIsdAs(ScionUtil.parseIA(safeGet(o, "isd_as").getAsString())); // localMtu = safeGet(o, "mtu").getAsInt(); + JsonElement dispatchedPorts = o.get("dispatched_ports"); + if (dispatchedPorts == null) { + as.setPortRange(LocalTopology.DispatcherPortRange.createEmpty()); + } else { + as.setPortRange(parsePortRange(dispatchedPorts.getAsString())); + } JsonObject brs = safeGet(o, "border_routers").getAsJsonObject(); for (Map.Entry e : brs.entrySet()) { JsonObject br = e.getValue().getAsJsonObject(); @@ -115,6 +122,28 @@ private static JsonElement safeGet(JsonObject o, String name) { return e; } + private static LocalTopology.DispatcherPortRange parsePortRange(String v) { + if (v.startsWith("\"") && v.endsWith("\"")) { + v = v.substring(1, v.length() - 2); + } + if ("-".equals(v)) { + return LocalTopology.DispatcherPortRange.createEmpty(); + } else if ("all".equalsIgnoreCase(v)) { + return LocalTopology.DispatcherPortRange.createAll(); + } else { + String[] sa = v.split("-"); + if (sa.length != 2) { + throw new ScionRuntimeException("Illegal expression in topo file dispatched_ports: " + v); + } + int portMin = Integer.parseInt(sa[0]); + int portMax = Integer.parseInt(sa[1]); + if (portMin < 1 || portMax < 1 || portMax > 65535 || portMin > portMax) { + throw new ScionRuntimeException("Illegal port values in topo file dispatched_ports: " + v); + } + return LocalTopology.DispatcherPortRange.create(portMin, portMax); + } + } + private static String mapToLoopbackV6(String s) { // Map any IPv6 address to ::1 String ip = IPHelper.extractIP(s); diff --git a/src/test/java/org/scion/jpan/testutil/MockBootstrapServer.java b/src/test/java/org/scion/jpan/testutil/MockBootstrapServer.java index e55b1533d..e0e665374 100644 --- a/src/test/java/org/scion/jpan/testutil/MockBootstrapServer.java +++ b/src/test/java/org/scion/jpan/testutil/MockBootstrapServer.java @@ -168,6 +168,10 @@ public long getLocalIsdAs() { return asInfo.getIsdAs(); } + public AsInfo getASInfo() { + return asInfo; + } + private class TopologyServerImpl implements Runnable { private final String topologyFile; private final String trcsFilesJson; diff --git a/src/test/java/org/scion/jpan/testutil/MockBorderRouter.java b/src/test/java/org/scion/jpan/testutil/MockBorderRouter.java index cf7389dde..1ee245f66 100644 --- a/src/test/java/org/scion/jpan/testutil/MockBorderRouter.java +++ b/src/test/java/org/scion/jpan/testutil/MockBorderRouter.java @@ -28,6 +28,7 @@ import org.scion.jpan.demo.inspector.ScmpHeader; import org.scion.jpan.internal.ScionHeaderParser; import org.scion.jpan.internal.ScmpParser; +import org.scion.jpan.internal.Shim; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -123,6 +124,9 @@ public void run() { private void forwardPacket(ByteBuffer buffer, SocketAddress srcAddress, DatagramChannel outgoing) throws IOException { InetSocketAddress dstAddress = PackageVisibilityHelper.getDstAddress(buffer); + if ("true".equalsIgnoreCase(System.getProperty(Shim.DEBUG_PROPERTY_START_SHIM))) { + dstAddress = MockNetwork.asInfo.mapDispatcherPorts(dstAddress); + } logger.info( "{} forwarding {} bytes from {} to {}", name, buffer.remaining(), srcAddress, dstAddress); diff --git a/src/test/java/org/scion/jpan/testutil/MockDaemon.java b/src/test/java/org/scion/jpan/testutil/MockDaemon.java index 1f9b89fef..7b4f9de71 100644 --- a/src/test/java/org/scion/jpan/testutil/MockDaemon.java +++ b/src/test/java/org/scion/jpan/testutil/MockDaemon.java @@ -15,11 +15,13 @@ package org.scion.jpan.testutil; import com.google.protobuf.ByteString; +import com.google.protobuf.Empty; import com.google.protobuf.Timestamp; import io.grpc.*; import io.grpc.stub.StreamObserver; import java.io.IOException; import java.net.InetSocketAddress; +import java.nio.file.Paths; import java.time.Instant; import java.util.ArrayList; import java.util.List; @@ -46,6 +48,7 @@ public class MockDaemon implements AutoCloseable { private final InetSocketAddress address; private Server server; private final List borderRouters; + private final AsInfo asInfo; private static final AtomicInteger callCount = new AtomicInteger(); private static final byte[] PATH_RAW_TINY_110_112 = { 0, 0, 32, 0, 1, 0, 11, 16, @@ -95,18 +98,20 @@ private MockDaemon(InetSocketAddress address) { InetSocketAddress bind1 = new InetSocketAddress("127.0.0.10", 31004); InetSocketAddress bind2 = new InetSocketAddress("127.0.0.25", 31012); this.borderRouters.add(new MockBorderRouter(0, bind1, bind2, 2, 1)); + asInfo = JsonFileParser.parseTopologyFile(Paths.get(MockBootstrapServer.TOPOFILE_TINY_110)); } private MockDaemon(InetSocketAddress address, List borderRouters) { this.address = address; this.borderRouters = borderRouters; + asInfo = JsonFileParser.parseTopologyFile(Paths.get(MockBootstrapServer.TOPOFILE_TINY_110)); } public MockDaemon start() throws IOException { int port = address.getPort(); server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create()) - .addService(new MockDaemon.DaemonImpl(borderRouters)) + .addService(new MockDaemon.DaemonImpl(borderRouters, asInfo)) .build() .start(); logger.info("Server started, listening on {}", address); @@ -126,34 +131,42 @@ public MockDaemon start() throws IOException { @Override public void close() throws IOException { - server.shutdown(); // Disable new tasks from being submitted - try { - // Wait a while for existing tasks to terminate - if (!server.awaitTermination(5, TimeUnit.SECONDS)) { - server.shutdownNow(); // Cancel currently executing tasks - // Wait a while for tasks to respond to being cancelled + if (server != null) { + server.shutdown(); // Disable new tasks from being submitted + try { + // Wait a while for existing tasks to terminate if (!server.awaitTermination(5, TimeUnit.SECONDS)) { - logger.error("Daemon server did not terminate"); + server.shutdownNow(); // Cancel currently executing tasks + // Wait a while for tasks to respond to being cancelled + if (!server.awaitTermination(5, TimeUnit.SECONDS)) { + logger.error("Daemon server did not terminate"); + } } + logger.info("Daemon server shut down"); + } catch (InterruptedException ie) { + // (Re-)Cancel if current thread also interrupted + server.shutdownNow(); + // Preserve interrupt status + Thread.currentThread().interrupt(); } - logger.info("Daemon server shut down"); - } catch (InterruptedException ie) { - // (Re-)Cancel if current thread also interrupted - server.shutdownNow(); - // Preserve interrupt status - Thread.currentThread().interrupt(); } } + public AsInfo getASInfo() { + return asInfo; + } + public static int getAndResetCallCount() { return callCount.getAndSet(0); } static class DaemonImpl extends DaemonServiceGrpc.DaemonServiceImplBase { final List borderRouters; + final AsInfo asInfo; - DaemonImpl(List borderRouters) { + DaemonImpl(List borderRouters, AsInfo asInfo) { this.borderRouters = borderRouters; + this.asInfo = asInfo; } @Override @@ -231,5 +244,15 @@ public void interfaces( responseObserver.onNext(replyBuilder.build()); responseObserver.onCompleted(); } + + @Override + public void portRange(Empty req, StreamObserver responseObserver) { + callCount.incrementAndGet(); + Daemon.PortRangeResponse.Builder replyBuilder = Daemon.PortRangeResponse.newBuilder(); + replyBuilder.setDispatchedPortStart(asInfo.getPortRange().getPortMin()); + replyBuilder.setDispatchedPortEnd(asInfo.getPortRange().getPortMax()); + responseObserver.onNext(replyBuilder.build()); + responseObserver.onCompleted(); + } } } diff --git a/src/test/java/org/scion/jpan/testutil/MockNetwork.java b/src/test/java/org/scion/jpan/testutil/MockNetwork.java index e8d4cc352..e03ef3ca7 100644 --- a/src/test/java/org/scion/jpan/testutil/MockNetwork.java +++ b/src/test/java/org/scion/jpan/testutil/MockNetwork.java @@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.scion.jpan.*; import org.scion.jpan.internal.IPHelper; +import org.scion.jpan.internal.Shim; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,6 +67,7 @@ public class MockNetwork { private static MockDaemon daemon = null; private static MockBootstrapServer topoServer; private static MockControlServer controlServer; + static AsInfo asInfo; private static MockNetwork mock; private final AsInfo asInfoLocal; @@ -109,7 +111,14 @@ private static synchronized void startTiny(String localTopo, String remoteTopo, String remoteIP = mock.asInfoLocal.getBorderRouterAddressByIA(ScionUtil.parseIA(TINY_SRV_ISD_AS)); remoteIP = IPHelper.extractIP(remoteIP); - MockScmpHandler.start(remoteIP); + boolean hasShim = + ScionUtil.getPropertyOrEnv( + Constants.PROPERTY_SHIM, Constants.ENV_SHIM, Constants.DEFAULT_SHIM); + if (!"true".equalsIgnoreCase(System.getProperty(Shim.DEBUG_PROPERTY_START_SHIM)) && !hasShim) { + // Do not start a SCMP handler on 30041 if we want to use SHIMs. + // The SHIM also includes its own SCMP handler. + MockScmpHandler.start(remoteIP); + } List brList = new ArrayList<>(); for (AsInfo.BorderRouter br : mock.asInfoLocal.getBorderRouters()) { @@ -151,12 +160,20 @@ private static synchronized void startTiny(String localTopo, String remoteTopo, MockDNS.install(TINY_SRV_ISD_AS, TINY_SRV_NAME_1, TINY_SRV_ADDR_1); - if (mode == Mode.NAPTR || mode == Mode.BOOTSTRAP) { - topoServer = MockBootstrapServer.start(localTopo, mode == Mode.NAPTR); - controlServer = MockControlServer.start(topoServer.getControlServerPort()); - } else if (mode == Mode.AS_ONLY) { - AsInfo asInfo = JsonFileParser.parseTopologyFile(Paths.get(localTopo)); - controlServer = MockControlServer.start(asInfo.getControlServerPort()); + switch (mode) { + case BOOTSTRAP: + case NAPTR: + topoServer = MockBootstrapServer.start(localTopo, mode == Mode.NAPTR); + controlServer = MockControlServer.start(topoServer.getControlServerPort()); + asInfo = topoServer.getASInfo(); + break; + case AS_ONLY: + asInfo = JsonFileParser.parseTopologyFile(Paths.get(localTopo)); + controlServer = MockControlServer.start(asInfo.getControlServerPort()); + break; + case DAEMON: + asInfo = daemon.getASInfo(); + break; } dropNextPackets.getAndSet(0); @@ -199,6 +216,7 @@ public static synchronized void stopTiny() { dropNextPackets.getAndSet(0); scmpErrorOnNextPacket.set(null); + asInfo = null; mock = null; } diff --git a/src/test/java/org/scion/jpan/testutil/MockScmpHandler.java b/src/test/java/org/scion/jpan/testutil/MockScmpHandler.java index da93b26f6..453a14752 100644 --- a/src/test/java/org/scion/jpan/testutil/MockScmpHandler.java +++ b/src/test/java/org/scion/jpan/testutil/MockScmpHandler.java @@ -42,6 +42,7 @@ public class MockScmpHandler implements Runnable { private static final Logger logger = LoggerFactory.getLogger(MockScmpHandler.class.getName()); + public static final int PORT = 30041; // Yes, we use 30042 to avoid conflicts with SHIM etc. private static ExecutorService handler; private static final AtomicInteger nAnswerTotal = new AtomicInteger(); private static CountDownLatch barrier = null; @@ -100,7 +101,7 @@ private MockScmpHandler(String ip) { @Override public void run() { Thread.currentThread().setName(name); - InetSocketAddress localAddress = new InetSocketAddress(ip, Constants.SCMP_PORT); + InetSocketAddress localAddress = new InetSocketAddress(ip, PORT); try (DatagramChannel chn = DatagramChannel.open().bind(localAddress); Selector selector = Selector.open()) { chn.configureBlocking(false); diff --git a/src/test/java/org/scion/jpan/testutil/PingPongChannelHelper.java b/src/test/java/org/scion/jpan/testutil/PingPongChannelHelper.java index 2c6b78abb..398d3e9a4 100644 --- a/src/test/java/org/scion/jpan/testutil/PingPongChannelHelper.java +++ b/src/test/java/org/scion/jpan/testutil/PingPongChannelHelper.java @@ -31,8 +31,9 @@ private PingPongChannelHelper( boolean connect, boolean resetCounters, String serverIsdAs, + InetSocketAddress serverAddress, ScionService service) { - super(nServers, nClients, nRounds, connect, resetCounters, serverIsdAs, service); + super(nServers, nClients, nRounds, connect, resetCounters, serverIsdAs, serverAddress, service); } public static Builder newBuilder(int nServers, int nClients, int nRounds) { @@ -99,16 +100,18 @@ public final void runImpl(ScionDatagramChannel channel) throws IOException { private class ServerEndpoint extends AbstractChannelEndpoint { private final Server server; private final int nRounds; + private final InetSocketAddress localAddress; - ServerEndpoint(Server server, int id, int nRounds) { + ServerEndpoint(Server server, int id, int nRounds, InetSocketAddress localAddress) { super(id, serverService); this.server = server; this.nRounds = nRounds; + this.localAddress = localAddress; // can be null } @Override public final void runImpl(ScionDatagramChannel channel) throws IOException { - channel.bind(null); + channel.bind(localAddress); registerStartUpServer(channel.getLocalAddress()); for (int i = 0; i < nRounds; i++) { server.run(channel); @@ -129,7 +132,7 @@ public void runPingPong(Server serverFn, Client clientFn) { try { start(); run( - (id, nRounds) -> new ServerEndpoint(serverFn, id, nRounds), + (id, nRounds) -> new ServerEndpoint(serverFn, id, nRounds, serverAddress), (id, path, nRounds) -> new ClientEndpoint(clientFn, id, path, nRounds, connectClients)); } finally { close(); @@ -170,6 +173,7 @@ public static void defaultServer(ScionDatagramChannel channel) throws IOExceptio } public static class Builder extends PingPongHelperBase.Builder { + private InetSocketAddress serverAddress = null; protected Builder(int nServers, int nClients, int nRounds) { super(nServers, nClients, nRounds, true); @@ -181,7 +185,19 @@ protected Builder(int nServers, int nClients, int nRounds, boolean connect) { public PingPongChannelHelper build() { return new PingPongChannelHelper( - nServers, nClients, nRounds, connectClients, checkCounters, serverIsdAs, service()); + nServers, + nClients, + nRounds, + connectClients, + checkCounters, + serverIsdAs, + serverAddress, + service()); + } + + public Builder serverBindAddress(InetSocketAddress serverBindAddress) { + this.serverAddress = serverBindAddress; + return this; } } } diff --git a/src/test/java/org/scion/jpan/testutil/PingPongHelperBase.java b/src/test/java/org/scion/jpan/testutil/PingPongHelperBase.java index 89d91d355..9950ef438 100644 --- a/src/test/java/org/scion/jpan/testutil/PingPongHelperBase.java +++ b/src/test/java/org/scion/jpan/testutil/PingPongHelperBase.java @@ -48,6 +48,7 @@ public class PingPongHelperBase { protected final boolean connectClients; private final boolean checkCounters; private final String serverIsdAs; + protected final InetSocketAddress serverAddress; protected final ScionService serverService; final CountDownLatch startUpBarrierClient; @@ -63,6 +64,7 @@ protected PingPongHelperBase( boolean connect, boolean checkCounters, String serverIsdAs, + InetSocketAddress serverAddress, ScionService serverService) { this.nClients = nClients; this.nServers = nServers; @@ -70,6 +72,7 @@ protected PingPongHelperBase( this.connectClients = connect; this.checkCounters = checkCounters; this.serverIsdAs = serverIsdAs; + this.serverAddress = serverAddress; this.serverService = serverService; startUpBarrierClient = new CountDownLatch(nClients); diff --git a/src/test/java/org/scion/jpan/testutil/PingPongSocketHelper.java b/src/test/java/org/scion/jpan/testutil/PingPongSocketHelper.java index 21176f20d..8dac4b1e7 100644 --- a/src/test/java/org/scion/jpan/testutil/PingPongSocketHelper.java +++ b/src/test/java/org/scion/jpan/testutil/PingPongSocketHelper.java @@ -34,8 +34,9 @@ private PingPongSocketHelper( boolean connect, boolean resetCounters, String serverIsdAs, + InetSocketAddress serverAddress, ScionService service) { - super(nServers, nClients, nRounds, connect, resetCounters, serverIsdAs, service); + super(nServers, nClients, nRounds, connect, resetCounters, serverIsdAs, serverAddress, service); } public static PingPongSocketHelper.Builder newBuilder(int nServers, int nClients, int nRounds) { @@ -215,7 +216,7 @@ protected Builder(int nServers, int nClients, int nRounds) { public PingPongSocketHelper build() { return new PingPongSocketHelper( - nServers, nClients, nRounds, connectClients, checkCounters, serverIsdAs, service()); + nServers, nClients, nRounds, connectClients, checkCounters, serverIsdAs, null, service()); } } } diff --git a/src/test/resources/topologies/README.txt b/src/test/resources/topologies/README.txt new file mode 100644 index 000000000..dbb8da288 --- /dev/null +++ b/src/test/resources/topologies/README.txt @@ -0,0 +1,13 @@ +The topology files were generated with the scionpropto SCION project. + +Files: +* topology-scionproto-0.10.json represent the old format up to 0.10. +* topology-scionproto-0.11.json represent the new format as of 0.11. +* dispatcher-port-... contain different values for the dispatched_port element +* no-discovery.json represents an AS that has no discovery/bootstrap server + +Folders: +* `minimal` is a relatively small topology without peering that is used for testing + path construction in JPAN +* `scionproto-tiny` is the "tiny" topology used in the scionproto SCION project +* `scionproto-default` is the "default" topology used in the scionproto SCION project diff --git a/src/test/resources/topologies/dispatcher-port-range-all.json b/src/test/resources/topologies/dispatcher-port-range-all.json new file mode 100644 index 000000000..e16233a93 --- /dev/null +++ b/src/test/resources/topologies/dispatcher-port-range-all.json @@ -0,0 +1,46 @@ +{ + "attributes": [], + "isd_as": "1-ff00:0:112", + "mtu": 1450, + "dispatched_ports": "all", + "control_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "discovery_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "border_routers": { + "br1-ff00_0_112-1": { + "internal_addr": "127.0.0.57:31032", + "interfaces": { + "495": { + "underlay": { + "public": "127.0.0.16:50000", + "remote": "127.0.0.17:50000" + }, + "isd_as": "1-ff00:0:130", + "link_to": "parent", + "mtu": 1472 + } + } + }, + "br1-ff00_0_112-2": { + "internal_addr": "127.0.0.58:31034", + "interfaces": { + "494": { + "underlay": { + "public": "127.0.0.11:50000", + "remote": "127.0.0.10:50000" + }, + "isd_as": "1-ff00:0:111", + "link_to": "parent", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/dispatcher-port-range-bad.json b/src/test/resources/topologies/dispatcher-port-range-bad.json new file mode 100644 index 000000000..a1325e6fe --- /dev/null +++ b/src/test/resources/topologies/dispatcher-port-range-bad.json @@ -0,0 +1,46 @@ +{ + "attributes": [], + "isd_as": "1-ff00:0:112", + "mtu": 1450, + "dispatched_ports": "100000-1000000", + "control_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "discovery_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "border_routers": { + "br1-ff00_0_112-1": { + "internal_addr": "127.0.0.57:31032", + "interfaces": { + "495": { + "underlay": { + "public": "127.0.0.16:50000", + "remote": "127.0.0.17:50000" + }, + "isd_as": "1-ff00:0:130", + "link_to": "parent", + "mtu": 1472 + } + } + }, + "br1-ff00_0_112-2": { + "internal_addr": "127.0.0.58:31034", + "interfaces": { + "494": { + "underlay": { + "public": "127.0.0.11:50000", + "remote": "127.0.0.10:50000" + }, + "isd_as": "1-ff00:0:111", + "link_to": "parent", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/dispatcher-port-range-empty.json b/src/test/resources/topologies/dispatcher-port-range-empty.json new file mode 100644 index 000000000..a72523e4d --- /dev/null +++ b/src/test/resources/topologies/dispatcher-port-range-empty.json @@ -0,0 +1,46 @@ +{ + "attributes": [], + "isd_as": "1-ff00:0:112", + "mtu": 1450, + "dispatched_ports": "-", + "control_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "discovery_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "border_routers": { + "br1-ff00_0_112-1": { + "internal_addr": "127.0.0.57:31032", + "interfaces": { + "495": { + "underlay": { + "public": "127.0.0.16:50000", + "remote": "127.0.0.17:50000" + }, + "isd_as": "1-ff00:0:130", + "link_to": "parent", + "mtu": 1472 + } + } + }, + "br1-ff00_0_112-2": { + "internal_addr": "127.0.0.58:31034", + "interfaces": { + "494": { + "underlay": { + "public": "127.0.0.11:50000", + "remote": "127.0.0.10:50000" + }, + "isd_as": "1-ff00:0:111", + "link_to": "parent", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/dispatcher-port-range-none.json b/src/test/resources/topologies/dispatcher-port-range-none.json new file mode 100644 index 000000000..6b5e3379c --- /dev/null +++ b/src/test/resources/topologies/dispatcher-port-range-none.json @@ -0,0 +1,45 @@ +{ + "attributes": [], + "isd_as": "1-ff00:0:112", + "mtu": 1450, + "control_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "discovery_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "border_routers": { + "br1-ff00_0_112-1": { + "internal_addr": "127.0.0.57:31032", + "interfaces": { + "495": { + "underlay": { + "public": "127.0.0.16:50000", + "remote": "127.0.0.17:50000" + }, + "isd_as": "1-ff00:0:130", + "link_to": "parent", + "mtu": 1472 + } + } + }, + "br1-ff00_0_112-2": { + "internal_addr": "127.0.0.58:31034", + "interfaces": { + "494": { + "underlay": { + "public": "127.0.0.11:50000", + "remote": "127.0.0.10:50000" + }, + "isd_as": "1-ff00:0:111", + "link_to": "parent", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/dispatcher-port-range.json b/src/test/resources/topologies/dispatcher-port-range.json new file mode 100644 index 000000000..f0ddaf8e7 --- /dev/null +++ b/src/test/resources/topologies/dispatcher-port-range.json @@ -0,0 +1,46 @@ +{ + "attributes": [], + "isd_as": "1-ff00:0:112", + "mtu": 1450, + "dispatched_ports": "31000-32767", + "control_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "discovery_service": { + "cs1-ff00_0_112-1": { + "addr": "127.0.0.59:31030" + } + }, + "border_routers": { + "br1-ff00_0_112-1": { + "internal_addr": "127.0.0.57:31032", + "interfaces": { + "495": { + "underlay": { + "public": "127.0.0.16:50000", + "remote": "127.0.0.17:50000" + }, + "isd_as": "1-ff00:0:130", + "link_to": "parent", + "mtu": 1472 + } + } + }, + "br1-ff00_0_112-2": { + "internal_addr": "127.0.0.58:31034", + "interfaces": { + "494": { + "underlay": { + "public": "127.0.0.11:50000", + "remote": "127.0.0.10:50000" + }, + "isd_as": "1-ff00:0:111", + "link_to": "parent", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/minimal/ASff00_0_110/topology.json b/src/test/resources/topologies/minimal/ASff00_0_110/topology.json index 76511fe41..2f5a81cd8 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_110/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_110/topology.json @@ -7,8 +7,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_110-1": { + "cs1-ff00_0_110-1": { "addr": "127.0.0.28:31000" } }, diff --git a/src/test/resources/topologies/minimal/ASff00_0_111/topology.json b/src/test/resources/topologies/minimal/ASff00_0_111/topology.json index d9b258eda..725ef19e8 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_111/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_111/topology.json @@ -5,8 +5,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_111-1": { + "cs1-ff00_0_111-1": { "addr": "127.0.0.36:31014" } }, diff --git a/src/test/resources/topologies/minimal/ASff00_0_1111/topology.json b/src/test/resources/topologies/minimal/ASff00_0_1111/topology.json index 3f3ddecf9..6133d4be2 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_1111/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_1111/topology.json @@ -5,8 +5,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_1111-1": { + "cs1-ff00_0_1111-1": { "addr": "127.0.0.42:31022" } }, diff --git a/src/test/resources/topologies/minimal/ASff00_0_1112/topology.json b/src/test/resources/topologies/minimal/ASff00_0_1112/topology.json index 696ad9af0..e1c92ab49 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_1112/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_1112/topology.json @@ -5,8 +5,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_1112-1": { + "cs1-ff00_0_1112-1": { "addr": "127.0.0.50:31026" } }, diff --git a/src/test/resources/topologies/minimal/ASff00_0_112/topology.json b/src/test/resources/topologies/minimal/ASff00_0_112/topology.json index e38b20dc0..710d4bd99 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_112/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_112/topology.json @@ -5,8 +5,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_112-1": { + "cs1-ff00_0_112-1": { "addr": "127.0.0.59:31030" } }, diff --git a/src/test/resources/topologies/minimal/ASff00_0_1121/topology.json b/src/test/resources/topologies/minimal/ASff00_0_1121/topology.json index 157f0931c..778fe6d67 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_1121/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_1121/topology.json @@ -5,8 +5,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_1121-1": { + "cs1-ff00_0_1121-1": { "addr": "127.0.0.66:31036" } }, diff --git a/src/test/resources/topologies/minimal/ASff00_0_120/topology.json b/src/test/resources/topologies/minimal/ASff00_0_120/topology.json index f49938f9c..814630ba2 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_120/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_120/topology.json @@ -7,8 +7,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_120-1": { + "cs1-ff00_0_120-1": { "addr": "127.0.0.75:31008" } }, diff --git a/src/test/resources/topologies/minimal/ASff00_0_121/topology.json b/src/test/resources/topologies/minimal/ASff00_0_121/topology.json index f9a8933ce..21b4b4ac0 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_121/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_121/topology.json @@ -5,8 +5,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_121-1": { + "cs1-ff00_0_121-1": { "addr": "127.0.0.82:31040" } }, diff --git a/src/test/resources/topologies/minimal/ASff00_0_210/topology.json b/src/test/resources/topologies/minimal/ASff00_0_210/topology.json index 4058d4f8c..00e34ac6e 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_210/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_210/topology.json @@ -7,8 +7,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs2-ff00_0_210-1": { + "cs2-ff00_0_210-1": { "addr": "127.0.0.91:31044" } }, diff --git a/src/test/resources/topologies/minimal/ASff00_0_211/topology.json b/src/test/resources/topologies/minimal/ASff00_0_211/topology.json index cf2683259..16da997b0 100644 --- a/src/test/resources/topologies/minimal/ASff00_0_211/topology.json +++ b/src/test/resources/topologies/minimal/ASff00_0_211/topology.json @@ -5,8 +5,9 @@ "test_dispatcher": true, "endhost_start_port": 1024, "endhost_end_port": 65535, + "dispatched_ports": "31000-32767", "control_service": { - "cs2-ff00_0_211-1": { + "cs2-ff00_0_211-1": { "addr": "127.0.0.98:31050" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_110/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_110/topology.json index 5afc56af5..75f494535 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_110/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_110/topology.json @@ -4,8 +4,9 @@ ], "isd_as": "1-ff00:0:110", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_110-1": { + "cs1-ff00_0_110-1": { "addr": "[fd00:f00d:cafe::7f00:14]:31000" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_111/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_111/topology.json index 1ca85d602..13c8de89b 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_111/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_111/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "1-ff00:0:111", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_111-1": { + "cs1-ff00_0_111-1": { "addr": "[fd00:f00d:cafe::7f00:1c]:31022" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_112/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_112/topology.json index 56b493073..f0ddaf8e7 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_112/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_112/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "1-ff00:0:112", "mtu": 1450, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_112-1": { + "cs1-ff00_0_112-1": { "addr": "127.0.0.59:31030" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_120/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_120/topology.json index 6d4696b19..5e5113784 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_120/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_120/topology.json @@ -4,8 +4,9 @@ ], "isd_as": "1-ff00:0:120", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_120-1": { + "cs1-ff00_0_120-1": { "addr": "127.0.0.68:31008" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_121/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_121/topology.json index 768fccbbe..7f03c1e2a 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_121/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_121/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "1-ff00:0:121", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_121-1": { + "cs1-ff00_0_121-1": { "addr": "127.0.0.85:31036" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_122/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_122/topology.json index 0d04ac8d8..bec431d32 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_122/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_122/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "1-ff00:0:122", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_122-1": { + "cs1-ff00_0_122-1": { "addr": "[fd00:f00d:cafe::7f00:23]:31046" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_130/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_130/topology.json index 6111b4b5b..ce16929fd 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_130/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_130/topology.json @@ -4,8 +4,9 @@ ], "isd_as": "1-ff00:0:130", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_130-1": { + "cs1-ff00_0_130-1": { "addr": "[fd00:f00d:cafe::7f00:2b]:31016" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_131/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_131/topology.json index ed2a7c432..4678b1dd8 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_131/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_131/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "1-ff00:0:131", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_131-1": { + "cs1-ff00_0_131-1": { "addr": "127.0.0.76:31052" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_132/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_132/topology.json index 041405f74..4fd46b60a 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_132/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_132/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "1-ff00:0:132", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_132-1": { + "cs1-ff00_0_132-1": { "addr": "[fd00:f00d:cafe::7f00:33]:31060" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_133/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_133/topology.json index 6699e03ef..0e38ae0de 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_133/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_133/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "1-ff00:0:133", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_133-1": { + "cs1-ff00_0_133-1": { "addr": "127.0.0.99:31066" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_210/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_210/topology.json index 981f723a5..24d6aba42 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_210/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_210/topology.json @@ -4,8 +4,9 @@ ], "isd_as": "2-ff00:0:210", "mtu": 1280, + "dispatched_ports": "31000-32767", "control_service": { - "cs2-ff00_0_210-1": { + "cs2-ff00_0_210-1": { "addr": "127.0.0.117:31072" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_211/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_211/topology.json index c3080e0a7..ad982047c 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_211/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_211/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "2-ff00:0:211", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs2-ff00_0_211-1": { + "cs2-ff00_0_211-1": { "addr": "[fd00:f00d:cafe::7f00:3a]:31092" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_212/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_212/topology.json index 4dc82fd26..71749cbc6 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_212/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_212/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "2-ff00:0:212", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs2-ff00_0_212-1": { + "cs2-ff00_0_212-1": { "addr": "127.0.0.107:31096" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_220/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_220/topology.json index 7cd6c2f91..63fd9131c 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_220/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_220/topology.json @@ -4,8 +4,9 @@ ], "isd_as": "2-ff00:0:220", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs2-ff00_0_220-1": { + "cs2-ff00_0_220-1": { "addr": "[fd00:f00d:cafe::7f00:45]:31082" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_221/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_221/topology.json index 5e1c4e815..ab3d9aa95 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_221/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_221/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "2-ff00:0:221", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs2-ff00_0_221-1": { + "cs2-ff00_0_221-1": { "addr": "127.0.0.132:31102" } }, diff --git a/src/test/resources/topologies/scionproto-default/ASff00_0_222/topology.json b/src/test/resources/topologies/scionproto-default/ASff00_0_222/topology.json index 6e441e423..36f61e769 100644 --- a/src/test/resources/topologies/scionproto-default/ASff00_0_222/topology.json +++ b/src/test/resources/topologies/scionproto-default/ASff00_0_222/topology.json @@ -2,8 +2,9 @@ "attributes": [], "isd_as": "2-ff00:0:222", "mtu": 1472, + "dispatched_ports": "31000-32767", "control_service": { - "cs2-ff00_0_222-1": { + "cs2-ff00_0_222-1": { "addr": "[fd00:f00d:cafe::7f00:53]:31110" } }, diff --git a/src/test/resources/topologies/scionproto-tiny/README.txt b/src/test/resources/topologies/scionproto-tiny/README.txt deleted file mode 100644 index cf93dd470..000000000 --- a/src/test/resources/topologies/scionproto-tiny/README.txt +++ /dev/null @@ -1,7 +0,0 @@ -This represents the scionproto tiny topology. -The files were originally copied from an older scionpropto release. - -topology-scionproto-0.11.json represent the new format as of 0.11. - -The old files wil be replaced (and support for them removed) once PRODUCTION has migrated to -0.11. \ No newline at end of file diff --git a/src/test/resources/topologies/scionproto-tiny/topology-110.json b/src/test/resources/topologies/scionproto-tiny/topology-110.json index c24e3fba4..927ef30c1 100644 --- a/src/test/resources/topologies/scionproto-tiny/topology-110.json +++ b/src/test/resources/topologies/scionproto-tiny/topology-110.json @@ -7,7 +7,7 @@ "test_dispatcher": true, "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_110-1": { + "cs1-ff00_0_110-1": { "addr": "127.0.0.11:31000" } }, diff --git a/src/test/resources/topologies/scionproto-tiny/topology-111.json b/src/test/resources/topologies/scionproto-tiny/topology-111.json index 3fd8b3928..bb660ce40 100644 --- a/src/test/resources/topologies/scionproto-tiny/topology-111.json +++ b/src/test/resources/topologies/scionproto-tiny/topology-111.json @@ -5,7 +5,7 @@ "test_dispatcher": true, "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_111-1": { + "cs1-ff00_0_111-1": { "addr": "127.0.0.18:31006" } }, diff --git a/src/test/resources/topologies/scionproto-tiny/topology-112.json b/src/test/resources/topologies/scionproto-tiny/topology-112.json index 65555fac8..c954d43fb 100644 --- a/src/test/resources/topologies/scionproto-tiny/topology-112.json +++ b/src/test/resources/topologies/scionproto-tiny/topology-112.json @@ -5,7 +5,7 @@ "test_dispatcher": true, "dispatched_ports": "31000-32767", "control_service": { - "cs1-ff00_0_112-1": { + "cs1-ff00_0_112-1": { "addr": "127.0.0.26:31010" } }, diff --git a/src/test/resources/topologies/topology-scionproto-0.10.json b/src/test/resources/topologies/topology-scionproto-0.10.json new file mode 100644 index 000000000..f72ed264a --- /dev/null +++ b/src/test/resources/topologies/topology-scionproto-0.10.json @@ -0,0 +1,62 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "1-ff00:0:110", + "mtu": 1400, + "dispatched_ports": "31000-32767", + "control_service": { + "cs1-ff00_0_110-1": { + "addr": "127.0.0.11:31000" + } + }, + "discovery_service": { + "cs1-ff00_0_110-1": { + "addr": "127.0.0.11:31000" + } + }, + "border_routers": { + "br1-ff00_0_110-1": { + "internal_addr": "127.0.0.9:31002", + "interfaces": { + "1": { + "underlay": { + "public": "127.0.0.4:50000", + "remote": "127.0.0.5:50000" + }, + "isd_as": "1-ff00:0:111", + "link_to": "child", + "mtu": 1280 + } + } + }, + "br1-ff00_0_110-2": { + "internal_addr": "127.0.0.10:31004", + "interfaces": { + "2": { + "underlay": { + "public": "[fd00:f00d:cafe::7f00:4]:50000", + "remote": "[fd00:f00d:cafe::7f00:5]:50000" + }, + "isd_as": "1-ff00:0:112", + "link_to": "child", + "mtu": 1472 + } + } + }, + "br1-ff00_0_110-3": { + "internal_addr": "127.0.0.10:31014", + "interfaces": { + "3": { + "underlay": { + "public": "[fd00:f00d:cafe::7f00:6]:50000", + "remote": "[fd00:f00d:cafe::7f00:7]:50000" + }, + "isd_as": "1-ff00:0:112", + "link_to": "child", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/scionproto-tiny/topology-scionproto-0.11.json b/src/test/resources/topologies/topology-scionproto-0.11.json similarity index 97% rename from src/test/resources/topologies/scionproto-tiny/topology-scionproto-0.11.json rename to src/test/resources/topologies/topology-scionproto-0.11.json index 943e18b04..71a1b45d5 100644 --- a/src/test/resources/topologies/scionproto-tiny/topology-scionproto-0.11.json +++ b/src/test/resources/topologies/topology-scionproto-0.11.json @@ -4,6 +4,7 @@ ], "isd_as": "1-ff00:0:110", "mtu": 1400, + "dispatched_ports": "31000-32767", "control_service": { "cs1-ff00_0_110-1": { "addr": "127.0.0.11:31000" From db55502aa1ee403385e18d39bb496dea0d2a07f6 Mon Sep 17 00:00:00 2001 From: Tilmann Date: Mon, 11 Nov 2024 17:09:05 +0100 Subject: [PATCH 2/8] Mostly fixed --- CHANGELOG.md | 4 ++-- .../java/org/scion/jpan/api/ScmpResponderTest.java | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cca536591..93d88c6ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,17 +37,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### Added - Add a SHIM, required for #130 (topo file port range support). - [#130](https://github.com/scionproto-contrib/jpan/pull/130) + [#131](https://github.com/scionproto-contrib/jpan/pull/131) - ManagedThread test helper. [#136](https://github.com/scionproto-contrib/jpan/pull/136) - Support for `dispatched_ports` in topo files [#130](https://github.com/scionproto-contrib/jpan/pull/130) TODO: -- Test demos with JUNIT topo and with scioproto topo - FIX: if no ephemeral ports are available, just use ANY port but enforce 30041 as return port. - Deprecate configureRemoteDispatcher() - Remove workaround from sendRaw() that checks for SHIM. Currently required for tests.... -> Do this in separate PR? +- Remove DEBUG_SHIM_PROP ### Changed - Buildified PingPong test helper. [#132](https://github.com/scionproto-contrib/jpan/pull/132) diff --git a/src/test/java/org/scion/jpan/api/ScmpResponderTest.java b/src/test/java/org/scion/jpan/api/ScmpResponderTest.java index 762a3a550..74520aa1e 100644 --- a/src/test/java/org/scion/jpan/api/ScmpResponderTest.java +++ b/src/test/java/org/scion/jpan/api/ScmpResponderTest.java @@ -21,12 +21,8 @@ import java.nio.ByteBuffer; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; import java.util.function.Predicate; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.scion.jpan.*; import org.scion.jpan.internal.Shim; import org.scion.jpan.testutil.ManagedThread; @@ -37,8 +33,8 @@ public class ScmpResponderTest { private static final ConcurrentLinkedQueue errors = new ConcurrentLinkedQueue<>(); - @BeforeAll - public static void beforeAll() { + @BeforeEach + void beforeEach() { System.setProperty(Constants.PROPERTY_SHIM, "false"); Shim.uninstall(); } From a12b26d7fc682c5c1a1ffebd08c405ce1596780a Mon Sep 17 00:00:00 2001 From: Tilmann Date: Mon, 11 Nov 2024 17:47:28 +0100 Subject: [PATCH 3/8] Mostly fixed --- src/test/java/org/scion/jpan/api/ScmpChannelTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/scion/jpan/api/ScmpChannelTest.java b/src/test/java/org/scion/jpan/api/ScmpChannelTest.java index 2525f3455..fc2aec358 100644 --- a/src/test/java/org/scion/jpan/api/ScmpChannelTest.java +++ b/src/test/java/org/scion/jpan/api/ScmpChannelTest.java @@ -350,13 +350,14 @@ void setUpScmpResponder_echo() throws IOException, InterruptedException { MockScmpHandler.stop(); // Shut down SCMP handler Path path = getPathTo112(InetAddress.getLoopbackAddress()); // sender is in 110; responder is in 112 + Thread t = null; try (ScmpChannel sender = Scmp.createChannel()) { sender.setScmpErrorListener(scmpMessage -> fail(scmpMessage.getTypeCode().getText())); sender.setOption(ScionSocketOptions.SCION_API_THROW_PARSER_FAILURE, true); // start responder CountDownLatch barrier = new CountDownLatch(1); - Thread t = new Thread(() -> scmpResponder(barrier, null)); + t = new Thread(() -> scmpResponder(barrier, null)); t.start(); barrier.await(); Thread.sleep(50); @@ -371,8 +372,10 @@ void setUpScmpResponder_echo() throws IOException, InterruptedException { // finish t.join(100); - t.interrupt(); // just in case. } finally { + if (t != null) { + t.interrupt(); // just in case + } MockNetwork.stopTiny(); } } From 3975f65a35852a3c4c8be2dae3d9ee09ab701461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilmann=20Z=C3=A4schke?= Date: Tue, 12 Nov 2024 16:17:19 +0100 Subject: [PATCH 4/8] It works again! DEBUG (almost) removed --- CHANGELOG.md | 1 + README.md | 22 +- .../scion/jpan/AbstractDatagramChannel.java | 2 +- .../org/scion/jpan/ScionDatagramSocket.java | 14 +- .../internal/SelectingDatagramChannel.java | 13 +- .../org/scion/jpan/socket/DatagramSocket.java | 2 +- .../api/DatagramChannelApiServerTest.java | 10 +- .../jpan/api/DatagramChannelPingPongTest.java | 12 +- .../jpan/api/DatagramSocketApiServerTest.java | 247 ++++++++++++++++++ .../jpan/api/DatagramSocketPingPongTest.java | 9 +- .../org/scion/jpan/testutil/JUnitSetUp.java | 2 +- .../scion/jpan/testutil/MockBorderRouter.java | 6 +- .../jpan/testutil/MockDatagramChannel.java | 39 ++- .../org/scion/jpan/testutil/MockNetwork.java | 7 +- .../jpan/testutil/PingPongSocketHelper.java | 3 +- 15 files changed, 338 insertions(+), 51 deletions(-) create mode 100644 src/test/java/org/scion/jpan/api/DatagramSocketApiServerTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 93d88c6ca..56fbbb36a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### TODO for 0.4.0 +- replace Selector.open() with channel.provider().openSelector(); - USe topo port range for connections in local AS! - Fix demos to return an "int" that can be tested! - ShimTest: TODO the serverService(null) should be removed once SHIM becomes the default diff --git a/README.md b/README.md index 73a65f758..e4115a28b 100644 --- a/README.md +++ b/README.md @@ -267,18 +267,18 @@ The following standard options are **not** supported: `DatagramSocket` work similar to `DatagramChannel` in terms of using `Path` or `Service`. `DatagramSocket` is somewhat discouraged because it requires storing/caching of paths internally -which can lead to increased memory usage of even failure to resolve paths, especially when handling +which can lead to increased memory usage or even failure to resolve paths, especially when handling multiple connections over a single socket. The problem is that `DatagramPacket` and `InetAddress` are not extensible to store path information. For a server to be able to send data back to a client, it has to remember these paths internally. This is done internally in a path cache that stores the received path for every remote IP address. -The cache is by default limited to 100 entries (`setPathCacheCapacity()`). In cse there are more +The cache is by default limited to 100 entries (`setPathCacheCapacity()`). In case there are more than 100 remote clients, the cache will 'forget' those paths that haven't been used for the longest time. That means the server won't be able to send anything anymore to these forgotten clients. This can become a security problem if an attacker initiates connections from many different (or -spoofed) IPs, causing the cache to consume a lot of memory or to overflow, being unable to +spoofed) IPs, causing the cache to consume a lot of memory or to overflow, becoming unable to answer to valid requests. Internally, the `DatagramSocket` uses a SCION `DatagraChannel`. @@ -297,16 +297,16 @@ API beyond the standard Java `DatagramScoket`: ## Performance pitfalls -- **Using `SocketAddress` for `send()`**. `send(buffer, socketAddress)` is a convenience function. However, when sending - multiple packets to the same destination, one should use `path = send(buffer, path)` or `connect()` + `write()` in - order to avoid frequent path lookups. +- **Using `SocketAddress` for `send()`**. `send(buffer, socketAddress)` is a convenience function. + However, when sending multiple packets to the same destination, one should use + `path = send(buffer, path)` or `connect()` + `write()` in order to avoid frequent path lookups. -- **Using expired path (client).** When using `send(buffer, path)` with an expired `Path`, the channel will - transparently look up a new path. This works but causes a path lookup for every `send()`. - Solution: always use the latest path returned by send, e.g. `path = send(buffer, path)`. +- **Using expired path (client).** When using `send(buffer, path)` with an expired `Path`, the + channel will transparently look up a new path. This works but causes a path lookup for every + `send()`. Solution: always use the latest path returned by send, e.g. `path = send(buffer, path)`. -- **Using expired path (server).** When using `send(buffer, path)` with an expired `Path`, the channel will - simple send it anyway. +- **Using expired path (server).** When using `send(buffer, path)` with an expired `Path`, the + channel will simple send it anyway. ## Configuration diff --git a/src/main/java/org/scion/jpan/AbstractDatagramChannel.java b/src/main/java/org/scion/jpan/AbstractDatagramChannel.java index f1e0bd202..2002a270b 100644 --- a/src/main/java/org/scion/jpan/AbstractDatagramChannel.java +++ b/src/main/java/org/scion/jpan/AbstractDatagramChannel.java @@ -360,7 +360,7 @@ protected ResponsePath receiveFromChannel( } } - private InetSocketAddress getFirstHopAddress(ByteBuffer buffer, InetSocketAddress srcAddress) { + protected InetSocketAddress getFirstHopAddress(ByteBuffer buffer, InetSocketAddress srcAddress) { if (getService() != null) { int oldPos = buffer.position(); int pathPos = ScionHeaderParser.extractPathHeaderPosition(buffer); diff --git a/src/main/java/org/scion/jpan/ScionDatagramSocket.java b/src/main/java/org/scion/jpan/ScionDatagramSocket.java index 0aaa568ca..2103fb6c3 100644 --- a/src/main/java/org/scion/jpan/ScionDatagramSocket.java +++ b/src/main/java/org/scion/jpan/ScionDatagramSocket.java @@ -76,10 +76,11 @@ public ScionDatagramSocket(SocketAddress bindAddress) throws SocketException { } // "private" to avoid ambiguity with DatagramSocket((SocketAddress) null) -> use create() - protected ScionDatagramSocket(ScionService service) throws SocketException { + protected ScionDatagramSocket(ScionService service, DatagramChannel channel) + throws SocketException { super(new DummyDatagramSocketImpl()); try { - channel = new SelectingDatagramChannel(service); + this.channel = new SelectingDatagramChannel(service, channel); } catch (IOException e) { throw new SocketException(e.getMessage()); } @@ -88,7 +89,7 @@ protected ScionDatagramSocket(ScionService service) throws SocketException { // "private" for consistency, all non-standard constructors are private -> use create() protected ScionDatagramSocket(SocketAddress bindAddress, ScionService service) throws SocketException { - this(service); + this(service, null); // DatagramSockets always immediately bind unless the bindAddress is null. if (bindAddress != null) { try { @@ -109,7 +110,12 @@ protected ScionDatagramSocket(SocketAddress bindAddress, ScionService service) * @return a new socket. */ public static ScionDatagramSocket create(ScionService service) throws SocketException { - return new ScionDatagramSocket(service); + return new ScionDatagramSocket(service, null); + } + + public static ScionDatagramSocket create(ScionService service, DatagramChannel channel) + throws SocketException { + return new ScionDatagramSocket(service, channel); } public static ScionDatagramSocket create(SocketAddress bindAddress, ScionService service) diff --git a/src/main/java/org/scion/jpan/internal/SelectingDatagramChannel.java b/src/main/java/org/scion/jpan/internal/SelectingDatagramChannel.java index 33c95dc21..64b81d7b0 100644 --- a/src/main/java/org/scion/jpan/internal/SelectingDatagramChannel.java +++ b/src/main/java/org/scion/jpan/internal/SelectingDatagramChannel.java @@ -36,10 +36,16 @@ public class SelectingDatagramChannel extends ScionDatagramChannel { private int timeoutMs = 0; public SelectingDatagramChannel(ScionService service) throws IOException { - super(service, DatagramChannel.open()); + this(service, null); + } + + public SelectingDatagramChannel(ScionService service, DatagramChannel channel) + throws IOException { + super(service, channel == null ? DatagramChannel.open() : channel); // selector - this.selector = Selector.open(); + DatagramChannel channel2 = channel == null ? DatagramChannel.open() : channel; + this.selector = channel2.provider().openSelector(); super.channel().configureBlocking(false); super.channel().register(selector, SelectionKey.OP_READ); } @@ -78,7 +84,8 @@ private ResponsePath receiveFromTimeoutChannel( // in extensions headers. hdrType = receiveExtensionHeader(buffer, hdrType); - ResponsePath path = ScionHeaderParser.extractResponsePath(buffer, srcAddress); + InetSocketAddress firstHopAddress = getFirstHopAddress(buffer, srcAddress); + ResponsePath path = ScionHeaderParser.extractResponsePath(buffer, firstHopAddress); if (hdrType == expectedHdrType) { return path; } diff --git a/src/main/java/org/scion/jpan/socket/DatagramSocket.java b/src/main/java/org/scion/jpan/socket/DatagramSocket.java index f7e2ebd17..879cd0f48 100644 --- a/src/main/java/org/scion/jpan/socket/DatagramSocket.java +++ b/src/main/java/org/scion/jpan/socket/DatagramSocket.java @@ -53,7 +53,7 @@ public DatagramSocket(SocketAddress bindAddress) throws SocketException { // "private" to avoid ambiguity with DatagramSocket((SocketAddress) null) -> use create() private DatagramSocket(ScionService service) throws SocketException { - super(service); + super(service, null); } // "private" for consistency, all non-standard constructors are private -> use create() diff --git a/src/test/java/org/scion/jpan/api/DatagramChannelApiServerTest.java b/src/test/java/org/scion/jpan/api/DatagramChannelApiServerTest.java index 63d75d63a..fe80b43f5 100644 --- a/src/test/java/org/scion/jpan/api/DatagramChannelApiServerTest.java +++ b/src/test/java/org/scion/jpan/api/DatagramChannelApiServerTest.java @@ -33,13 +33,6 @@ import org.scion.jpan.testutil.MockDatagramChannel; import org.scion.jpan.testutil.MockNetwork; -/** - * Test that typical "server" operations do not require a ScionService. - * - *

This is service-less operation is required for servers that should not use (or may not have) - * access to a daemon or control service. Service-less operation is mainly implemented to ensure - * good server performance by avoiding costly network calls to daemons etc. - */ class DatagramChannelApiServerTest { @AfterEach @@ -152,7 +145,8 @@ void receive_withNullService() throws IOException { @Test void receive_correctSrc_divergentBR() throws IOException { String topoFile = "topologies/scionproto-tiny/topology-112.json"; - // Check that the ResponsePath's first hop is looked up by from the border router table + // Check that the ResponsePath's first hop is looked up from the border router table, + // i.e. that it doesn't simply use the underlay's source address as first hop. byte[] scionSrcBytes = {10, 0, 123, 123}; byte[] underLaySrcBytes = {192 - 256, 168 - 256, 123, 123}; InetSocketAddress underLaySrc = diff --git a/src/test/java/org/scion/jpan/api/DatagramChannelPingPongTest.java b/src/test/java/org/scion/jpan/api/DatagramChannelPingPongTest.java index f9f2c6d0d..82b361088 100644 --- a/src/test/java/org/scion/jpan/api/DatagramChannelPingPongTest.java +++ b/src/test/java/org/scion/jpan/api/DatagramChannelPingPongTest.java @@ -48,9 +48,17 @@ void testWithServerNoService() { PingPongChannelHelper.Server serverFn = (socket) -> server(socket, true); PingPongChannelHelper.Client clientFn = this::client; PingPongChannelHelper pph = - PingPongChannelHelper.newBuilder(1, 10, 10).serverService(null).build(); + PingPongChannelHelper.newBuilder(1, 10, 10) + .serverService(null) + .checkCounters(false) + .build(); pph.runPingPong(serverFn, clientFn); - assertEquals(2 * 10 * 10, MockNetwork.getAndResetForwardCount()); + // We count only "forward" packets from client to server: + // The BR forwards the packet to the SHIM, the SHIM sends it to the server, the server + // sends it directly back to the SHIM (where it came from) + // who sends it to directly to the client without going through the BR. + // This is of course wrong, but it doesn't affect the test. + assertEquals(1 * 10 * 10, MockNetwork.getAndResetForwardCount()); } private void client(ScionDatagramChannel channel, Path requestPath, int id) throws IOException { diff --git a/src/test/java/org/scion/jpan/api/DatagramSocketApiServerTest.java b/src/test/java/org/scion/jpan/api/DatagramSocketApiServerTest.java new file mode 100644 index 000000000..1a42c30fe --- /dev/null +++ b/src/test/java/org/scion/jpan/api/DatagramSocketApiServerTest.java @@ -0,0 +1,247 @@ +// Copyright 2023 ETH Zurich +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.scion.jpan.api; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.file.Paths; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.scion.jpan.*; +import org.scion.jpan.demo.inspector.ScionPacketInspector; +import org.scion.jpan.internal.IPHelper; +import org.scion.jpan.testutil.AsInfo; +import org.scion.jpan.testutil.ExamplePacket; +import org.scion.jpan.testutil.JsonFileParser; +import org.scion.jpan.testutil.MockDNS; +import org.scion.jpan.testutil.MockDaemon; +import org.scion.jpan.testutil.MockDatagramChannel; +import org.scion.jpan.testutil.MockNetwork; + +class DatagramSocketApiServerTest { + + @AfterEach + public void afterEach() throws IOException { + MockDaemon.closeDefault(); + MockDNS.clear(); + ScionService.closeDefault(); + } + + @Test + void open_withoutService() throws IOException { + MockNetwork.startTiny(); + // check that open() (without service argument) creates a default service. + ScionService.closeDefault(); + try (ScionDatagramSocket socket = new ScionDatagramSocket()) { + assertEquals(Scion.defaultService(), socket.getService()); + } finally { + MockNetwork.stopTiny(); + } + } + + @Test + void open_withoutService2() throws IOException { + MockNetwork.startTiny(); + // check that open() (without service argument) uses the default service. + ScionService.closeDefault(); + ScionService service = Scion.defaultService(); + try (ScionDatagramSocket socket = new ScionDatagramSocket()) { + assertEquals(service, socket.getService()); + } finally { + MockNetwork.stopTiny(); + } + } + + @Test + void open_withNullService() throws IOException { + // check that open() (without service argument) does not internally create a ScionService. + ScionService.closeDefault(); + try (ScionDatagramSocket socket = ScionDatagramSocket.create(null)) { + assertNull(socket.getService()); + } + } + + @Test + void open_withNullService2() throws IOException { + MockNetwork.startTiny(); + // check that open() (without service argument) does not internally use a ScionService, even if + // one exists. + ScionService.closeDefault(); + Scion.defaultService(); + try (ScionDatagramSocket socket = ScionDatagramSocket.create(null)) { + assertNull(socket.getService()); + } finally { + MockNetwork.stopTiny(); + } + } + + @Test + void bind_withNullService() throws IOException { + // check that bind() does not internally require a ScionService. + ScionService.closeDefault(); + try (ScionDatagramSocket socket = ScionDatagramSocket.create(null)) { + socket.bind(new InetSocketAddress("127.0.0.1", 12345)); + assertNull(socket.getService()); + } + } + + @Test + void send_withNullService() throws IOException { + // check that send(Path) does not internally require a ScionService. + ScionService.closeDefault(); + + SocketAddress addr = new InetSocketAddress("127.0.0.1", 1); + MockDatagramChannel mock = MockDatagramChannel.open(); + mock.setReceiveCallback( + buf -> { + buf.put(ExamplePacket.PACKET_BYTES_SERVER_E2E_PING); + return addr; + }); + mock.setSendCallback((buf, addr2) -> 42); + + try (ScionDatagramSocket socket = ScionDatagramSocket.create(null, mock)) { + socket.bind(new InetSocketAddress("127.0.0.1", 12345)); + assertNull(socket.getService()); + // First, we need to receive a packet. + DatagramPacket buffer = new DatagramPacket(new byte[1000], 1000); + socket.receive(buffer); + + // Now, send it. + socket.send(buffer); + assertNull(socket.getService()); + } + } + + @Test + void receive_withNullService() throws IOException { + // check that receive() does not internally require a ScionService. + SocketAddress addr = new InetSocketAddress("127.0.0.1", 12345); + MockDatagramChannel mock = MockDatagramChannel.open(); + mock.setReceiveCallback( + buf -> { + buf.put(ExamplePacket.PACKET_BYTES_SERVER_E2E_PING); + return addr; + }); + try (ScionDatagramSocket socket = ScionDatagramSocket.create(null, mock)) { + socket.bind(new InetSocketAddress("127.0.0.1", 12345)); + assertNull(socket.getService()); + DatagramPacket packet = new DatagramPacket(new byte[100], 0); + socket.receive(packet); + assertNull(socket.getService()); + } + } + + @Test + void receive_correctSrc_divergentBR() throws IOException { + String topoFile = "topologies/scionproto-tiny/topology-112.json"; + // Check that the ResponsePath's first hop is looked up from the border router table, + // i.e. that it doesn't simply use the underlay's source address as first hop. + byte[] scionSrcBytes = {10, 0, 123, 123}; + byte[] underLaySrcBytes = {192 - 256, 168 - 256, 123, 123}; + InetSocketAddress underLaySrc = + new InetSocketAddress(InetAddress.getByAddress(underLaySrcBytes), 12345); + + // Find border router address + long isdAs112 = ScionUtil.parseIA(MockNetwork.TINY_CLIENT_ISD_AS); + AsInfo as112 = JsonFileParser.parseTopologyFile(Paths.get(topoFile)); + String brAddressString = as112.getBorderRouterAddressByIA(isdAs112); + InetSocketAddress brAddress = IPHelper.toInetSocketAddress(brAddressString); + + // prepare packet + ScionPacketInspector spi = new ScionPacketInspector(); + spi.read(ByteBuffer.wrap(ExamplePacket.PACKET_BYTES_SERVER_E2E_PING)); + spi.getScionHeader().setSrcHostAddress(scionSrcBytes); + InetAddress scionSrcIP = spi.getScionHeader().getSrcHostAddress(); + int scionSrcPort = spi.getOverlayHeaderUdp().getSrcPort(); + InetSocketAddress scionSrc = new InetSocketAddress(scionSrcIP, scionSrcPort); + + MockDatagramChannel mdc = MockDatagramChannel.open(); + mdc.setReceiveCallback( + buf -> { + spi.writePacket(buf, new byte[0]); + return underLaySrc; + }); + + MockNetwork.startTiny(); + ScionService service = null; + try { + service = Scion.newServiceWithTopologyFile(topoFile); + try (ScionDatagramSocket socket = ScionDatagramSocket.create(service, mdc)) { + DatagramPacket buffer = new DatagramPacket(new byte[1000], 1000); + socket.receive(buffer); + InetSocketAddress address = (InetSocketAddress) buffer.getSocketAddress(); + Path path = socket.getCachedPath(address); + assertEquals(brAddress, path.getFirstHopAddress()); + assertNotEquals(scionSrc, path.getFirstHopAddress()); + assertNotEquals(underLaySrc, path.getFirstHopAddress()); + assertEquals(scionSrc.getAddress(), path.getRemoteAddress()); + assertEquals(scionSrc.getPort(), path.getRemotePort()); + assertEquals(scionSrc.getAddress(), address.getAddress()); + assertEquals(scionSrc.getPort(), address.getPort()); + } finally { + ScionService.closeDefault(); + MockNetwork.stopTiny(); + } + } finally { + if (service != null) { + service.close(); + } + } + } + + @Test + void receive_correctSrc_emptyPath() throws IOException { + // In case of an empty path, the ResponsePath's first hop is the IP src address + InetSocketAddress underLaySrc = + new InetSocketAddress(InetAddress.getByAddress(new byte[] {10, 0, 123, 123}), 12345); + + ScionPacketInspector spi = new ScionPacketInspector(); + spi.read(ByteBuffer.wrap(ExamplePacket.PACKET_BYTES_SERVER_E2E_PING)); + spi.getPathHeaderScion().reset(); // set path to [] + InetAddress scionSrcIP = spi.getScionHeader().getSrcHostAddress(); + int scionSrcPort = spi.getOverlayHeaderUdp().getSrcPort(); + InetSocketAddress scionSrc = new InetSocketAddress(scionSrcIP, scionSrcPort); + + MockDatagramChannel mdc = MockDatagramChannel.open(); + mdc.setReceiveCallback( + buf -> { + spi.writePacket(buf, new byte[0]); + return underLaySrc; + }); + + MockNetwork.startTiny(); + ScionService service = null; + try { + service = Scion.newServiceWithTopologyFile("topologies/scionproto-tiny/topology-112.json"); + try (ScionDatagramSocket socket = ScionDatagramSocket.create(service, mdc)) { + DatagramPacket buffer = new DatagramPacket(new byte[1000], 1000); + socket.receive(buffer); + Path path = socket.getCachedPath(((InetSocketAddress) buffer.getSocketAddress())); + assertEquals(underLaySrc, path.getFirstHopAddress()); + assertNotEquals(scionSrc, path.getFirstHopAddress()); + } finally { + ScionService.closeDefault(); + MockNetwork.stopTiny(); + } + } finally { + if (service != null) { + service.close(); + } + } + } +} diff --git a/src/test/java/org/scion/jpan/api/DatagramSocketPingPongTest.java b/src/test/java/org/scion/jpan/api/DatagramSocketPingPongTest.java index 70501564a..d3c91d454 100644 --- a/src/test/java/org/scion/jpan/api/DatagramSocketPingPongTest.java +++ b/src/test/java/org/scion/jpan/api/DatagramSocketPingPongTest.java @@ -49,9 +49,14 @@ void testWithServerNoService() { PingPongSocketHelper.Server serverFn = (socket) -> server(socket, true); PingPongSocketHelper.Client clientFn = this::client; PingPongSocketHelper pph = - PingPongSocketHelper.newBuilder(1, 10, 10).serverService(null).build(); + PingPongSocketHelper.newBuilder(1, 10, 10).serverService(null).checkCounters(false).build(); pph.runPingPong(serverFn, clientFn); - assertEquals(2 * 10 * 10, MockNetwork.getAndResetForwardCount()); + // We count only "forward" packets from client to server: + // The BR forwards the packet to the SHIM, the SHIM sends it to the server, the server + // sends it directly back to the SHIM (where it came from) + // who sends it to directly to the client without going through the BR. + // This is of course wrong, but it doesn't affect the test. + assertEquals(1 * 10 * 10, MockNetwork.getAndResetForwardCount()); } private void client(ScionDatagramSocket socket, Path requestPath, int id) throws IOException { diff --git a/src/test/java/org/scion/jpan/testutil/JUnitSetUp.java b/src/test/java/org/scion/jpan/testutil/JUnitSetUp.java index ce800b4ca..0d15b1e3b 100644 --- a/src/test/java/org/scion/jpan/testutil/JUnitSetUp.java +++ b/src/test/java/org/scion/jpan/testutil/JUnitSetUp.java @@ -63,7 +63,7 @@ public void beforeAll(ExtensionContext context) { @Override public void close() { - // System.out.println("Singleton::Finish-Once"); + // Nothing } @Override diff --git a/src/test/java/org/scion/jpan/testutil/MockBorderRouter.java b/src/test/java/org/scion/jpan/testutil/MockBorderRouter.java index 1ee245f66..53e97f9b3 100644 --- a/src/test/java/org/scion/jpan/testutil/MockBorderRouter.java +++ b/src/test/java/org/scion/jpan/testutil/MockBorderRouter.java @@ -22,13 +22,14 @@ import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.util.Iterator; +import org.scion.jpan.Constants; import org.scion.jpan.PackageVisibilityHelper; +import org.scion.jpan.ScionUtil; import org.scion.jpan.Scmp; import org.scion.jpan.demo.inspector.ScionPacketInspector; import org.scion.jpan.demo.inspector.ScmpHeader; import org.scion.jpan.internal.ScionHeaderParser; import org.scion.jpan.internal.ScmpParser; -import org.scion.jpan.internal.Shim; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -124,7 +125,8 @@ public void run() { private void forwardPacket(ByteBuffer buffer, SocketAddress srcAddress, DatagramChannel outgoing) throws IOException { InetSocketAddress dstAddress = PackageVisibilityHelper.getDstAddress(buffer); - if ("true".equalsIgnoreCase(System.getProperty(Shim.DEBUG_PROPERTY_START_SHIM))) { + if (ScionUtil.getPropertyOrEnv( + Constants.PROPERTY_SHIM, Constants.ENV_SHIM, Constants.DEFAULT_SHIM)) { dstAddress = MockNetwork.asInfo.mapDispatcherPorts(dstAddress); } logger.info( diff --git a/src/test/java/org/scion/jpan/testutil/MockDatagramChannel.java b/src/test/java/org/scion/jpan/testutil/MockDatagramChannel.java index 5c0b09557..b13108e38 100644 --- a/src/test/java/org/scion/jpan/testutil/MockDatagramChannel.java +++ b/src/test/java/org/scion/jpan/testutil/MockDatagramChannel.java @@ -73,7 +73,7 @@ public static MockDatagramChannel open() throws IOException { } protected MockDatagramChannel() { - super(SelectorProvider.provider()); + super(MockSelectorProvider.provider()); } public void setReceiveCallback(Function cb) { @@ -297,12 +297,17 @@ public Set selectedKeys() { @Override public int selectNow() throws IOException { - return 0; + throw new UnsupportedOperationException(); + // return 0; } @Override public int select(long timeout) throws IOException { - return 0; + try { + return connectCallback.call(); + } catch (Exception e) { + throw new IOException(e); + } } @Override @@ -316,40 +321,54 @@ public int select() throws IOException { @Override public Selector wakeup() { - return null; + throw new UnsupportedOperationException(); + // return this; } } public static class MockSelectorProvider extends SelectorProvider { + private static final MockSelectorProvider INSTANCE = new MockSelectorProvider(); + + public static MockSelectorProvider provider() { + return INSTANCE; + } + + private MockSelectorProvider() {} + @Override public DatagramChannel openDatagramChannel() throws IOException { - return null; + throw new UnsupportedOperationException(); + // return null; } @Override public DatagramChannel openDatagramChannel(ProtocolFamily family) throws IOException { - return null; + throw new UnsupportedOperationException(); + // return null; } @Override public Pipe openPipe() throws IOException { - return null; + throw new UnsupportedOperationException(); + // return null; } @Override public AbstractSelector openSelector() throws IOException { - return null; + return new MockSelector(this); } @Override public ServerSocketChannel openServerSocketChannel() throws IOException { - return null; + throw new UnsupportedOperationException(); + // return null; } @Override public SocketChannel openSocketChannel() throws IOException { - return null; + throw new UnsupportedOperationException(); + // return null; } } diff --git a/src/test/java/org/scion/jpan/testutil/MockNetwork.java b/src/test/java/org/scion/jpan/testutil/MockNetwork.java index e03ef3ca7..a670e4c75 100644 --- a/src/test/java/org/scion/jpan/testutil/MockNetwork.java +++ b/src/test/java/org/scion/jpan/testutil/MockNetwork.java @@ -29,7 +29,6 @@ import java.util.concurrent.atomic.AtomicReference; import org.scion.jpan.*; import org.scion.jpan.internal.IPHelper; -import org.scion.jpan.internal.Shim; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -111,10 +110,8 @@ private static synchronized void startTiny(String localTopo, String remoteTopo, String remoteIP = mock.asInfoLocal.getBorderRouterAddressByIA(ScionUtil.parseIA(TINY_SRV_ISD_AS)); remoteIP = IPHelper.extractIP(remoteIP); - boolean hasShim = - ScionUtil.getPropertyOrEnv( - Constants.PROPERTY_SHIM, Constants.ENV_SHIM, Constants.DEFAULT_SHIM); - if (!"true".equalsIgnoreCase(System.getProperty(Shim.DEBUG_PROPERTY_START_SHIM)) && !hasShim) { + if (!ScionUtil.getPropertyOrEnv( + Constants.PROPERTY_SHIM, Constants.ENV_SHIM, Constants.DEFAULT_SHIM)) { // Do not start a SCMP handler on 30041 if we want to use SHIMs. // The SHIM also includes its own SCMP handler. MockScmpHandler.start(remoteIP); diff --git a/src/test/java/org/scion/jpan/testutil/PingPongSocketHelper.java b/src/test/java/org/scion/jpan/testutil/PingPongSocketHelper.java index 8dac4b1e7..fba87d270 100644 --- a/src/test/java/org/scion/jpan/testutil/PingPongSocketHelper.java +++ b/src/test/java/org/scion/jpan/testutil/PingPongSocketHelper.java @@ -169,7 +169,8 @@ public void runPingPongSharedServerSocket(Server receiverFn, Server senderFn, Cl try { start(); int port = MockNetwork.getTinyServerAddress().getPort(); - try (ScionDatagramSocket socket = new ScionDatagramSocket(port)) { + try (ScionDatagramSocket socket = + ScionDatagramSocket.create(new InetSocketAddress(port), serverService)) { run( (id, nRounds) -> new ServerEndpointMT((id % 2 == 0) ? receiverFn : senderFn, socket, id, nRounds), From a4ddb47d17dd2729b7cbb891cc17989528035a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilmann=20Z=C3=A4schke?= Date: Tue, 12 Nov 2024 17:05:01 +0100 Subject: [PATCH 5/8] It works again! DEBUG removed --- CHANGELOG.md | 6 +----- README.md | 5 +++-- src/main/java/org/scion/jpan/AbstractDatagramChannel.java | 2 ++ src/main/java/org/scion/jpan/ScionDatagramSocket.java | 2 ++ src/main/java/org/scion/jpan/internal/Shim.java | 5 ----- src/test/java/org/scion/jpan/internal/ShimTest.java | 2 -- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56fbbb36a..9152b0caa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,15 +40,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add a SHIM, required for #130 (topo file port range support). [#131](https://github.com/scionproto-contrib/jpan/pull/131) - ManagedThread test helper. [#136](https://github.com/scionproto-contrib/jpan/pull/136) -- Support for `dispatched_ports` in topo files +- Support for `dispatched_ports` in topo files. Deprecated `configureRemoteDispatcher()`. [#130](https://github.com/scionproto-contrib/jpan/pull/130) TODO: - FIX: if no ephemeral ports are available, just use ANY port but enforce 30041 as return port. -- Deprecate configureRemoteDispatcher() -- Remove workaround from sendRaw() that checks for SHIM. Currently required for tests.... - -> Do this in separate PR? -- Remove DEBUG_SHIM_PROP ### Changed - Buildified PingPong test helper. [#132](https://github.com/scionproto-contrib/jpan/pull/132) diff --git a/README.md b/README.md index e4115a28b..564501733 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,11 @@ JPAN can be used in one of the following ways: - You can use JPAN stand-alone (without local SCION installation), however it must listen on port 30041 for incoming SCION packets because SCION routers currently will forward data only to that port. -- If you are contacting an endhost within your own AS, and the endhost uses a dispatcher, then you +- ~~If you are contacting an endhost within your own AS, and the endhost uses a dispatcher, then you must set the flag `ScionDatagramChannel.configureRemoteDispatcher(true)`. This ensure that the outgoing packet is sent to port 30041 on the remote machine. The flag has no effect on traffic - sent to a remote AS. + sent to a remote AS.~~ + JPAN uses the topo file's port range to detect which parts need to be mapped to 30041. - If you need a local SCION installation on your machine (Go implementation), consider using the dispatch-off branch/PR. - When you need to run a local system with dispatcher, you can try to use port forwarding diff --git a/src/main/java/org/scion/jpan/AbstractDatagramChannel.java b/src/main/java/org/scion/jpan/AbstractDatagramChannel.java index 2002a270b..0b1e12223 100644 --- a/src/main/java/org/scion/jpan/AbstractDatagramChannel.java +++ b/src/main/java/org/scion/jpan/AbstractDatagramChannel.java @@ -419,7 +419,9 @@ protected void checkListeners(Scmp.Message scmpMsg) { * * @param hasDispatcher Set to 'true' if remote end-host uses a dispatcher and requires using port * 30041. + * @deprecated Not required anymore, will be removed for 0.6.0 */ + @Deprecated // TODO remove for 0.6.0 public void configureRemoteDispatcher(boolean hasDispatcher) { this.cfgRemoteDispatcher = hasDispatcher; } diff --git a/src/main/java/org/scion/jpan/ScionDatagramSocket.java b/src/main/java/org/scion/jpan/ScionDatagramSocket.java index 2103fb6c3..c86e1d84d 100644 --- a/src/main/java/org/scion/jpan/ScionDatagramSocket.java +++ b/src/main/java/org/scion/jpan/ScionDatagramSocket.java @@ -640,7 +640,9 @@ public synchronized void setPathPolicy(PathPolicy pathPolicy) throws IOException * @param hasDispatcher Set to 'true' if remote end-host uses a dispatcher and requires using port * 30041. * @see ScionDatagramChannel#configureRemoteDispatcher(boolean) + * @deprecated Not required anymore, will be removed for 0.6.0 */ + @Deprecated // TODO remove for 0.6.0 public synchronized ScionDatagramSocket setRemoteDispatcher(boolean hasDispatcher) { channel.configureRemoteDispatcher(hasDispatcher); return this; diff --git a/src/main/java/org/scion/jpan/internal/Shim.java b/src/main/java/org/scion/jpan/internal/Shim.java index c2546526a..f2b1a1d0e 100644 --- a/src/main/java/org/scion/jpan/internal/Shim.java +++ b/src/main/java/org/scion/jpan/internal/Shim.java @@ -38,11 +38,6 @@ * address encoded in the SCION header. If parsing of the SCION header fails, the packet is dropped. */ public class Shim implements AutoCloseable { - /** Property that allows specifying that the SHIM should NOT be started. */ - public static final String DEBUG_PROPERTY_START_SHIM = "DEBUG_SCION_SHIM"; - - public static final String DEBUG_ENV_START_SHIM = "org.scion.debug.shim"; - private static final Logger log = LoggerFactory.getLogger(Shim.class); private static final AtomicReference singleton = new AtomicReference<>(); private final ScmpResponder scmpResponder; diff --git a/src/test/java/org/scion/jpan/internal/ShimTest.java b/src/test/java/org/scion/jpan/internal/ShimTest.java index 576ea0a67..91f51456c 100644 --- a/src/test/java/org/scion/jpan/internal/ShimTest.java +++ b/src/test/java/org/scion/jpan/internal/ShimTest.java @@ -39,7 +39,6 @@ class ShimTest { void beforeEach() { Scion.closeDefault(); Shim.uninstall(); - System.setProperty(Shim.DEBUG_PROPERTY_START_SHIM, "true"); shimForwardingCounter.set(0); } @@ -51,7 +50,6 @@ void afterEach() { @AfterAll static void afterAll() { - System.setProperty(Shim.DEBUG_PROPERTY_START_SHIM, "false"); MockNetwork.stopTiny(); } From dbc03b51e4f646de65f51b3ac0052afdb096377e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilmann=20Z=C3=A4schke?= Date: Tue, 12 Nov 2024 17:22:36 +0100 Subject: [PATCH 6/8] It works again! DEBUG removed --- CHANGELOG.md | 6 +++--- src/test/java/org/scion/jpan/demo/ShowpathsDemo.java | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9152b0caa..dc1a556d8 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. ## [Unreleased] ### TODO for 0.4.0 +- AbstractDataChannel + - Remove receive(expectedHeaderType) -> just check for SCMP, otherwise -> default + - Move buildHeader() -> UDP-part out of this function. - replace Selector.open() with channel.provider().openSelector(); - USe topo port range for connections in local AS! - Fix demos to return an "int" that can be tested! @@ -43,9 +46,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Support for `dispatched_ports` in topo files. Deprecated `configureRemoteDispatcher()`. [#130](https://github.com/scionproto-contrib/jpan/pull/130) -TODO: -- FIX: if no ephemeral ports are available, just use ANY port but enforce 30041 as return port. - ### Changed - Buildified PingPong test helper. [#132](https://github.com/scionproto-contrib/jpan/pull/132) - Server to use BR addresses instead of received addresses. diff --git a/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java b/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java index ab17511a6..bef68f0b7 100644 --- a/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java +++ b/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java @@ -63,16 +63,17 @@ public static int run() throws IOException { switch (NETWORK) { case JUNIT_MOCK_V4: { + System.setProperty(Constants.PROPERTY_SHIM, "false"); // disable SHIM DemoTopology.configureMockV4(); InetAddress remote = MockScmpHandler.getAddress().getAddress(); MockDNS.install("1-ff00:0:112", "localhost", remote.toString()); - ShowpathsDemo demo = new ShowpathsDemo(); - int n = demo.runDemo(DemoConstants.ia110); + int n = runDemo(DemoConstants.ia110); DemoTopology.shutDown(); return n; } case JUNIT_MOCK_V6: { + System.setProperty(Constants.PROPERTY_SHIM, "false"); // disable SHIM DemoTopology.configureMockV6(); MockDNS.install("1-ff00:0:112", "ip6-localhost", "::1"); int n = runDemo(DemoConstants.ia110); From a1f8425dea287ad05a866cc9ec94312ec3ac10be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilmann=20Z=C3=A4schke?= Date: Tue, 12 Nov 2024 17:27:33 +0100 Subject: [PATCH 7/8] It works again! DEBUG removed --- src/main/java/org/scion/jpan/AbstractDatagramChannel.java | 4 ++-- src/main/java/org/scion/jpan/ScionDatagramSocket.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/scion/jpan/AbstractDatagramChannel.java b/src/main/java/org/scion/jpan/AbstractDatagramChannel.java index 0b1e12223..ef322d8eb 100644 --- a/src/main/java/org/scion/jpan/AbstractDatagramChannel.java +++ b/src/main/java/org/scion/jpan/AbstractDatagramChannel.java @@ -419,9 +419,9 @@ protected void checkListeners(Scmp.Message scmpMsg) { * * @param hasDispatcher Set to 'true' if remote end-host uses a dispatcher and requires using port * 30041. - * @deprecated Not required anymore, will be removed for 0.6.0 + * @deprecated Not required anymore, will be removed for 0.5.0 */ - @Deprecated // TODO remove for 0.6.0 + @Deprecated // TODO remove for 0.5.0 public void configureRemoteDispatcher(boolean hasDispatcher) { this.cfgRemoteDispatcher = hasDispatcher; } diff --git a/src/main/java/org/scion/jpan/ScionDatagramSocket.java b/src/main/java/org/scion/jpan/ScionDatagramSocket.java index c86e1d84d..327bd655d 100644 --- a/src/main/java/org/scion/jpan/ScionDatagramSocket.java +++ b/src/main/java/org/scion/jpan/ScionDatagramSocket.java @@ -640,9 +640,9 @@ public synchronized void setPathPolicy(PathPolicy pathPolicy) throws IOException * @param hasDispatcher Set to 'true' if remote end-host uses a dispatcher and requires using port * 30041. * @see ScionDatagramChannel#configureRemoteDispatcher(boolean) - * @deprecated Not required anymore, will be removed for 0.6.0 + * @deprecated Not required anymore, will be removed for 0.5.0 */ - @Deprecated // TODO remove for 0.6.0 + @Deprecated // TODO remove for 0.5.0 public synchronized ScionDatagramSocket setRemoteDispatcher(boolean hasDispatcher) { channel.configureRemoteDispatcher(hasDispatcher); return this; From fe35105b09e346665b6a42252168ade957074217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tilmann=20Z=C3=A4schke?= Date: Tue, 12 Nov 2024 17:49:54 +0100 Subject: [PATCH 8/8] It works again! DEBUG removed --- CHANGELOG.md | 5 +++++ README.md | 28 +++++++++++++++++++++++----- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc1a556d8..21c5ccfab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Inherit DatagramChannel - Consider using https://github.com/ascopes/protobuf-maven-plugin (more up to date) +**BREAKING CHANGE** +- The SHIM now occupies port 30041. This means any application trying to use that port will fail. + - Solution #1: Just use any other port instead, the SHIM will forward traffic to it. + - Solution #2: Disable the SHIM with + ### Added - Add a SHIM, required for #130 (topo file port range support). [#131](https://github.com/scionproto-contrib/jpan/pull/131) diff --git a/README.md b/README.md index 564501733..ead5401f7 100644 --- a/README.md +++ b/README.md @@ -343,17 +343,35 @@ while the other options are skipped if no property or environment variable is de ### DNS + JPAN will check the OS default DNS server to resolve SCION addresses. In addition, addresses can be specified in a `/etc/scion/hosts` file. The location of the hosts file is configurable, see next section. +### SHIM + +Every JPAN application will try to start a +[SHIM dispatcher](https://docs.scion.org/en/latest/dev/design/router-port-dispatch.html) +on port 30041. The SHIM is required to support the `dispathed_ports` feature in topo files. + +A SHIM does no traffic checking, it blindly forwards every parseable packet to the inscribed SCION +destination address. That means a JPAN SHIM will act as a SHIM for all applications on a machine. +(The slight problem being that if the application stops, the SHIM is stopped, leaving all other +applications without a SHIM). + +If the SHIM cannot be started because port 30041 is taken, the application will start anyway, +assuming that another SHIM is running on 30041. + +Whether a SHIM is started can be controlled with a configuration option, see below. + ### Other Options -| Option | Java property | Environment variable | Default value | -|----------------------------------------------------------------------------------------------------------------------|---------------------------------------|--------------------------------------|--------------------| -| Path expiry margin. Before sending a packet a new path is requested if the path is about to expire within X seconds. | `org.scion.pathExpiryMargin` | `SCION_PATH_EXPIRY_MARGIN` | 10 | -| Location of `hosts` file. Multiple location can be specified separated by `;`. | `org.scion.hostsFiles` | `SCION_HOSTS_FILES` | `/etc/scion/hosts` | -| Minimize segment requests to local AS at the cost of reduced range of path available. | `org.scion.resolver.experimentalMinimizeRequests` | `EXPERIMENTAL_SCION_RESOLVER_MINIMIZE_REQUESTS` | `false` | +| Option | Java property | Environment variable | Default value | +|------------------------------------------------------------------------------------------------------|---------------------------------------------------|-------------------------------------------------|--------------------| +| Path expiry margin. Before sending a packet a new path is requested if the path is about to expire within X seconds. | `org.scion.pathExpiryMargin` | `SCION_PATH_EXPIRY_MARGIN` | 10 | +| Location of `hosts` file. Multiple location can be specified separated by `;`. | `org.scion.hostsFiles` | `SCION_HOSTS_FILES` | `/etc/scion/hosts` | +| Start SHIM. | `org.scion.shim` | `SCION_SHIM` | `true` | +| Minimize segment requests to local AS at the cost of reduced range of path available. | `org.scion.resolver.experimentalMinimizeRequests` | `EXPERIMENTAL_SCION_RESOLVER_MINIMIZE_REQUESTS` | `false` | `EXPERIMENTAL_SCION_RESOLVER_MINIMIZE_REQUESTS` is a non-standard option that request CORE segments only of other path can be constructed. This may reduce response time when requesting new paths. It is very likely,