Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache daemon bootstrap info #157

Merged
merged 3 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
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 @@
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) {

Check warning on line 109 in src/main/java/org/scion/jpan/ScionService.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/scion/jpan/ScionService.java#L109

Added line #L109 was not covered by tests
// 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 @@
}
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 @@
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 @@
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 @@
* @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;

Check warning on line 590 in src/main/java/org/scion/jpan/ScionService.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/scion/jpan/ScionService.java#L590

Added line #L590 was not covered by tests
}
}
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
Loading