diff --git a/.gitignore b/.gitignore
index 502a62f..8bc310c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,3 +38,4 @@ build/
.DS_Store
/.idea/
/.mvn/
+/config.json
diff --git a/app/pom.xml b/app/pom.xml
new file mode 100644
index 0000000..8a3b0d0
--- /dev/null
+++ b/app/pom.xml
@@ -0,0 +1,28 @@
+
+
+ 4.0.0
+
+ ru.kirillius
+ pf-sdn
+ 0.1.0.0
+
+
+ app
+
+
+ 24
+ 24
+ UTF-8
+
+
+
+ ru.kirillius
+ core
+ 0.1.0.0
+ compile
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/ru/kirillius/pf/sdn/App.java b/app/src/main/java/ru/kirillius/pf/sdn/App.java
new file mode 100644
index 0000000..a1a465b
--- /dev/null
+++ b/app/src/main/java/ru/kirillius/pf/sdn/App.java
@@ -0,0 +1,62 @@
+package ru.kirillius.pf.sdn;
+
+import lombok.Getter;
+import org.eclipse.jetty.server.Server;
+import ru.kirillius.pf.sdn.External.API.HENetBGPInfoProvider;
+import ru.kirillius.pf.sdn.core.Auth.AuthManager;
+import ru.kirillius.pf.sdn.core.Config;
+import ru.kirillius.pf.sdn.core.Context;
+import ru.kirillius.pf.sdn.core.Networking.AutonomousSystemInformationService;
+import ru.kirillius.pf.sdn.core.Networking.NetworkManager;
+
+import java.io.File;
+import java.io.IOException;
+
+public class App implements Context {
+ public static void main(String[] args) {
+ new App();
+ }
+
+ private final static File configFile = new File("config.json");
+
+ public App() {
+ try {
+ config = Config.load(configFile);
+ } catch (IOException e) {
+ config = new Config();
+ try {
+ Config.store(config, configFile);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ authManager = new AuthManager(this);
+ server = new Server();
+ autonomousSystemInformationService = new AutonomousSystemInformationService();
+ autonomousSystemInformationService.setProvider(new HENetBGPInfoProvider());
+ networkManager = new NetworkManager(this);
+ var inputResources = networkManager.getInputResources();
+ inputResources.add(config.getCustomResources());
+ networkManager.triggerUpdate();
+
+ while (networkManager.isUpdatingNow()) {
+ Thread.yield();
+ }
+
+ return;
+ }
+
+ @Getter
+ private final NetworkManager networkManager;
+ @Getter
+ private Config config;
+ @Getter
+ private final AuthManager authManager;
+ @Getter
+ private final Server server;
+ @Getter
+ private final AutonomousSystemInformationService autonomousSystemInformationService;
+
+
+}
diff --git a/app/src/main/java/ru/kirillius/pf/sdn/External/API/HENetBGPInfoProvider.java b/app/src/main/java/ru/kirillius/pf/sdn/External/API/HENetBGPInfoProvider.java
new file mode 100644
index 0000000..ec76931
--- /dev/null
+++ b/app/src/main/java/ru/kirillius/pf/sdn/External/API/HENetBGPInfoProvider.java
@@ -0,0 +1,56 @@
+package ru.kirillius.pf.sdn.External.API;
+
+import lombok.SneakyThrows;
+import org.jetbrains.annotations.NotNull;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import ru.kirillius.pf.sdn.core.Networking.AutonomousSystemInfoProvider;
+import ru.kirillius.pf.sdn.core.Networking.IPv4Subnet;
+import ru.kirillius.utils.logging.SystemLogger;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class HENetBGPInfoProvider implements AutonomousSystemInfoProvider {
+ @Override
+ @SneakyThrows
+ public List getPrefixes(int as) {
+ try (var client = HttpClient.newHttpClient()) {
+ var request = HttpRequest.newBuilder().uri(URI.create("https://bgp.he.net/super-lg/report/api/v1/prefixes/originated/" + as)).header("Accept", "application/json").GET().build();
+ var response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
+ if (response.statusCode() == 200) {
+ try (var inputStream = response.body()) {
+ return getIPv4Subnets(inputStream);
+ }
+ } else {
+ SystemLogger.error("Unable to get info about AS" + as, CTX);
+ return Collections.emptyList();
+ }
+ }
+ }
+
+ private static @NotNull ArrayList getIPv4Subnets(InputStream inputStream) {
+ var json = new JSONObject(new JSONTokener(inputStream));
+ var array = json.getJSONArray("prefixes");
+ var list = new ArrayList();
+ array.forEach(obj -> {
+ var jo = (JSONObject) obj;
+
+ var prefix = jo.getString("Prefix");
+ if (prefix.indexOf('.') == -1) {
+ return;
+ }
+ list.add(new IPv4Subnet(prefix));
+
+ });
+ return list;
+ }
+
+ private final static String CTX = HENetBGPInfoProvider.class.getSimpleName();
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java
index b0e5f9d..11c9579 100644
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Config.java
@@ -10,6 +10,7 @@ import ru.kirillius.json.JSONProperty;
import ru.kirillius.json.JSONSerializable;
import ru.kirillius.json.JSONUtility;
import ru.kirillius.pf.sdn.core.Auth.AuthToken;
+import ru.kirillius.pf.sdn.core.Networking.NetworkResourceBundle;
import java.io.*;
import java.util.Collections;
@@ -39,13 +40,33 @@ public class Config {
@Setter
@Getter
@JSONProperty
- private String passwordHash = null;
+ private String passwordHash = "";
@Setter
@Getter
@JSONProperty
private int httpPort = 8081;
+ @Setter
+ @Getter
+ @JSONProperty
+ private boolean mergeSubnets = true;
+
+ @Setter
+ @Getter
+ @JSONProperty
+ private int mergeSubnetsCount = 10;
+
+ @Setter
+ @Getter
+ @JSONProperty
+ private NetworkResourceBundle customResources = new NetworkResourceBundle();
+
+ @Setter
+ @Getter
+ @JSONProperty
+ private NetworkResourceBundle filteredResources = new NetworkResourceBundle();
+
public void update() {
try {
store(this, loadedConfigFile);
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java
index a029ce5..4327a74 100644
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Context.java
@@ -2,6 +2,8 @@ package ru.kirillius.pf.sdn.core;
import org.eclipse.jetty.server.Server;
import ru.kirillius.pf.sdn.core.Auth.AuthManager;
+import ru.kirillius.pf.sdn.core.Networking.AutonomousSystemInformationService;
+import ru.kirillius.pf.sdn.core.Networking.NetworkManager;
public interface Context {
Config getConfig();
@@ -10,4 +12,8 @@ public interface Context {
Server getServer();
+ AutonomousSystemInformationService getAutonomousSystemInformationService();
+
+ NetworkManager getNetworkManager();
+
}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInfoProvider.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInfoProvider.java
new file mode 100644
index 0000000..3d1c3ce
--- /dev/null
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInfoProvider.java
@@ -0,0 +1,7 @@
+package ru.kirillius.pf.sdn.core.Networking;
+
+import java.util.List;
+
+public interface AutonomousSystemInfoProvider {
+ List getPrefixes(int as);
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInformationService.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInformationService.java
new file mode 100644
index 0000000..bb39c77
--- /dev/null
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/AutonomousSystemInformationService.java
@@ -0,0 +1,34 @@
+package ru.kirillius.pf.sdn.core.Networking;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+@NoArgsConstructor
+public class AutonomousSystemInformationService implements Closeable {
+ private final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+
+ @Getter
+ @Setter
+ private AutonomousSystemInfoProvider provider = null;
+
+
+ public Future> getPrefixes(int as) {
+ return executor.submit(() -> provider.getPrefixes(as));
+ }
+
+ @Override
+ public void close() throws IOException {
+ executor.shutdown();
+ }
+
+
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/IPv4Subnet.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/IPv4Subnet.java
index a007038..d4bd9a2 100644
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/IPv4Subnet.java
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/IPv4Subnet.java
@@ -1,18 +1,46 @@
package ru.kirillius.pf.sdn.core.Networking;
import lombok.Getter;
+import ru.kirillius.json.JSONSerializer;
+import ru.kirillius.json.SerializationException;
import ru.kirillius.pf.sdn.core.Util.IPv4Util;
+import java.util.Objects;
import java.util.regex.Pattern;
public class IPv4Subnet {
+ public final static class Serializer implements JSONSerializer {
+
+ @Override
+ public Object serialize(IPv4Subnet subnet) throws SerializationException {
+ return subnet.toString();
+ }
+
+ @Override
+ public IPv4Subnet deserialize(Object o, Class> aClass) throws SerializationException {
+ return new IPv4Subnet((String) o);
+ }
+ }
+
private final long address;
@Getter
private final int prefixLength;
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || getClass() != o.getClass()) return false;
+ IPv4Subnet that = (IPv4Subnet) o;
+ return address == that.address && prefixLength == that.prefixLength;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, prefixLength);
+ }
+
public IPv4Subnet(String subnet) {
var split = subnet.split(Pattern.quote("/"));
if (split.length != 2) {
@@ -45,7 +73,7 @@ public class IPv4Subnet {
public boolean overlaps(IPv4Subnet subnet) {
var minPrefixLength = Math.min(prefixLength, subnet.prefixLength);
var commonMask = IPv4Util.calculateMask(minPrefixLength);
- if (commonMask != prefixLength) {
+ if (minPrefixLength != prefixLength) {
return false; //can't overlap larger prefix
}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java
new file mode 100644
index 0000000..214e451
--- /dev/null
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkManager.java
@@ -0,0 +1,105 @@
+package ru.kirillius.pf.sdn.core.Networking;
+
+import lombok.Getter;
+import ru.kirillius.pf.sdn.core.Context;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class NetworkManager implements Closeable {
+ private final ExecutorService executor = Executors.newSingleThreadExecutor();
+
+ private Context context;
+
+ public NetworkManager(Context context) {
+ this.context = context;
+ }
+
+ private final AtomicReference> updateProcess = new AtomicReference<>();
+ private final AtomicBoolean cacheInvalid = new AtomicBoolean(true);
+ @Getter
+ private final NetworkResourceBundle inputResources = new NetworkResourceBundle();
+ @Getter
+ private final NetworkResourceBundle outputResources = new NetworkResourceBundle();
+
+ public boolean isUpdatingNow() {
+ var future = updateProcess.get();
+ return future != null && !future.isDone() && !future.isCancelled();
+ }
+
+ private final Map> prefixCache = new ConcurrentHashMap<>();
+
+ public synchronized void triggerUpdate() {
+ if (isUpdatingNow()) {
+ return;
+ }
+ updateProcess.set(executor.submit(() -> {
+ var config = context.getConfig();
+ var filteredResources = config.getFilteredResources();
+ var asn = new ArrayList<>(inputResources.getASN());
+ asn.removeAll(filteredResources.getASN());
+
+
+ if (cacheInvalid.get()) {
+ fetchPrefixes(asn);
+ }
+
+ var subnets = new HashSet<>(inputResources.getSubnets());
+ prefixCache.values().forEach(subnets::addAll);
+ filteredResources.getSubnets().forEach(subnets::remove);
+
+ var domains = new HashSet<>(inputResources.getDomains());
+ filteredResources.getDomains().forEach(domains::remove);
+ //check overlaps
+
+ var domainsToRemove = new HashSet();
+ for (var domainToMatch : domains) {
+ var pattern = "." + domainToMatch;
+ for (var domain : domains) {
+ if (domain.endsWith(pattern)) {
+ domainsToRemove.add(domain);
+ }
+ }
+ }
+
+ domains.removeAll(domainsToRemove);
+
+ outputResources.setASN(Collections.unmodifiableList(asn));
+ outputResources.setSubnets(subnets.stream().toList());
+ outputResources.setDomains(domains.stream().toList());
+
+ }));
+ }
+
+ public void invalidateCache() {
+ cacheInvalid.set(true);
+ }
+
+ private void fetchPrefixes(List systems) {
+ systems.forEach(as -> {
+ var service = context.getAutonomousSystemInformationService();
+ var future = service.getPrefixes(as);
+
+ while (!future.isDone() && !future.isCancelled()) {
+ Thread.yield();
+ }
+
+ try {
+ var iPv4Subnets = future.get();
+ prefixCache.put(as, iPv4Subnets);
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+
+ @Override
+ public void close() throws IOException {
+ executor.shutdown();
+ }
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkResourceBundle.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkResourceBundle.java
new file mode 100644
index 0000000..c29939d
--- /dev/null
+++ b/core/src/main/java/ru/kirillius/pf/sdn/core/Networking/NetworkResourceBundle.java
@@ -0,0 +1,40 @@
+package ru.kirillius.pf.sdn.core.Networking;
+
+import lombok.*;
+import ru.kirillius.json.JSONArrayProperty;
+import ru.kirillius.json.JSONSerializable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@JSONSerializable
+public class NetworkResourceBundle {
+ @Getter
+ @Setter
+ @JSONArrayProperty(type = Integer.class)
+ private List ASN = new ArrayList<>();
+ @Getter
+ @Setter
+ @JSONArrayProperty(type = IPv4Subnet.class, serializer = IPv4Subnet.Serializer.class)
+ private List subnets = new ArrayList<>();
+ @Getter
+ @Setter
+ @JSONArrayProperty(type = String.class)
+ private List domains = new ArrayList<>();
+
+ public void clear() {
+ ASN.clear();
+ subnets.clear();
+ domains.clear();
+ }
+
+ public void add(NetworkResourceBundle networkResourceBundle) {
+ ASN.addAll(networkResourceBundle.getASN());
+ subnets.addAll(networkResourceBundle.getSubnets());
+ domains.addAll(networkResourceBundle.getDomains());
+ }
+
+}
diff --git a/core/src/main/java/ru/kirillius/pf/sdn/core/Util/BGPUtility.java b/core/src/main/java/ru/kirillius/pf/sdn/core/Util/BGPUtility.java
deleted file mode 100644
index d5df553..0000000
--- a/core/src/main/java/ru/kirillius/pf/sdn/core/Util/BGPUtility.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package ru.kirillius.pf.sdn.core.Util;
-
-public class BGPUtility {
-
-}
diff --git a/core/src/test/java/ru/kirillius/pf/sdn/core/Networking/IPv4SubnetTest.java b/core/src/test/java/ru/kirillius/pf/sdn/core/Networking/IPv4SubnetTest.java
index 078471c..cc080ee 100644
--- a/core/src/test/java/ru/kirillius/pf/sdn/core/Networking/IPv4SubnetTest.java
+++ b/core/src/test/java/ru/kirillius/pf/sdn/core/Networking/IPv4SubnetTest.java
@@ -13,7 +13,7 @@ class IPv4SubnetTest {
var validPrefixes = List.of(0, 5, 20, 32);
var invalidPrefixes = List.of(-1, 33, 800);
var validAddresses = List.of("1.2.3.4", "0.0.0.0", "255.255.255.255");
- var invalidAddresses = List.of("1.2.3.04", "0.0.0", "255.255.255.255.255", "1.2.3.256", "-1.0.0.0");
+ var invalidAddresses = List.of("1", "0.0.0", "255.255.255.255.255", "1.2.3.256", "-1.0.0.0");
validPrefixes.forEach(prefix -> {
@@ -27,7 +27,7 @@ class IPv4SubnetTest {
try {
var subnet = new IPv4Subnet(address, prefix);
throw new AssertionError();
- } catch (Exception e) {
+ } catch (Throwable e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class);
}
});
@@ -38,7 +38,7 @@ class IPv4SubnetTest {
try {
var subnet = new IPv4Subnet(address, prefix);
throw new AssertionError();
- } catch (Exception e) {
+ } catch (Throwable e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class);
}
});
@@ -49,7 +49,7 @@ class IPv4SubnetTest {
try {
var subnet = new IPv4Subnet(address, prefix);
throw new AssertionError();
- } catch (Exception e) {
+ } catch (Throwable e) {
assertThat(e).isInstanceOf(IllegalArgumentException.class);
}
});
diff --git a/launcher/pom.xml b/launcher/pom.xml
deleted file mode 100644
index 9126b2c..0000000
--- a/launcher/pom.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
- 4.0.0
-
- ru.kirillius
- pf-sdn
- 0.1.0.0
-
-
- launcher
-
-
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 268337f..94bdf3d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,9 +10,9 @@
pom
web-client
- launcher
core
web-server
+ app
@@ -94,12 +94,7 @@
hibernate-commons
2.2.0.0
-
-
- org.javassist
- javassist
- 3.29.2-GA
-
+
@@ -112,9 +107,10 @@
org.projectlombok
lombok
- 1.18.34
+ 1.18.40
provided
+
\ No newline at end of file