diff options
-rw-r--r-- | settings.gradle.kts | 1 | ||||
-rw-r--r-- | tunnel/build.gradle.kts | 1 | ||||
-rw-r--r-- | tunnel/src/main/java/com/wireguard/android/backend/Bgp.java | 285 | ||||
-rw-r--r-- | tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java | 13 |
4 files changed, 300 insertions, 0 deletions
diff --git a/settings.gradle.kts b/settings.gradle.kts index 91bc0b90..e87d2a0d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,4 +19,5 @@ dependencyResolutionManagement { rootProject.name = "wireguard-android" include(":tunnel") +include(":bgp-java") include(":ui") diff --git a/tunnel/build.gradle.kts b/tunnel/build.gradle.kts index 7f6bd0e6..230d38e5 100644 --- a/tunnel/build.gradle.kts +++ b/tunnel/build.gradle.kts @@ -67,6 +67,7 @@ android { } dependencies { + implementation(project(":bgp-java")) implementation(libs.androidx.annotation) implementation(libs.androidx.collection) implementation(libs.grpc.android) diff --git a/tunnel/src/main/java/com/wireguard/android/backend/Bgp.java b/tunnel/src/main/java/com/wireguard/android/backend/Bgp.java new file mode 100644 index 00000000..a6a8b420 --- /dev/null +++ b/tunnel/src/main/java/com/wireguard/android/backend/Bgp.java @@ -0,0 +1,285 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.wireguard.android.backend; + +import android.net.TrafficStats; +import android.util.Log; + +import com.lumaserv.bgp.BGPListener; +import com.lumaserv.bgp.BGPServer; +import com.lumaserv.bgp.BGPSession; +import com.lumaserv.bgp.BGPSessionConfiguration; +import com.lumaserv.bgp.protocol.AFI; +import com.lumaserv.bgp.protocol.BGPPacket; +import com.lumaserv.bgp.protocol.IPPrefix; +import com.lumaserv.bgp.protocol.SAFI; +import com.lumaserv.bgp.protocol.attribute.ASPathAttribute; +import com.lumaserv.bgp.protocol.attribute.MPReachableNLRIAttribute; +import com.lumaserv.bgp.protocol.attribute.NextHopAttribute; +import com.lumaserv.bgp.protocol.attribute.OriginAttribute; +import com.lumaserv.bgp.protocol.attribute.PathAttribute; +import com.lumaserv.bgp.protocol.attribute.TunnelEncapsAttribute; +import com.lumaserv.bgp.protocol.message.BGPUpdate; + +import com.wireguard.android.backend.Backend; +import com.wireguard.config.InetEndpoint; +import com.wireguard.config.InetNetwork; +import com.wireguard.crypto.Key; +import com.wireguard.crypto.KeyFormatException; + +import io.grpc.ManagedChannel; + +import java.io.IOException; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.SocketException; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.List; + +import javax.net.SocketFactory; + +public class Bgp implements BGPListener { + private static final String TAG = "WireGuard/Bgp"; + private static final String SESSION = "demosession"; + private static final int MY_ASN = (int)4200000201L; + private static final int REMOTE_ASN = (int)4200000010L; + private static final String REMOTE_ADDR = "10.49.32.1"; + private static final String REMOTE_ID = "10.49.160.1"; + private static final String LOCAL_ID = "10.49.33.218"; + private static final int PORT = 0; + private static final int STATS_TAG = 1; // FIXME + + private final Backend backend; + private final ManagedChannel channel; + private final Tunnel tunnel; + private final int tunnelHandle; + private BGPServer server; + + public Bgp(Backend backend, ManagedChannel channel, Tunnel tunnel, int tunnelHandle) { + this.backend = backend; + this.channel = channel; + this.tunnel = tunnel; + this.tunnelHandle = tunnelHandle; + } + + @Override + public void onOpen(BGPSession session) { + // DO WHAT YOU WANT + Log.i(TAG, "onOpen"); + + // BGPUpdate update; + // { + // List<IPPrefix> prefixes = new ArrayList<>(1); + // prefixes.add(new IPPrefix(new byte[]{10, 49, 124, 105}, 32)); + // List<PathAttribute> attrs = new ArrayList<>(); + // attrs.add(new OriginAttribute(OriginAttribute.Origin.IGP)); + // attrs.add(new NextHopAttribute().setAddress(new byte[]{10, 49, 125, 105})); + // ASPathAttribute.Segment seg = new ASPathAttribute.Segment(); + // seg.setType(ASPathAttribute.Segment.Type.SEQUENCE); + // seg.getAsns().add(MY_ASN); + // ASPathAttribute asPath = new ASPathAttribute(session); + // asPath.getSegments().add(seg); + // attrs.add(asPath); + // update = new BGPUpdate().setAttributes(attrs).setPrefixes(prefixes); + // } + + // BGPUpdate update2; + // try { + // List<PathAttribute> attrs = new ArrayList<>(); + // attrs.add(new OriginAttribute(OriginAttribute.Origin.IGP)); + // ASPathAttribute.Segment seg = new ASPathAttribute.Segment(); + // seg.setType(ASPathAttribute.Segment.Type.SEQUENCE); + // seg.getAsns().add(MY_ASN); + // ASPathAttribute asPath = new ASPathAttribute(session); + // List<IPPrefix> prefixes = new ArrayList<>(1); + // prefixes.add(new IPPrefix(new byte[]{0x20, 0x1, 0x04, 0x70, (byte)0xdf, (byte)0xae, 0x63, 0, 0, 0, 0, 0, 0, 0, 0x01, 0x05}, 128)); + // MPReachableNLRIAttribute mpr = new MPReachableNLRIAttribute(); + // mpr.setAfi(AFI.IPV6).setSafi(SAFI.UNICAST).setNextHop(InetAddress.getByName("2001:470:dfae:6300::1:105")).setNlriPrefixes(prefixes); + // attrs.add(mpr); + // asPath.getSegments().add(seg); + // attrs.add(asPath); + // update2 = new BGPUpdate().setAttributes(attrs); + // } catch (UnknownHostException ex) { + // throw new RuntimeException(ex); + // } + + // try { + // session.sendUpdate(update); + // session.sendUpdate(update2); + // } catch (IOException ex) { + // throw new RuntimeException(ex); + // } + } + + @Override + public void onUpdate(BGPSession session, BGPUpdate update) { + // DO WHAT YOU WANT + Log.i(TAG, "onUpdate: " + update.getPrefixes() + ",-" + update.getWithdrawnPrefixes() + "," + update.getAttributes()); + MPReachableNLRIAttribute mpr = null; + TunnelEncapsAttribute te = null; + + for (PathAttribute attr: update.getAttributes()) { + if (attr instanceof TunnelEncapsAttribute) { + te = (TunnelEncapsAttribute)attr; + } else if (attr instanceof MPReachableNLRIAttribute) { + mpr = (MPReachableNLRIAttribute)attr; + } + } + + if (te == null) { + return; + } + + TunnelEncapsAttribute.WireGuard wg = null; + TunnelEncapsAttribute.Color col = null; + TunnelEncapsAttribute.EgressEndpoint ep = null; + TunnelEncapsAttribute.UDPDestinationPort port = null; + + for (TunnelEncapsAttribute.Tunnel t: te.getTunnels()) { + if (t.getType() != 51820) { + continue; + } + + for (TunnelEncapsAttribute.SubTlv st: t.getSubTlvs()) { + if (st instanceof TunnelEncapsAttribute.WireGuard) { + wg = (TunnelEncapsAttribute.WireGuard)st; + } else if (st instanceof TunnelEncapsAttribute.Color) { + col = (TunnelEncapsAttribute.Color)st; + } else if (st instanceof TunnelEncapsAttribute.EgressEndpoint) { + ep = (TunnelEncapsAttribute.EgressEndpoint)st; + } else if (st instanceof TunnelEncapsAttribute.UDPDestinationPort) { + port = (TunnelEncapsAttribute.UDPDestinationPort)st; + } + } + } + + if (wg == null) { + return; + } + + try { + Key publicKey = Key.fromBytes(wg.getPublicKey()); + InetEndpoint endpoint = null; + + if (ep != null && port != null) { + endpoint = InetEndpoint.fromAddress(ep.getAddress(), port.getPort()); + } + + tunnel.onEndpointChange(publicKey, endpoint); + + List<InetNetwork> addNetworks = new ArrayList<>(); + List<InetNetwork> removeNetworks = new ArrayList<>(); + + for (IPPrefix prefix: update.getPrefixes()) { + try { + addNetworks.add(new InetNetwork(InetAddress.getByAddress(prefix.getAddress()), prefix.getLength())); + } catch (UnknownHostException ignore) { + } + } + + for (IPPrefix prefix: update.getWithdrawnPrefixes()) { + try { + removeNetworks.add(new InetNetwork(InetAddress.getByAddress(prefix.getAddress()), prefix.getLength())); + } catch (UnknownHostException ignore) { + } + } + + if (mpr != null && (mpr.getAfi() == AFI.IPV6 || mpr.getAfi() == AFI.IPV4) && mpr.getSafi() == SAFI.UNICAST) { + for (IPPrefix prefix: mpr.getNlriPrefixes()) { + try { + addNetworks.add(new InetNetwork(InetAddress.getByAddress(prefix.getAddress()), prefix.getLength())); + } catch (UnknownHostException ignore) { + } + } + } + + tunnel.onAllowedIpsChange(publicKey, addNetworks, removeNetworks); + // backend.addAllowedIps(tunnel, publicKey, addNetworks); + // backend.removeAllowedIps(tunnel, publicKey, addNetworks); // TODO + } catch (KeyFormatException ex) { + Log.w(TAG, "Key.fromBytes " + ex); + } + } + + @Override + public void onClose(BGPSession session) { + // NOT YET IMPLEMENTED + Log.i(TAG, "onClose"); + } + + public boolean startServer() { + stopServer(); + try { + SocketFactory factory = new SocketFactory() { + private Socket taggedSocket(Socket sock) throws SocketException { + TrafficStats.tagSocket(sock); + return sock; + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + TrafficStats.setThreadStatsTag(STATS_TAG); + return taggedSocket(new Socket(host, port)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + TrafficStats.setThreadStatsTag(STATS_TAG); + return taggedSocket(new Socket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + TrafficStats.setThreadStatsTag(STATS_TAG); + return taggedSocket(new Socket(address, port, localAddress, localPort)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + TrafficStats.setThreadStatsTag(STATS_TAG); + return taggedSocket(new Socket(host, port, localHost, localPort)); + } + }; + + BGPSessionConfiguration config = + new BGPSessionConfiguration(SESSION, + MY_ASN, + ip(LOCAL_ID), + REMOTE_ASN, + ip(REMOTE_ID), + null, // Remote address + factory, + this); + TrafficStats.setThreadStatsTag(STATS_TAG); + ServerSocket socket = new ServerSocket(PORT); + //TrafficStats.tagSocket(socket); + // Set + server = new BGPServer(socket); + server.getSessionConfigurations().add(config); + server.connect(config, REMOTE_ADDR); + return true; + } catch (IOException ex) { + return false; + } + } + + public void stopServer() { + if (server != null) { + server.shutdown(); + server = null; + } + } + + private static byte[] ip(String s) throws UnknownHostException { + InetAddress addr = InetAddress.getByName(s); + byte[] data = addr.getAddress(); + if (data.length != 4) + throw new UnknownHostException(s + ": Not an IPv4 address"); + return data; + } +} diff --git a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java index 438bf9e8..6e8d565c 100644 --- a/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java +++ b/tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java @@ -111,6 +111,7 @@ public final class GoBackend implements Backend { @Nullable private Network activeNetwork; private ManagedChannel channel; private boolean obtainDhcpLease = false; + @Nullable private Bgp bgp; /** * Public constructor for GoBackend. @@ -424,6 +425,10 @@ public final class GoBackend implements Backend { service.protect(wgGetSocketV4(currentTunnelHandle)); service.protect(wgGetSocketV6(currentTunnelHandle)); Log.i(TAG, "Dhcp done"); + + bgp = new Bgp(this, channel, currentTunnel, currentTunnelHandle); + bgp.startServer(); + currentTunnel.onDhcpChange(dhcp); } @@ -689,6 +694,10 @@ public final class GoBackend implements Backend { currentTunnel = null; currentTunnelHandle = -1; currentConfig = null; + if (bgp != null) { + bgp.stopServer(); + bgp = null; + } stopHttpProxy(); if (vpnNetworkCallback != null) connectivityManager.unregisterNetworkCallback(vpnNetworkCallback); @@ -760,6 +769,10 @@ public final class GoBackend implements Backend { @Override public void onDestroy() { if (owner != null) { + if (owner.bgp != null) { + owner.bgp.stopServer(); + owner.bgp = null; + } owner.stopHttpProxy(); final Tunnel tunnel = owner.currentTunnel; if (tunnel != null) { |