parent
a19fa8f2fa
commit
2ee1ff4ca4
9 changed files with 120 additions and 58 deletions
|
@ -1,5 +1,6 @@
|
||||||
package eu.m724.autopeerer.client;
|
package eu.m724.autopeerer.client;
|
||||||
|
|
||||||
|
import eu.m724.autopeerer.client.bird.BirdLive;
|
||||||
import eu.m724.autopeerer.client.wireguard.WireGuardLive;
|
import eu.m724.autopeerer.client.wireguard.WireGuardLive;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -20,7 +21,9 @@ public class Main {
|
||||||
URI serverUri = URI.create(config.getString("remote"));
|
URI serverUri = URI.create(config.getString("remote"));
|
||||||
|
|
||||||
var wireGuardLive = new WireGuardLive(new File(config.getString("wireguard.directory")));
|
var wireGuardLive = new WireGuardLive(new File(config.getString("wireguard.directory")));
|
||||||
var packetHandler = new PacketHandler(wireGuardLive);
|
var birdLive = new BirdLive(new File(config.getString("bird.directory")));
|
||||||
|
|
||||||
|
var packetHandler = new PacketHandler(wireGuardLive, birdLive, config.getString("link-local"));
|
||||||
var client = new MyWebsocketClient(serverUri, packetHandler);
|
var client = new MyWebsocketClient(serverUri, packetHandler);
|
||||||
|
|
||||||
client.connect();
|
client.connect();
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package eu.m724.autopeerer.client;
|
package eu.m724.autopeerer.client;
|
||||||
|
|
||||||
|
import eu.m724.autopeerer.client.bird.BirdLive;
|
||||||
|
import eu.m724.autopeerer.client.bird.BirdSession;
|
||||||
import eu.m724.autopeerer.client.wireguard.WireGuardKeys;
|
import eu.m724.autopeerer.client.wireguard.WireGuardKeys;
|
||||||
import eu.m724.autopeerer.client.wireguard.WireGuardLive;
|
import eu.m724.autopeerer.client.wireguard.WireGuardLive;
|
||||||
import eu.m724.autopeerer.client.wireguard.WireGuardSession;
|
import eu.m724.autopeerer.client.wireguard.WireGuardSession;
|
||||||
|
@ -17,15 +19,20 @@ import java.nio.ByteBuffer;
|
||||||
import java.nio.file.FileAlreadyExistsException;
|
import java.nio.file.FileAlreadyExistsException;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class PacketHandler {
|
public class PacketHandler {
|
||||||
Consumer<ByteBuffer> sender;
|
Consumer<ByteBuffer> sender;
|
||||||
|
|
||||||
private final WireGuardLive wireGuardLive;
|
private final WireGuardLive wireGuardLive;
|
||||||
|
private final BirdLive birdLive;
|
||||||
|
private final String serverLinkLocal;
|
||||||
|
|
||||||
public PacketHandler(WireGuardLive wireGuardLive) {
|
public PacketHandler(WireGuardLive wireGuardLive, BirdLive birdLive, String serverLinkLocal) {
|
||||||
this.wireGuardLive = wireGuardLive;
|
this.wireGuardLive = wireGuardLive;
|
||||||
|
this.birdLive = birdLive;
|
||||||
|
this.serverLinkLocal = serverLinkLocal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle(ByteBuffer bytes) {
|
void handle(ByteBuffer bytes) {
|
||||||
|
@ -49,7 +56,7 @@ public class PacketHandler {
|
||||||
if (p instanceof PingRequestPacket packet) {
|
if (p instanceof PingRequestPacket packet) {
|
||||||
handlePingRequest(packet);
|
handlePingRequest(packet);
|
||||||
} else if (p instanceof SessionRequestPacket packet) {
|
} else if (p instanceof SessionRequestPacket packet) {
|
||||||
handleVpnRequest(packet);
|
handleSessionRequest(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,22 +95,29 @@ public class PacketHandler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleVpnRequest(SessionRequestPacket packet) {
|
private void handleSessionRequest(SessionRequestPacket packet) {
|
||||||
var privateKey = WireGuardKeys.generatePrivateKey();
|
var privateKey = WireGuardKeys.generatePrivateKey();
|
||||||
var publicKey = WireGuardKeys.derivePublicKey(privateKey);
|
var publicKey = WireGuardKeys.derivePublicKey(privateKey);
|
||||||
|
|
||||||
// TODO fill port and link local
|
int port = ThreadLocalRandom.current().nextInt() & 0xFFFF;
|
||||||
var session = new WireGuardSession(12345, privateKey, "fefe:fefe::fefe", packet.linkLocal.toCompressedString(), packet.endpointHost + ":" + packet.endpointPort, packet.publicKey);
|
if (packet.asn > 4242420000L) {
|
||||||
|
port = (int) (packet.asn % 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
var wireGuardSession = new WireGuardSession(port, privateKey, serverLinkLocal, packet.linkLocal.toCompressedString(), packet.endpointHost + ":" + packet.endpointPort, packet.publicKey);
|
||||||
|
var birdSession = new BirdSession(packet.asn, packet.linkLocal.toCompressedString());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
wireGuardLive.saveSession(packet.sessionId, session);
|
wireGuardLive.saveSession(packet.asn, wireGuardSession);
|
||||||
System.out.printf("Created session #%d to %s\n", packet.sessionId, packet.endpointHost);
|
birdLive.saveSession(birdSession);
|
||||||
Packets.send(new SessionResponsePacket(packet.sessionId, SessionResponsePacket.SessionResult.OK, session.listenPort(), publicKey), sender);
|
|
||||||
|
System.out.printf("Created session AS%d to %s\n", packet.asn, packet.endpointHost);
|
||||||
|
Packets.send(new SessionResponsePacket(packet.asn, SessionResponsePacket.SessionResult.OK, wireGuardSession.listenPort(), publicKey), sender);
|
||||||
} catch (FileAlreadyExistsException e) {
|
} catch (FileAlreadyExistsException e) {
|
||||||
System.err.println("Tried to create a session which already exists: #" + packet.sessionId);
|
System.err.println("Tried to create a session which already exists: AS" + packet.asn);
|
||||||
Packets.send(new SessionResponsePacket(packet.sessionId, SessionResponsePacket.SessionResult.ERROR, -1, null), sender);
|
Packets.send(new SessionResponsePacket(packet.asn, SessionResponsePacket.SessionResult.DUPLICATE, -1, null), sender);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Packets.send(new SessionResponsePacket(packet.sessionId, SessionResponsePacket.SessionResult.ERROR, -1, null), sender);
|
Packets.send(new SessionResponsePacket(packet.asn, SessionResponsePacket.SessionResult.ERROR, -1, null), sender);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
33
src/main/java/eu/m724/autopeerer/client/bird/BirdLive.java
Normal file
33
src/main/java/eu/m724/autopeerer/client/bird/BirdLive.java
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package eu.m724.autopeerer.client.bird;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
|
||||||
|
public class BirdLive {
|
||||||
|
private final File configsPath;
|
||||||
|
|
||||||
|
public BirdLive(File configsPath) {
|
||||||
|
System.out.println("Bird config path: " + configsPath.getAbsolutePath());
|
||||||
|
this.configsPath = configsPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveSession(BirdSession session) throws IOException {
|
||||||
|
File file = new File(configsPath, "ap_" + session.asn() + ".conf");
|
||||||
|
Files.writeString(file.toPath(), session.config(), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BirdSession getSession(long asn) throws IOException {
|
||||||
|
File file = new File(configsPath, "ap_" + asn + ".conf");
|
||||||
|
|
||||||
|
String s = Files.readString(file.toPath());
|
||||||
|
return BirdSession.fromString(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean existsSession(long asn) {
|
||||||
|
File file = new File(configsPath, "ap_" + asn + ".conf");
|
||||||
|
return file.exists();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
package eu.m724.autopeerer.client.bird;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public record BirdSession(
|
||||||
|
long asn,
|
||||||
|
String linkLocal
|
||||||
|
) {
|
||||||
|
private static Pattern PATTERN = Pattern.compile("protocol bgp as\\d+ from dnpeers \\{\n"
|
||||||
|
+ " neighbor [0-9a-f:]+ % as\\d+ as \\d+;\n"
|
||||||
|
+ "}");
|
||||||
|
|
||||||
|
public String config() {
|
||||||
|
return """
|
||||||
|
protocol bgp %s from dnpeers {
|
||||||
|
neighbor %s %% %s as %d;
|
||||||
|
}"""
|
||||||
|
.formatted("as" + asn, linkLocal, "as" + asn, asn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BirdSession fromString(String s) {;
|
||||||
|
var matcher = PATTERN.matcher(s);
|
||||||
|
if (!matcher.matches()) return null;
|
||||||
|
|
||||||
|
var asn = Integer.parseInt(matcher.group(0));
|
||||||
|
var linkLocal = matcher.group(1);
|
||||||
|
|
||||||
|
return new BirdSession(
|
||||||
|
asn, linkLocal
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,24 +8,24 @@ public class WireGuardLive {
|
||||||
private final File configsPath;
|
private final File configsPath;
|
||||||
|
|
||||||
public WireGuardLive(File configsPath) {
|
public WireGuardLive(File configsPath) {
|
||||||
System.out.println(configsPath.getAbsolutePath());
|
System.out.println("WireGuard config path: " + configsPath.getAbsolutePath());
|
||||||
this.configsPath = configsPath;
|
this.configsPath = configsPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveSession(int sessionId, WireGuardSession session) throws IOException {
|
public void saveSession(long asn, WireGuardSession session) throws IOException {
|
||||||
File file = new File(configsPath, "ap_" + sessionId + ".conf");
|
File file = new File(configsPath, "ap_" + asn + ".conf");
|
||||||
Files.writeString(file.toPath(), session.config(), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
|
Files.writeString(file.toPath(), session.config(), StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WireGuardSession getSession(int sessionId) throws IOException {
|
public WireGuardSession getSession(long asn) throws IOException {
|
||||||
File file = new File(configsPath, "ap_" + sessionId + ".conf");
|
File file = new File(configsPath, "ap_" + asn + ".conf");
|
||||||
|
|
||||||
String s = Files.readString(file.toPath());
|
String s = Files.readString(file.toPath());
|
||||||
return WireGuardSession.fromString(s);
|
return WireGuardSession.fromString(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean existsSession(int sessionId) {
|
public boolean existsSession(long asn) {
|
||||||
File file = new File(configsPath, "ap_" + sessionId + ".conf");
|
File file = new File(configsPath, "ap_" + asn + ".conf");
|
||||||
return file.exists();
|
return file.exists();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,13 @@ import java.nio.ByteBuffer;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
public class SessionResponsePacket implements Packet<SessionResponsePacket> {
|
public class SessionResponsePacket implements Packet<SessionResponsePacket> {
|
||||||
public final short sessionId;
|
public final long asn;
|
||||||
public final SessionResult result;
|
public final SessionResult result;
|
||||||
public final int port;
|
public final int port;
|
||||||
public final String publicKey;
|
public final String publicKey;
|
||||||
|
|
||||||
public SessionResponsePacket(short connectionId, SessionResult result, int port, String publicKey) {
|
public SessionResponsePacket(long asn, SessionResult result, int port, String publicKey) {
|
||||||
this.sessionId = connectionId;
|
this.asn = asn;
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
|
@ -24,7 +24,7 @@ public class SessionResponsePacket implements Packet<SessionResponsePacket> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SessionResponsePacket deserialize(ByteBuffer buffer) throws Exception {
|
public static SessionResponsePacket deserialize(ByteBuffer buffer) throws Exception {
|
||||||
var id = buffer.getShort();
|
var asn = Integer.toUnsignedLong(buffer.getInt());
|
||||||
var result = SessionResult.values()[buffer.get()];
|
var result = SessionResult.values()[buffer.get()];
|
||||||
|
|
||||||
int port = -1;
|
int port = -1;
|
||||||
|
@ -38,15 +38,14 @@ public class SessionResponsePacket implements Packet<SessionResponsePacket> {
|
||||||
publicKey = Base64.getEncoder().encodeToString(pkb);
|
publicKey = Base64.getEncoder().encodeToString(pkb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new SessionResponsePacket(asn, result, port, publicKey);
|
||||||
return new SessionResponsePacket(id, result, port, publicKey);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer serialize() {
|
public ByteBuffer serialize() {
|
||||||
var buffer = ByteBuffer.allocate(result == SessionResult.OK ? 37 : 3);
|
var buffer = ByteBuffer.allocate(result == SessionResult.OK ? 39 : 5);
|
||||||
|
|
||||||
buffer.putShort(sessionId);
|
buffer.putInt((int) asn);
|
||||||
buffer.put((byte) result.ordinal());
|
buffer.put((byte) result.ordinal());
|
||||||
|
|
||||||
if (result == SessionResult.OK) {
|
if (result == SessionResult.OK) {
|
||||||
|
@ -54,11 +53,10 @@ public class SessionResponsePacket implements Packet<SessionResponsePacket> {
|
||||||
buffer.put(Base64.getDecoder().decode(publicKey));
|
buffer.put(Base64.getDecoder().decode(publicKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SessionResult {
|
public enum SessionResult {
|
||||||
OK, ERROR
|
OK, ERROR, DUPLICATE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,15 +12,15 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
public class SessionRequestPacket implements Packet<SessionRequestPacket> {
|
public class SessionRequestPacket implements Packet<SessionRequestPacket> {
|
||||||
public final short sessionId;
|
public final long asn;
|
||||||
public final IPv6Address linkLocal;
|
public final IPv6Address linkLocal;
|
||||||
public final String publicKey;
|
public final String publicKey;
|
||||||
public final String endpointHost;
|
public final String endpointHost;
|
||||||
public final int endpointPort;
|
public final int endpointPort;
|
||||||
public final long asn;
|
|
||||||
|
|
||||||
public SessionRequestPacket(short sessionId, IPv6Address linkLocal, String publicKey, String endpointHost, int endpointPort, long asn) {
|
public SessionRequestPacket(long asn, IPv6Address linkLocal, String publicKey, String endpointHost, int endpointPort) {
|
||||||
this.sessionId = sessionId;
|
this.asn = asn;
|
||||||
|
assert asn < 0xFFFFFFFFL;
|
||||||
|
|
||||||
this.linkLocal = linkLocal;
|
this.linkLocal = linkLocal;
|
||||||
assert new IPAddressString("fe80::/10").getAddress().contains(linkLocal);
|
assert new IPAddressString("fe80::/10").getAddress().contains(linkLocal);
|
||||||
|
@ -37,9 +37,6 @@ public class SessionRequestPacket implements Packet<SessionRequestPacket> {
|
||||||
|
|
||||||
this.endpointPort = endpointPort;
|
this.endpointPort = endpointPort;
|
||||||
assert endpointPort > 0 && endpointPort < 65536;
|
assert endpointPort > 0 && endpointPort < 65536;
|
||||||
|
|
||||||
this.asn = asn;
|
|
||||||
assert asn < 0xFFFFFFFFL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,7 +45,7 @@ public class SessionRequestPacket implements Packet<SessionRequestPacket> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SessionRequestPacket deserialize(ByteBuffer buffer) throws Exception {
|
public static SessionRequestPacket deserialize(ByteBuffer buffer) throws Exception {
|
||||||
var id = buffer.getShort();
|
var asn = Integer.toUnsignedLong(buffer.getInt());
|
||||||
|
|
||||||
var ll = new byte[16];
|
var ll = new byte[16];
|
||||||
buffer.get(ll);
|
buffer.get(ll);
|
||||||
|
@ -65,22 +62,19 @@ public class SessionRequestPacket implements Packet<SessionRequestPacket> {
|
||||||
buffer.get(ep);
|
buffer.get(ep);
|
||||||
var endpointHost = new String(ep, StandardCharsets.US_ASCII);
|
var endpointHost = new String(ep, StandardCharsets.US_ASCII);
|
||||||
|
|
||||||
var asn = Integer.toUnsignedLong(buffer.getInt());
|
return new SessionRequestPacket(asn, linkLocal, publicKey, endpointHost, endpointPort);
|
||||||
|
|
||||||
return new SessionRequestPacket(id, linkLocal, publicKey, endpointHost, endpointPort, asn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuffer serialize() {
|
public ByteBuffer serialize() {
|
||||||
var buffer = ByteBuffer.allocate(57 + endpointHost.length());
|
var buffer = ByteBuffer.allocate(55 + endpointHost.length());
|
||||||
|
|
||||||
buffer.putShort(sessionId); // 2b
|
buffer.putInt((int) asn);
|
||||||
buffer.put(linkLocal.getBytes()); // 16b
|
buffer.put(linkLocal.getBytes()); // 16b
|
||||||
buffer.put(Base64.getDecoder().decode(publicKey)); // 32b
|
buffer.put(Base64.getDecoder().decode(publicKey)); // 32b
|
||||||
buffer.putShort((short) endpointPort); // 2b
|
buffer.putShort((short) endpointPort); // 2b
|
||||||
buffer.put((byte) endpointHost.length()); // 1b
|
buffer.put((byte) endpointHost.length()); // 1b
|
||||||
buffer.put(endpointHost.getBytes(StandardCharsets.US_ASCII));
|
buffer.put(endpointHost.getBytes(StandardCharsets.US_ASCII));
|
||||||
buffer.putInt((int) asn);
|
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,20 +33,6 @@ public class MyWebsocketServer extends WebSocketServer {
|
||||||
var state = new ClientState(++id, conn);
|
var state = new ClientState(++id, conn);
|
||||||
states.put(conn, state);
|
states.put(conn, state);
|
||||||
System.out.printf("[%d] Connected: %s\n", id, conn.getRemoteSocketAddress().getHostString());
|
System.out.printf("[%d] Connected: %s\n", id, conn.getRemoteSocketAddress().getHostString());
|
||||||
|
|
||||||
// TODO testing
|
|
||||||
/*state.send(new PingRequestPacket((short) 1, InetAddress.getByName("1.1.1.1")));
|
|
||||||
state.send(new PingRequestPacket((short) 2, InetAddress.getByName("1.2.3.4")));
|
|
||||||
state.send(new PingRequestPacket((short) 3, InetAddress.getByName("1.1.1.2")));*/
|
|
||||||
|
|
||||||
state.send(new SessionRequestPacket(
|
|
||||||
(short) 1,
|
|
||||||
new IPAddressString("fe80::dead:fed").getAddress().toIPv6(),
|
|
||||||
"IBusHriGmiJaqbp0IGfClDDHXcei8+JL1MIHjueheUw=",
|
|
||||||
"end.point",
|
|
||||||
51820,
|
|
||||||
4242420000L
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
remote=ws://127.0.0.1:8002
|
remote=ws://127.0.0.1:8002
|
||||||
wireguard.directory=config/wg
|
wireguard.directory=config/wg
|
||||||
|
bird.directory=config/bird
|
||||||
|
link-local=fe80::129:0
|
Loading…
Reference in a new issue