Skip to content

Commit

Permalink
Cache daemon bootstrap info (#157)
Browse files Browse the repository at this point in the history
* Cache daemon bootstrap info

---------

Co-authored-by: Tilmann Zäschke <tilmann.zaeschke@inf.ethz.ch>
  • Loading branch information
tzaeschke and Tilmann Zäschke authored Jan 13, 2025
1 parent 70f59b9 commit f66f640
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 154 deletions.
14 changes: 7 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

### TODO for 0.5.0

- ScionService.getBorderRouterAddress() needs to cache border routers!
-> Best: Implement a 2nd version of LocalTopology based on Daemon information!
- Confirm that SCMP uses random IDs (see spec, e.g. https://docs.scion.org/en/latest/dev/design/router-port-dispatch.html)

- Ensure (+ more tests???) that client responds on underlay address
- Change MockControlService to return parsed Segments i.o. hardcoded segments.
- Consider refactoring: separate Datagram classes for STUN/SCMP/UDP handling
- TODO implement PathPolicy https://docs.scion.org/en/latest/dev/design/PathPolicy.html

- Change MockControlService to return parsed Segments i.o. hardcoded segments.
- Consider refactoring: separate Datagram classes for STUN/SCMP/UDP handling

- TEST all(default) with AUTO??? BR????
- Requires improved MockChannel that can handle STUN requests (even if returning no packet)
Expand Down Expand Up @@ -67,8 +65,10 @@ For example: Path.getFirstHopAddress(), DatagramChannel.setPathPolicy()
- Fixed parsing of /etc/hosts [#150](https://github.com/scionproto-contrib/jpan/pull/150)
- Fixed warning when dnsjava parses /etc/hosts with SCION adresses
[#155](https://github.com/scionproto-contrib/jpan/pull/155)
- Cleanup: `TestUtil` class for string output and slepp()
- Cleanup: `TestUtil` class for string output and sleep()
[#156](https://github.com/scionproto-contrib/jpan/pull/156)
- Stop contacting daemon of every received packet
[#157](https://github.com/scionproto-contrib/jpan/pull/157)

## [0.4.1] - 2024-11-22

Expand Down
120 changes: 21 additions & 99 deletions src/main/java/org/scion/jpan/ScionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import static org.scion.jpan.Constants.PROPERTY_DNS_SEARCH_DOMAINS;
import static org.scion.jpan.Constants.PROPERTY_USE_OS_SEARCH_DOMAINS;

import com.google.protobuf.Empty;
import io.grpc.*;
import java.io.IOException;
import java.net.InetAddress;
Expand All @@ -39,7 +38,6 @@
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import org.scion.jpan.internal.*;
import org.scion.jpan.proto.control_plane.SegmentLookupServiceGrpc;
Expand Down Expand Up @@ -76,12 +74,9 @@ public class ScionService {
private final ScionBootstrapper bootstrapper;
private final DaemonServiceGrpc.DaemonServiceBlockingStub daemonStub;
private final SegmentLookupServiceGrpc.SegmentLookupServiceBlockingStub segmentStub;
private LocalTopology.DispatcherPortRange portRange;

private final boolean minimizeRequests;
private final ManagedChannel channel;
private static final long ISD_AS_NOT_SET = -1;
private final AtomicLong localIsdAs = new AtomicLong(ISD_AS_NOT_SET);
private Thread shutdownHook;
private final HostsFileParser hostsFile = new HostsFileParser();
private final SimpleCache<String, ScionAddress> scionAddressCache = new SimpleCache<>(100);
Expand All @@ -105,7 +100,17 @@ protected ScionService(String addressOrHost, Mode mode) {
channel = Grpc.newChannelBuilder(addressOrHost, InsecureChannelCredentials.create()).build();
daemonStub = DaemonServiceGrpc.newBlockingStub(channel);
segmentStub = null;
bootstrapper = null;
try {
bootstrapper = ScionBootstrapper.createViaDaemon(daemonStub);
} catch (RuntimeException e) {
// If this fails for whatever reason we want to make sure that the channel is closed.
try {
close();
} catch (IOException ex) {
// Ignore, we just want to get out.
}
throw e;
}
} else {
LOG.info("Bootstrapping with control service: mode={} target={}", mode.name(), addressOrHost);
if (mode == Mode.BOOTSTRAP_VIA_DNS) {
Expand All @@ -120,15 +125,13 @@ protected ScionService(String addressOrHost, Mode mode) {
}
String csHost = bootstrapper.getLocalTopology().getControlServerAddress();
LOG.info("Bootstrapping with control service: {}", csHost);
localIsdAs.set(bootstrapper.getLocalTopology().getIsdAs());
// TODO InsecureChannelCredentials: Implement authentication!
channel = Grpc.newChannelBuilder(csHost, InsecureChannelCredentials.create()).build();
daemonStub = null;
segmentStub = SegmentLookupServiceGrpc.newBlockingStub(channel);
}
shutdownHook = addShutdownHook();
try {
getLocalIsdAs(); // Init
checkStartShim();
} catch (RuntimeException e) {
// If this fails for whatever reason we want to make sure that the channel is closed.
Expand Down Expand Up @@ -275,31 +278,6 @@ public ScionDatagramChannel openChannel(java.nio.channels.DatagramChannel channe
return ScionDatagramChannel.open(this, channel);
}

Daemon.ASResponse getASInfo() {
Daemon.ASRequest request = Daemon.ASRequest.newBuilder().setIsdAs(0).build();
Daemon.ASResponse response;
try {
response = daemonStub.aS(request);
} catch (StatusRuntimeException e) {
if (e.getStatus().getCode() == Status.Code.UNAVAILABLE) {
throw new ScionRuntimeException("Could not connect to SCION daemon: " + e.getMessage(), e);
}
throw new ScionRuntimeException("Error while getting AS info: " + e.getMessage(), e);
}
return response;
}

Map<Long, Daemon.Interface> getInterfaces() {
Daemon.InterfacesRequest request = Daemon.InterfacesRequest.newBuilder().build();
Daemon.InterfacesResponse response;
try {
response = daemonStub.interfaces(request);
} catch (StatusRuntimeException e) {
throw new ScionRuntimeException(e);
}
return response.getInterfacesMap();
}

private List<Daemon.Path> getPathList(long srcIsdAs, long dstIsdAs) {
if (daemonStub != null) {
return getPathListDaemon(srcIsdAs, dstIsdAs);
Expand Down Expand Up @@ -409,24 +387,8 @@ private List<Path> getPaths(ScionAddress dstAddress, int dstPort) {
return scionPaths;
}

Map<String, Daemon.ListService> getServices() throws ScionException {
Daemon.ServicesRequest request = Daemon.ServicesRequest.newBuilder().build();
Daemon.ServicesResponse response;
try {
response = daemonStub.services(request);
} catch (StatusRuntimeException e) {
throw new ScionException(e);
}
return response.getServicesMap();
}

public long getLocalIsdAs() {
if (localIsdAs.get() == ISD_AS_NOT_SET) {
// Yes, this may be called multiple time by different threads, but it should be
// faster than `synchronize`.
localIsdAs.set(getASInfo().getIsdAs());
}
return localIsdAs.get();
return bootstrapper.getLocalTopology().getIsdAs();
}

/**
Expand Down Expand Up @@ -609,62 +571,22 @@ List<Daemon.Path> getPathListCS(long srcIsdAs, long dstIsdAs) {
* @return Mapping of external addresses, potentially one for each border router.
*/
NatMapping getNatMapping(DatagramChannel channel) {
return NatMapping.createMapping(getLocalIsdAs(), channel, getBorderRouterAddresses());
List<InetSocketAddress> interfaces =
bootstrapper.getLocalTopology().getBorderRouters().stream()
.map(LocalTopology.BorderRouter::getInternalAddress)
.collect(Collectors.toList());
return NatMapping.createMapping(getLocalIsdAs(), channel, interfaces);
}

LocalTopology.DispatcherPortRange getLocalPortRange() {
if (portRange == null) {
if (bootstrapper != null) {
portRange = bootstrapper.getLocalTopology().getPortRange();
} else if (daemonStub != null) {
// try daemon
Daemon.PortRangeResponse response;
try {
response = daemonStub.portRange(Empty.getDefaultInstance());
portRange =
LocalTopology.DispatcherPortRange.create(
response.getDispatchedPortStart(), response.getDispatchedPortEnd());
} catch (StatusRuntimeException e) {
LOG.warn("ERROR getting port range from daemon: {}", e.getMessage());
// Daemon doesn't support port range.
portRange = LocalTopology.DispatcherPortRange.createEmpty();
}
} else {
portRange = LocalTopology.DispatcherPortRange.createAll();
}
}
return portRange;
return bootstrapper.getLocalTopology().getPortRange();
}

InetSocketAddress getBorderRouterAddress(int interfaceID) {
if (daemonStub != null) {
final String MSG = "No border router found for interfaceID: ";
String address =
getInterfaces().entrySet().stream()
.filter(entry -> entry.getKey() == interfaceID)
.findAny()
.orElseThrow(() -> new ScionRuntimeException(MSG + interfaceID))
.getValue()
.getAddress()
.getAddress();
return IPHelper.toInetSocketAddress(address);
} else {
String address = bootstrapper.getLocalTopology().getBorderRouterAddress(interfaceID);
return IPHelper.toInetSocketAddress(address);
}
return bootstrapper.getLocalTopology().getBorderRouterAddress(interfaceID);
}

private List<InetSocketAddress> getBorderRouterAddresses() {
if (daemonStub != null) {
return getInterfaces().values().stream()
.map(anInterface -> anInterface.getAddress().getAddress())
.map(IPHelper::toInetSocketAddress)
.collect(Collectors.toList());
} else {
return bootstrapper.getLocalTopology().getBorderRouters().stream()
.map(LocalTopology.BorderRouter::getInternalAddress)
.map(IPHelper::toInetSocketAddress)
.collect(Collectors.toList());
}
DaemonServiceGrpc.DaemonServiceBlockingStub getDaemonConnection() {
return daemonStub;
}
}
5 changes: 2 additions & 3 deletions src/main/java/org/scion/jpan/internal/GlobalTopology.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,17 @@
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
* 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) {
private GlobalTopology(ScionBootstrapper server) {
if (server == null) {
this.isEmpty = true;
} else {
Expand Down
Loading

0 comments on commit f66f640

Please sign in to comment.