This library is 100% pure Java network stack for using SCION. It currently provides support for UDP and SCMP. More information about SCION can be found in Getting Started and more generally here. JPAN provides functionality similar to snet (Go), PAN (Go) and scion-rs (Rust).
The following artifact contains the complete SCION Java implementation:
<dependency>
<groupId>org.scion</groupId>
<artifactId>jpan</artifactId>
<version>0.2.0</version>
</dependency>
- 100% Java
- UDP over SCION via
DatagramChannel
orDatagramSocket
- SCMP (ICMP for SCION)
- Works stand-alone or with a local SCION daemon (without dispatcher, see below)
- API:
Selector
forDatagramChannel
- Autodetection of NAT external IP
- Path creation with short-cuts, on-path and peering routes
- Improve docs, demos and testing
- Many more
JPAN connects directly to SCION without dispatcher.
Currently (April 2024), the SCION system uses a "dispatcher" (a process that runs on endhosts, listens on a fixed port (30041) and forwards any incoming SCION packets, after stripping the SCION header, to local application).
JPAN cannot be used with a dispatcher. JPAN can be used in one of the following ways:
- You can use JPAN stand-alone (without local SCION installation), however it must listen on port 30041 for incoming SCION packets because SCION routers currently will forward data only to that port.
- If you are contacting an endhost within your own AS, and the endhost uses a dispatcher, then you
must set the flag
DatagramChannel.configureRemoteDispatcher(true)
. This ensure that the outgoing packet is sent to port 30041 on the remote machine. The flag has no effect on traffic sent to a remote AS. - If you need a local SCION installation on your machine (Go implementation), consider using the dispatch-off branch/PR.
- When you need to run a local system with dispatcher, you can try to use port forwarding to forward incoming data to your Java application port. The application port must not be 30041.
JPAN does not work well when using a local NAT. The problem is that the SCION header must contain the external IP address (i.e. the IP visible to first border router) of the end host. When using a NAT, this needs to be the external IP of the NAT.
JPAN cannot currently auto-detect this IP.
To work with a NAT, please use setOverrideSourceAddress(externalAddress)
(new experimental feature)
to force JPAN to use the specified external address instead of the eternal IP of the end-host.
Note that this solution only works for NATs, there is currently no solution for proxies.
The central classes of the API are:
DatagramChannel
: This class works like ajava.nio.channel.DatagramChannel
. It implementsChannel
andByteChannel
. Scattering, gathering, multicast and selectors are currently not supported.ScionSocketAddress
is anInetSocketAddress
with the IP of a Scion enabled endhost. AScionSocketAddress
also has the ISD/AS code of that endhost and a path to the that endhost.Path
objects contain a route to a destination ("raw path") plus the full destination, i.e. SCION-enabled IP address and port.- If the path was created by the
ScionService
then it hasPathMetadata
with meta information (bandwidth, geo info, etc). - A path returned by
receive()
(as part of aScionSocketAddress
) has no meta information.
- If the path was created by the
PathPolicy
is an interface with several example implementations for: first path returned by daemon (default), max bandwidth, min latency, min hops, ...ScionService
: Provides methods to request paths and get ISD/AS information.ScionService
instances can be created with theScion
class. The first instance that is created will subsequently returned byScion.defaultService()
.Scion
,ScionUtil
,ScionConstants
: Utility classes.ScionSocketOptions
: Options for theDatagramChannel
.Scmp
:ScmpType
andScmpCode
enums with text messages.Message
(for SCMP errors) andEchoMessage
/TracerouteMessage
types.createChannel(...)
for sending echo and traceroute requests
Supported:
- DatagramChannel support: read(), write(), receive(), send(), bind(), connect(), ...
- DatagramSocket support
- Path selection policies
- Path expiry/refresh
- Packet validation
- SCION address lookup via DNS/TXT entry or
/etc/scion/hosts
(see https://github.com/netsec-ethz/scion-apps) - Configurable:
- daemon address
- bootstrapping via topo file, bootstrapper IP, DNS NAPTR entry or /etc/resolv.conf
- path expiry
- Packet inspector for debugging
- No "dispatcher"
Missing:
- DatagramChannel support for Selectors
- Path construction with short-cuts, on-path, peering
- EPIC
- RHINE
- ...
A simple client looks like this:
InetSocketAddress addr = new InetSocketAddress(...);
try (DatagramChannel channel = DatagramChannel.open()) {
channel.configureBlocking(true);
channel.connect(addr);
channel.write(ByteBuffer.wrap("Hello Scion".getBytes()));
...
ByteBuffer response = ByteBuffer.allocate(1000);
channel.read(response);
}
If you want to work on JPAN or simply browse the code locally, you can install it locally.
JPAN is available as a
Maven artifact.
Many IDEs comes with maven plugins. If you want to use Maven from the command line, you
can install it with sudo apt install maven
(Ubuntu etc) or download it
here.
To install it locally:
git clone https://github.com/scionproto-contrib/jpan.git
cd jpan
mvn clean install
Note on MacOS the tests may fail, see
Troubleshooting below.
To skip tests, please use mvn clean test -DskipTests=true
.
Some demos can be found in src/test/java/org/scion/jpan/demo.
Before running the demos, you may have set up your development environment
and execute mvn install -DskipTests=true
.
After compilation, demos can be executed from the IDE (recommended) or from command line.
For example: mvn exec:java -Dexec.mainClass="org.scion.jpan.demo.ScmpEchoDemo"
.
The following demos are included:
DatagramChannel
ping pong PingPongChannelClient.java and PingPongChannelServer.javaDatagramSocket
ping pong PingPongSocketClient.java and PingPongSocketServer.java- SCMP echo: ScmpEchoDemo.java
- SCMP traceroute ScmpTracerouteDemo.java
- Showpaths: ShowpathsDemo.java
If you encounter problems, please check the troubleshooting section below.
- Reference manual: https://docs.scion.org
- Reference implementation of SCION: https://github.com/scionproto/scion
- SCIONLab, a global testbed for SCION applications: https://www.scionlab.org/
- Awesome SCION, a collection of SCION projects: https://github.com/scionproto/awesome-scion
The JUnit tests in this Java project use a very rudimentary simulated network. For proper testing it is recommended to use one of the following:
- scionproto, the reference implementation of SCION, comes with a framework that allows defining a topology and running a local network with daemons, control servers, border routers and more, see docs.
- SEED is a network emulator that can emulate SCION networks.
- SCIONlab is a world wide testing framework for SCION. You can define your own AS and use the whole network. It runs as overlay over normal internet so it has limited security guarantees and possibly reduced performance compared to native SCION.
- SCIERA is a network of Universities with SCION connection. It is part part of the global SCION network
- AWS offers SCION nodes with connection to the global SCION network.
In order to find a path to a destination IP, a DatagramChannel
or DatagramSocket
must know the
ISD/AS numbers of the destination.
If the destination host has a DNS TXT entry for SCION then this be used to determine the
destination ISD/AS. For example, if dig TXT your-domain.org
returns something like
your-domain.org. 610 IN TXT "scion=64-2:0:9,129.x.x.x"
, then you can simply
use something like:
InetSocketAddress serverAddress = new InetSocketAddress("your-domain.org", 80);
channel.connect(serverAddress);
or
InetSocketAddress serverAddress = new InetSocketAddress("your-domain.org", 80);
Path path = Scion.getDefaultService().lookupAndGetPath(serverAddress, PathPolicy.DEFAULT);
channel.send(buffer, path);
Alternatively, the ISD/AS can be specified explicitly in several ways.
Create a file /etc/scion/hosts
to assign ISD/AS ans SCION IP to host names:
# /etc/scion/hosts test file
1-ff00:0:111,[42.0.0.11] test-server
1-ff00:0:112,[42.0.0.12] test-server-1 test-server-2
1-ff00:0:113,[::42] test-server-ipv6
We can use the ISD/AS directly to request a path:
long isdAs = ScionUtil.parseIA("64-2:0:9");
InetSocketAddress serverAddress = new InetSocketAddress("129.x.x.x", 80);
Path path = Scion.defaultService().getPaths(isdAs, serverAddress).get(0);
channel.connect(path);
Options are defined in ScionSocketOptions
, see javadoc for details.
Option | Default | Short description |
---|---|---|
SCION_API_THROW_PARSER_FAILURE |
false |
Throw exception when receiving an invalid packet |
SCION_PATH_EXPIRY_MARGIN |
2 |
A new path is requested if now + margin > pathExpirationDate |
The following standard options are not supported:
Option |
---|
StandardSocketOptions.SO_BROADCAST |
StandardSocketOptions.IP_MULTICAST_IF |
StandardSocketOptions.IP_MULTICAST_TTL |
StandardSocketOptions.IP_MULTICAST_LOOP |
DatagramSocket
work similar to DatagramChannel
in terms of using Path
or Service
.
DatagramSocket
is somewhat discouraged because it requires storing/caching of paths internally
which can lead to increased memory usage of even failure to resolve paths, especially when handling
multiple connections over a single socket.
The problem is that DatagramPacket
and InetAddress
are not extensible to store path information.
For a server to be able to send data back to a client, it has to remember these paths internally.
This is done internally in a path cache that stores the received path for every remote IP address.
The cache is by default limited to 100 entries (setPathCacheCapacity()
). In cse there are more
than 100 remote clients, the cache will 'forget' those paths that haven't been used for the longest
time. That means the server won't be able to send anything anymore to these forgotten clients.
This can become a security problem if an attacker initiates connections from many different (or spoofed) IPs, causing the cache to consume a lot of memory or to overflow, being unable to answer to valid requests.
Internally, the DatagramSocket
uses a SCION DatagraChannel
.
API beyond the standard Java DatagramScoket
:
create(ScionService)
andcreate(SocketAddress, ScionService)
for creating aDatagramSocket
with a non-defaultScionService
.connect(Path path)
for connecting to a remote hostgetConnectionPath()
gets the connected path if the socket has been connectedgetCachedPath(InetAddress address)
get the cached path for a given IPsetPathCacheCapacity(int capacity)
andgetPathCacheCapacity()
for managing the cache sizesetOption(...)
andgetOption()
are supported even though they were only added in Java 9. They support the same (additional) options asDatagramChannel
.
-
Using
SocketAddress
forsend()
.send(buffer, socketAddress)
is a convenience function. However, when sending multiple packets to the same destination, one should usepath = send(buffer, path)
orconnect()
+write()
in order to avoid frequent path lookups. -
Using expired path (client). When using
send(buffer, path)
with an expiredPath
, the channel will transparently look up a new path. This works but causes a path lookup for everysend()
. Solution: always use the latest path returned by send, e.g.path = send(buffer, path)
. -
Using expired path (server). When using
send(buffer, path)
with an expiredPath
, the channel will simple send it anyway.
JPAN can be used in standalone mode or with a local daemon.
- Standalone mode will directly connect to a topology server and control server, in a properly configured AS this should all happen automatically - this is the RECOMMENDED WAY of using this library.
- The daemon is available if you have a local installation of SCION.
There are also several methods that allow specifying a local topology file, a topology server address or a different DNS server with a scion NAPTR record. These are only meant for debugging.
The method Scion.defaultService()
(internally called by DatagramChannel.open()
) will
attempt to get network information in the following order until it succeeds:
- For debugging: Check for local topology file (if file name is given)
- For debugging: Check for bootstrap server address (if address is given)
- For debugging: Check for DNS NAPTR record (if record entry name is given)
- Check for to daemon
- Check search domain (as given in
/etc/resolv.conf
) for topology server
The reason that the daemon is checked last is that it has a default setting (localhost:30255
)
while the other options are skipped if no property or environment variable is defined.
Option | Java property | Environment variable | Default value |
---|---|---|---|
Daemon port, IP, or IP:port | org.scion.daemon |
SCION_DAEMON |
localhost:30255 |
Bootstrap topology file path | org.scion.bootstrap.topoFile |
SCION_BOOTSTRAP_TOPO_FILE |
|
Bootstrap server host | org.scion.bootstrap.host |
SCION_BOOTSTRAP_HOST |
|
Bootstrap DNS NAPTR entry host name | org.scion.bootstrap.naptr.name |
SCION_BOOTSTRAP_NAPTR_NAME |
|
Bootstrap DNS NAPTR entry host name | org.scion.test.useOsSearchDomains |
SCION_USE_OS_SEARCH_DOMAINS |
true |
JPAN will check the OS default DNS server to resolve SCION addresses.
In addition, addresses can be specified in a /etc/scion/hosts
file. The location of the hosts file
is configurable, see next section.
Option | Java property | Environment variable | Default value |
---|---|---|---|
Path expiry margin. Before sending a packet a new path is requested if the path is about to expire within X seconds. | org.scion.pathExpiryMargin |
SCION_PATH_EXPIRY_MARGIN |
10 |
Location of hosts file. Multiple location can be specified separated by ; . |
org.scion.hostsFiles |
SCION_HOSTS_FILES |
/etc/scion/hosts |
Minimize segment requests to local AS at the cost of reduced range of path available. | org.scion.resolver.experimentalMinimizeRequests |
EXPERIMENTAL_SCION_RESOLVER_MINIMIZE_REQUESTS |
false |
EXPERIMENTAL_SCION_RESOLVER_MINIMIZE_REQUESTS
is a non-standard option that request CORE segments only of other
path can be constructed. This may reduce response time when requesting new paths. It is very likely,
but not guaranteed, that the shortest path (fewest hops) will be available.
If this property is not set (= default), CORE segments are always requested, resulting in additional
path options. While these additional path options almost always result in longer paths, they may have
other advantages.
JPAN uses the slf4j logging library. To use it, you have to install a logger. For example, to use the slf4j simple logger, add the following to your dependencies (eg. maven pom file):
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>
Then enable the logger by placing a simplelogger.properties
file into you resources folder, or enable logging programmatically with
System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "INFO");
before using any JPAN code.
A common problem is that the certificates of the testbed have expired (default validity: 3 days).
The certificates can be renewed by recreating the network with
./scion.sh topology -c <your_topology_here.topo>
.
This error occurs when requesting a path with an ISD/AS code that is not known in the network.
Solving this requires some additional configuration, see setOverrideSourceAddress
above.
[WARNING] thread Thread[grpc-default-worker-ELG-1-1,5,com.app.SimpleScmp] was interrupted but is still alive after waiting at least 15000msecs
...
[WARNING] Couldn't destroy threadgroup org.codehaus.mojo.exec.ExecJavaMojo$IsolatedThreadGroup[name=com.app.SimpleScmp,maxpri=10]
java.lang.IllegalThreadStateException
at java.lang.ThreadGroup.destroy (ThreadGroup.java:803)
at org.codehaus.mojo.exec.ExecJavaMojo.execute (ExecJavaMojo.java:321)
...
This can happen in your JUnit tests if the ScionService
is not closed properly.
To fix, close the service manually, for example by calling ScionService.close()
.
In normal applications this is rarely necessary because services are closed automatically by a
shut-down hook when the application shuts down.
Compilation failure: Compilation failure:
[ERROR] ...<...>ServiceGrpc.java:[7,18] cannot find symbol
[ERROR] symbol: class Generated
[ERROR] location: package javax.annotation
This can be fixed by building with Java JDK 1.8.
This happens because the tests uses local IP addresses other than 127.0.0.1, e.g. 127.0.0.15.
These are blocked by default on MacOS. To enable these addresses you can run the script
./config/enable-macos-loopback.sh
.
This project is licensed under the Apache License, Version 2.0 (see LICENSE or https://www.apache.org/licenses/LICENSE-2.0).