Skip to content

Commit

Permalink
squash-45
Browse files Browse the repository at this point in the history
  • Loading branch information
Tilmann Zäschke committed Mar 25, 2024
1 parent 324b243 commit 84cb80d
Show file tree
Hide file tree
Showing 16 changed files with 2,589 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
## [Unreleased]
### Added
- Code coverage. [#11](https://github.com/tzaeschke/phtree-cpp/pull/11)
- `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"
Expand Down
38 changes: 35 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
- `/etc/scion/hosts` and `/etc/hosts`, see https://github.com/netsec-ethz/scion-apps
Expand Down Expand Up @@ -76,7 +75,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 @@ -221,6 +220,39 @@ Options are defined in `ScionSocketOptions`, see javadoc for details.
| `SN_API_WRITE_TO_USER_BUFFER` | `false` | Throw exception when receiving an invalid packet |
| `SN_PATH_EXPIRY_MARGIN` | `2` | A new path is requested if `now + margin > pathExpirationDate` |


## 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
14 changes: 14 additions & 0 deletions src/main/java/org/scion/ScionSocketOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,20 @@ public final class ScionSocketOptions {
public static final SocketOption<Integer> SN_PATH_EXPIRY_MARGIN =
new SciSocketOption<>("SN_PATH_EXPIRY_MARGIN", Integer.class);

/**
* When cleaning up the path cache, the {@link org.scion.socket.DatagramSocket} will retain the
* SN_RESPONSE_PATH_CACHE_MIN most recent paths.
*/
public static final SocketOption<Integer> SN_RESPONSE_PATH_CACHE_MIN =
new SciSocketOption<>("SN_RESPONSE_PATH_CACHE_MIN", Integer.class);

/**
* A {@link org.scion.socket.DatagramSocket} will clean up its path cache once it passes
* SN_RESPONSE_PATH_CACHE_MAX entries.
*/
public static final SocketOption<Integer> SN_RESPONSE_PATH_CACHE_MAX =
new SciSocketOption<>("SN_RESPONSE_PATH_CACHE_MAX", Integer.class);

/**
* Set the traffic class SCION header.
*
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 84cb80d

Please sign in to comment.