Skip to content

Commit

Permalink
TRC fetcher (#110)
Browse files Browse the repository at this point in the history
* TRC fetcher

---------

Co-authored-by: Tilmann Zäschke <tilmann.zaeschke@inf.ethz.ch>
  • Loading branch information
tzaeschke and Tilmann Zäschke authored Jul 26, 2024
1 parent 1126a02 commit 2f95bf0
Show file tree
Hide file tree
Showing 31 changed files with 994 additions and 434 deletions.
18 changes: 17 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

## [Unreleased]

### TODO for 0.3.0
- Demo that connects to Francois' website
- Support topofile port range
- `ResponsePath` is now package private (not public anymore)
- remove ScionAddress?
- Remove getPaths(long dstIsdAs, InetSocketAddress dstScionAddress) <- ISD + Scion address!!!
- ScionDatagramChannel
- GatheringByteChannel, ScatteringByteChannel
- Selector support
- Inherit DatagramChannel
- AS switching?

### TODO post 0.3.0
- Upgrade all JUnit topo files to post 0.11 with new format (including port range)
- ...

### Added
- nothing yet
- Support for bootstrapper TRC metadata. [#110](https://github.com/scionproto-contrib/jpan/pull/110)

### Changed
- Clean up TODO and deprecation info. [#100](https://github.com/scionproto-contrib/jpan/pull/100)
Expand Down
18 changes: 9 additions & 9 deletions src/main/java/org/scion/jpan/ScionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public class ScionService {
private static final String ERR_INVALID_TXT_LOG2 = ERR_INVALID_TXT + "{} {}";
private static ScionService defaultService = null;

private final LocalTopology localTopology;
private final ScionBootstrapper bootstrapper;
private final DaemonServiceGrpc.DaemonServiceBlockingStub daemonStub;
private final SegmentLookupServiceGrpc.SegmentLookupServiceBlockingStub segmentStub;

Expand All @@ -94,22 +94,22 @@ protected ScionService(String addressOrHost, Mode mode) {
channel = Grpc.newChannelBuilder(addressOrHost, InsecureChannelCredentials.create()).build();
daemonStub = DaemonServiceGrpc.newBlockingStub(channel);
segmentStub = null;
localTopology = null;
bootstrapper = null;
} else {
LOG.info("Bootstrapping with control service: mode={} target={}", mode.name(), addressOrHost);
if (mode == Mode.BOOTSTRAP_VIA_DNS) {
localTopology = ScionBootstrapper.createViaDns(addressOrHost).getTopology();
bootstrapper = ScionBootstrapper.createViaDns(addressOrHost);
} else if (mode == Mode.BOOTSTRAP_SERVER_IP) {
localTopology = ScionBootstrapper.createViaBootstrapServerIP(addressOrHost).getTopology();
bootstrapper = ScionBootstrapper.createViaBootstrapServerIP(addressOrHost);
} else if (mode == Mode.BOOTSTRAP_TOPO_FILE) {
java.nio.file.Path file = Paths.get(addressOrHost);
localTopology = ScionBootstrapper.createViaTopoFile(file).getTopology();
bootstrapper = ScionBootstrapper.createViaTopoFile(file);
} else {
throw new UnsupportedOperationException();
}
String csHost = localTopology.getControlServerAddress();
String csHost = bootstrapper.getLocalTopology().getControlServerAddress();
LOG.info("Bootstrapping with control service: {}", csHost);
localIsdAs.set(localTopology.getLocalIsdAs());
localIsdAs.set(bootstrapper.getLocalTopology().getLocalIsdAs());
// TODO InsecureChannelCredentials: Implement authentication!
channel = Grpc.newChannelBuilder(csHost, InsecureChannelCredentials.create()).build();
daemonStub = null;
Expand Down Expand Up @@ -596,7 +596,7 @@ private Long parseTxtRecordToIA(String txtEntry) {

// Do not expose protobuf types on API!
List<Daemon.Path> getPathListCS(long srcIsdAs, long dstIsdAs) {
return Segments.getPaths(segmentStub, localTopology, srcIsdAs, dstIsdAs);
return Segments.getPaths(segmentStub, bootstrapper, srcIsdAs, dstIsdAs);
}

/**
Expand Down Expand Up @@ -626,7 +626,7 @@ List<String> getBorderRouterStrings() {
.map(i -> i.getAddress().getAddress())
.collect(Collectors.toList());
} else {
return localTopology.getBorderRouterAddresses();
return bootstrapper.getLocalTopology().getBorderRouterAddresses();
}
}
}
187 changes: 187 additions & 0 deletions src/main/java/org/scion/jpan/internal/GlobalTopology.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
// 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.internal;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.time.Instant;
import java.util.*;
import org.scion.jpan.ScionRuntimeException;
import org.scion.jpan.ScionUtil;

/** Parse a topology file into a local topology. */
public class GlobalTopology {

private final Map<Integer, Isd> 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.
*/
private final boolean isEmpty;

public GlobalTopology(ScionBootstrapper server) {
if (server == null) {
this.isEmpty = true;
} else {
this.isEmpty = false;
getTrcFiles(server);
}
}

public static synchronized GlobalTopology create(ScionBootstrapper server) {
return new GlobalTopology(server);
}

public static GlobalTopology createEmpty() {
return new GlobalTopology(null);
}

private static JsonElement safeGet(JsonObject o, String name) {
JsonElement e = o.get(name);
if (e == null) {
throw new ScionRuntimeException("Entry not found in topology file: " + name);
}
return e;
}

public Optional<Boolean> isCoreAs(long isdAs) {
if (isEmpty) {
return Optional.empty();
}
int isdCode = ScionUtil.extractIsd(isdAs);
Isd isd = world.get(isdCode);
if (isd == null) {
throw new ScionRuntimeException("Unknown ISD: " + isdCode);
}

for (Long core : isd.coreASes) {
if (core == isdAs) {
return Optional.of(true);
}
}
return Optional.of(false);
}

private void getTrcFiles(ScionBootstrapper server) {
String filesString = server.fetchFile("trcs");
parseTrcFiles(filesString);

for (Isd isd : world.values()) {
String fileName = "isd" + isd.isd + "-b" + isd.baseNumber + "-s" + isd.serialNumber;
String file = server.fetchFile("trcs/" + fileName);
parseTrcFile(file, isd);
}
}

private void parseTrcFiles(String trcFile) {
JsonElement jsonTree = JsonParser.parseString(trcFile);
JsonArray entries = jsonTree.getAsJsonArray();
for (int i = 0; i < entries.size(); i++) {
JsonObject entry = entries.get(i).getAsJsonObject();
for (Map.Entry<String, JsonElement> e : entry.entrySet()) {
JsonObject cs = e.getValue().getAsJsonObject();
int base = cs.get("base_number").getAsInt();
int isd = cs.get("isd").getAsInt();
int serial = cs.get("serial_number").getAsInt();
world.put(isd, new Isd(isd, base, serial));
}
}
}

private static void parseTrcFile(String trcFile, Isd isd) {
JsonElement jsonTree = JsonParser.parseString(trcFile);
if (jsonTree.isJsonObject()) {
JsonObject o = jsonTree.getAsJsonObject();

JsonArray authoritativeAses = safeGet(o, "authoritative_ases").getAsJsonArray();
for (int i = 0; i < authoritativeAses.size(); i++) {
String isdCode = authoritativeAses.get(i).getAsString();
isd.authorativeASes.add(ScionUtil.parseIA(isdCode));
}
JsonArray coreAses = safeGet(o, "core_ases").getAsJsonArray();
for (int i = 0; i < coreAses.size(); i++) {
String isdCode = coreAses.get(i).getAsString();
isd.coreASes.add(ScionUtil.parseIA(isdCode));
}

isd.description = safeGet(o, "description").getAsString();

JsonObject id = safeGet(o, "id").getAsJsonObject();
int base = id.get("base_number").getAsInt();
int isdCode = id.get("isd").getAsInt();
int serial = id.get("serial_number").getAsInt();
if (isd.isd != isdCode || isd.baseNumber != base || isd.serialNumber != serial) {
throw new IllegalStateException("ISD/Base/Serial mismatch in TRC file.");
}

JsonObject validity = safeGet(o, "validity").getAsJsonObject();
String afterStr = validity.get("not_after").getAsString();
String beforeStr = validity.get("not_before").getAsString();
isd.notAfter = Instant.parse(afterStr);
isd.notBefore = Instant.parse(beforeStr);
}
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Isd isd : world.values()) {
sb.append("ISD: ").append(isd).append('\n');
}
return sb.toString();
}

private static class Isd {
String description;
int baseNumber;
int isd;
int serialNumber;
Instant notAfter;
Instant notBefore;
final List<Long> authorativeASes = new ArrayList<>();
final List<Long> coreASes = new ArrayList<>();

Isd(int isd, int base, int serial) {
this.isd = isd;
this.baseNumber = base;
this.serialNumber = serial;
}

@Override
public String toString() {
return "{"
+ "description='"
+ description
+ '\''
+ ", baseNumber="
+ baseNumber
+ ", isd="
+ isd
+ ", serialNumber="
+ serialNumber
+ ", notAfter="
+ notAfter
+ ", notBefore="
+ notBefore
+ ", authorativeASes="
+ authorativeASes
+ ", coreASes="
+ coreASes
+ '}';
}
}
}
Loading

0 comments on commit 2f95bf0

Please sign in to comment.