Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pre/post up/down support for rooted GoBackend #23

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions tunnel/src/main/java/com/wireguard/android/backend/GoBackend.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public final class GoBackend implements Backend {
@Nullable private static AlwaysOnCallback alwaysOnCallback;
private static GhettoCompletableFuture<VpnService> vpnService = new GhettoCompletableFuture<>();
private final Context context;
private final TunnelActionHandler tunnelActionHandler;
@Nullable private Config currentConfig;
@Nullable private Tunnel currentTunnel;
private int currentTunnelHandle = -1;
Expand All @@ -54,8 +55,20 @@ public final class GoBackend implements Backend {
* @param context An Android {@link Context}
*/
public GoBackend(final Context context) {
this(context, new NoopTunnelActionHandler());
}

/**
* Public constructor for GoBackend
*
* @param context An Android {@link Context}
* @param tunnelActionHandler A {@link TunnelActionHandler} for executing Pre/Post Up/Down
* scripts when a tunnel's state changes
*/
public GoBackend(final Context context, final TunnelActionHandler tunnelActionHandler) {
adamirr marked this conversation as resolved.
Show resolved Hide resolved
SharedLibraryLoader.loadSharedLibrary(context, "wg-go");
this.context = context;
this.tunnelActionHandler = tunnelActionHandler;
}

/**
Expand Down Expand Up @@ -279,7 +292,9 @@ private void setStateInternal(final Tunnel tunnel, @Nullable final Config config
if (tun == null)
throw new BackendException(Reason.TUN_CREATION_ERROR);
Log.d(TAG, "Go backend v" + wgVersion());
tunnelActionHandler.runPreUp(config.getInterface().getPreUp());
currentTunnelHandle = wgTurnOn(tunnel.getName(), tun.detachFd(), goConfig);
tunnelActionHandler.runPostUp(config.getInterface().getPostUp());
}
if (currentTunnelHandle < 0)
throw new BackendException(Reason.GO_ACTIVATION_ERROR_CODE, currentTunnelHandle);
Expand All @@ -295,7 +310,11 @@ private void setStateInternal(final Tunnel tunnel, @Nullable final Config config
return;
}

if (currentConfig != null)
tunnelActionHandler.runPreDown(currentConfig.getInterface().getPreDown());
wgTurnOff(currentTunnelHandle);
if (currentConfig != null)
tunnelActionHandler.runPostDown(currentConfig.getInterface().getPostDown());
currentTunnel = null;
currentTunnelHandle = -1;
currentConfig = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package com.wireguard.android.backend;

import java.util.Collection;

/**
* A {@link TunnelActionHandler} implementation that does not execute any scripts.
*/
public final class NoopTunnelActionHandler implements TunnelActionHandler {

@Override
public void runPreUp(final Collection<String> scripts) {

}

@Override
public void runPostUp(final Collection<String> scripts) {

}

@Override
public void runPreDown(final Collection<String> scripts) {

}

@Override
public void runPostDown(final Collection<String> scripts) {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package com.wireguard.android.backend;

import android.util.Log;

import com.wireguard.android.util.RootShell;
import com.wireguard.android.util.RootShell.RootShellException;
import com.wireguard.util.NonNullForAll;

import java.io.IOException;
import java.util.Collection;

/**
* A {@link TunnelActionHandler} implementation that executes scripts using a root shell.
* Scripts are executed sequentially. If there is an error executing a script for a given step
* the remaining scripts in that step are skipped.
*/
@NonNullForAll
public final class RootTunnelActionHandler implements TunnelActionHandler {

private static final String TAG = "WireGuard/TunnelAction";
private final RootShell rootShell;

public RootTunnelActionHandler(final RootShell rootShell) {
this.rootShell = rootShell;
}

@Override
public void runPreDown(final Collection<String> scripts) {
if (scripts.isEmpty()) return;
Log.d(TAG, "Running PreDown scripts");
runTunnelScripts(scripts);
}

@Override
public void runPostDown(final Collection<String> scripts) {
if (scripts.isEmpty()) return;
Log.d(TAG, "Running PostDown scripts");
runTunnelScripts(scripts);
}

@Override
public void runPreUp(final Collection<String> scripts) {
if (scripts.isEmpty()) return;
Log.d(TAG, "Running PreUp scripts");
runTunnelScripts(scripts);
}

@Override
public void runPostUp(final Collection<String> scripts) {
if (scripts.isEmpty()) return;
Log.d(TAG, "Running PostUp scripts");
runTunnelScripts(scripts);
}

private void runTunnelScripts(final Iterable<String> scripts) {
for (final String script : scripts) {
if (script.contains("%i")) {
Log.e(TAG, "'%i' syntax is not supported with the GoBackend. Aborting");
return;
}

try {
rootShell.run(null, script);
} catch (final IOException | RootShellException e) {
Log.e(TAG, "Failed to execute script.", e);
return;
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright © 2020 WireGuard LLC. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/

package com.wireguard.android.backend;

import java.util.Collection;

/**
* Handles executing Pre/Post Up/Down scripts when the state of the WireGuard tunnel changes
*/
public interface TunnelActionHandler {

/**
* Execute scripts before bringing up the tunnel
*
* @param scripts Collection of scripts to execute
*/
void runPreUp(Collection<String> scripts);

/**
* Execute scripts after bringing up the tunnel
*
* @param scripts Collection of scripts to execute
*/
void runPostUp(Collection<String> scripts);

/**
* Execute scripts before bringing down the tunnel
*
* @param scripts Collection of scripts to execute
*/
void runPreDown(Collection<String> scripts);

/**
* Execute scripts after bringing down the tunnel
*
* @param scripts Collection of scripts to execute
*/
void runPostDown(Collection<String> scripts);
}
84 changes: 83 additions & 1 deletion tunnel/src/main/java/com/wireguard/config/Interface.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.wireguard.util.NonNullForAll;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -45,6 +46,10 @@ public final class Interface {
private final KeyPair keyPair;
private final Optional<Integer> listenPort;
private final Optional<Integer> mtu;
private final List<String> preUp;
private final List<String> postUp;
private final List<String> preDown;
private final List<String> postDown;

private Interface(final Builder builder) {
// Defensively copy to ensure immutability even if the Builder is reused.
Expand All @@ -55,6 +60,10 @@ private Interface(final Builder builder) {
keyPair = Objects.requireNonNull(builder.keyPair, "Interfaces must have a private key");
listenPort = builder.listenPort;
mtu = builder.mtu;
preUp = Collections.unmodifiableList(new ArrayList<>(builder.preUp));
postUp = Collections.unmodifiableList(new ArrayList<>(builder.postUp));
preDown = Collections.unmodifiableList(new ArrayList<>(builder.preDown));
postDown = Collections.unmodifiableList(new ArrayList<>(builder.postDown));
}

/**
Expand Down Expand Up @@ -93,6 +102,18 @@ public static Interface parse(final Iterable<? extends CharSequence> lines)
case "privatekey":
builder.parsePrivateKey(attribute.getValue());
break;
case "preup":
builder.parsePreUp(attribute.getValue());
break;
case "postup":
builder.parsePostUp(attribute.getValue());
break;
case "predown":
builder.parsePreDown(attribute.getValue());
break;
case "postdown":
builder.parsePostDown(attribute.getValue());
break;
default:
throw new BadConfigException(Section.INTERFACE, Location.TOP_LEVEL,
Reason.UNKNOWN_ATTRIBUTE, attribute.getKey());
Expand All @@ -112,7 +133,12 @@ public boolean equals(final Object obj) {
&& includedApplications.equals(other.includedApplications)
&& keyPair.equals(other.keyPair)
&& listenPort.equals(other.listenPort)
&& mtu.equals(other.mtu);
&& mtu.equals(other.mtu)
&& preUp.equals(other.preUp)
&& postUp.equals(other.postUp)
&& preDown.equals(other.preDown)
&& postDown.equals(other.postDown);

}

/**
Expand Down Expand Up @@ -182,6 +208,22 @@ public Optional<Integer> getMtu() {
return mtu;
}

public List<String> getPreUp() {
return preUp;
}

public List<String> getPostUp() {
return postUp;
}

public List<String> getPreDown() {
return preDown;
}

public List<String> getPostDown() {
return postDown;
}

@Override
public int hashCode() {
int hash = 1;
Expand All @@ -192,6 +234,10 @@ public int hashCode() {
hash = 31 * hash + keyPair.hashCode();
hash = 31 * hash + listenPort.hashCode();
hash = 31 * hash + mtu.hashCode();
hash = 31 * hash + preUp.hashCode();
hash = 31 * hash + postUp.hashCode();
hash = 31 * hash + preDown.hashCode();
hash = 31 * hash + postDown.hashCode();
return hash;
}

Expand Down Expand Up @@ -231,6 +277,14 @@ public String toWgQuickString() {
listenPort.ifPresent(lp -> sb.append("ListenPort = ").append(lp).append('\n'));
mtu.ifPresent(m -> sb.append("MTU = ").append(m).append('\n'));
sb.append("PrivateKey = ").append(keyPair.getPrivateKey().toBase64()).append('\n');
for (final String script : preUp)
sb.append("PreUp = ").append(script).append('\n');
for (final String script : postUp)
sb.append("PostUp = ").append(script).append('\n');
for (final String script : preDown)
sb.append("PreDown = ").append(script).append('\n');
for (final String script : postDown)
sb.append("PostDown = ").append(script).append('\n');
Comment on lines +280 to +287
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fails since Android's wg-quick implementation doesn't support these actions. I have some changes pending that add support there as well but I'll wait to send that patch until the outcome of the potential redesign is completed.

return sb.toString();
}

Expand Down Expand Up @@ -263,6 +317,14 @@ public static final class Builder {
private Optional<Integer> listenPort = Optional.empty();
// Defaults to not present.
private Optional<Integer> mtu = Optional.empty();
// Defaults to empty list
private List<String> preUp = new ArrayList<>();
// Defaults to empty list
private List<String> postUp = new ArrayList<>();
// Defaults to empty list
private List<String> preDown = new ArrayList<>();
// Defaults to empty list
private List<String> postDown = new ArrayList<>();

public Builder addAddress(final InetNetwork address) {
addresses.add(address);
Expand Down Expand Up @@ -366,6 +428,26 @@ public Builder parsePrivateKey(final String privateKey) throws BadConfigExceptio
}
}

public Builder parsePreUp(final String script) {
preUp.add(script);
return this;
}

public Builder parsePostUp(final String script) {
postUp.add(script);
return this;
}

public Builder parsePreDown(final String script) {
preDown.add(script);
return this;
}

public Builder parsePostDown(final String script) {
postDown.add(script);
return this;
}

public Builder setKeyPair(final KeyPair keyPair) {
this.keyPair = keyPair;
return this;
Expand Down
5 changes: 5 additions & 0 deletions tunnel/src/test/java/com/wireguard/config/ConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;

Expand Down Expand Up @@ -45,5 +46,9 @@ public void valid_config_parses_correctly() throws IOException, ParseException {
assertEquals("Test config has exactly one peer", 1, config.getPeers().size());
assertEquals("Test config's allowed IPs are 0.0.0.0/0 and ::0/0", config.getPeers().get(0).getAllowedIps(), expectedAllowedIps);
assertEquals("Test config has one DNS server", 1, config.getInterface().getDnsServers().size());
assertEquals("Test config loads multiple pre up scripts", Arrays.asList("echo \"pre up 1\"", "echo \"pre up 2\""), config.getInterface().getPreUp());
assertEquals("Test config loads single post up script", Collections.singletonList("echo \"post up 1\""), config.getInterface().getPostUp());
assertEquals("Test config loads single pre down script", Collections.singletonList("echo \"pre down 1\""), config.getInterface().getPreDown());
assertEquals("Test config loads single post down scripts", Collections.singletonList("echo \"post down 1\""), config.getInterface().getPostDown());
}
}
6 changes: 6 additions & 0 deletions tunnel/src/test/resources/working.conf
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
Address = 192.0.2.2/32,2001:db8:ffff:ffff:ffff:ffff:ffff:ffff/128
DNS = 192.0.2.0
PrivateKey = TFlmmEUC7V7VtiDYLKsbP5rySTKLIZq1yn8lMqK83wo=
PreUp = echo "pre up 1"
PreUp = echo "pre up 2"
PostUp = echo "post up 1"
PreDown = echo "pre down 1"
PostDown = echo "post down 1"

[Peer]
AllowedIPs = 0.0.0.0/0, ::0/0
Endpoint = 192.0.2.1:51820
Expand Down
Loading