Skip to content

Commit

Permalink
squash-8
Browse files Browse the repository at this point in the history
  • Loading branch information
Tilmann Zäschke committed Apr 3, 2024
1 parent 99677d0 commit 89c5f96
Show file tree
Hide file tree
Showing 17 changed files with 2,641 additions and 5 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
[#38](https://github.com/netsec-ethz/scion-java-client/pull/38)
- Support for `/etc/scion/hosts` and for OS search domains (e.g. `/etc/resolv.conf`).
[#40](https://github.com/netsec-ethz/scion-java-client/pull/40)

- `DatagramSocket` [#31](https://github.com/netsec-ethz/scion-java-client/pull/31)
TODO:
- document/test additional methods in socket (connect, get/set Cache/Path)

### Changed
- BREAKING CHANGE: Changed maven artifactId to "client"
[#9](https://github.com/netsec-ethz/scion-java-client/pull/9)
Expand Down
37 changes: 34 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ The following artifact contains the complete SCION Java client:
```

### Planned features
- `DatagramSocket` and `DatagramPacket`
- `Selector` for `DatagramChannel`
- Path creation with short-cuts, on-path and peering routes
- Improve docs, demos and testing
Expand Down Expand Up @@ -75,7 +74,8 @@ The central classes of the API are:

### Features
Supported:
- DatagramChannel support: read(), write(), receive(), send(), bind(), connect(), ...
- DatagramChannel support: read(), write(), receive(), send(), bind(), connect(), ...
- DatagramSocket support
- Path selection policies
- Path expiry/refresh
- Packet validation
Expand All @@ -90,7 +90,6 @@ Supported:

Missing:
- DatagramChannel support for Selectors
- DatagramSockets
- Path construction with short-cuts, on-path, peering
- EPIC
- RHINE
Expand Down Expand Up @@ -232,6 +231,38 @@ The following standard options are **not** supported:
| `StandardSocketOptions.IP_MULTICAST_TTL` |
| `StandardSocketOptions.IP_MULTICAST_LOOP` |

## DatagramSocket

`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)` and `create(SocketAddress, ScionService)` for creating a `DatagramSocket`
with a non-default `ScionService`.
* `connect(RequestPath path)` for connecting to a remote host
* `getConnectionPath()` gets the connected path if the socket has been connected
* `getCachedPath(InetAddress address)` get the cached path for a given IP
* `setPathCacheCapacity(int capacity)` and `getPathCacheCapacity()` for managing the cache size
* `setOption(...)` and `getOption()` are supported even though they were only added in Java 9.
They support the same (additional) options as `DatagramChannel`.


## Performance pitfalls

- **Using `SocketAddress` for `send()`**. `send(buffer, socketAddress)` is a convenience function. However, when sending
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/scion/ScionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ List<Daemon.Path> getPathListDaemon(long srcIsdAs, long dstIsdAs) {
* @throws IOException if an errors occurs while querying paths.
*/
public List<RequestPath> getPaths(InetSocketAddress dstAddress) throws IOException {
if (dstAddress.getHostName() != null) {
ScionAddress address = getScionAddress(dstAddress.getHostName());
address.getInetAddress();
// TODO use result & cache result
}

long dstIsdAs = getIsdAs(dstAddress.getHostString());
return getPaths(dstIsdAs, dstAddress);
}
Expand Down
103 changes: 103 additions & 0 deletions src/main/java/org/scion/internal/SimpleCache.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2024 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.internal;

import java.util.HashMap;
import java.util.TreeMap;

/**
* A simple cache that min/max watermark. Once max watermark is reached, the cache removes entries
* until min watermark is reached. Least recently accessed entries are removed first.
*
* @param <K> The key
* @param <V> The value
*/
public class SimpleCache<K, V> {
private final TreeMap<Long, Entry> ageMap = new TreeMap<>();
private final HashMap<K, Entry> lookupMap = new HashMap<>();

private int capacity;
private long opCount = 0;

public SimpleCache(int capacity) {
if (capacity < 1) {
throw new IllegalArgumentException();
}
this.capacity = capacity;
}

public void put(K key, V value) {
Entry e = lookupMap.get(key);
if (e == null) {
e = new Entry(opCount++, key, value);
checkCapacity(1);
lookupMap.put(key, e);
} else {
ageMap.remove(e.age);
e.age = opCount++;
e.value = value;
}
ageMap.put(e.age, e);
}

public V get(K key) {
Entry e = lookupMap.get(key);
if (e == null) {
return null;
}

ageMap.remove(e.age);
e.age = opCount++;
ageMap.put(e.age, e);

return e.value;
}

public void clear() {
lookupMap.clear();
ageMap.clear();
}

Check warning on line 71 in src/main/java/org/scion/internal/SimpleCache.java

View check run for this annotation

Codecov / codecov/patch

src/main/java/org/scion/internal/SimpleCache.java#L69-L71

Added lines #L69 - L71 were not covered by tests

public void setCapacity(int capacity) {
if (capacity < 1) {
throw new IllegalArgumentException();
}
this.capacity = capacity;
checkCapacity(0);
}

public int getCapacity() {
return capacity;
}

private void checkCapacity(int spare) {
while (lookupMap.size() + spare > capacity) {
Entry e = ageMap.pollFirstEntry().getValue();
lookupMap.remove(e.key);
}
}

private class Entry {
private long age;
private final K key;
private V value;

public Entry(long l, K key, V value) {
this.age = l;
this.key = key;
this.value = value;
}
}
}
Loading

0 comments on commit 89c5f96

Please sign in to comment.