diff --git a/CHANGELOG.md b/CHANGELOG.md index 1613d945..d4f963ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - SocketConcurrency test takes too long. [#108](https://github.com/scionproto-contrib/jpan/issues/108) - Fixed useless error message when providing incorrect daemon address. Also: made port optional (default = 30255) [#114](https://github.com/scionproto-contrib/jpan/pull/114) +- Fixed SCMP packet loss on SCMP receive [#120](https://github.com/scionproto-contrib/jpan/pull/120) ### Removed - Removed some useless IP printing functions. diff --git a/default-ihaquwv8.png b/default-ihaquwv8.png new file mode 100644 index 00000000..801143ae Binary files /dev/null and b/default-ihaquwv8.png differ diff --git a/mytopo-8w67kdb8.png b/mytopo-8w67kdb8.png new file mode 100644 index 00000000..784f9ece Binary files /dev/null and b/mytopo-8w67kdb8.png differ diff --git a/mytopo.topo b/mytopo.topo new file mode 100644 index 00000000..f4004604 --- /dev/null +++ b/mytopo.topo @@ -0,0 +1,96 @@ +--- # My Topology +ASes: + "64-2:0:23": + core: true + voting: true + authoritative: true + cert_issuer: 64-2:0:13 + "64-2:0:13": + core: true + voting: true + authoritative: true + issuing: true + "64-0:0:e816": + cert_issuer: 64-2:0:13 + "64-0:0:ce7": + core: true + voting: true + authoritative: true + issuing: true + "64-0:0:3d07": + core: true + voting: true + authoritative: true + cert_issuer: 64-2:0:13 + "64-0:0:1a4a": + core: true + voting: true + authoritative: true + cert_issuer: 64-2:0:13 + "64-2:0:9": + cert_issuer: 64-2:0:13 + "64-0:0:303e": + core: true + voting: true + authoritative: true + cert_issuer: 64-2:0:13 + "64-0:0:22f": + core: true + voting: true + authoritative: true + cert_issuer: 64-2:0:13 +links: + - {a: "64-0:0:ce7#19", b: "64-0:0:3d07#5", linkAtoB: CORE} + - {a: "64-0:0:303e#5", b: "64-0:0:3d07#1", linkAtoB: CORE} + - {a: "64-0:0:ce7#22", b: "64-0:0:303e#7", linkAtoB: CORE} + - {a: "64-0:0:ce7#10", b: "64-0:0:1a4a#8", linkAtoB: CORE} + - {a: "64-0:0:3d07#2", b: "64-2:0:13#24", linkAtoB: CORE} + - {a: "64-0:0:3d07#11", b: "64-2:0:13#29", linkAtoB: CORE} + - {a: "64-0:0:3d07#3", b: "64-2:0:13#22", linkAtoB: CORE} + - {a: "64-0:0:3d07#27", b: "64-2:0:13#46", linkAtoB: CORE} + - {a: "64-0:0:1a4a#22", b: "64-0:0:3d07#13", linkAtoB: CORE} + - {a: "64-0:0:22f#17", b: "64-0:0:ce7#1", linkAtoB: CORE} + - {a: "64-2:0:13#19", b: "64-2:0:23#2", linkAtoB: CORE} + - {a: "64-0:0:e816#35", b: "64-2:0:28#4", linkAtoB: CORE} + - {a: "64-0:0:3d07#17", b: "64-2:0:28#1", linkAtoB: CORE} + - {a: "64-0:0:e816#8", b: "64-2:0:13#33", linkAtoB: CORE} + - {a: "64-0:0:22f#26", b: "64-0:0:303e#4", linkAtoB: CORE} + - {a: "64-0:0:22f#22", b: "64-0:0:3d07#9", linkAtoB: CORE} + - {a: "64-0:0:3d07#20", b: "64-2:0:13#23", linkAtoB: CORE} + - {a: "64-0:0:3d07#24", b: "64-0:0:e816#3", linkAtoB: CORE} + - {a: "64-0:0:ce7#20", b: "64-0:0:3d07#12", linkAtoB: CORE} + - {a: "64-0:0:ce7#11", b: "64-0:0:1a4a#11", linkAtoB: CORE} + - {a: "64-0:0:22f#31", b: "64-2:0:13#31", linkAtoB: CORE} + - {a: "64-0:0:1a4a#3", b: "64-2:0:13#11", linkAtoB: CORE} + - {a: "64-0:0:22f#20", b: "64-2:0:13#16", linkAtoB: CORE} + - {a: "64-0:0:3d07#28", b: "64-2:0:13#44", linkAtoB: CORE} + - {a: "64-0:0:3d07#18", b: "64-2:0:28#2", linkAtoB: CORE} + - {a: "64-0:0:1a4a#35", b: "64-0:0:e816#9", linkAtoB: CORE} + - {a: "64-0:0:ce7#2", b: "64-2:0:13#7", linkAtoB: CORE} + - {a: "64-0:0:22f#21", b: "64-0:0:3d07#8", linkAtoB: CORE} + - {a: "64-0:0:22f#4", b: "64-2:0:13#15", linkAtoB: CORE} + - {a: "64-0:0:1a4a#34", b: "64-0:0:e816#4", linkAtoB: CORE} + - {a: "64-0:0:3d07#23", b: "64-0:0:e816#1", linkAtoB: CORE} + - {a: "64-2:0:13#30", b: "64-2:0:23#7", linkAtoB: CORE} + - {a: "64-0:0:22f#35", b: "64-0:0:303e#20003", linkAtoB: CORE} + - {a: "64-0:0:303e#2", b: "64-2:0:13#28", linkAtoB: CORE} + - {a: "64-0:0:22f#19", b: "64-0:0:ce7#9", linkAtoB: CORE} + - {a: "64-0:0:22f#5", b: "64-2:0:9#1", linkAtoB: CORE} + - {a: "64-2:0:13#21", b: "64-2:0:23#4", linkAtoB: CORE} + - {a: "64-0:0:3d07#29", b: "64-2:0:13#45", linkAtoB: CORE} + - {a: "64-0:0:22f#34", b: "64-0:0:e816#5", linkAtoB: CORE} + - {a: "64-0:0:22f#16", b: "64-0:0:1a4a#10", linkAtoB: CORE} + - {a: "64-0:0:303e#6", b: "64-0:0:3d07#10", linkAtoB: CORE} + - {a: "64-0:0:3d07#22", b: "64-0:0:e816#2", linkAtoB: CORE} + - {a: "64-0:0:22f#36", b: "64-0:0:303e#20004", linkAtoB: CORE} + - {a: "64-0:0:303e#1", b: "64-2:0:13#26", linkAtoB: CORE} + - {a: "64-0:0:1a4a#1", b: "64-2:0:13#9", linkAtoB: CORE} + - {a: "64-0:0:ce7#4", b: "64-2:0:13#18", linkAtoB: CORE} + - {a: "64-0:0:22f#6", b: "64-2:0:9#2", linkAtoB: CORE} + - {a: "64-0:0:1a4a#27", b: "64-0:0:3d07#16", linkAtoB: CORE} + - {a: "64-0:0:e816#34", b: "64-2:0:28#3", linkAtoB: CORE} + - {a: "64-0:0:22f#15", b: "64-0:0:1a4a#9", linkAtoB: CORE} + - {a: "64-0:0:e816#7", b: "64-2:0:13#34", linkAtoB: CORE} + - {a: "64-0:0:22f#25", b: "64-0:0:303e#3", linkAtoB: CORE} + - {a: "64-0:0:ce7#16", b: "64-2:0:23#5", linkAtoB: CORE} + - {a: "64-0:0:22f#33", b: "64-0:0:e816#6", linkAtoB: CORE} diff --git a/pom.xml b/pom.xml index 05388357..8043ed40 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 6.4.0 3.3.0 - 2.20 + 2.23 3.2.0 3.11.0 3.1.1 diff --git a/src/main/java/org/scion/jpan/ScmpSenderAsync.java b/src/main/java/org/scion/jpan/ScmpSenderAsync.java index e48b1e7d..162b7972 100644 --- a/src/main/java/org/scion/jpan/ScmpSenderAsync.java +++ b/src/main/java/org/scion/jpan/ScmpSenderAsync.java @@ -26,6 +26,7 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import org.scion.jpan.internal.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -37,8 +38,7 @@ public class ScmpSenderAsync implements AutoCloseable { private final AtomicInteger sequenceIDs = new AtomicInteger(0); private final ConcurrentHashMap timers = new ConcurrentHashMap<>(); private final Timer timer = new Timer(true); - private Thread receiver; - private final CountDownLatch receiverBarrier = new CountDownLatch(1); + private final Thread receiver; private final ResponseHandler handler; public static Builder newBuilder(ResponseHandler handler) { @@ -64,31 +64,31 @@ private ScmpSenderAsync( throws IOException { this.channel = new InternalChannel(service, port, channel, selector); this.handler = handler; - startReceiver(); + this.receiver = startHandler(this::receiveTask, "ScmpSender-receiver"); } - private void startReceiver() { - this.receiver = new Thread(this::handleReceive, "ScmpSender-receiver"); - this.receiver.setDaemon(true); - this.receiver.start(); + private Thread startHandler(Consumer task, String name) { + CountDownLatch barrier = new CountDownLatch(1); + Thread thread = new Thread(() -> task.accept(barrier), name); + thread.setDaemon(true); + thread.start(); try { - if (!this.receiverBarrier.await(1, TimeUnit.SECONDS)) { - throw new IllegalStateException("Could not start receiver thread."); + if (!barrier.await(1, TimeUnit.SECONDS)) { + throw new IllegalStateException("Could not start receiver thread: " + name); } + return thread; } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new ScionRuntimeException(e); } } - private void stopReceiver() { - if (receiver != null) { - this.receiver.interrupt(); - try { - this.receiver.join(100); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } + private void stopHandler(Thread thread) { + thread.interrupt(); + try { + thread.join(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); } } @@ -178,9 +178,13 @@ public int getTimeOut() { @Override public void close() throws IOException { - channel.close(); timer.cancel(); - stopReceiver(); + stopHandler(receiver); + channel.close(); + } + + public T getOption(SocketOption option) throws IOException { + return channel.getOption(option); } /** @@ -227,7 +231,6 @@ void sendEchoRequest(Scmp.EchoMessage request) throws IOException { writeLock().lock(); try { Path path = request.getPath(); - super.channel().connect(path.getFirstHopAddress()); ByteBuffer buffer = getBufferSend(DEFAULT_BUFFER_SIZE); // EchoHeader = 8 + data int len = 8 + request.getData().length; @@ -241,9 +244,6 @@ void sendEchoRequest(Scmp.EchoMessage request) throws IOException { sendRequest(request, buffer, path); } finally { writeLock().unlock(); - if (super.channel().isConnected()) { - super.channel().disconnect(); - } } } @@ -252,7 +252,6 @@ void sendTracerouteRequest(Scmp.TracerouteMessage request, PathHeaderParser.Node writeLock().lock(); try { Path path = request.getPath(); - super.channel().connect(path.getFirstHopAddress()); ByteBuffer buffer = getBufferSend(DEFAULT_BUFFER_SIZE); // TracerouteHeader = 24 int len = 24; @@ -269,9 +268,6 @@ void sendTracerouteRequest(Scmp.TracerouteMessage request, PathHeaderParser.Node sendRequest(request, buffer, path); } finally { writeLock().unlock(); - if (super.channel().isConnected()) { - super.channel().disconnect(); - } } } @@ -308,6 +304,8 @@ private void readIncomingScmp(SelectionKey key) throws IOException { buffer.flip(); if (validate(buffer)) { InternalConstants.HdrTypes hdrType = ScionHeaderParser.extractNextHeader(buffer); + ResponsePath receivePath = ScionHeaderParser.extractResponsePath(buffer, srcAddress); + int packetLength = ScionHeaderParser.extractPacketLength(buffer); // From here on we use linear reading using the buffer's position() mechanism buffer.position(ScionHeaderParser.extractHeaderLength(buffer)); // Check for extension headers. @@ -318,7 +316,7 @@ private void readIncomingScmp(SelectionKey key) throws IOException { if (hdrType != InternalConstants.HdrTypes.SCMP) { return; // drop } - handleIncomingScmp(buffer, srcAddress); + handleIncomingScmp(buffer, receivePath, packetLength); } } catch (ScionException e) { // Validation problem -> ignore @@ -328,10 +326,10 @@ private void readIncomingScmp(SelectionKey key) throws IOException { } } - private void handleIncomingScmp(ByteBuffer buffer, InetSocketAddress srcAddress) { + private void handleIncomingScmp(ByteBuffer buffer, ResponsePath receivePath, int packetLength) { long currentNanos = System.nanoTime(); - ResponsePath receivePath = ScionHeaderParser.extractResponsePath(buffer, srcAddress); - Scmp.Message msg = ScmpParser.consume(buffer, receivePath); + int bufferStart = buffer.position(); + Scmp.Message msg = ScmpParser.consume(buffer, receivePath, packetLength); if (msg.getTypeCode().isError()) { handler.onError((Scmp.ErrorMessage) msg); checkListeners(msg); @@ -346,7 +344,7 @@ private void handleIncomingScmp(ByteBuffer buffer, InetSocketAddress srcAddress) ((Scmp.TimedMessage) msg).assignRequest(request, currentNanos); handler.onResponse((Scmp.TimedMessage) msg); } else if (msg.getTypeCode() == Scmp.TypeCode.TYPE_129) { - ((Scmp.EchoMessage) msg).setSizeReceived(buffer.position()); + ((Scmp.EchoMessage) msg).setSizeReceived(buffer.position() - bufferStart); ((Scmp.TimedMessage) msg).assignRequest(request, currentNanos); handler.onResponse((Scmp.TimedMessage) msg); } else { @@ -378,9 +376,9 @@ public void setOverrideSourceAddress(InetSocketAddress overrideSourceAddress) { channel.setOverrideSourceAddress(overrideSourceAddress); } - private void handleReceive() { + private void receiveTask(CountDownLatch barrier) { try { - receiverBarrier.countDown(); + barrier.countDown(); channel.receiveAsync(); } catch (IOException e) { handler.onException(e); diff --git a/src/main/java/org/scion/jpan/internal/GlobalTopology.java b/src/main/java/org/scion/jpan/internal/GlobalTopology.java index 5a3ea5ed..5f9b8f66 100644 --- a/src/main/java/org/scion/jpan/internal/GlobalTopology.java +++ b/src/main/java/org/scion/jpan/internal/GlobalTopology.java @@ -27,6 +27,7 @@ public class GlobalTopology { private final Map world = new HashMap<>(); + /** * The topology is "empty" if it wasn't initialized with TRC file (or TRC metadata). THis can * happen when it is initialized from a local topology file without bootstrap server. diff --git a/src/main/java/org/scion/jpan/internal/ScionHeaderParser.java b/src/main/java/org/scion/jpan/internal/ScionHeaderParser.java index aae129c6..3cee5b56 100644 --- a/src/main/java/org/scion/jpan/internal/ScionHeaderParser.java +++ b/src/main/java/org/scion/jpan/internal/ScionHeaderParser.java @@ -52,6 +52,17 @@ public static void extractUserPayload(ByteBuffer data, ByteBuffer userBuffer) { data.position(pos); } + /** + * Extract the total packet length without changing the buffer's position. + * + * @param data The datagram to read from. + */ + public static int extractPacketLength(ByteBuffer data) { + int headerLen = extractHeaderLength(data); + int payloadLen = ByteUtil.toUnsigned(data.getShort(data.position() + 6)); + return headerLen + payloadLen; + } + /** * Extract the remote socket address and path without changing the buffer's position. * @@ -140,7 +151,7 @@ public static ResponsePath extractResponsePath( * @return The type of the next header. */ public static InternalConstants.HdrTypes extractNextHeader(ByteBuffer data) { - int nextHeader = ByteUtil.toUnsigned(data.get(4)); + int nextHeader = ByteUtil.toUnsigned(data.get(data.position() + 4)); return InternalConstants.HdrTypes.parse(nextHeader); } diff --git a/src/main/java/org/scion/jpan/internal/ScmpParser.java b/src/main/java/org/scion/jpan/internal/ScmpParser.java index 85d1ecee..3647477c 100644 --- a/src/main/java/org/scion/jpan/internal/ScmpParser.java +++ b/src/main/java/org/scion/jpan/internal/ScmpParser.java @@ -64,7 +64,8 @@ public static void buildScmpTraceroute( public static Scmp.Type extractType(ByteBuffer data) { // Avoid changing the position! - return Scmp.Type.parse(ByteUtil.toUnsigned(data.get(data.position()))); + int headerLength = ScionHeaderParser.extractHeaderLength(data); + return Scmp.Type.parse(ByteUtil.toUnsigned(data.get(headerLength))); } /** @@ -113,7 +114,7 @@ public static void consume(ByteBuffer data, Scmp.Message holder) { * @param data packet data * @return SCMP message */ - public static Scmp.Message consume(ByteBuffer data, ResponsePath path) { + public static Scmp.Message consume(ByteBuffer data, ResponsePath path, int packetLength) { int type = ByteUtil.toUnsigned(data.get()); int code = ByteUtil.toUnsigned(data.get()); data.getShort(); // checksum @@ -128,7 +129,7 @@ public static Scmp.Message consume(ByteBuffer data, ResponsePath path) { case INFO_129: Scmp.EchoMessage echo = Scmp.EchoMessage.createEmpty(path); echo.setMessageArgs(sc, short1, short2); - echo.setData(new byte[data.remaining()]); + echo.setData(new byte[packetLength - data.position()]); data.get(echo.getData()); return echo; case INFO_130: diff --git a/src/test/java/org/scion/jpan/ProtobufSegmentDemo.java b/src/test/java/org/scion/jpan/ProtobufSegmentDemo.java index 52e9cfa0..b974ced3 100644 --- a/src/test/java/org/scion/jpan/ProtobufSegmentDemo.java +++ b/src/test/java/org/scion/jpan/ProtobufSegmentDemo.java @@ -22,7 +22,6 @@ import io.grpc.StatusRuntimeException; import java.time.Instant; import java.util.*; -import org.scion.jpan.demo.DemoConstants; import org.scion.jpan.proto.control_plane.Seg; import org.scion.jpan.proto.control_plane.SegExtensions; import org.scion.jpan.proto.control_plane.SegmentLookupServiceGrpc; @@ -36,14 +35,17 @@ public class ProtobufSegmentDemo { private final ManagedChannel channel; public static void main(String[] args) throws ScionException { - // ProtobufSegmentDemo demo = new ProtobufSegmentDemo(csETH); - // demo.getSegments(iaETH, iaETH_CORE); - // demo.getSegments(toWildcard(iaETH), toWildcard(iaAnapayaHK)); - ProtobufSegmentDemo demo = new ProtobufSegmentDemo(DemoConstants.csAddr110_minimal); - // demo.getSegments(ia110, ia121); - demo.getSegments(DemoConstants.ia110, DemoConstants.ia1111); - // demo.getSegments(toWildcard(ia121), ia121); - // demo.getSegments(toWildcard(ia120), toWildcard(ia210)); + // // ProtobufSegmentDemo demo = new ProtobufSegmentDemo(csETH); + // // demo.getSegments(iaETH, iaETH_CORE); + // // demo.getSegments(toWildcard(iaETH), toWildcard(iaAnapayaHK)); + // ProtobufSegmentDemo demo = new ProtobufSegmentDemo(DemoConstants.csAddr110_minimal); + // // demo.getSegments(ia110, ia121); + // demo.getSegments(DemoConstants.ia110, DemoConstants.ia1111); + // // demo.getSegments(toWildcard(ia121), ia121); + // // demo.getSegments(toWildcard(ia120), toWildcard(ia210)); + + ProtobufSegmentDemo demoLab = new ProtobufSegmentDemo("127.0.0.71:31000"); + demoLab.getSegments(ScionUtil.parseIA("1-ff00:0:1001"), ScionUtil.parseIA("1-ff00:0:1007")); } public ProtobufSegmentDemo(String csAddress) { diff --git a/src/test/java/org/scion/jpan/ProtobufSegmentMapperDemo.java b/src/test/java/org/scion/jpan/ProtobufSegmentMapperDemo.java new file mode 100644 index 00000000..14c28036 --- /dev/null +++ b/src/test/java/org/scion/jpan/ProtobufSegmentMapperDemo.java @@ -0,0 +1,419 @@ +// 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; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Timestamp; +import io.grpc.Grpc; +import io.grpc.InsecureChannelCredentials; +import io.grpc.ManagedChannel; +import io.grpc.StatusRuntimeException; +import java.io.FileWriter; +import java.io.IOException; +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; +import org.scion.jpan.proto.control_plane.Seg; +import org.scion.jpan.proto.control_plane.SegExtensions; +import org.scion.jpan.proto.control_plane.SegmentLookupServiceGrpc; +import org.scion.jpan.proto.control_plane.experimental.SegDetachedExtensions; +import org.scion.jpan.proto.crypto.Signed; + +/** + * Small demo that requests and prints segments requested from a control service. The segments are + * analyzed to create a topology file. + */ +public class ProtobufSegmentMapperDemo { + + private final SegmentLookupServiceGrpc.SegmentLookupServiceBlockingStub segmentStub; + private final ManagedChannel channel; + + public static void main(String[] args) throws IOException { + // // ProtobufSegmentDemo demo = new ProtobufSegmentDemo(csETH); + // // demo.getSegments(iaETH, iaETH_CORE); + // // demo.getSegments(toWildcard(iaETH), toWildcard(iaAnapayaHK)); + // ProtobufSegmentDemo demo = new ProtobufSegmentDemo(DemoConstants.csAddr110_minimal); + // // demo.getSegments(ia110, ia121); + // demo.getSegments(DemoConstants.ia110, DemoConstants.ia1111); + // // demo.getSegments(toWildcard(ia121), ia121); + // // demo.getSegments(toWildcard(ia120), toWildcard(ia210)); + + // ProtobufSegmentMapperDemo demoLab = + // new ProtobufSegmentMapperDemo("[fd00:f00d:cafe::7f00:14]:31000"); + // demoLab.getSegments(ScionUtil.parseIA("1-ff00:0:110"), ScionUtil.parseIA("2-ff00:0:210")); + + // ProtobufSegmentMapperDemo demoLab = new ProtobufSegmentMapperDemo("127.0.0.71:31000"); + // demoLab.getSegments(ScionUtil.parseIA("1-ff00:0:1001"), ScionUtil.parseIA("1-ff00:0:1007")); + + // 64-2:0:28 Cyberlink Cloud Test AS + ProtobufSegmentMapperDemo demoLab = new ProtobufSegmentMapperDemo("192.168.53.20:30252"); + demoLab.getSegments(ScionUtil.parseIA("64-0:0:0"), ScionUtil.parseIA("64-0:0:0")); + } + + public ProtobufSegmentMapperDemo(String csAddress) { + channel = Grpc.newChannelBuilder(csAddress, InsecureChannelCredentials.create()).build(); + segmentStub = SegmentLookupServiceGrpc.newBlockingStub(channel); + } + + private void getSegments(long srcIsdAs, long dstIsdAs) throws IOException { + // LOG.info("*** GetASInfo ***"); + System.out.println( + "Requesting segments: " + + ScionUtil.toStringIA(srcIsdAs) + + " -> " + + ScionUtil.toStringIA(dstIsdAs)); + if (srcIsdAs == dstIsdAs) { + return; + } + Seg.SegmentsRequest request = + Seg.SegmentsRequest.newBuilder().setSrcIsdAs(srcIsdAs).setDstIsdAs(dstIsdAs).build(); + Seg.SegmentsResponse response; + try { + response = segmentStub.segments(request); + } catch (StatusRuntimeException e) { + throw new ScionException("Error while getting Segment info: " + e.getMessage(), e); + } + print(response); + } + + private static void print(Seg.SegmentsResponse response) throws IOException { + analyze(response); + // try { + // for (Map.Entry seg : + // response.getSegmentsMap().entrySet()) { + // System.out.println( + // "SEG: key=" + // + Seg.SegmentType.forNumber(seg.getKey()) + // + " -> n=" + // + seg.getValue().getSegmentsCount()); + // for (Seg.PathSegment pathSegment : seg.getValue().getSegmentsList()) { + // print(pathSegment); + // } + // } + // } catch (InvalidProtocolBufferException e) { + // throw new ScionException(e); + // } + } + + private static class AsInfo { + final long isdAs; + final Map links = new HashMap<>(); + + AsInfo(long isdAs) { + this.isdAs = isdAs; + } + + public void addLink(long id, AsLink link) { + links.put(id, link); + } + } + + private static class AsLink { + private final long as0; + private final long as1; + private final long id0; + private final long id1; + + public AsLink(long isdAs, long nextIsdAs, long id0, long id1) { + this.as0 = isdAs; + this.as1 = nextIsdAs; + this.id0 = id0; + this.id1 = id1; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AsLink)) { + return false; + } + AsLink asLink = (AsLink) o; + return as0 == asLink.as0 && as1 == asLink.as1 && id0 == asLink.id0 && id1 == asLink.id1; + } + + @Override + public int hashCode() { + return Objects.hash(as0, as1, id0, id1); + } + } + + private static void analyze(Seg.SegmentsResponse response) throws IOException { + List segments = getPathSegments(response); + + Map asMap = new HashMap<>(); + Set asLinks = new HashSet<>(); + for (PathSegment s : segments) { + AsInfo asPrev = null; + long idPrev = -1; + for (int i = 0; i < s.getAsEntriesCount(); i++) { + Seg.ASEntrySignedBody body = s.getAsEntries(i); + Seg.HopEntry hopEntry = body.getHopEntry(); + Seg.HopField hopField = hopEntry.getHopField(); + + long isdAs = body.getIsdAs(); + long nextIsdAs = body.getNextIsdAs(); + AsInfo as0 = asMap.computeIfAbsent(isdAs, l -> l > 0 ? new AsInfo(l) : null); + AsInfo as1 = asMap.computeIfAbsent(nextIsdAs, l -> l > 0 ? new AsInfo(l) : null); + long id0 = hopField.getEgress(); + long id1 = hopField.getIngress(); + + if (i >= 1) { + // This is a bit weird, but it works. + AsLink link = new AsLink(asPrev.isdAs, isdAs, idPrev, id1); + asLinks.add(link); + // as0.addLink(id0, link); + // as1.addLink(id1, link); + } + asPrev = as0; + idPrev = id0; + } + } + + write(asMap, asLinks); + } + + private static void write(Map asMap, Set asLinks) throws IOException { + // write + FileWriter writer = new FileWriter("mytopo.topo"); + + String NL = System.lineSeparator(); + writer.append("--- # My Topology").append(NL); + writer.append("ASes:").append(NL); + for (AsInfo as : asMap.values()) { + writer.append(" \"").append(ScionUtil.toStringIA(as.isdAs)).append("\":").append(NL); + writer.append(" core: true").append(NL); + writer.append(" voting: true").append(NL); + writer.append(" authoritative: true").append(NL); + writer.append(" issuing: true").append(NL); + } + writer.append("links:").append(NL); + // - {a: "1-ff00:0:1001#21", b: "1-ff00:0:1002#11", linkAtoB: CORE} + for (AsLink l : asLinks) { + writer + .append(" - {a: \"") + .append(ScionUtil.toStringIA(l.as0)) + .append("#") + .append(String.valueOf(l.id0)); + writer + .append("\", b: \"") + .append(ScionUtil.toStringIA(l.as1)) + .append("#") + .append(String.valueOf(l.id1)); + writer.append("\", linkAtoB: CORE}").append(NL); + } + + writer.flush(); + writer.close(); + } + + private static List getPathSegments(Seg.SegmentsResponse response) { + List pathSegments = new ArrayList<>(); + for (Map.Entry seg : + response.getSegmentsMap().entrySet()) { + SegmentType type = SegmentType.from(Seg.SegmentType.forNumber(seg.getKey())); + seg.getValue() + .getSegmentsList() + .forEach(path -> pathSegments.add(new PathSegment(path, type))); + System.out.println( + "Segments found of type " + type.name() + ": " + seg.getValue().getSegmentsCount()); + } + return pathSegments; + } + + private static Seg.ASEntrySignedBody getBody(Seg.ASEntry asEntry) { + if (!asEntry.hasSigned()) { + throw new UnsupportedOperationException("Unsigned entries are not supported"); + } + Signed.SignedMessage sm = asEntry.getSigned(); + try { + Signed.HeaderAndBodyInternal habi = + Signed.HeaderAndBodyInternal.parseFrom(sm.getHeaderAndBody()); + return Seg.ASEntrySignedBody.parseFrom(habi.getBody()); + } catch (InvalidProtocolBufferException e) { + throw new ScionRuntimeException(e); + } + } + + private static Seg.SegmentInformation getInfo(Seg.PathSegment pathSegment) { + try { + return Seg.SegmentInformation.parseFrom(pathSegment.getSegmentInfo()); + } catch (InvalidProtocolBufferException e) { + throw new ScionRuntimeException(e); + } + } + + private enum SegmentType { + UP, + CORE, + DOWN; + + public static SegmentType from(Seg.SegmentType segmentType) { + switch (segmentType) { + case SEGMENT_TYPE_UP: + return UP; + case SEGMENT_TYPE_DOWN: + return DOWN; + case SEGMENT_TYPE_CORE: + return CORE; + default: + throw new IllegalArgumentException("type=" + segmentType); + } + } + } + + private static class PathSegment { + final Seg.PathSegment segment; + final List bodies; + final Seg.SegmentInformation info; + final SegmentType type; // + + PathSegment(Seg.PathSegment segment, SegmentType type) { + this.segment = segment; + this.bodies = + Collections.unmodifiableList( + segment.getAsEntriesList().stream() + .map(ProtobufSegmentMapperDemo::getBody) + .collect(Collectors.toList())); + this.info = getInfo(segment); + this.type = type; + } + + public Seg.ASEntrySignedBody getAsEntriesFirst() { + return bodies.get(0); + } + + public Seg.ASEntrySignedBody getAsEntriesLast() { + return bodies.get(bodies.size() - 1); + } + + public List getAsEntriesList() { + return bodies; + } + + public Seg.ASEntrySignedBody getAsEntries(int i) { + return bodies.get(i); + } + + public int getAsEntriesCount() { + return bodies.size(); + } + + public ByteString getSegmentInfo() { + return segment.getSegmentInfo(); + } + + public boolean isCore() { + return type == SegmentType.CORE; + } + } + + public static void print(Seg.PathSegment pathSegment) throws InvalidProtocolBufferException { + System.out.println(" PathSeg: size=" + pathSegment.getSegmentInfo().size()); + Seg.SegmentInformation segInf = Seg.SegmentInformation.parseFrom(pathSegment.getSegmentInfo()); + System.out.println( + " SegInfo: ts=" + + Instant.ofEpochSecond(segInf.getTimestamp()) + + " id=" + + segInf.getSegmentId()); + for (Seg.ASEntry asEntry : pathSegment.getAsEntriesList()) { + if (asEntry.hasSigned()) { + Signed.SignedMessage sm = asEntry.getSigned(); + System.out.println( + " AS: signed=" + + sm.getHeaderAndBody().size() + + " signature size=" + + sm.getSignature().size()); + // System.out.println( + // " Header/Body=" + Arrays.toString(sm.getHeaderAndBody().toByteArray())); + // System.out.println( + // " Signature =" + Arrays.toString(sm.getSignature().toByteArray())); + + Signed.HeaderAndBodyInternal habi = + Signed.HeaderAndBodyInternal.parseFrom(sm.getHeaderAndBody()); + // System.out.println( + // " habi: " + habi.getHeader().size() + " " + + // habi.getBody().size()); + Signed.Header header = Signed.Header.parseFrom(habi.getHeader()); + // TODO body for signature verification?!? + Timestamp ts = header.getTimestamp(); + System.out.println( + " AS header: " + + header.getSignatureAlgorithm() + + " time=" + + Instant.ofEpochSecond(ts.getSeconds(), ts.getNanos()) + + " meta=" + + header.getMetadata().size() + + " data=" + + header.getAssociatedDataLength()); + + Seg.ASEntrySignedBody body = Seg.ASEntrySignedBody.parseFrom(habi.getBody()); + System.out.println( + " AS Body: IA=" + + ScionUtil.toStringIA(body.getIsdAs()) + + " nextIA=" + + ScionUtil.toStringIA(body.getNextIsdAs()) + // + " nPeers=" + // + body.getPeerEntriesCount() + + " mtu=" + + body.getMtu()); + Seg.HopEntry he = body.getHopEntry(); + System.out.println(" HopEntry: " + he.hasHopField() + " mtu=" + he.getIngressMtu()); + + if (he.hasHopField()) { + Seg.HopField hf = he.getHopField(); + System.out.println( + " HopField: exp=" + + hf.getExpTime() + + " ingress=" + + hf.getIngress() + + " egress=" + + hf.getEgress()); + } + if (body.hasExtensions()) { + SegExtensions.PathSegmentExtensions pse = body.getExtensions(); + if (pse.hasStaticInfo()) { + SegExtensions.StaticInfoExtension sie = pse.getStaticInfo(); + System.out.println( + " Static: latencies=" + + sie.getLatency().getIntraCount() + + "/" + + sie.getLatency().getInterCount() + + " bandwidth=" + + sie.getBandwidth().getIntraCount() + + "/" + + sie.getBandwidth().getInterCount() + + " geo=" + + sie.getGeoCount() + + " interfaces=" + + sie.getLinkTypeCount() + + " note=" + + sie.getNote()); + } + } + } + if (asEntry.hasUnsigned()) { + SegExtensions.PathSegmentUnsignedExtensions psue = asEntry.getUnsigned(); + System.out.println(" AS: hasEpic=" + psue.hasEpic()); + if (psue.hasEpic()) { + SegDetachedExtensions.EPICDetachedExtension epic = psue.getEpic(); + System.out.println(" EPIC: " + epic.getAuthHopEntry().size() + " ..."); + } + } + } + } +} diff --git a/src/test/java/org/scion/jpan/api/ScmpSenderAsyncTest.java b/src/test/java/org/scion/jpan/api/ScmpSenderAsyncTest.java index 6263cc84..8b3f52a5 100644 --- a/src/test/java/org/scion/jpan/api/ScmpSenderAsyncTest.java +++ b/src/test/java/org/scion/jpan/api/ScmpSenderAsyncTest.java @@ -21,8 +21,10 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; import org.junit.jupiter.api.AfterAll; @@ -121,45 +123,120 @@ void getHandler() throws IOException { } } + @Test + void getOption() throws IOException { + MockNetwork.startTiny(); + EchoHandler handler = new EchoHandler(); + try (ScmpSenderAsync channel = Scmp.newSenderAsyncBuilder(handler).build()) { + assertFalse(channel.getOption(ScionSocketOptions.SCION_API_THROW_PARSER_FAILURE)); + assertEquals(handler, channel.getHandler()); + channel.setOption(ScionSocketOptions.SCION_API_THROW_PARSER_FAILURE, true); + assertTrue(channel.getOption(ScionSocketOptions.SCION_API_THROW_PARSER_FAILURE)); + } finally { + MockNetwork.stopTiny(); + } + } + @Test void sendEcho() throws IOException { - testEcho(this::getPathTo112); + testEcho(this::getPathTo112, 5); } @Test void sendEcho_localAS_BR() throws IOException { - testEcho(this::getPathToLocalAS_BR); - assertEquals(1, MockNetwork.getAndResetForwardCount()); // 1! - assertEquals(1, MockScmpHandler.getAndResetAnswerTotal()); + int n = 5; + testEcho(this::getPathToLocalAS_BR, n); + assertEquals(n, MockNetwork.getAndResetForwardCount()); // 1! + assertEquals(n, MockScmpHandler.getAndResetAnswerTotal()); } @Test void sendEcho_localAS_BR_30041() throws IOException { - testEcho(this::getPathToLocalAS_BR_30041); + int n = 5; + testEcho(this::getPathToLocalAS_BR_30041, n); assertEquals(0, MockNetwork.getAndResetForwardCount()); // 0! - assertEquals(1, MockScmpHandler.getAndResetAnswerTotal()); + assertEquals(n, MockScmpHandler.getAndResetAnswerTotal()); } - private void testEcho(Supplier pathSupplier) throws IOException { + private void testEcho(Supplier pathSupplier, int n) throws IOException { MockNetwork.startTiny(); - MockNetwork.answerNextScmpEchos(1); + MockNetwork.answerNextScmpEchos(n); + EchoHandler handler = new EchoHandler(); + try (ScmpSenderAsync channel = Scmp.newSenderAsyncBuilder(handler).build()) { + channel.setOption(ScionSocketOptions.SCION_API_THROW_PARSER_FAILURE, true); + byte[] data = new byte[] {1, 2, 3, 4, 5}; + Path path = pathSupplier.get(); + for (int i = 0; i < 5; i++) { + int seqId = channel.sendEcho(path, ByteBuffer.wrap(data)); + assertEquals(i, seqId); + Scmp.EchoMessage result = handler.get(); + assertEquals(seqId, result.getSequenceNumber(), "i=" + i); + assertEquals(Scmp.TypeCode.TYPE_129, result.getTypeCode(), "T/O=" + result.isTimedOut()); + assertTrue(result.getNanoSeconds() > 0); + assertTrue(result.getNanoSeconds() < 100_000_000); // 10 ms + assertArrayEquals(data, result.getData()); + assertFalse(result.isTimedOut()); + Path returnPath = result.getPath(); + assertEquals(path.getRemoteAddress(), returnPath.getRemoteAddress()); + assertEquals(Constants.SCMP_PORT, returnPath.getRemotePort()); + assertEquals(path.getRemoteIsdAs(), returnPath.getRemoteIsdAs()); + } + } finally { + MockNetwork.stopTiny(); + } + } + + @Test + void sendEcho_async() throws IOException { + testEchoAsync(this::getPathTo112, 25); + } + + @Test + void sendEcho_localAS_BR_async() throws IOException { + int n = 25; + testEchoAsync(this::getPathToLocalAS_BR, n); + assertEquals(n, MockNetwork.getAndResetForwardCount()); // 1! + assertEquals(n, MockScmpHandler.getAndResetAnswerTotal()); + } + + @Test + void sendEcho_localAS_BR_30041_async() throws IOException { + int n = 25; + testEchoAsync(this::getPathToLocalAS_BR_30041, n); + assertEquals(0, MockNetwork.getAndResetForwardCount()); // 0! + assertEquals(n, MockScmpHandler.getAndResetAnswerTotal()); + } + + private void testEchoAsync(Supplier pathSupplier, int n) throws IOException { + MockNetwork.startTiny(); + MockNetwork.answerNextScmpEchos(n); EchoHandler handler = new EchoHandler(); try (ScmpSenderAsync channel = Scmp.newSenderAsyncBuilder(handler).build()) { channel.setOption(ScionSocketOptions.SCION_API_THROW_PARSER_FAILURE, true); byte[] data = new byte[] {1, 2, 3, 4, 5}; Path path = pathSupplier.get(); - int seqId = channel.sendEcho(path, ByteBuffer.wrap(data)); - Scmp.EchoMessage result = handler.get(); - assertEquals(seqId, result.getSequenceNumber()); - assertEquals(Scmp.TypeCode.TYPE_129, result.getTypeCode(), "T/O=" + result.isTimedOut()); - assertTrue(result.getNanoSeconds() > 0); - assertTrue(result.getNanoSeconds() < 100_000_000); // 10 ms - assertArrayEquals(data, result.getData()); - assertFalse(result.isTimedOut()); - Path returnPath = result.getPath(); - assertEquals(path.getRemoteAddress(), returnPath.getRemoteAddress()); - assertEquals(Constants.SCMP_PORT, returnPath.getRemotePort()); - assertEquals(path.getRemoteIsdAs(), returnPath.getRemoteIsdAs()); + HashSet seqIDs = new HashSet<>(); + for (int i = 0; i < n; i++) { + seqIDs.add(channel.sendEcho(path, ByteBuffer.wrap(data))); + } + + List results = handler.get(n); + long nTimedOut = results.stream().filter(Scmp.TimedMessage::isTimedOut).count(); + assertEquals(0, nTimedOut); + for (int i = 0; i < n; i++) { + Scmp.EchoMessage result = results.get(i); + assertTrue(seqIDs.contains(result.getSequenceNumber())); + seqIDs.remove(result.getSequenceNumber()); + assertEquals(Scmp.TypeCode.TYPE_129, result.getTypeCode(), "T/O=" + result.isTimedOut()); + assertTrue(result.getNanoSeconds() > 0); + assertTrue(result.getNanoSeconds() < 500_000_000); // 10 ms + assertArrayEquals(data, result.getData()); + assertFalse(result.isTimedOut()); + Path returnPath = result.getPath(); + assertEquals(path.getRemoteAddress(), returnPath.getRemoteAddress()); + assertEquals(Constants.SCMP_PORT, returnPath.getRemotePort()); + assertEquals(path.getRemoteIsdAs(), returnPath.getRemoteIsdAs()); + } } finally { MockNetwork.stopTiny(); } @@ -266,6 +343,44 @@ private void testTraceroute(Supplier path, int nHops) throws IOException { } } + @Test + void sendTraceroute_async() throws IOException { + testTracerouteAsync(this::getPathTo112, 25); + } + + private void testTracerouteAsync(Supplier pathSupplier, int nRepeat) throws IOException { + MockNetwork.startTiny(); + MockNetwork.answerNextScmpEchos(nRepeat); + TraceHandler handler = new TraceHandler(); + try (ScmpSenderAsync channel = Scmp.newSenderAsyncBuilder(handler).build()) { + channel.setOption(ScionSocketOptions.SCION_API_THROW_PARSER_FAILURE, true); + Path path = pathSupplier.get(); + HashSet seqIDs = new HashSet<>(); + for (int i = 0; i < nRepeat; i++) { + seqIDs.addAll(channel.sendTraceroute(path)); + } + + List results = handler.get(seqIDs.size()); + long nTimedOut = results.stream().filter(Scmp.TimedMessage::isTimedOut).count(); + assertEquals(0, nTimedOut); + for (int i = 0; i < nRepeat; i++) { + Scmp.TracerouteMessage result = results.get(i); + assertTrue(seqIDs.contains(result.getSequenceNumber())); + seqIDs.remove(result.getSequenceNumber()); + assertEquals(Scmp.TypeCode.TYPE_131, result.getTypeCode(), "T/O=" + result.isTimedOut()); + assertTrue(result.getNanoSeconds() > 0); + assertTrue(result.getNanoSeconds() < 500_000_000); // 10 ms + assertFalse(result.isTimedOut()); + Path returnPath = result.getPath(); + assertEquals(path.getRemoteAddress(), returnPath.getRemoteAddress()); + assertEquals(Constants.SCMP_PORT, returnPath.getRemotePort()); + assertEquals(path.getRemoteIsdAs(), returnPath.getRemoteIsdAs()); + } + } finally { + MockNetwork.stopTiny(); + } + } + @Test void sendTraceroute_timeout() throws IOException { MockNetwork.startTiny(); @@ -373,6 +488,45 @@ private void testTracerouteLast(Supplier pathSupplier, boolean success) th } } + @Test + void sendTracerouteLast_async() throws IOException { + testTracerouteLastAsync(this::getPathTo112, 25); + } + + private void testTracerouteLastAsync(Supplier pathSupplier, int nRepeat) + throws IOException { + MockNetwork.startTiny(); + MockNetwork.answerNextScmpEchos(nRepeat); + TraceHandler handler = new TraceHandler(); + try (ScmpSenderAsync channel = Scmp.newSenderAsyncBuilder(handler).build()) { + channel.setOption(ScionSocketOptions.SCION_API_THROW_PARSER_FAILURE, true); + Path path = pathSupplier.get(); + HashSet seqIDs = new HashSet<>(); + for (int i = 0; i < nRepeat; i++) { + seqIDs.add(channel.sendTracerouteLast(path)); + } + + List results = handler.get(seqIDs.size()); + long nTimedOut = results.stream().filter(Scmp.TimedMessage::isTimedOut).count(); + assertEquals(0, nTimedOut); + for (int i = 0; i < nRepeat; i++) { + Scmp.TracerouteMessage result = results.get(i); + assertTrue(seqIDs.contains(result.getSequenceNumber())); + seqIDs.remove(result.getSequenceNumber()); + assertEquals(Scmp.TypeCode.TYPE_131, result.getTypeCode(), "T/O=" + result.isTimedOut()); + assertTrue(result.getNanoSeconds() > 0); + assertTrue(result.getNanoSeconds() < 500_000_000); // 10 ms + assertFalse(result.isTimedOut()); + Path returnPath = result.getPath(); + assertEquals(path.getRemoteAddress(), returnPath.getRemoteAddress()); + assertEquals(Constants.SCMP_PORT, returnPath.getRemotePort()); + assertEquals(path.getRemoteIsdAs(), returnPath.getRemoteIsdAs()); + } + } finally { + MockNetwork.stopTiny(); + } + } + @Test void sendTracerouteLast_timeout() throws IOException { MockNetwork.startTiny(); @@ -479,10 +633,7 @@ private Path getPathToLocalAS(String addressStr, int port) { private ScmpSenderAsync exceptionSender(ScmpHandler handler) throws IOException { MockDatagramChannel errorChannel = MockDatagramChannel.open(); - errorChannel.setSendCallback( - (byteBuffer, socketAddress) -> { - return 0; - }); + errorChannel.setSendCallback((byteBuffer, socketAddress) -> 0); // This selector throws an Exception when activated. MockDatagramChannel.MockSelector selector = MockDatagramChannel.MockSelector.open(); selector.setConnectCallback( @@ -497,10 +648,7 @@ private ScmpSenderAsync exceptionSender(ScmpHandler handler) throws IOExcepti private ScmpSenderAsync errorSender(ScmpHandler handler) throws IOException { MockDatagramChannel errorChannel = MockDatagramChannel.open(); - errorChannel.setSendCallback( - (byteBuffer, socketAddress) -> { - return 0; - }); + errorChannel.setSendCallback((byteBuffer, socketAddress) -> 0); errorChannel.setReceiveCallback( buffer -> { buffer.put(PING_ERROR_4_51_HK); @@ -519,20 +667,9 @@ private abstract static class ScmpHandler implements ScmpSenderAsync.Response final AtomicInteger exceptionCounter = new AtomicInteger(); volatile Scmp.ErrorMessage error = null; volatile Throwable exception = null; + final List responses = new CopyOnWriteArrayList<>(); - synchronized void handleError(Scmp.ErrorMessage msg) { - error = msg; - errorCounter.incrementAndGet(); - this.notifyAll(); - } - - synchronized void handleException(Throwable t) { - exception = t; - exceptionCounter.incrementAndGet(); - this.notifyAll(); - } - - protected T waitForResult(Supplier checkResult) throws IOException { + protected List waitForResult(int count) throws IOException { try { while (true) { synchronized (this) { @@ -542,8 +679,9 @@ protected T waitForResult(Supplier checkResult) throws IOException { if (exception != null) { throw new IOException(exception); } - T result = checkResult.get(); - if (result != null) { + if (responses.size() >= count) { + ArrayList result = new ArrayList<>(responses); + responses.clear(); return result; } this.wait(); @@ -558,79 +696,54 @@ protected T waitForResult(Supplier checkResult) throws IOException { protected synchronized void reset() { error = null; exception = null; + responses.clear(); } @Override - public void onError(Scmp.ErrorMessage msg) { - handleError(msg); - } - - @Override - public void onException(Throwable t) { - handleException(t); - } - } - - private static class EchoHandler extends ScmpHandler { - Scmp.EchoMessage response = null; - - synchronized void handle(Scmp.EchoMessage msg) { - response = msg; + @SuppressWarnings("unchecked") + public final synchronized void onResponse(Scmp.TimedMessage msg) { + responses.add((T) msg); this.notifyAll(); } - Scmp.EchoMessage get() throws IOException { - return super.waitForResult(() -> response); - } - @Override - public synchronized void reset() { - super.reset(); - response = null; + public final synchronized void onTimeout(Scmp.TimedMessage msg) { + onResponse(msg); } @Override - public void onResponse(Scmp.TimedMessage msg) { - handle((Scmp.EchoMessage) msg); + public final synchronized void onError(Scmp.ErrorMessage msg) { + error = msg; + errorCounter.incrementAndGet(); + this.notifyAll(); } @Override - public void onTimeout(Scmp.TimedMessage msg) { - handle((Scmp.EchoMessage) msg); + public final synchronized void onException(Throwable t) { + exception = t; + exceptionCounter.incrementAndGet(); + this.notifyAll(); } } - private static class TraceHandler extends ScmpHandler> { - private ArrayList responses = new ArrayList<>(); + private static class EchoHandler extends ScmpHandler { + Scmp.EchoMessage get() throws IOException { + return super.waitForResult(1).get(0); + } - synchronized void handle(Scmp.TracerouteMessage msg) { - responses.add(msg); - this.notifyAll(); + List get(int count) throws IOException { + return super.waitForResult(count); } + } + private static class TraceHandler extends ScmpHandler { /** * @param count Number of SCMP response that should be registered before get() returns. * @return 'Count' or more messages. * @throws IOException in case of a problem */ List get(int count) throws IOException { - return super.waitForResult(() -> responses.size() >= count ? responses : null); - } - - @Override - public synchronized void reset() { - super.reset(); - responses = new ArrayList<>(); - } - - @Override - public void onResponse(Scmp.TimedMessage msg) { - handle((Scmp.TracerouteMessage) msg); - } - - @Override - public void onTimeout(Scmp.TimedMessage msg) { - handle((Scmp.TracerouteMessage) msg); + return super.waitForResult(count); } } } diff --git a/src/test/java/org/scion/jpan/api/ScmpSenderTest.java b/src/test/java/org/scion/jpan/api/ScmpSenderTest.java index 3d83ace8..44c2541c 100644 --- a/src/test/java/org/scion/jpan/api/ScmpSenderTest.java +++ b/src/test/java/org/scion/jpan/api/ScmpSenderTest.java @@ -304,13 +304,9 @@ private ScmpSender exceptionSender() throws IOException { (byteBuffer, socketAddress) -> { return 0; }); - errorChannel.setThrowOnConnect(true); - // This selector throws an Exception when activated. + errorChannel.setThrowOnSend(true); MockDatagramChannel.MockSelector selector = MockDatagramChannel.MockSelector.open(); - selector.setConnectCallback( - () -> { - throw new IOException(); - }); + errorChannel.setReceiveCallback(byteBuffer -> null); return Scmp.newSenderBuilder().setDatagramChannel(errorChannel).setSelector(selector).build(); } diff --git a/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java b/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java index e610909d..25da3390 100644 --- a/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java +++ b/src/test/java/org/scion/jpan/demo/ShowpathsDemo.java @@ -82,7 +82,8 @@ public static void main(String[] args) throws IOException { { // Local port must be 30041 for networks that expect a dispatcher ShowpathsDemo demo = new ShowpathsDemo(Constants.DISPATCHER_PORT); - demo.runDemo(DemoConstants.iaAnapayaHK); + demo.runDemo(ScionUtil.parseIA("64-2:0:28")); + // demo.runDemo(DemoConstants.iaAnapayaHK); // demo.runDemo(DemoConstants.iaOVGU); break; } diff --git a/src/test/java/org/scion/jpan/demo/TopologyMapperDemo.java b/src/test/java/org/scion/jpan/demo/TopologyMapperDemo.java new file mode 100644 index 00000000..e2adddb3 --- /dev/null +++ b/src/test/java/org/scion/jpan/demo/TopologyMapperDemo.java @@ -0,0 +1,305 @@ +// 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.demo; + +import java.io.FileWriter; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.*; +import org.scion.jpan.*; +import org.scion.jpan.testutil.MockDNS; +import org.scion.jpan.testutil.Scenario; + +/** + * This demo mimics the "scion ping" command available in scionproto (...). This demo also demonstrates different ways + * of connecting to a network:
+ * - JUNIT_MOCK shows how to use the mock network in this library (for JUnit tests)
+ * - SCION_PROTO shows how to connect to a local topology from the scionproto go implementation such + * as "tiny". Note that the constants for "minimal" differ somewhat from the scionproto topology. + *
+ * - PRODUCTION shows different ways how to connect to the production network. Note: While the + * production network uses the dispatcher, the demo needs to use port 30041. + * + *

Commented out lines show alternative ways to connect or alternative destinations. + */ +public class TopologyMapperDemo { + + public static boolean SIMPLIFY = true; + public static boolean PRINT = true; + private static Network NETWORK = Network.PRODUCTION; + private final int localPort; + + public enum Network { + JUNIT_MOCK, // SCION Java JUnit mock network + SCION_PROTO, // Try to connect to scionproto networks, e.g. "tiny" + PRODUCTION // production network + } + + public static void init(boolean print, TopologyMapperDemo.Network network) { + PRINT = print; + NETWORK = network; + } + + public TopologyMapperDemo() { + this(12345); // Any port is fine unless we connect to a dispatcher network + } + + public TopologyMapperDemo(int localPort) { + this.localPort = localPort; + } + + public static void main(String[] args) throws IOException { + switch (NETWORK) { + case JUNIT_MOCK: + { + DemoTopology.configureMock(); + MockDNS.install("1-ff00:0:112", "ip6-localhost", "::1"); + TopologyMapperDemo demo = new TopologyMapperDemo(); + demo.runDemo(DemoConstants.ia110); + DemoTopology.shutDown(); + break; + } + case SCION_PROTO: + { + // ./bazel-bin/scion/cmd/scion/scion_/scion showpaths 2-ff00:0:222 --sciond + // 127.0.0.100:30255 + + // Use scenario builder to get access to relevant IP addresses + Scenario scenario = Scenario.readFrom("topologies/scionproto-default"); + // long srcIsdAs = ScionUtil.parseIA("2-ff00:0:212"); + // long srcIsdAs = ScionUtil.parseIA("1-ff00:0:132"); // TODO FIX!!! + long srcIsdAs = ScionUtil.parseIA("1-ff00:0:133"); + long dstIsdAs = ScionUtil.parseIA("2-ff00:0:222"); + + if (false) { + // Alternative #1: Bootstrap from topo file + System.setProperty( + Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, + "topologies/scionproto-default/ASff00_0_212/topology.json"); + } else { + // Alternative #2: Bootstrap from SCION daemon + System.setProperty(Constants.PROPERTY_DAEMON, scenario.getDaemon(srcIsdAs)); + } + + // // System.setProperty(Constants.PROPERTY_BOOTSTRAP_TOPO_FILE, + // // "topologies/minimal/ASff00_0_1111/topology.json"); + // //System.setProperty(Constants.PROPERTY_DAEMON, + // "[fd00:f00d:cafe::7f00:33]:31060"); // 0;132 + // System.setProperty(Constants.PROPERTY_DAEMON, "127.0.0.99:31066"); // 0:133 + TopologyMapperDemo demo = new TopologyMapperDemo(); + // demo.runDemo(ScionUtil.parseIA("2-ff00:0:211")); + demo.runDemo(ScionUtil.parseIA("2-ff00:0:222")); + break; + } + case PRODUCTION: + { + // Local port must be 30041 for networks that expect a dispatcher + TopologyMapperDemo demo = new TopologyMapperDemo(Constants.DISPATCHER_PORT); + demo.runDemo(ScionUtil.parseIA("64-2:0:28")); + // demo.runDemo(DemoConstants.iaAnapayaHK); + // demo.runDemo(DemoConstants.iaOVGU); + break; + } + } + Scion.closeDefault(); + } + + private void runDemo(long destinationIA) throws IOException { + ScionService service = Scion.defaultService(); + // dummy address + InetSocketAddress destinationAddress = + new InetSocketAddress(InetAddress.getLoopbackAddress(), 12345); + List paths = service.getPaths(destinationIA, destinationAddress); + if (paths.isEmpty()) { + String src = ScionUtil.toStringIA(service.getLocalIsdAs()); + String dst = ScionUtil.toStringIA(destinationIA); + 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; + for (Path path : paths) { + PathMetadata meta = path.getMetadata(); + println("[" + id++ + "] Hops: " + ScionUtil.toStringPath(meta)); + } + analyze(paths); + } + + private static void println(String msg) { + if (PRINT) { + System.out.println(msg); + } + } + + private static class AsInfo { + final long isdAs; + final Map links = new HashMap<>(); + + AsInfo(long isdAs) { + this.isdAs = isdAs; + } + + public void addLink(long id, AsLink link) { + links.put(id, link); + } + } + + private static class AsLink { + private final long as0; + private final long as1; + private final long id0; + private final long id1; + + public AsLink(long isdAs0, long isdAs1, long id0, long id1) { + if (isdAs0 < isdAs1) { + this.as0 = isdAs0; + this.as1 = isdAs1; + this.id0 = id0; + this.id1 = id1; + } else { + this.as0 = isdAs1; + this.as1 = isdAs0; + this.id0 = id1; + this.id1 = id0; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AsLink)) { + return false; + } + AsLink asLink = (AsLink) o; + return as0 == asLink.as0 && as1 == asLink.as1 && id0 == asLink.id0 && id1 == asLink.id1; + } + + @Override + public int hashCode() { + return Objects.hash(as0, as1, id0, id1); + } + } + + private static void analyze(List paths) throws IOException { + Map asMap = new HashMap<>(); + Set asLinks = new HashSet<>(); + for (Path p : paths) { + PathMetadata pm = p.getMetadata(); + int nInterfaces = pm.getInterfacesList().size(); + AsInfo asPrev = null; + long idPrev = -1; + + for (int i = 0; i < nInterfaces; i++) { + PathMetadata.PathInterface pIf = pm.getInterfacesList().get(i); + + if (i % 2 == 0) { + long isdAs = pIf.getIsdAs(); + asPrev = asMap.computeIfAbsent(isdAs, l -> l > 0 ? new AsInfo(l) : null); + idPrev = pIf.getId(); + // sb.append(ScionUtil.toStringIA(pIf.getIsdAs())).append(" "); + // sb.append(pIf.getId()).append(">"); + } else { + long isdAs = pIf.getIsdAs(); + long id1 = pIf.getId(); + AsLink link = new AsLink(asPrev.isdAs, isdAs, idPrev, id1); + asLinks.add(link); + // sb.append(pIf.getId()).append(" "); + } + } + } + + write(asMap, asLinks); + } + + private static void write(Map asMap, Set asLinks) throws IOException { + // write + FileWriter writer = new FileWriter("mytopo.topo"); + + String issuer = "1-ff00:0:210"; + for (AsInfo as : asMap.values()) { + if (issuingMap.contains(as.isdAs)) { + issuer = ScionUtil.toStringIA(as.isdAs); + break; + } + } + + String NL = System.lineSeparator(); + writer.append("--- # My Topology").append(NL); + writer.append("ASes:").append(NL); + for (AsInfo as : asMap.values()) { + writer.append(" \"").append(ScionUtil.toStringIA(as.isdAs)).append("\":").append(NL); + if (coreMap.contains(as.isdAs)) { + writer.append(" core: true").append(NL); + writer.append(" voting: true").append(NL); + writer.append(" authoritative: true").append(NL); + } + if (issuingMap.contains(as.isdAs)) { + writer.append(" issuing: true").append(NL); + } else { + writer.append(" cert_issuer: ").append(issuer).append(NL); // TODO + } + } + writer.append("links:").append(NL); + // - {a: "1-ff00:0:1001#21", b: "1-ff00:0:1002#11", linkAtoB: CORE} + for (AsLink l : asLinks) { + writer + .append(" - {a: \"") + .append(ScionUtil.toStringIA(l.as0)) + .append("#") + .append(String.valueOf(l.id0)); + writer + .append("\", b: \"") + .append(ScionUtil.toStringIA(l.as1)) + .append("#") + .append(String.valueOf(l.id1)); + writer.append("\", linkAtoB: CORE}").append(NL); + } + + writer.flush(); + writer.close(); + } + + private static final HashSet coreMap = new HashSet<>(); + private static final HashSet issuingMap = new HashSet<>(); + + static { + // 64 - 559 // SWITCH CH Core + // 64 - 3303 // Swisscom CH Core, Issuing + // 64 - 6730 // Sunrise CH Core + // 64 - 12350 // VTX CH Core + // 64 - 13030 // Init7 CH Core + // 64 - 15623 // Cyberlink Core + // 64 - 2:0:13 // Anapaya CONNECT Zurich Core, Issuing + // 64 - 2:0:23 // InterCloud CH Zurich Core + // 64 - 2:0:56 // Armasuisse Andermatt Core + coreMap.add(ScionUtil.parseIA("64-559")); // SWITCH CH Core + coreMap.add(ScionUtil.parseIA("64-3303")); // Swisscom CH Core, Issuing + issuingMap.add(ScionUtil.parseIA("64-3303")); // Swisscom CH Core, Issuing + coreMap.add(ScionUtil.parseIA("64-6730")); // Sunrise CH Core + coreMap.add(ScionUtil.parseIA("64-12350")); // VTX CH Core + coreMap.add(ScionUtil.parseIA("64-13030")); // Init7 CH Core + coreMap.add(ScionUtil.parseIA("64-15623")); // Cyberlink Core + coreMap.add(ScionUtil.parseIA("64-2:0:13")); // Anapaya CONNECT Zurich Core, Issuing + issuingMap.add(ScionUtil.parseIA("64-2:0:13")); // Anapaya CONNECT Zurich Core, Issuing + coreMap.add(ScionUtil.parseIA("64-2:0:23")); // InterCloud CH Zurich Core + coreMap.add(ScionUtil.parseIA("64-2:0:56")); // Armasuisse Andermatt Core + } +} diff --git a/src/test/java/org/scion/jpan/demo/inspector/HopField.java b/src/test/java/org/scion/jpan/demo/inspector/HopField.java index 7360cbf2..2c4f690b 100644 --- a/src/test/java/org/scion/jpan/demo/inspector/HopField.java +++ b/src/test/java/org/scion/jpan/demo/inspector/HopField.java @@ -26,22 +26,26 @@ public class HopField { private boolean r3; private boolean r4; private boolean r5; + /** * 1 bit : ConsIngress Router Alert. If the ConsIngress Router Alert is set, the ingress router * (in construction direction) will process the L4 payload in the packet. */ private boolean flagI; + /** * 1 bit : ConsEgress Router Alert. If the ConsEgress Router Alert is set, the egress router (in * construction direction) will process the L4 payload in the packet. */ private boolean flagE; + /** * 8 bits : Expiry time of a hop field. The expiration time expressed is relative. An absolute * expiration time in seconds is computed in combination with the timestamp field (from the * corresponding info field) as follows: abs_time = timestamp + (1+expiryTime)*24*60*60/256 */ private int expiryTime; + // 16 bits : consIngress : The 16-bits ingress interface IDs in construction direction. private int consIngress; // 16 bits : consEgress : The 16-bits egress interface IDs in construction direction. diff --git a/src/test/java/org/scion/jpan/demo/inspector/ScmpHeader.java b/src/test/java/org/scion/jpan/demo/inspector/ScmpHeader.java index 0300376f..b9acf92b 100644 --- a/src/test/java/org/scion/jpan/demo/inspector/ScmpHeader.java +++ b/src/test/java/org/scion/jpan/demo/inspector/ScmpHeader.java @@ -128,4 +128,8 @@ public void setTraceData(long isdAs, int ifID) { this.traceIsdAs = isdAs; this.traceIfID = ifID; } + + public int getSequenceId() { + return short2; + } } diff --git a/src/test/java/org/scion/jpan/demo/inspector/ScmpInspectorTest.java b/src/test/java/org/scion/jpan/demo/inspector/ScmpInspectorTest.java index ebf0705e..8acef2a7 100644 --- a/src/test/java/org/scion/jpan/demo/inspector/ScmpInspectorTest.java +++ b/src/test/java/org/scion/jpan/demo/inspector/ScmpInspectorTest.java @@ -16,7 +16,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.io.IOException; import java.nio.ByteBuffer; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Test; @@ -89,7 +88,7 @@ public static void afterAll() { } @Test - void testScmpError_WrongPacketSize() throws IOException { + void testScmpError_WrongPacketSize() { ByteBuffer data = ByteBuffer.wrap(SCMP_PKT_SIZE).asReadOnlyBuffer(); ScionPacketInspector spi = ScionPacketInspector.readPacket(data); ScmpHeader hdr = spi.getScmpHeader(); @@ -98,7 +97,7 @@ void testScmpError_WrongPacketSize() throws IOException { } @Test - void testScmpError_WrongPacketSize2() throws IOException { + void testScmpError_WrongPacketSize2() { ByteBuffer data = ByteBuffer.wrap(SCMP_PACKET_SIZE2).asReadOnlyBuffer(); ScionPacketInspector spi = ScionPacketInspector.readPacket(data); ScmpHeader hdr = spi.getScmpHeader(); @@ -107,7 +106,7 @@ void testScmpError_WrongPacketSize2() throws IOException { } @Test - void testScmpError_WrongSrcIsdAs() throws IOException { + void testScmpError_WrongSrcIsdAs() { ByteBuffer data = ByteBuffer.wrap(WRONG_SRC_ISD_AS).asReadOnlyBuffer(); ScionPacketInspector spi = ScionPacketInspector.readPacket(data); ScmpHeader hdr = spi.getScmpHeader(); diff --git a/src/test/java/org/scion/jpan/testutil/MockDatagramChannel.java b/src/test/java/org/scion/jpan/testutil/MockDatagramChannel.java index 7748dda2..5c0b0955 100644 --- a/src/test/java/org/scion/jpan/testutil/MockDatagramChannel.java +++ b/src/test/java/org/scion/jpan/testutil/MockDatagramChannel.java @@ -56,7 +56,7 @@ public class MockDatagramChannel extends java.nio.channels.DatagramChannel { private SocketAddress bindAddress; private SocketAddress connectAddress; - private boolean throwOnConnect = false; + private boolean throwOnSend = false; private Function receiveCallback = byteBuffer -> { @@ -84,8 +84,8 @@ public void setSendCallback(BiFunction cb) { sendCallback = cb; } - public void setThrowOnConnect(boolean flag) { - this.throwOnConnect = flag; + public void setThrowOnSend(boolean flag) { + this.throwOnSend = flag; } @Override @@ -135,9 +135,6 @@ public boolean isConnected() { @Override public java.nio.channels.DatagramChannel connect(SocketAddress socketAddress) throws IOException { - if (throwOnConnect) { - throw new IOException(); - } connectAddress = socketAddress; isConnected = true; if (bindAddress == null) { @@ -168,6 +165,9 @@ public SocketAddress receive(ByteBuffer byteBuffer) throws IOException { @Override public int send(ByteBuffer byteBuffer, SocketAddress socketAddress) throws IOException { + if (throwOnSend) { + throw new IOException(); + } if (bindAddress == null) { bindAddress = BIND_ANY_SOCKET; } diff --git a/src/test/java/org/scion/jpan/testutil/MockNetwork.java b/src/test/java/org/scion/jpan/testutil/MockNetwork.java index 5d1d59cd..22bc4ad3 100644 --- a/src/test/java/org/scion/jpan/testutil/MockNetwork.java +++ b/src/test/java/org/scion/jpan/testutil/MockNetwork.java @@ -37,7 +37,6 @@ import org.scion.jpan.*; 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.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -362,7 +361,6 @@ private void forwardPacket(ByteBuffer buffer, SocketAddress srcAddress, Datagram private void handleScmp(ByteBuffer buffer, SocketAddress srcAddress, DatagramChannel outgoing) throws IOException { - buffer.position(ScionHeaderParser.extractHeaderLength(buffer)); Scmp.Type type0 = ScmpParser.extractType(buffer); // ignore SCMP responses if (type0 == Scmp.Type.INFO_129 || type0 == Scmp.Type.INFO_131) { diff --git a/src/test/java/org/scion/jpan/testutil/MockScmpHandler.java b/src/test/java/org/scion/jpan/testutil/MockScmpHandler.java index d2f36eb9..56b3f7a8 100644 --- a/src/test/java/org/scion/jpan/testutil/MockScmpHandler.java +++ b/src/test/java/org/scion/jpan/testutil/MockScmpHandler.java @@ -146,7 +146,6 @@ public void run() { private void handleScmp(ByteBuffer buffer, SocketAddress srcAddress, DatagramChannel incoming) throws IOException { - buffer.position(ScionHeaderParser.extractHeaderLength(buffer)); Scmp.Type type0 = ScmpParser.extractType(buffer); // ignore SCMP responses if (type0 == Scmp.Type.INFO_129 || type0 == Scmp.Type.INFO_131) { @@ -157,10 +156,10 @@ private void handleScmp(ByteBuffer buffer, SocketAddress srcAddress, DatagramCha buffer.rewind(); InetSocketAddress dstAddress = PackageVisibilityHelper.getDstAddress(buffer); // From here on we use linear reading using the buffer's position() mechanism - buffer.position(ScionHeaderParser.extractHeaderLength(buffer)); Path path = PackageVisibilityHelper.getResponsePath(buffer, (InetSocketAddress) srcAddress); Scmp.Type type = ScmpParser.extractType(buffer); Scmp.Message scmpMsg = PackageVisibilityHelper.createMessage(type, path); + buffer.position(ScionHeaderParser.extractHeaderLength(buffer)); ScmpParser.consume(buffer, scmpMsg); logger.info( " received SCMP {} {}", scmpMsg.getTypeCode().name(), scmpMsg.getTypeCode().getText()); diff --git a/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1001/topology.json b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1001/topology.json new file mode 100644 index 00000000..7d2b6e2c --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1001/topology.json @@ -0,0 +1,105 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "1-ff00:0:1001", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs1-ff00_0_1001-1": { + "addr": "127.0.0.71:31000" + } + }, + "discovery_service": { + "cs1-ff00_0_1001-1": { + "addr": "127.0.0.71:31000" + } + }, + "border_routers": { + "br1-ff00_0_1001-1": { + "internal_addr": "127.0.0.65:31002", + "interfaces": { + "21": { + "underlay": { + "local": "127.0.0.4:50000", + "remote": "127.0.0.5:50000" + }, + "isd_as": "1-ff00:0:1002", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1001-2": { + "internal_addr": "127.0.0.66:31004", + "interfaces": { + "22": { + "underlay": { + "local": "127.0.0.6:50000", + "remote": "127.0.0.7:50000" + }, + "isd_as": "1-ff00:0:1002", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1001-3": { + "internal_addr": "127.0.0.67:31006", + "interfaces": { + "31": { + "underlay": { + "local": "127.0.0.8:50000", + "remote": "127.0.0.9:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1001-4": { + "internal_addr": "127.0.0.68:31008", + "interfaces": { + "32": { + "underlay": { + "local": "127.0.0.10:50000", + "remote": "127.0.0.11:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1001-5": { + "internal_addr": "127.0.0.69:31010", + "interfaces": { + "41": { + "underlay": { + "local": "127.0.0.12:50000", + "remote": "127.0.0.13:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1001-6": { + "internal_addr": "127.0.0.70:31012", + "interfaces": { + "42": { + "underlay": { + "local": "127.0.0.14:50000", + "remote": "127.0.0.15:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1002/topology.json b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1002/topology.json new file mode 100644 index 00000000..afe7d6d8 --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1002/topology.json @@ -0,0 +1,133 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "1-ff00:0:1002", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs1-ff00_0_1002-1": { + "addr": "127.0.0.89:31014" + } + }, + "discovery_service": { + "cs1-ff00_0_1002-1": { + "addr": "127.0.0.89:31014" + } + }, + "border_routers": { + "br1-ff00_0_1002-1": { + "internal_addr": "127.0.0.81:31016", + "interfaces": { + "11": { + "underlay": { + "local": "127.0.0.5:50000", + "remote": "127.0.0.4:50000" + }, + "isd_as": "1-ff00:0:1001", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1002-2": { + "internal_addr": "127.0.0.82:31018", + "interfaces": { + "12": { + "underlay": { + "local": "127.0.0.7:50000", + "remote": "127.0.0.6:50000" + }, + "isd_as": "1-ff00:0:1001", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1002-3": { + "internal_addr": "127.0.0.83:31020", + "interfaces": { + "31": { + "underlay": { + "local": "127.0.0.16:50000", + "remote": "127.0.0.17:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1002-4": { + "internal_addr": "127.0.0.84:31022", + "interfaces": { + "32": { + "underlay": { + "local": "127.0.0.18:50000", + "remote": "127.0.0.19:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1002-5": { + "internal_addr": "127.0.0.85:31024", + "interfaces": { + "41": { + "underlay": { + "local": "127.0.0.20:50000", + "remote": "127.0.0.21:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1002-6": { + "internal_addr": "127.0.0.86:31026", + "interfaces": { + "42": { + "underlay": { + "local": "127.0.0.22:50000", + "remote": "127.0.0.23:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1002-7": { + "internal_addr": "127.0.0.87:31028", + "interfaces": { + "51": { + "underlay": { + "local": "127.0.0.24:50000", + "remote": "127.0.0.25:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1002-8": { + "internal_addr": "127.0.0.88:31030", + "interfaces": { + "52": { + "underlay": { + "local": "127.0.0.26:50000", + "remote": "127.0.0.27:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1003/topology.json b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1003/topology.json new file mode 100644 index 00000000..308f0444 --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1003/topology.json @@ -0,0 +1,161 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "1-ff00:0:1003", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs1-ff00_0_1003-1": { + "addr": "127.0.0.107:31032" + } + }, + "discovery_service": { + "cs1-ff00_0_1003-1": { + "addr": "127.0.0.107:31032" + } + }, + "border_routers": { + "br1-ff00_0_1003-1": { + "internal_addr": "127.0.0.98:31034", + "interfaces": { + "11": { + "underlay": { + "local": "127.0.0.9:50000", + "remote": "127.0.0.8:50000" + }, + "isd_as": "1-ff00:0:1001", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1003-2": { + "internal_addr": "127.0.0.99:31036", + "interfaces": { + "12": { + "underlay": { + "local": "127.0.0.11:50000", + "remote": "127.0.0.10:50000" + }, + "isd_as": "1-ff00:0:1001", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1003-3": { + "internal_addr": "127.0.0.100:31038", + "interfaces": { + "21": { + "underlay": { + "local": "127.0.0.17:50000", + "remote": "127.0.0.16:50000" + }, + "isd_as": "1-ff00:0:1002", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1003-4": { + "internal_addr": "127.0.0.101:31040", + "interfaces": { + "22": { + "underlay": { + "local": "127.0.0.19:50000", + "remote": "127.0.0.18:50000" + }, + "isd_as": "1-ff00:0:1002", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1003-5": { + "internal_addr": "127.0.0.102:31042", + "interfaces": { + "41": { + "underlay": { + "local": "127.0.0.30:50000", + "remote": "127.0.0.31:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1003-6": { + "internal_addr": "127.0.0.103:31044", + "interfaces": { + "42": { + "underlay": { + "local": "127.0.0.32:50000", + "remote": "127.0.0.33:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1003-7": { + "internal_addr": "127.0.0.104:31046", + "interfaces": { + "51": { + "underlay": { + "local": "127.0.0.34:50000", + "remote": "127.0.0.35:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1003-8": { + "internal_addr": "127.0.0.105:31048", + "interfaces": { + "52": { + "underlay": { + "local": "127.0.0.36:50000", + "remote": "127.0.0.37:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1003-9": { + "internal_addr": "127.0.0.106:31050", + "interfaces": { + "61": { + "underlay": { + "local": "127.0.0.38:50000", + "remote": "127.0.0.39:50000" + }, + "isd_as": "1-ff00:0:1006", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1003-10": { + "internal_addr": "127.0.0.97:31052", + "interfaces": { + "62": { + "underlay": { + "local": "127.0.0.28:50000", + "remote": "127.0.0.29:50000" + }, + "isd_as": "1-ff00:0:1006", + "link_to": "core", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1004/topology.json b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1004/topology.json new file mode 100644 index 00000000..368392fc --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1004/topology.json @@ -0,0 +1,189 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "1-ff00:0:1004", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs1-ff00_0_1004-1": { + "addr": "127.0.0.141:31054" + } + }, + "discovery_service": { + "cs1-ff00_0_1004-1": { + "addr": "127.0.0.141:31054" + } + }, + "border_routers": { + "br1-ff00_0_1004-1": { + "internal_addr": "127.0.0.132:31056", + "interfaces": { + "11": { + "underlay": { + "local": "127.0.0.13:50000", + "remote": "127.0.0.12:50000" + }, + "isd_as": "1-ff00:0:1001", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-2": { + "internal_addr": "127.0.0.133:31058", + "interfaces": { + "12": { + "underlay": { + "local": "127.0.0.15:50000", + "remote": "127.0.0.14:50000" + }, + "isd_as": "1-ff00:0:1001", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-3": { + "internal_addr": "127.0.0.134:31060", + "interfaces": { + "21": { + "underlay": { + "local": "127.0.0.21:50000", + "remote": "127.0.0.20:50000" + }, + "isd_as": "1-ff00:0:1002", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-4": { + "internal_addr": "127.0.0.135:31062", + "interfaces": { + "22": { + "underlay": { + "local": "127.0.0.23:50000", + "remote": "127.0.0.22:50000" + }, + "isd_as": "1-ff00:0:1002", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-5": { + "internal_addr": "127.0.0.136:31064", + "interfaces": { + "31": { + "underlay": { + "local": "127.0.0.31:50000", + "remote": "127.0.0.30:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-6": { + "internal_addr": "127.0.0.137:31066", + "interfaces": { + "32": { + "underlay": { + "local": "127.0.0.33:50000", + "remote": "127.0.0.32:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-7": { + "internal_addr": "127.0.0.138:31068", + "interfaces": { + "51": { + "underlay": { + "local": "127.0.0.46:50000", + "remote": "127.0.0.47:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-8": { + "internal_addr": "127.0.0.139:31070", + "interfaces": { + "52": { + "underlay": { + "local": "127.0.0.48:50000", + "remote": "127.0.0.49:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-9": { + "internal_addr": "127.0.0.140:31072", + "interfaces": { + "61": { + "underlay": { + "local": "127.0.0.50:50000", + "remote": "127.0.0.51:50000" + }, + "isd_as": "1-ff00:0:1006", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-10": { + "internal_addr": "127.0.0.129:31074", + "interfaces": { + "62": { + "underlay": { + "local": "127.0.0.40:50000", + "remote": "127.0.0.41:50000" + }, + "isd_as": "1-ff00:0:1006", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-11": { + "internal_addr": "127.0.0.130:31076", + "interfaces": { + "71": { + "underlay": { + "local": "127.0.0.42:50000", + "remote": "127.0.0.43:50000" + }, + "isd_as": "1-ff00:0:1007", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1004-12": { + "internal_addr": "127.0.0.131:31078", + "interfaces": { + "72": { + "underlay": { + "local": "127.0.0.44:50000", + "remote": "127.0.0.45:50000" + }, + "isd_as": "1-ff00:0:1007", + "link_to": "core", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1005/topology.json b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1005/topology.json new file mode 100644 index 00000000..63f1230a --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1005/topology.json @@ -0,0 +1,161 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "1-ff00:0:1005", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs1-ff00_0_1005-1": { + "addr": "127.0.0.123:31080" + } + }, + "discovery_service": { + "cs1-ff00_0_1005-1": { + "addr": "127.0.0.123:31080" + } + }, + "border_routers": { + "br1-ff00_0_1005-1": { + "internal_addr": "127.0.0.114:31082", + "interfaces": { + "21": { + "underlay": { + "local": "127.0.0.25:50000", + "remote": "127.0.0.24:50000" + }, + "isd_as": "1-ff00:0:1002", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1005-2": { + "internal_addr": "127.0.0.115:31084", + "interfaces": { + "22": { + "underlay": { + "local": "127.0.0.27:50000", + "remote": "127.0.0.26:50000" + }, + "isd_as": "1-ff00:0:1002", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1005-3": { + "internal_addr": "127.0.0.116:31086", + "interfaces": { + "31": { + "underlay": { + "local": "127.0.0.35:50000", + "remote": "127.0.0.34:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1005-4": { + "internal_addr": "127.0.0.117:31088", + "interfaces": { + "32": { + "underlay": { + "local": "127.0.0.37:50000", + "remote": "127.0.0.36:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1005-5": { + "internal_addr": "127.0.0.118:31090", + "interfaces": { + "41": { + "underlay": { + "local": "127.0.0.47:50000", + "remote": "127.0.0.46:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1005-6": { + "internal_addr": "127.0.0.119:31092", + "interfaces": { + "42": { + "underlay": { + "local": "127.0.0.49:50000", + "remote": "127.0.0.48:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1005-7": { + "internal_addr": "127.0.0.120:31094", + "interfaces": { + "61": { + "underlay": { + "local": "127.0.0.54:50000", + "remote": "127.0.0.55:50000" + }, + "isd_as": "1-ff00:0:1006", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1005-8": { + "internal_addr": "127.0.0.121:31096", + "interfaces": { + "62": { + "underlay": { + "local": "127.0.0.56:50000", + "remote": "127.0.0.57:50000" + }, + "isd_as": "1-ff00:0:1006", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1005-9": { + "internal_addr": "127.0.0.122:31098", + "interfaces": { + "71": { + "underlay": { + "local": "127.0.0.58:50000", + "remote": "127.0.0.59:50000" + }, + "isd_as": "1-ff00:0:1007", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1005-10": { + "internal_addr": "127.0.0.113:31100", + "interfaces": { + "72": { + "underlay": { + "local": "127.0.0.52:50000", + "remote": "127.0.0.53:50000" + }, + "isd_as": "1-ff00:0:1007", + "link_to": "core", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1006/topology.json b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1006/topology.json new file mode 100644 index 00000000..ae316fe3 --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1006/topology.json @@ -0,0 +1,133 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "1-ff00:0:1006", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs1-ff00_0_1006-1": { + "addr": "127.0.0.169:31102" + } + }, + "discovery_service": { + "cs1-ff00_0_1006-1": { + "addr": "127.0.0.169:31102" + } + }, + "border_routers": { + "br1-ff00_0_1006-1": { + "internal_addr": "127.0.0.161:31104", + "interfaces": { + "31": { + "underlay": { + "local": "127.0.0.39:50000", + "remote": "127.0.0.38:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1006-2": { + "internal_addr": "127.0.0.162:31106", + "interfaces": { + "32": { + "underlay": { + "local": "127.0.0.29:50000", + "remote": "127.0.0.28:50000" + }, + "isd_as": "1-ff00:0:1003", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1006-3": { + "internal_addr": "127.0.0.163:31108", + "interfaces": { + "41": { + "underlay": { + "local": "127.0.0.51:50000", + "remote": "127.0.0.50:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1006-4": { + "internal_addr": "127.0.0.164:31110", + "interfaces": { + "42": { + "underlay": { + "local": "127.0.0.41:50000", + "remote": "127.0.0.40:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1006-5": { + "internal_addr": "127.0.0.165:31112", + "interfaces": { + "51": { + "underlay": { + "local": "127.0.0.55:50000", + "remote": "127.0.0.54:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1006-6": { + "internal_addr": "127.0.0.166:31114", + "interfaces": { + "52": { + "underlay": { + "local": "127.0.0.57:50000", + "remote": "127.0.0.56:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1006-7": { + "internal_addr": "127.0.0.167:31116", + "interfaces": { + "71": { + "underlay": { + "local": "127.0.0.60:50000", + "remote": "127.0.0.61:50000" + }, + "isd_as": "1-ff00:0:1007", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1006-8": { + "internal_addr": "127.0.0.168:31118", + "interfaces": { + "72": { + "underlay": { + "local": "127.0.0.62:50000", + "remote": "127.0.0.63:50000" + }, + "isd_as": "1-ff00:0:1007", + "link_to": "core", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1007/topology.json b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1007/topology.json new file mode 100644 index 00000000..d803fc19 --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/ASff00_0_1007/topology.json @@ -0,0 +1,105 @@ +{ + "attributes": [ + "core" + ], + "isd_as": "1-ff00:0:1007", + "mtu": 1472, + "test_dispatcher": true, + "dispatched_ports": "31000-32767", + "control_service": { + "cs1-ff00_0_1007-1": { + "addr": "127.0.0.183:31120" + } + }, + "discovery_service": { + "cs1-ff00_0_1007-1": { + "addr": "127.0.0.183:31120" + } + }, + "border_routers": { + "br1-ff00_0_1007-1": { + "internal_addr": "127.0.0.177:31122", + "interfaces": { + "41": { + "underlay": { + "local": "127.0.0.43:50000", + "remote": "127.0.0.42:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1007-2": { + "internal_addr": "127.0.0.178:31124", + "interfaces": { + "42": { + "underlay": { + "local": "127.0.0.45:50000", + "remote": "127.0.0.44:50000" + }, + "isd_as": "1-ff00:0:1004", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1007-3": { + "internal_addr": "127.0.0.179:31126", + "interfaces": { + "51": { + "underlay": { + "local": "127.0.0.59:50000", + "remote": "127.0.0.58:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1007-4": { + "internal_addr": "127.0.0.180:31128", + "interfaces": { + "52": { + "underlay": { + "local": "127.0.0.53:50000", + "remote": "127.0.0.52:50000" + }, + "isd_as": "1-ff00:0:1005", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1007-5": { + "internal_addr": "127.0.0.181:31130", + "interfaces": { + "61": { + "underlay": { + "local": "127.0.0.61:50000", + "remote": "127.0.0.60:50000" + }, + "isd_as": "1-ff00:0:1006", + "link_to": "core", + "mtu": 1472 + } + } + }, + "br1-ff00_0_1007-6": { + "internal_addr": "127.0.0.182:31132", + "interfaces": { + "62": { + "underlay": { + "local": "127.0.0.63:50000", + "remote": "127.0.0.62:50000" + }, + "isd_as": "1-ff00:0:1006", + "link_to": "core", + "mtu": 1472 + } + } + } + } +} diff --git a/src/test/resources/topologies/scionproto-scionlab/ISD1/trcs/ISD1-B1-S1.trc b/src/test/resources/topologies/scionproto-scionlab/ISD1/trcs/ISD1-B1-S1.trc new file mode 100644 index 00000000..198de286 --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/ISD1/trcs/ISD1-B1-S1.trc @@ -0,0 +1,326 @@ +-----BEGIN TRC----- +MII8mwYJKoZIhvcNAQcCoII8jDCCPIgCAQExDTALBglghkgBZQMEAgEwgirQBgkq +hkiG9w0BBwGggirBBIIqvTCCKrkCAQAwCQIBAQIBAQIBATAiGA8yMDI0MDkzMDE2 +MDYxNloYDzIwMjUxMjI0MTYwNjE2WgIBAAEBADAAAgEEMFsTC2ZmMDA6MDoxMDA3 +EwtmZjAwOjA6MTAwMRMLZmYwMDowOjEwMDITC2ZmMDA6MDoxMDAzEwtmZjAwOjA6 +MTAwNBMLZmYwMDowOjEwMDUTC2ZmMDA6MDoxMDA2MFsTC2ZmMDA6MDoxMDA3Ewtm +ZjAwOjA6MTAwMRMLZmYwMDowOjEwMDITC2ZmMDA6MDoxMDAzEwtmZjAwOjA6MTAw +NBMLZmYwMDowOjEwMDUTC2ZmMDA6MDoxMDA2DBhUZXN0Y3J5cHRvIFRSQyBmb3Ig +SVNEIDEwgimkMIICCzCCAbGgAwIBAgIULakjzGSnOePmELVmk+d1brFEQJYwCgYI +KoZIzj0EAwIwUTEvMC0GA1UEAxMmMS1mZjAwOjA6MTAwMSBSb290IENlcnRpZmlj +YXRlIC0gR0VOIEkxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMTAeFw0y +NDA5MzAxNjA2MTZaFw0yNjA5MzAxNjA3MTZaMFExLzAtBgNVBAMTJjEtZmYwMDow +OjEwMDEgUm9vdCBDZXJ0aWZpY2F0ZSAtIEdFTiBJMR4wHAYLKwYBBAGDsBwBAgET +DTEtZmYwMDowOjEwMDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASsScVPl7v9 +HZo0IlZhyucaa0RUV4P8DgX12arJDD9xEBt7KA6TUUsGtlBagDmdAPngMeCHt3WW +yII0qF6Rlcbio2cwZTAOBgNVHQ8BAf8EBAMCAgQwIAYDVR0lBBkwFwYIKwYBBQUH +AwgGCysGAQQBg7AcAQMDMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFBhI +uWHM1n2kTyFwa2oZq9Bx0gn8MAoGCCqGSM49BAMCA0gAMEUCICx0Lp00rAf3w0Lf +Ch6T62OyMiQcRwAhER4SURkcJ/e6AiEA2p7pWrXgtLsS/x8oSz0jaR0NC/hOEnKw +spF8qbNQZ4cwggHsMIIBkaADAgECAhQnBLW11wRtPafpXJxJNc0CfXhH+jAKBggq +hkjOPQQDAjBTMTEwLwYDVQQDEygxLWZmMDA6MDoxMDAxIFJlZ3VsYXIgVm90aW5n +IENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDEwHhcN +MjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBTMTEwLwYDVQQDEygxLWZmMDA6 +MDoxMDAxIFJlZ3VsYXIgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwB +AgETDTEtZmYwMDowOjEwMDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATTbP5r +ScLSDr/Q3w6pqWRIVlDg+4t9y6N20GNiwBe9/xLRLf3OVl2eWeS8YIucaZuWKJyB +RPoe0dShqi3hjjTno0MwQTAgBgNVHSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwB +AwIwHQYDVR0OBBYEFMSPf9uqyfudMcLEiAa++W71cgaWMAoGCCqGSM49BAMCA0kA +MEYCIQClOolfb2UrY8h2Fn0uWc7MLP128f/RCpTKJscV9Eg6vwIhANy5K3UsZfNv +MidC4a2KXUBMUkAO0FJzoUyBN5eo6pReMIIB7zCCAZWgAwIBAgIUP6EkSTjlkyig +4HXzYqoHA3pTcaYwCgYIKoZIzj0EAwIwVTEzMDEGA1UEAxMqMS1mZjAwOjA6MTAw +MSBTZW5zaXRpdmUgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgET +DTEtZmYwMDowOjEwMDEwHhcNMjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBV +MTMwMQYDVQQDEyoxLWZmMDA6MDoxMDAxIFNlbnNpdGl2ZSBWb3RpbmcgQ2VydGlm +aWNhdGUxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMTBZMBMGByqGSM49 +AgEGCCqGSM49AwEHA0IABLSQ4pc3Hn+tFoCEPDzImkAfpMwf16glEjD+uGxnIkG4 +VuGToRAkFlLcwYpVkCY2DvCTR691cpBoUSts0Ec1UzGjQzBBMCAGA1UdJQQZMBcG +CCsGAQUFBwMIBgsrBgEEAYOwHAEDATAdBgNVHQ4EFgQUiRjNjDvEHEYE0Pps2ZnF +XWXe5lIwCgYIKoZIzj0EAwIDSAAwRQIgMToREXqwLhYv1lcT7Ez44tUW3//39qv6 +YqE7FHfYLdgCIQDBqhjPYjLvv1CMbYlfgmZTpDOP+L6L8jww3EO9s0CG3DCCAg0w +ggGyoAMCAQICFQCUpv8GFp8piP/4PIK2K7pJuDz80zAKBggqhkjOPQQDAjBRMS8w +LQYDVQQDEyYxLWZmMDA6MDoxMDAyIFJvb3QgQ2VydGlmaWNhdGUgLSBHRU4gSTEe +MBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDAyMB4XDTI0MDkzMDE2MDYxNloX +DTI2MDkzMDE2MDcxNlowUTEvMC0GA1UEAxMmMS1mZjAwOjA6MTAwMiBSb290IENl +cnRpZmljYXRlIC0gR0VOIEkxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAw +MjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABP4gc6n07E5yQKNHgkF+O5RAXqfP +yw5yLW0FJko702tmV5gI1F0SLyoDfN/Ab1GdZgrN1OUnuoY9EZw4RIc6enCjZzBl +MA4GA1UdDwEB/wQEAwICBDAgBgNVHSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwB +AwMwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUlrDh47Zn7zAdTlOBxOYx +YRQhYQIwCgYIKoZIzj0EAwIDSQAwRgIhAIOwQNZKEST9A/YExb9nOdyhj7W7xw7J +qGydQZwPho6lAiEA2nE5MMUkXVb7ZgBo/u/0l4k5CiRWXODRKAzce/REA34wggHs +MIIBkqADAgECAhUAz8YqlgSi3TjtAE5NgJ8e7MYtJjAwCgYIKoZIzj0EAwIwUzEx +MC8GA1UEAxMoMS1mZjAwOjA6MTAwMiBSZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0 +ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDAyMB4XDTI0MDkzMDE2MDYx +NloXDTI2MDkzMDE2MDcxNlowUzExMC8GA1UEAxMoMS1mZjAwOjA6MTAwMiBSZWd1 +bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6 +MDoxMDAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEI9d/+9uwXyuG4ODPiAeQ +ofsPsQeXfMUUifBlvvgf8tggp/pPDYtNDFePFXae7YMhxmu8JvmrJCO2U/Zy+jos +WqNDMEEwIAYDVR0lBBkwFwYIKwYBBQUHAwgGCysGAQQBg7AcAQMCMB0GA1UdDgQW +BBTUxH1h6U44/NM7F4v3zPG4rL3WkTAKBggqhkjOPQQDAgNIADBFAiEA2V9uluSX +9+wzd3k2hKaH7LHKCN7VHAf3PBrjrSrqt8ACIAP4dZ4743Ifdux+XoS/fSdeSBvM +IF68dPRGw/YLV0tJMIIB7zCCAZWgAwIBAgIUXyDe38ogki5c2OeU8hm/VsqBqrIw +CgYIKoZIzj0EAwIwVTEzMDEGA1UEAxMqMS1mZjAwOjA6MTAwMiBTZW5zaXRpdmUg +Vm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEw +MDIwHhcNMjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBVMTMwMQYDVQQDEyox +LWZmMDA6MDoxMDAyIFNlbnNpdGl2ZSBWb3RpbmcgQ2VydGlmaWNhdGUxHjAcBgsr +BgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMjBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABExgbTHVfDlDOc8Sj6qIMe8zfKegwXM/awAbm0zM1g5w54IWvCDDwJYwyvH1 +NLQIaYPJ8U433ykKpP7J18eGUIyjQzBBMCAGA1UdJQQZMBcGCCsGAQUFBwMIBgsr +BgEEAYOwHAEDATAdBgNVHQ4EFgQU9cTcQcslMB5xG82ZhwHlqKT+QsIwCgYIKoZI +zj0EAwIDSAAwRQIhAJQlG1C/nLuC9RhMDr7+VbIUZHdO6RJp5SXU5UhBDLEeAiBo +6WVa48Idw4SA5ikpXvYS2WpVGKMxP4T+2rgpMrasYzCCAgswggGyoAMCAQICFQDD +wdiLl8l0lM5M5ZFEg3lSaqDjPzAKBggqhkjOPQQDAjBRMS8wLQYDVQQDEyYxLWZm +MDA6MDoxMDAzIFJvb3QgQ2VydGlmaWNhdGUgLSBHRU4gSTEeMBwGCysGAQQBg7Ac +AQIBEw0xLWZmMDA6MDoxMDAzMB4XDTI0MDkzMDE2MDYxNloXDTI2MDkzMDE2MDcx +NlowUTEvMC0GA1UEAxMmMS1mZjAwOjA6MTAwMyBSb290IENlcnRpZmljYXRlIC0g +R0VOIEkxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMzBZMBMGByqGSM49 +AgEGCCqGSM49AwEHA0IABFT8kpiNc/5+anZcUP8UZuUaae2kyTQaenaUKr5oMYqI +P/OOPTrywv8UiGlf6r+h/9W0TTsbQH81VLS+9JJ0AB6jZzBlMA4GA1UdDwEB/wQE +AwICBDAgBgNVHSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwBAwMwEgYDVR0TAQH/ +BAgwBgEB/wIBATAdBgNVHQ4EFgQU8KAax9hVCXVHRPWumrErP1ROUsMwCgYIKoZI +zj0EAwIDRwAwRAIgE5yvB1MZn4IIo04MBDWxMKG2MWm+JmgYD/xRhuj5T4YCIHtg +1wGWPe/oH9aK91MyxgL5cLGQrzFV6ETiVsxL+glCMIIB6jCCAZGgAwIBAgIUHQDc +Q1G4NKdMJ0/AeZNNl/km6TUwCgYIKoZIzj0EAwIwUzExMC8GA1UEAxMoMS1mZjAw +OjA6MTAwMyBSZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7Ac +AQIBEw0xLWZmMDA6MDoxMDAzMB4XDTI0MDkzMDE2MDYxNloXDTI2MDkzMDE2MDcx +NlowUzExMC8GA1UEAxMoMS1mZjAwOjA6MTAwMyBSZWd1bGFyIFZvdGluZyBDZXJ0 +aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDAzMFkwEwYHKoZI +zj0CAQYIKoZIzj0DAQcDQgAE8oK2L/O5HRY7TOCXO2r8qjizS9MGredseokCnEYF +B/bscgoMkQCZ13PmUhIB/R7egiO5B1gQcGeHCv3T3nYTWKNDMEEwIAYDVR0lBBkw +FwYIKwYBBQUHAwgGCysGAQQBg7AcAQMCMB0GA1UdDgQWBBRlwxi5nALpc9qXbijs +j6ER1ZHp3DAKBggqhkjOPQQDAgNHADBEAiA6MBkifpGXUk1mkD1G/ndKjYNDCitO +rHqsWZwV09PokQIgTGQHdXTBJ46re5REok+Lvaf5xQ8SYUsH7YX2F2lmLB0wggHv +MIIBlaADAgECAhRiK51/sGQlaNLRL+xO09tc8y1uODAKBggqhkjOPQQDAjBVMTMw +MQYDVQQDEyoxLWZmMDA6MDoxMDAzIFNlbnNpdGl2ZSBWb3RpbmcgQ2VydGlmaWNh +dGUxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMzAeFw0yNDA5MzAxNjA2 +MTZaFw0yNjA5MzAxNjA3MTZaMFUxMzAxBgNVBAMTKjEtZmYwMDowOjEwMDMgU2Vu +c2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZm +MDA6MDoxMDAzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMPjbPkogvZUhskry +KqecgjJEIj6CrJOBVRU6cSGRL1sROnhztJFU6Rr49A9UFQ8Md0nRueZjOrwtGtKx +J1F9/KNDMEEwIAYDVR0lBBkwFwYIKwYBBQUHAwgGCysGAQQBg7AcAQMBMB0GA1Ud +DgQWBBQHCjm3DQRVgHcKsJwlKOWzTSdzojAKBggqhkjOPQQDAgNIADBFAiEAzPOo +EoA/BB7d1hs/C5IfvL9Su0WqSbAXun5nJI+elIMCIEuINbSZ97H5KwxAgP9lYZZq +rc8lEh+INyaLK2QhuzLMMIICDDCCAbKgAwIBAgIVAP9cUIpti6SGNSRpGhsBk90a +DdllMAoGCCqGSM49BAMCMFExLzAtBgNVBAMTJjEtZmYwMDowOjEwMDQgUm9vdCBD +ZXJ0aWZpY2F0ZSAtIEdFTiBJMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEw +MDQwHhcNMjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBRMS8wLQYDVQQDEyYx +LWZmMDA6MDoxMDA0IFJvb3QgQ2VydGlmaWNhdGUgLSBHRU4gSTEeMBwGCysGAQQB +g7AcAQIBEw0xLWZmMDA6MDoxMDA0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +2YnOEpHRL3NQZWkpTWTL5zAzN66AOBpP9XdZclddVnlj2k4JsktvrlWJ668d/MgS +MIJUHieHeNVDSsC2fk73c6NnMGUwDgYDVR0PAQH/BAQDAgIEMCAGA1UdJQQZMBcG +CCsGAQUFBwMIBgsrBgEEAYOwHAEDAzASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1Ud +DgQWBBT0qeKAXQ7CE33pHg3pHknVO+ucyTAKBggqhkjOPQQDAgNIADBFAiB6EiLX +hmAhTlKfj6VsIzP82F2r3ngKWicb452OIi9YaAIhAJxQvm8jKwUzz6wkpT6zrX0J +XYVmHiCgX+GqP7M8m7NMMIIB6zCCAZGgAwIBAgIUfAjLUhzT9U0AY69fEKvO8cJK +oMMwCgYIKoZIzj0EAwIwUzExMC8GA1UEAxMoMS1mZjAwOjA6MTAwNCBSZWd1bGFy +IFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDox +MDA0MB4XDTI0MDkzMDE2MDYxNloXDTI2MDkzMDE2MDcxNlowUzExMC8GA1UEAxMo +MS1mZjAwOjA6MTAwNCBSZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysG +AQQBg7AcAQIBEw0xLWZmMDA6MDoxMDA0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD +QgAE4d9ncolrAjCOeVZph+YUSBCQ2Mkfsowz+TaEx55FrqJ4pZ5yJKrG0pYK+7Et +Od70tzoNHE+8h6bc+IlvohphaaNDMEEwIAYDVR0lBBkwFwYIKwYBBQUHAwgGCysG +AQQBg7AcAQMCMB0GA1UdDgQWBBS8RwKc9evSdZr1I8tNrCez3YNPwTAKBggqhkjO +PQQDAgNIADBFAiBIJ8sDbS/NOAxbRHZmvihDWXC2qmrx6Ytxkp3zkyACqgIhAI/N +McbkujaV7RqUjcW9oGORCxpMinScIF8ou/xzmqh+MIIB8TCCAZagAwIBAgIVAJBn +3Y5c123WJuGimeV2o7rtfnM7MAoGCCqGSM49BAMCMFUxMzAxBgNVBAMTKjEtZmYw +MDowOjEwMDQgU2Vuc2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQB +g7AcAQIBEw0xLWZmMDA6MDoxMDA0MB4XDTI0MDkzMDE2MDYxNloXDTI2MDkzMDE2 +MDcxNlowVTEzMDEGA1UEAxMqMS1mZjAwOjA6MTAwNCBTZW5zaXRpdmUgVm90aW5n +IENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDQwWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAARXC+kNVkgCCzZk75WTpg6DWQ7/FFcvFjNR +ZCKrtWwMjZiSnig/mQmgCfFNo4DedZrEZwy8jdcZ3i9y0ltTCBsZo0MwQTAgBgNV +HSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwBAwEwHQYDVR0OBBYEFCqjI9gOIY4A +zoN2c4iURXsNWK/iMAoGCCqGSM49BAMCA0kAMEYCIQD+p6zvcc7tkG5NjYGfCfq5 +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 +RuXw3NdGPWJGbGjiuTAKBggqhkjOPQQDAjBTMTEwLwYDVQQDEygxLWZmMDA6MDox +MDA3IFJlZ3VsYXIgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgET +DTEtZmYwMDowOjEwMDcwHhcNMjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBT +MTEwLwYDVQQDEygxLWZmMDA6MDoxMDA3IFJlZ3VsYXIgVm90aW5nIENlcnRpZmlj +YXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDcwWTATBgcqhkjOPQIB +BggqhkjOPQMBBwNCAASq91ko+6ESHFZ5no1HkheGGa1DDJNtxm3/713RbZ6QGpHN +Jw8cds6iLAFNAz+KMst3d/acs4LPjIxKMg4kgmDAo0MwQTAgBgNVHSUEGTAXBggr +BgEFBQcDCAYLKwYBBAGDsBwBAwIwHQYDVR0OBBYEFAMH4qg/uveIURqncc4gheYk +tyAPMAoGCCqGSM49BAMCA0kAMEYCIQCg3CC0/zyQ9VSlbUTiAgK6b4MiZ6oJsX9U +lln8Qmf6yQIhAKUewLQIO2u+Y8kraeIe0RKn2bA1IacIURjHr9mz6NHpMIIB7zCC +AZagAwIBAgIVAPIobO4T2BfJB6jLW0lGuw/nCuA0MAoGCCqGSM49BAMCMFUxMzAx +BgNVBAMTKjEtZmYwMDowOjEwMDcgU2Vuc2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0 +ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDA3MB4XDTI0MDkzMDE2MDYx +NloXDTI2MDkzMDE2MDcxNlowVTEzMDEGA1UEAxMqMS1mZjAwOjA6MTAwNyBTZW5z +aXRpdmUgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYw +MDowOjEwMDcwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQc44i42zmcd56jn0SB +0g7zcABhqfwpoHHcAsshWiHu892a2Rdhjlz7HlNmYbcLQ94CatYfvLF833Jon++6 +pV6Ho0MwQTAgBgNVHSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwBAwEwHQYDVR0O +BBYEFAJLKgRbxcKNz/V9vE64JP+ka2H7MAoGCCqGSM49BAMCA0cAMEQCIBcBw3C+ +NxSM5bYIUzDpUwDjd038AYwwRO5XGNg/OV4aAiB9Hwf8SF+SKOJoeAkVxQmpTpmP +AkG7QxheMn/ZAkyDTDGCEZ4wggE8AgEBMGswUzExMC8GA1UEAxMoMS1mZjAwOjA6 +MTAwNiBSZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIB +Ew0xLWZmMDA6MDoxMDA2AhR/Tx/5n09gpLTBF2CC6Oq79o22BzALBglghkgBZQME +AgGgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0y +NDA5MzAxNjA3MTZaMC8GCSqGSIb3DQEJBDEiBCDLd1R2SkqTeKNX9x0M/hWgyk1A +NQpS5VqNyvtOv6l/jDAKBggqhkjOPQQDAgRGMEQCIGNYr7/9lFJTh8hw23yrJsFa +1d7IV3X9UgrzLROEBW0RAiBRXu7s5O88zRNevIqZqYvdyxLUjfN9ziGNUhx2rTNG +njCCAT0CAQEwazBTMTEwLwYDVQQDEygxLWZmMDA6MDoxMDAxIFJlZ3VsYXIgVm90 +aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDEC +FCcEtbXXBG09p+lcnEk1zQJ9eEf6MAsGCWCGSAFlAwQCAaBpMBgGCSqGSIb3DQEJ +AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDkzMDE2MDcxNlowLwYJ +KoZIhvcNAQkEMSIEIMt3VHZKSpN4o1f3HQz+FaDKTUA1ClLlWo3K+06/qX+MMAoG +CCqGSM49BAMCBEcwRQIgRqtk8+F7x/0cOvT4iop2w0s1iOmJlcOQgAQ64XYn+TcC +IQCIlKFIwb9NvDJi8c/8IAMjYyos2O1+5Ib1U3quwmEvPTCCAT0CAQEwazBTMTEw +LwYDVQQDEygxLWZmMDA6MDoxMDAzIFJlZ3VsYXIgVm90aW5nIENlcnRpZmljYXRl +MR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDMCFB0A3ENRuDSnTCdPwHmT +TZf5Juk1MAsGCWCGSAFlAwQCAaBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEw +HAYJKoZIhvcNAQkFMQ8XDTI0MDkzMDE2MDcxNlowLwYJKoZIhvcNAQkEMSIEIMt3 +VHZKSpN4o1f3HQz+FaDKTUA1ClLlWo3K+06/qX+MMAoGCCqGSM49BAMCBEcwRQIg +KJdN6kopPyhsZSy5ScE2Uci+61ksitAa8e/WDzA4zvgCIQDGK1nK/kiu8knbj9zZ +YKhGHKg0oTaYM4oITprOd9sUGTCCAT0CAQEwazBTMTEwLwYDVQQDEygxLWZmMDA6 +MDoxMDA0IFJlZ3VsYXIgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwB +AgETDTEtZmYwMDowOjEwMDQCFHwIy1Ic0/VNAGOvXxCrzvHCSqDDMAsGCWCGSAFl +AwQCAaBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X +DTI0MDkzMDE2MDcxNlowLwYJKoZIhvcNAQkEMSIEIMt3VHZKSpN4o1f3HQz+FaDK +TUA1ClLlWo3K+06/qX+MMAoGCCqGSM49BAMCBEcwRQIgZxsACghDJpYjVEweyO6D +NZCN9Xm/oy3YXEegWqFKs4wCIQCW9+yoPHYyDbMXJy4RR7t2rqcRA3Jy8FK3eXz6 +SXC/ATCCAT0CAQEwbDBTMTEwLwYDVQQDEygxLWZmMDA6MDoxMDAyIFJlZ3VsYXIg +Vm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEw +MDICFQDPxiqWBKLdOO0ATk2Anx7sxi0mMDALBglghkgBZQMEAgGgaTAYBgkqhkiG +9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNDA5MzAxNjA3MTZa +MC8GCSqGSIb3DQEJBDEiBCDLd1R2SkqTeKNX9x0M/hWgyk1ANQpS5VqNyvtOv6l/ +jDAKBggqhkjOPQQDAgRGMEQCIEpQro8ZwwxiKYk8rO+uBS2ljP0e0tOgQ6kAq3Xd +t/4BAiBVb+XMB7zxriO7LvL1mGddZhSTdU5ias1n/ohTswzCIjCCAT4CAQEwbDBT +MTEwLwYDVQQDEygxLWZmMDA6MDoxMDA1IFJlZ3VsYXIgVm90aW5nIENlcnRpZmlj +YXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDUCFQDu5fcqgr1tE7Nr +nlv8jZaB1NkDJDALBglghkgBZQMEAgGgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN +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 +ZSBWb3RpbmcgQ2VydGlmaWNhdGUxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6 +MTAwNwIVAPIobO4T2BfJB6jLW0lGuw/nCuA0MAsGCWCGSAFlAwQCAaBpMBgGCSqG +SIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDkzMDE2MDcx +NlowLwYJKoZIhvcNAQkEMSIEIMt3VHZKSpN4o1f3HQz+FaDKTUA1ClLlWo3K+06/ +qX+MMAoGCCqGSM49BAMCBEYwRAIgL70vFeqhFjM13CrTZSf2Vs7kSxHumG+BqtV5 +xy0CyyMCIHGP0CylRczIqFpraRHjxFZ/ujCeIY987h0bHSI9sOdcMIIBQAIBATBu +MFUxMzAxBgNVBAMTKjEtZmYwMDowOjEwMDQgU2Vuc2l0aXZlIFZvdGluZyBDZXJ0 +aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDA0AhUAkGfdjlzX +bdYm4aKZ5Xajuu1+czswCwYJYIZIAWUDBAIBoGkwGAYJKoZIhvcNAQkDMQsGCSqG +SIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjQwOTMwMTYwNzE2WjAvBgkqhkiG9w0B +CQQxIgQgy3dUdkpKk3ijV/cdDP4VoMpNQDUKUuVajcr7Tr+pf4wwCgYIKoZIzj0E +AwIERzBFAiBtdv8PgSqAcwsWlkkEnkkyWrQcjAI+Jj+BDg3mtNJM0wIhAOsnb5/X +29aZ23n2PscxtSY39AG6jJi53DsPvmPg7T6VMIIBQAIBATBuMFUxMzAxBgNVBAMT +KjEtZmYwMDowOjEwMDYgU2Vuc2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwG +CysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDA2AhUA8f9ayC2/00y1LYHXKfNWZzv/ +0akwCwYJYIZIAWUDBAIBoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkq +hkiG9w0BCQUxDxcNMjQwOTMwMTYwNzE2WjAvBgkqhkiG9w0BCQQxIgQgy3dUdkpK +k3ijV/cdDP4VoMpNQDUKUuVajcr7Tr+pf4wwCgYIKoZIzj0EAwIERzBFAiEA5hOx +ioEtA0qylUBPUGvLI3cBg/B9Crlx/Ogccyyiu9kCICa+mIXvDynrBrFKMUb1objz +KJ1gowhX7fcvnPNkWdQ/ +-----END TRC----- diff --git a/src/test/resources/topologies/scionproto-scionlab/as_list.yml b/src/test/resources/topologies/scionproto-scionlab/as_list.yml new file mode 100644 index 00000000..6ba7674a --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/as_list.yml @@ -0,0 +1,8 @@ +Core: +- 1-ff00:0:1001 +- 1-ff00:0:1002 +- 1-ff00:0:1003 +- 1-ff00:0:1004 +- 1-ff00:0:1005 +- 1-ff00:0:1006 +- 1-ff00:0:1007 diff --git a/src/test/resources/topologies/scionproto-scionlab/ifids.yml b/src/test/resources/topologies/scionproto-scionlab/ifids.yml new file mode 100644 index 00000000..b3b01da5 --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/ifids.yml @@ -0,0 +1,67 @@ +1-ff00:0:1001: + br1-ff00_0_1001-1 21: br1-ff00_0_1002-1 11 + br1-ff00_0_1001-2 22: br1-ff00_0_1002-2 12 + br1-ff00_0_1001-3 31: br1-ff00_0_1003-1 11 + br1-ff00_0_1001-4 32: br1-ff00_0_1003-2 12 + br1-ff00_0_1001-5 41: br1-ff00_0_1004-1 11 + br1-ff00_0_1001-6 42: br1-ff00_0_1004-2 12 +1-ff00:0:1002: + br1-ff00_0_1002-1 11: br1-ff00_0_1001-1 21 + br1-ff00_0_1002-2 12: br1-ff00_0_1001-2 22 + br1-ff00_0_1002-3 31: br1-ff00_0_1003-3 21 + br1-ff00_0_1002-4 32: br1-ff00_0_1003-4 22 + br1-ff00_0_1002-5 41: br1-ff00_0_1004-3 21 + br1-ff00_0_1002-6 42: br1-ff00_0_1004-4 22 + br1-ff00_0_1002-7 51: br1-ff00_0_1005-1 21 + br1-ff00_0_1002-8 52: br1-ff00_0_1005-2 22 +1-ff00:0:1003: + br1-ff00_0_1003-1 11: br1-ff00_0_1001-3 31 + br1-ff00_0_1003-10 62: br1-ff00_0_1006-2 32 + br1-ff00_0_1003-2 12: br1-ff00_0_1001-4 32 + br1-ff00_0_1003-3 21: br1-ff00_0_1002-3 31 + br1-ff00_0_1003-4 22: br1-ff00_0_1002-4 32 + br1-ff00_0_1003-5 41: br1-ff00_0_1004-5 31 + br1-ff00_0_1003-6 42: br1-ff00_0_1004-6 32 + br1-ff00_0_1003-7 51: br1-ff00_0_1005-3 31 + br1-ff00_0_1003-8 52: br1-ff00_0_1005-4 32 + br1-ff00_0_1003-9 61: br1-ff00_0_1006-1 31 +1-ff00:0:1004: + br1-ff00_0_1004-1 11: br1-ff00_0_1001-5 41 + br1-ff00_0_1004-10 62: br1-ff00_0_1006-4 42 + br1-ff00_0_1004-11 71: br1-ff00_0_1007-1 41 + br1-ff00_0_1004-12 72: br1-ff00_0_1007-2 42 + br1-ff00_0_1004-2 12: br1-ff00_0_1001-6 42 + br1-ff00_0_1004-3 21: br1-ff00_0_1002-5 41 + br1-ff00_0_1004-4 22: br1-ff00_0_1002-6 42 + br1-ff00_0_1004-5 31: br1-ff00_0_1003-5 41 + br1-ff00_0_1004-6 32: br1-ff00_0_1003-6 42 + br1-ff00_0_1004-7 51: br1-ff00_0_1005-5 41 + br1-ff00_0_1004-8 52: br1-ff00_0_1005-6 42 + br1-ff00_0_1004-9 61: br1-ff00_0_1006-3 41 +1-ff00:0:1005: + br1-ff00_0_1005-1 21: br1-ff00_0_1002-7 51 + br1-ff00_0_1005-10 72: br1-ff00_0_1007-4 52 + br1-ff00_0_1005-2 22: br1-ff00_0_1002-8 52 + br1-ff00_0_1005-3 31: br1-ff00_0_1003-7 51 + br1-ff00_0_1005-4 32: br1-ff00_0_1003-8 52 + br1-ff00_0_1005-5 41: br1-ff00_0_1004-7 51 + br1-ff00_0_1005-6 42: br1-ff00_0_1004-8 52 + br1-ff00_0_1005-7 61: br1-ff00_0_1006-5 51 + br1-ff00_0_1005-8 62: br1-ff00_0_1006-6 52 + br1-ff00_0_1005-9 71: br1-ff00_0_1007-3 51 +1-ff00:0:1006: + br1-ff00_0_1006-1 31: br1-ff00_0_1003-9 61 + br1-ff00_0_1006-2 32: br1-ff00_0_1003-10 62 + br1-ff00_0_1006-3 41: br1-ff00_0_1004-9 61 + br1-ff00_0_1006-4 42: br1-ff00_0_1004-10 62 + br1-ff00_0_1006-5 51: br1-ff00_0_1005-7 61 + br1-ff00_0_1006-6 52: br1-ff00_0_1005-8 62 + br1-ff00_0_1006-7 71: br1-ff00_0_1007-5 61 + br1-ff00_0_1006-8 72: br1-ff00_0_1007-6 62 +1-ff00:0:1007: + br1-ff00_0_1007-1 41: br1-ff00_0_1004-11 71 + br1-ff00_0_1007-2 42: br1-ff00_0_1004-12 72 + br1-ff00_0_1007-3 51: br1-ff00_0_1005-9 71 + br1-ff00_0_1007-4 52: br1-ff00_0_1005-10 72 + br1-ff00_0_1007-5 61: br1-ff00_0_1006-7 71 + br1-ff00_0_1007-6 62: br1-ff00_0_1006-8 72 diff --git a/src/test/resources/topologies/scionproto-scionlab/sciond_addresses.json b/src/test/resources/topologies/scionproto-scionlab/sciond_addresses.json new file mode 100644 index 00000000..2dc372a9 --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/sciond_addresses.json @@ -0,0 +1,9 @@ +{ + "1-ff00:0:1001": "127.0.0.72", + "1-ff00:0:1002": "127.0.0.90", + "1-ff00:0:1003": "127.0.0.108", + "1-ff00:0:1004": "127.0.0.142", + "1-ff00:0:1005": "127.0.0.124", + "1-ff00:0:1006": "127.0.0.170", + "1-ff00:0:1007": "127.0.0.184" +} \ No newline at end of file diff --git a/src/test/resources/topologies/scionproto-scionlab/scionlab.topo b/src/test/resources/topologies/scionproto-scionlab/scionlab.topo new file mode 100644 index 00000000..8dba42c5 --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/scionlab.topo @@ -0,0 +1,73 @@ +--- # Tiny Topology +ASes: + "1-ff00:0:1001": + core: true + voting: true + authoritative: true + issuing: true + "1-ff00:0:1002": + core: true + voting: true + authoritative: true + issuing: true + "1-ff00:0:1003": + core: true + voting: true + authoritative: true + issuing: true + "1-ff00:0:1004": + core: true + voting: true + authoritative: true + issuing: true + "1-ff00:0:1005": + core: true + voting: true + authoritative: true + issuing: true + "1-ff00:0:1006": + core: true + voting: true + authoritative: true + issuing: true + "1-ff00:0:1007": + core: true + voting: true + authoritative: true + issuing: true +links: + - {a: "1-ff00:0:1001#21", b: "1-ff00:0:1002#11", linkAtoB: CORE} + - {a: "1-ff00:0:1001#22", b: "1-ff00:0:1002#12", linkAtoB: CORE} + - {a: "1-ff00:0:1001#31", b: "1-ff00:0:1003#11", linkAtoB: CORE} + - {a: "1-ff00:0:1001#32", b: "1-ff00:0:1003#12", linkAtoB: CORE} + - {a: "1-ff00:0:1001#41", b: "1-ff00:0:1004#11", linkAtoB: CORE} + - {a: "1-ff00:0:1001#42", b: "1-ff00:0:1004#12", linkAtoB: CORE} + + - {a: "1-ff00:0:1002#31", b: "1-ff00:0:1003#21", linkAtoB: CORE} + - {a: "1-ff00:0:1002#32", b: "1-ff00:0:1003#22", linkAtoB: CORE} + - {a: "1-ff00:0:1002#41", b: "1-ff00:0:1004#21", linkAtoB: CORE} + - {a: "1-ff00:0:1002#42", b: "1-ff00:0:1004#22", linkAtoB: CORE} + - {a: "1-ff00:0:1002#51", b: "1-ff00:0:1005#21", linkAtoB: CORE} + - {a: "1-ff00:0:1002#52", b: "1-ff00:0:1005#22", linkAtoB: CORE} + + - {a: "1-ff00:0:1003#41", b: "1-ff00:0:1004#31", linkAtoB: CORE} + - {a: "1-ff00:0:1003#42", b: "1-ff00:0:1004#32", linkAtoB: CORE} + - {a: "1-ff00:0:1003#51", b: "1-ff00:0:1005#31", linkAtoB: CORE} + - {a: "1-ff00:0:1003#52", b: "1-ff00:0:1005#32", linkAtoB: CORE} + - {a: "1-ff00:0:1003#61", b: "1-ff00:0:1006#31", linkAtoB: CORE} + - {a: "1-ff00:0:1003#62", b: "1-ff00:0:1006#32", linkAtoB: CORE} + + - {a: "1-ff00:0:1004#51", b: "1-ff00:0:1005#41", linkAtoB: CORE} + - {a: "1-ff00:0:1004#52", b: "1-ff00:0:1005#42", linkAtoB: CORE} + - {a: "1-ff00:0:1004#61", b: "1-ff00:0:1006#41", linkAtoB: CORE} + - {a: "1-ff00:0:1004#62", b: "1-ff00:0:1006#42", linkAtoB: CORE} + - {a: "1-ff00:0:1004#71", b: "1-ff00:0:1007#41", linkAtoB: CORE} + - {a: "1-ff00:0:1004#72", b: "1-ff00:0:1007#42", linkAtoB: CORE} + + - {a: "1-ff00:0:1005#61", b: "1-ff00:0:1006#51", linkAtoB: CORE} + - {a: "1-ff00:0:1005#62", b: "1-ff00:0:1006#52", linkAtoB: CORE} + - {a: "1-ff00:0:1005#71", b: "1-ff00:0:1007#51", linkAtoB: CORE} + - {a: "1-ff00:0:1005#72", b: "1-ff00:0:1007#52", linkAtoB: CORE} + + - {a: "1-ff00:0:1006#71", b: "1-ff00:0:1007#61", linkAtoB: CORE} + - {a: "1-ff00:0:1006#72", b: "1-ff00:0:1007#62", linkAtoB: CORE} diff --git a/src/test/resources/topologies/scionproto-scionlab/trcs/ISD1-B1-S1.trc b/src/test/resources/topologies/scionproto-scionlab/trcs/ISD1-B1-S1.trc new file mode 100644 index 00000000..198de286 --- /dev/null +++ b/src/test/resources/topologies/scionproto-scionlab/trcs/ISD1-B1-S1.trc @@ -0,0 +1,326 @@ +-----BEGIN TRC----- +MII8mwYJKoZIhvcNAQcCoII8jDCCPIgCAQExDTALBglghkgBZQMEAgEwgirQBgkq +hkiG9w0BBwGggirBBIIqvTCCKrkCAQAwCQIBAQIBAQIBATAiGA8yMDI0MDkzMDE2 +MDYxNloYDzIwMjUxMjI0MTYwNjE2WgIBAAEBADAAAgEEMFsTC2ZmMDA6MDoxMDA3 +EwtmZjAwOjA6MTAwMRMLZmYwMDowOjEwMDITC2ZmMDA6MDoxMDAzEwtmZjAwOjA6 +MTAwNBMLZmYwMDowOjEwMDUTC2ZmMDA6MDoxMDA2MFsTC2ZmMDA6MDoxMDA3Ewtm +ZjAwOjA6MTAwMRMLZmYwMDowOjEwMDITC2ZmMDA6MDoxMDAzEwtmZjAwOjA6MTAw +NBMLZmYwMDowOjEwMDUTC2ZmMDA6MDoxMDA2DBhUZXN0Y3J5cHRvIFRSQyBmb3Ig +SVNEIDEwgimkMIICCzCCAbGgAwIBAgIULakjzGSnOePmELVmk+d1brFEQJYwCgYI +KoZIzj0EAwIwUTEvMC0GA1UEAxMmMS1mZjAwOjA6MTAwMSBSb290IENlcnRpZmlj +YXRlIC0gR0VOIEkxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMTAeFw0y +NDA5MzAxNjA2MTZaFw0yNjA5MzAxNjA3MTZaMFExLzAtBgNVBAMTJjEtZmYwMDow +OjEwMDEgUm9vdCBDZXJ0aWZpY2F0ZSAtIEdFTiBJMR4wHAYLKwYBBAGDsBwBAgET +DTEtZmYwMDowOjEwMDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASsScVPl7v9 +HZo0IlZhyucaa0RUV4P8DgX12arJDD9xEBt7KA6TUUsGtlBagDmdAPngMeCHt3WW +yII0qF6Rlcbio2cwZTAOBgNVHQ8BAf8EBAMCAgQwIAYDVR0lBBkwFwYIKwYBBQUH +AwgGCysGAQQBg7AcAQMDMBIGA1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFBhI +uWHM1n2kTyFwa2oZq9Bx0gn8MAoGCCqGSM49BAMCA0gAMEUCICx0Lp00rAf3w0Lf +Ch6T62OyMiQcRwAhER4SURkcJ/e6AiEA2p7pWrXgtLsS/x8oSz0jaR0NC/hOEnKw +spF8qbNQZ4cwggHsMIIBkaADAgECAhQnBLW11wRtPafpXJxJNc0CfXhH+jAKBggq +hkjOPQQDAjBTMTEwLwYDVQQDEygxLWZmMDA6MDoxMDAxIFJlZ3VsYXIgVm90aW5n +IENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDEwHhcN +MjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBTMTEwLwYDVQQDEygxLWZmMDA6 +MDoxMDAxIFJlZ3VsYXIgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwB +AgETDTEtZmYwMDowOjEwMDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATTbP5r +ScLSDr/Q3w6pqWRIVlDg+4t9y6N20GNiwBe9/xLRLf3OVl2eWeS8YIucaZuWKJyB +RPoe0dShqi3hjjTno0MwQTAgBgNVHSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwB +AwIwHQYDVR0OBBYEFMSPf9uqyfudMcLEiAa++W71cgaWMAoGCCqGSM49BAMCA0kA +MEYCIQClOolfb2UrY8h2Fn0uWc7MLP128f/RCpTKJscV9Eg6vwIhANy5K3UsZfNv +MidC4a2KXUBMUkAO0FJzoUyBN5eo6pReMIIB7zCCAZWgAwIBAgIUP6EkSTjlkyig +4HXzYqoHA3pTcaYwCgYIKoZIzj0EAwIwVTEzMDEGA1UEAxMqMS1mZjAwOjA6MTAw +MSBTZW5zaXRpdmUgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgET +DTEtZmYwMDowOjEwMDEwHhcNMjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBV +MTMwMQYDVQQDEyoxLWZmMDA6MDoxMDAxIFNlbnNpdGl2ZSBWb3RpbmcgQ2VydGlm +aWNhdGUxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMTBZMBMGByqGSM49 +AgEGCCqGSM49AwEHA0IABLSQ4pc3Hn+tFoCEPDzImkAfpMwf16glEjD+uGxnIkG4 +VuGToRAkFlLcwYpVkCY2DvCTR691cpBoUSts0Ec1UzGjQzBBMCAGA1UdJQQZMBcG +CCsGAQUFBwMIBgsrBgEEAYOwHAEDATAdBgNVHQ4EFgQUiRjNjDvEHEYE0Pps2ZnF +XWXe5lIwCgYIKoZIzj0EAwIDSAAwRQIgMToREXqwLhYv1lcT7Ez44tUW3//39qv6 +YqE7FHfYLdgCIQDBqhjPYjLvv1CMbYlfgmZTpDOP+L6L8jww3EO9s0CG3DCCAg0w +ggGyoAMCAQICFQCUpv8GFp8piP/4PIK2K7pJuDz80zAKBggqhkjOPQQDAjBRMS8w +LQYDVQQDEyYxLWZmMDA6MDoxMDAyIFJvb3QgQ2VydGlmaWNhdGUgLSBHRU4gSTEe +MBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDAyMB4XDTI0MDkzMDE2MDYxNloX +DTI2MDkzMDE2MDcxNlowUTEvMC0GA1UEAxMmMS1mZjAwOjA6MTAwMiBSb290IENl +cnRpZmljYXRlIC0gR0VOIEkxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAw +MjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABP4gc6n07E5yQKNHgkF+O5RAXqfP +yw5yLW0FJko702tmV5gI1F0SLyoDfN/Ab1GdZgrN1OUnuoY9EZw4RIc6enCjZzBl +MA4GA1UdDwEB/wQEAwICBDAgBgNVHSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwB +AwMwEgYDVR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUlrDh47Zn7zAdTlOBxOYx +YRQhYQIwCgYIKoZIzj0EAwIDSQAwRgIhAIOwQNZKEST9A/YExb9nOdyhj7W7xw7J +qGydQZwPho6lAiEA2nE5MMUkXVb7ZgBo/u/0l4k5CiRWXODRKAzce/REA34wggHs +MIIBkqADAgECAhUAz8YqlgSi3TjtAE5NgJ8e7MYtJjAwCgYIKoZIzj0EAwIwUzEx +MC8GA1UEAxMoMS1mZjAwOjA6MTAwMiBSZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0 +ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDAyMB4XDTI0MDkzMDE2MDYx +NloXDTI2MDkzMDE2MDcxNlowUzExMC8GA1UEAxMoMS1mZjAwOjA6MTAwMiBSZWd1 +bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6 +MDoxMDAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEI9d/+9uwXyuG4ODPiAeQ +ofsPsQeXfMUUifBlvvgf8tggp/pPDYtNDFePFXae7YMhxmu8JvmrJCO2U/Zy+jos +WqNDMEEwIAYDVR0lBBkwFwYIKwYBBQUHAwgGCysGAQQBg7AcAQMCMB0GA1UdDgQW +BBTUxH1h6U44/NM7F4v3zPG4rL3WkTAKBggqhkjOPQQDAgNIADBFAiEA2V9uluSX +9+wzd3k2hKaH7LHKCN7VHAf3PBrjrSrqt8ACIAP4dZ4743Ifdux+XoS/fSdeSBvM +IF68dPRGw/YLV0tJMIIB7zCCAZWgAwIBAgIUXyDe38ogki5c2OeU8hm/VsqBqrIw +CgYIKoZIzj0EAwIwVTEzMDEGA1UEAxMqMS1mZjAwOjA6MTAwMiBTZW5zaXRpdmUg +Vm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEw +MDIwHhcNMjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBVMTMwMQYDVQQDEyox +LWZmMDA6MDoxMDAyIFNlbnNpdGl2ZSBWb3RpbmcgQ2VydGlmaWNhdGUxHjAcBgsr +BgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMjBZMBMGByqGSM49AgEGCCqGSM49AwEH +A0IABExgbTHVfDlDOc8Sj6qIMe8zfKegwXM/awAbm0zM1g5w54IWvCDDwJYwyvH1 +NLQIaYPJ8U433ykKpP7J18eGUIyjQzBBMCAGA1UdJQQZMBcGCCsGAQUFBwMIBgsr +BgEEAYOwHAEDATAdBgNVHQ4EFgQU9cTcQcslMB5xG82ZhwHlqKT+QsIwCgYIKoZI +zj0EAwIDSAAwRQIhAJQlG1C/nLuC9RhMDr7+VbIUZHdO6RJp5SXU5UhBDLEeAiBo +6WVa48Idw4SA5ikpXvYS2WpVGKMxP4T+2rgpMrasYzCCAgswggGyoAMCAQICFQDD +wdiLl8l0lM5M5ZFEg3lSaqDjPzAKBggqhkjOPQQDAjBRMS8wLQYDVQQDEyYxLWZm +MDA6MDoxMDAzIFJvb3QgQ2VydGlmaWNhdGUgLSBHRU4gSTEeMBwGCysGAQQBg7Ac +AQIBEw0xLWZmMDA6MDoxMDAzMB4XDTI0MDkzMDE2MDYxNloXDTI2MDkzMDE2MDcx +NlowUTEvMC0GA1UEAxMmMS1mZjAwOjA6MTAwMyBSb290IENlcnRpZmljYXRlIC0g +R0VOIEkxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMzBZMBMGByqGSM49 +AgEGCCqGSM49AwEHA0IABFT8kpiNc/5+anZcUP8UZuUaae2kyTQaenaUKr5oMYqI +P/OOPTrywv8UiGlf6r+h/9W0TTsbQH81VLS+9JJ0AB6jZzBlMA4GA1UdDwEB/wQE +AwICBDAgBgNVHSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwBAwMwEgYDVR0TAQH/ +BAgwBgEB/wIBATAdBgNVHQ4EFgQU8KAax9hVCXVHRPWumrErP1ROUsMwCgYIKoZI +zj0EAwIDRwAwRAIgE5yvB1MZn4IIo04MBDWxMKG2MWm+JmgYD/xRhuj5T4YCIHtg +1wGWPe/oH9aK91MyxgL5cLGQrzFV6ETiVsxL+glCMIIB6jCCAZGgAwIBAgIUHQDc +Q1G4NKdMJ0/AeZNNl/km6TUwCgYIKoZIzj0EAwIwUzExMC8GA1UEAxMoMS1mZjAw +OjA6MTAwMyBSZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7Ac +AQIBEw0xLWZmMDA6MDoxMDAzMB4XDTI0MDkzMDE2MDYxNloXDTI2MDkzMDE2MDcx +NlowUzExMC8GA1UEAxMoMS1mZjAwOjA6MTAwMyBSZWd1bGFyIFZvdGluZyBDZXJ0 +aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDAzMFkwEwYHKoZI +zj0CAQYIKoZIzj0DAQcDQgAE8oK2L/O5HRY7TOCXO2r8qjizS9MGredseokCnEYF +B/bscgoMkQCZ13PmUhIB/R7egiO5B1gQcGeHCv3T3nYTWKNDMEEwIAYDVR0lBBkw +FwYIKwYBBQUHAwgGCysGAQQBg7AcAQMCMB0GA1UdDgQWBBRlwxi5nALpc9qXbijs +j6ER1ZHp3DAKBggqhkjOPQQDAgNHADBEAiA6MBkifpGXUk1mkD1G/ndKjYNDCitO +rHqsWZwV09PokQIgTGQHdXTBJ46re5REok+Lvaf5xQ8SYUsH7YX2F2lmLB0wggHv +MIIBlaADAgECAhRiK51/sGQlaNLRL+xO09tc8y1uODAKBggqhkjOPQQDAjBVMTMw +MQYDVQQDEyoxLWZmMDA6MDoxMDAzIFNlbnNpdGl2ZSBWb3RpbmcgQ2VydGlmaWNh +dGUxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6MTAwMzAeFw0yNDA5MzAxNjA2 +MTZaFw0yNjA5MzAxNjA3MTZaMFUxMzAxBgNVBAMTKjEtZmYwMDowOjEwMDMgU2Vu +c2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZm +MDA6MDoxMDAzMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMPjbPkogvZUhskry +KqecgjJEIj6CrJOBVRU6cSGRL1sROnhztJFU6Rr49A9UFQ8Md0nRueZjOrwtGtKx +J1F9/KNDMEEwIAYDVR0lBBkwFwYIKwYBBQUHAwgGCysGAQQBg7AcAQMBMB0GA1Ud +DgQWBBQHCjm3DQRVgHcKsJwlKOWzTSdzojAKBggqhkjOPQQDAgNIADBFAiEAzPOo +EoA/BB7d1hs/C5IfvL9Su0WqSbAXun5nJI+elIMCIEuINbSZ97H5KwxAgP9lYZZq +rc8lEh+INyaLK2QhuzLMMIICDDCCAbKgAwIBAgIVAP9cUIpti6SGNSRpGhsBk90a +DdllMAoGCCqGSM49BAMCMFExLzAtBgNVBAMTJjEtZmYwMDowOjEwMDQgUm9vdCBD +ZXJ0aWZpY2F0ZSAtIEdFTiBJMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEw +MDQwHhcNMjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBRMS8wLQYDVQQDEyYx +LWZmMDA6MDoxMDA0IFJvb3QgQ2VydGlmaWNhdGUgLSBHRU4gSTEeMBwGCysGAQQB +g7AcAQIBEw0xLWZmMDA6MDoxMDA0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +2YnOEpHRL3NQZWkpTWTL5zAzN66AOBpP9XdZclddVnlj2k4JsktvrlWJ668d/MgS +MIJUHieHeNVDSsC2fk73c6NnMGUwDgYDVR0PAQH/BAQDAgIEMCAGA1UdJQQZMBcG +CCsGAQUFBwMIBgsrBgEEAYOwHAEDAzASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1Ud +DgQWBBT0qeKAXQ7CE33pHg3pHknVO+ucyTAKBggqhkjOPQQDAgNIADBFAiB6EiLX +hmAhTlKfj6VsIzP82F2r3ngKWicb452OIi9YaAIhAJxQvm8jKwUzz6wkpT6zrX0J +XYVmHiCgX+GqP7M8m7NMMIIB6zCCAZGgAwIBAgIUfAjLUhzT9U0AY69fEKvO8cJK +oMMwCgYIKoZIzj0EAwIwUzExMC8GA1UEAxMoMS1mZjAwOjA6MTAwNCBSZWd1bGFy +IFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDox +MDA0MB4XDTI0MDkzMDE2MDYxNloXDTI2MDkzMDE2MDcxNlowUzExMC8GA1UEAxMo +MS1mZjAwOjA6MTAwNCBSZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysG +AQQBg7AcAQIBEw0xLWZmMDA6MDoxMDA0MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD +QgAE4d9ncolrAjCOeVZph+YUSBCQ2Mkfsowz+TaEx55FrqJ4pZ5yJKrG0pYK+7Et +Od70tzoNHE+8h6bc+IlvohphaaNDMEEwIAYDVR0lBBkwFwYIKwYBBQUHAwgGCysG +AQQBg7AcAQMCMB0GA1UdDgQWBBS8RwKc9evSdZr1I8tNrCez3YNPwTAKBggqhkjO +PQQDAgNIADBFAiBIJ8sDbS/NOAxbRHZmvihDWXC2qmrx6Ytxkp3zkyACqgIhAI/N +McbkujaV7RqUjcW9oGORCxpMinScIF8ou/xzmqh+MIIB8TCCAZagAwIBAgIVAJBn +3Y5c123WJuGimeV2o7rtfnM7MAoGCCqGSM49BAMCMFUxMzAxBgNVBAMTKjEtZmYw +MDowOjEwMDQgU2Vuc2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQB +g7AcAQIBEw0xLWZmMDA6MDoxMDA0MB4XDTI0MDkzMDE2MDYxNloXDTI2MDkzMDE2 +MDcxNlowVTEzMDEGA1UEAxMqMS1mZjAwOjA6MTAwNCBTZW5zaXRpdmUgVm90aW5n +IENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDQwWTAT +BgcqhkjOPQIBBggqhkjOPQMBBwNCAARXC+kNVkgCCzZk75WTpg6DWQ7/FFcvFjNR +ZCKrtWwMjZiSnig/mQmgCfFNo4DedZrEZwy8jdcZ3i9y0ltTCBsZo0MwQTAgBgNV +HSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwBAwEwHQYDVR0OBBYEFCqjI9gOIY4A +zoN2c4iURXsNWK/iMAoGCCqGSM49BAMCA0kAMEYCIQD+p6zvcc7tkG5NjYGfCfq5 +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 +RuXw3NdGPWJGbGjiuTAKBggqhkjOPQQDAjBTMTEwLwYDVQQDEygxLWZmMDA6MDox +MDA3IFJlZ3VsYXIgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgET +DTEtZmYwMDowOjEwMDcwHhcNMjQwOTMwMTYwNjE2WhcNMjYwOTMwMTYwNzE2WjBT +MTEwLwYDVQQDEygxLWZmMDA6MDoxMDA3IFJlZ3VsYXIgVm90aW5nIENlcnRpZmlj +YXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDcwWTATBgcqhkjOPQIB +BggqhkjOPQMBBwNCAASq91ko+6ESHFZ5no1HkheGGa1DDJNtxm3/713RbZ6QGpHN +Jw8cds6iLAFNAz+KMst3d/acs4LPjIxKMg4kgmDAo0MwQTAgBgNVHSUEGTAXBggr +BgEFBQcDCAYLKwYBBAGDsBwBAwIwHQYDVR0OBBYEFAMH4qg/uveIURqncc4gheYk +tyAPMAoGCCqGSM49BAMCA0kAMEYCIQCg3CC0/zyQ9VSlbUTiAgK6b4MiZ6oJsX9U +lln8Qmf6yQIhAKUewLQIO2u+Y8kraeIe0RKn2bA1IacIURjHr9mz6NHpMIIB7zCC +AZagAwIBAgIVAPIobO4T2BfJB6jLW0lGuw/nCuA0MAoGCCqGSM49BAMCMFUxMzAx +BgNVBAMTKjEtZmYwMDowOjEwMDcgU2Vuc2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0 +ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDA3MB4XDTI0MDkzMDE2MDYx +NloXDTI2MDkzMDE2MDcxNlowVTEzMDEGA1UEAxMqMS1mZjAwOjA6MTAwNyBTZW5z +aXRpdmUgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYw +MDowOjEwMDcwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQc44i42zmcd56jn0SB +0g7zcABhqfwpoHHcAsshWiHu892a2Rdhjlz7HlNmYbcLQ94CatYfvLF833Jon++6 +pV6Ho0MwQTAgBgNVHSUEGTAXBggrBgEFBQcDCAYLKwYBBAGDsBwBAwEwHQYDVR0O +BBYEFAJLKgRbxcKNz/V9vE64JP+ka2H7MAoGCCqGSM49BAMCA0cAMEQCIBcBw3C+ +NxSM5bYIUzDpUwDjd038AYwwRO5XGNg/OV4aAiB9Hwf8SF+SKOJoeAkVxQmpTpmP +AkG7QxheMn/ZAkyDTDGCEZ4wggE8AgEBMGswUzExMC8GA1UEAxMoMS1mZjAwOjA6 +MTAwNiBSZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIB +Ew0xLWZmMDA6MDoxMDA2AhR/Tx/5n09gpLTBF2CC6Oq79o22BzALBglghkgBZQME +AgGgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0y +NDA5MzAxNjA3MTZaMC8GCSqGSIb3DQEJBDEiBCDLd1R2SkqTeKNX9x0M/hWgyk1A +NQpS5VqNyvtOv6l/jDAKBggqhkjOPQQDAgRGMEQCIGNYr7/9lFJTh8hw23yrJsFa +1d7IV3X9UgrzLROEBW0RAiBRXu7s5O88zRNevIqZqYvdyxLUjfN9ziGNUhx2rTNG +njCCAT0CAQEwazBTMTEwLwYDVQQDEygxLWZmMDA6MDoxMDAxIFJlZ3VsYXIgVm90 +aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDEC +FCcEtbXXBG09p+lcnEk1zQJ9eEf6MAsGCWCGSAFlAwQCAaBpMBgGCSqGSIb3DQEJ +AzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDkzMDE2MDcxNlowLwYJ +KoZIhvcNAQkEMSIEIMt3VHZKSpN4o1f3HQz+FaDKTUA1ClLlWo3K+06/qX+MMAoG +CCqGSM49BAMCBEcwRQIgRqtk8+F7x/0cOvT4iop2w0s1iOmJlcOQgAQ64XYn+TcC +IQCIlKFIwb9NvDJi8c/8IAMjYyos2O1+5Ib1U3quwmEvPTCCAT0CAQEwazBTMTEw +LwYDVQQDEygxLWZmMDA6MDoxMDAzIFJlZ3VsYXIgVm90aW5nIENlcnRpZmljYXRl +MR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDMCFB0A3ENRuDSnTCdPwHmT +TZf5Juk1MAsGCWCGSAFlAwQCAaBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEw +HAYJKoZIhvcNAQkFMQ8XDTI0MDkzMDE2MDcxNlowLwYJKoZIhvcNAQkEMSIEIMt3 +VHZKSpN4o1f3HQz+FaDKTUA1ClLlWo3K+06/qX+MMAoGCCqGSM49BAMCBEcwRQIg +KJdN6kopPyhsZSy5ScE2Uci+61ksitAa8e/WDzA4zvgCIQDGK1nK/kiu8knbj9zZ +YKhGHKg0oTaYM4oITprOd9sUGTCCAT0CAQEwazBTMTEwLwYDVQQDEygxLWZmMDA6 +MDoxMDA0IFJlZ3VsYXIgVm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwB +AgETDTEtZmYwMDowOjEwMDQCFHwIy1Ic0/VNAGOvXxCrzvHCSqDDMAsGCWCGSAFl +AwQCAaBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8X +DTI0MDkzMDE2MDcxNlowLwYJKoZIhvcNAQkEMSIEIMt3VHZKSpN4o1f3HQz+FaDK +TUA1ClLlWo3K+06/qX+MMAoGCCqGSM49BAMCBEcwRQIgZxsACghDJpYjVEweyO6D +NZCN9Xm/oy3YXEegWqFKs4wCIQCW9+yoPHYyDbMXJy4RR7t2rqcRA3Jy8FK3eXz6 +SXC/ATCCAT0CAQEwbDBTMTEwLwYDVQQDEygxLWZmMDA6MDoxMDAyIFJlZ3VsYXIg +Vm90aW5nIENlcnRpZmljYXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEw +MDICFQDPxiqWBKLdOO0ATk2Anx7sxi0mMDALBglghkgBZQMEAgGgaTAYBgkqhkiG +9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNDA5MzAxNjA3MTZa +MC8GCSqGSIb3DQEJBDEiBCDLd1R2SkqTeKNX9x0M/hWgyk1ANQpS5VqNyvtOv6l/ +jDAKBggqhkjOPQQDAgRGMEQCIEpQro8ZwwxiKYk8rO+uBS2ljP0e0tOgQ6kAq3Xd +t/4BAiBVb+XMB7zxriO7LvL1mGddZhSTdU5ias1n/ohTswzCIjCCAT4CAQEwbDBT +MTEwLwYDVQQDEygxLWZmMDA6MDoxMDA1IFJlZ3VsYXIgVm90aW5nIENlcnRpZmlj +YXRlMR4wHAYLKwYBBAGDsBwBAgETDTEtZmYwMDowOjEwMDUCFQDu5fcqgr1tE7Nr +nlv8jZaB1NkDJDALBglghkgBZQMEAgGgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcN +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 +ZSBWb3RpbmcgQ2VydGlmaWNhdGUxHjAcBgsrBgEEAYOwHAECARMNMS1mZjAwOjA6 +MTAwNwIVAPIobO4T2BfJB6jLW0lGuw/nCuA0MAsGCWCGSAFlAwQCAaBpMBgGCSqG +SIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTI0MDkzMDE2MDcx +NlowLwYJKoZIhvcNAQkEMSIEIMt3VHZKSpN4o1f3HQz+FaDKTUA1ClLlWo3K+06/ +qX+MMAoGCCqGSM49BAMCBEYwRAIgL70vFeqhFjM13CrTZSf2Vs7kSxHumG+BqtV5 +xy0CyyMCIHGP0CylRczIqFpraRHjxFZ/ujCeIY987h0bHSI9sOdcMIIBQAIBATBu +MFUxMzAxBgNVBAMTKjEtZmYwMDowOjEwMDQgU2Vuc2l0aXZlIFZvdGluZyBDZXJ0 +aWZpY2F0ZTEeMBwGCysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDA0AhUAkGfdjlzX +bdYm4aKZ5Xajuu1+czswCwYJYIZIAWUDBAIBoGkwGAYJKoZIhvcNAQkDMQsGCSqG +SIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjQwOTMwMTYwNzE2WjAvBgkqhkiG9w0B +CQQxIgQgy3dUdkpKk3ijV/cdDP4VoMpNQDUKUuVajcr7Tr+pf4wwCgYIKoZIzj0E +AwIERzBFAiBtdv8PgSqAcwsWlkkEnkkyWrQcjAI+Jj+BDg3mtNJM0wIhAOsnb5/X +29aZ23n2PscxtSY39AG6jJi53DsPvmPg7T6VMIIBQAIBATBuMFUxMzAxBgNVBAMT +KjEtZmYwMDowOjEwMDYgU2Vuc2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0ZTEeMBwG +CysGAQQBg7AcAQIBEw0xLWZmMDA6MDoxMDA2AhUA8f9ayC2/00y1LYHXKfNWZzv/ +0akwCwYJYIZIAWUDBAIBoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkq +hkiG9w0BCQUxDxcNMjQwOTMwMTYwNzE2WjAvBgkqhkiG9w0BCQQxIgQgy3dUdkpK +k3ijV/cdDP4VoMpNQDUKUuVajcr7Tr+pf4wwCgYIKoZIzj0EAwIERzBFAiEA5hOx +ioEtA0qylUBPUGvLI3cBg/B9Crlx/Ogccyyiu9kCICa+mIXvDynrBrFKMUb1objz +KJ1gowhX7fcvnPNkWdQ/ +-----END TRC-----