Development discussion of WireGuard
 help / color / mirror / Atom feed
* [PATCH wireguard-tools] wg-quick: android: add support for {Pre, Post}{Up, Down} hooks
@ 2025-05-25  8:04 Claire Elaina
  2025-05-25 12:45 ` Jason A. Donenfeld
  0 siblings, 1 reply; 3+ messages in thread
From: Claire Elaina @ 2025-05-25  8:04 UTC (permalink / raw)
  To: wireguard; +Cc: Claire Elaina

---
 src/wg-quick/android.c | 96 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 89 insertions(+), 7 deletions(-)

diff --git a/src/wg-quick/android.c b/src/wg-quick/android.c
index 1263ee4..8a8df47 100644
--- a/src/wg-quick/android.c
+++ b/src/wg-quick/android.c
@@ -60,6 +60,15 @@ static void *xcalloc(size_t nmemb, size_t size)
 	exit(errno);
 }
 
+static void *xrealloc(void *ptr, size_t size)
+{
+	void *ret = realloc(ptr, size);
+	if (ret)
+		return ret;
+	perror("Error: realloc");
+	exit(errno);
+}
+
 static void *xstrdup(const char *str)
 {
 	char *ret = strdup(str);
@@ -126,6 +135,27 @@ static void free_command_buffer(struct command_buffer *c)
 	free(c->line);
 }
 
+struct str_list {
+	char **items;
+	size_t len;
+};
+
+static void append_str_list(struct str_list *l, char *item)
+{
+	l->len++;
+	l->items = xrealloc(l->items, sizeof(char*) * l->len);
+	l->items[l->len - 1] = item;
+}
+
+static void free_str_list(struct str_list *l)
+{
+	if (!l)
+		return;
+	for (size_t i = 0; i < l->len; ++i)
+		free(l->items[i]);
+	free(l->items);
+}
+
 static void freep(void *p)
 {
 	free(*(void **)p);
@@ -140,6 +170,7 @@ static void fclosep(FILE **f)
 #define _cleanup_regfree_ _cleanup_(regfree)
 
 #define DEFINE_CMD(name) _cleanup_(free_command_buffer) struct command_buffer name = { 0 };
+#define DEFINE_STR_LIST(name) _cleanup_(free_str_list) struct str_list name = { 0 };
 
 static char *vcmd_ret(struct command_buffer *c, const char *cmd_fmt, va_list args)
 {
@@ -239,6 +270,12 @@ _printf_(1, 2) static void cndc(const char *cmd_fmt, ...)
 	}
 }
 
+static void execute_hooks(const struct str_list *hooks)
+{
+	for (size_t i = 0; i < hooks->len; ++i)
+		cmd("%s", hooks->items[i]);
+}
+
 /* Values are from AOSP repository platform/frameworks/native in libs/binder/ndk/include_ndk/android/binder_status.h. */
 enum {
 	STATUS_OK = 0,
@@ -1112,7 +1149,7 @@ static void cmd_up_cleanup(void)
 	free(cleanup_iface);
 }
 
-static void cmd_up(const char *iface, const char *config, unsigned int mtu, const char *addrs, const char *dnses, const char *excluded_applications, const char *included_applications)
+static void cmd_up(const char *iface, const char *config, unsigned int mtu, const char *addrs, const char *dnses, const char *excluded_applications, const char *included_applications, const struct str_list *pre_up, const struct str_list *post_up)
 {
 	DEFINE_CMD(c);
 	unsigned int netid = 0;
@@ -1127,6 +1164,7 @@ static void cmd_up(const char *iface, const char *config, unsigned int mtu, cons
 	atexit(cmd_up_cleanup);
 
 	add_if(iface);
+	execute_hooks(pre_up);
 	set_config(iface, config);
 	listen_port = determine_listen_port(iface);
 	up_if(&netid, iface, listen_port);
@@ -1135,6 +1173,7 @@ static void cmd_up(const char *iface, const char *config, unsigned int mtu, cons
 	set_routes(iface, netid);
 	set_mtu(iface, mtu);
 	set_users(netid, excluded_applications, included_applications);
+	execute_hooks(post_up);
 	broadcast_change();
 
 	free(cleanup_iface);
@@ -1142,7 +1181,7 @@ static void cmd_up(const char *iface, const char *config, unsigned int mtu, cons
 	exit(EXIT_SUCCESS);
 }
 
-static void cmd_down(const char *iface)
+static void cmd_down(const char *iface, const struct str_list *pre_down, const struct str_list *post_down)
 {
 	DEFINE_CMD(c);
 	bool found = false;
@@ -1161,12 +1200,14 @@ static void cmd_down(const char *iface)
 		exit(EMEDIUMTYPE);
 	}
 
+	execute_hooks(pre_down);
 	del_if(iface);
+	execute_hooks(post_down);
 	broadcast_change();
 	exit(EXIT_SUCCESS);
 }
 
-static void parse_options(char **iface, char **config, unsigned int *mtu, char **addrs, char **dnses, char **excluded_applications, char **included_applications, const char *arg)
+static void parse_options(char **iface, char **config, unsigned int *mtu, char **addrs, char **dnses, char **excluded_applications, char **included_applications, struct str_list *pre_up, struct str_list *post_up, struct str_list *pre_down, struct str_list *post_down, const char *arg)
 {
 	_cleanup_fclose_ FILE *file = NULL;
 	_cleanup_free_ char *line = NULL;
@@ -1236,6 +1277,27 @@ static void parse_options(char **iface, char **config, unsigned int *mtu, char *
 		}
 		clean[j] = '\0';
 
+		char *line_value = strchr(line, '=');
+		_cleanup_free_ char *unstripped_value = NULL;
+		if (line_value) {
+			/* Skip equal sign. */
+			line_value++;
+
+			/* Skip leading whitespace. */
+			while (isspace(line_value[0]))
+				line_value++;
+
+			/* Calculate length of the value without trailing whitespace. */
+			size_t line_value_len = strlen(line_value);
+			while (line_value_len && isspace(line_value[line_value_len - 1]))
+				line_value_len--;
+
+			/* Create the string. */
+			unstripped_value = xmalloc(line_value_len + 1);
+			memcpy(unstripped_value, line_value, line_value_len);
+			unstripped_value[line_value_len] = '\0';
+		}
+
 		if (clean[0] == '[')
 			in_interface_section = false;
 		if (!strcasecmp(clean, "[Interface]"))
@@ -1256,6 +1318,22 @@ static void parse_options(char **iface, char **config, unsigned int *mtu, char *
 			} else if (!strncasecmp(clean, "MTU=", 4) && j > 4) {
 				*mtu = atoi(clean + 4);
 				continue;
+			} else if (!strncasecmp(clean, "PreUp=", 6) && j > 6) {
+				append_str_list(pre_up, unstripped_value);
+				unstripped_value = NULL;
+				continue;
+			} else if (!strncasecmp(clean, "PostUp=", 7) && j > 7) {
+				append_str_list(post_up, unstripped_value);
+				unstripped_value = NULL;
+				continue;
+			} else if (!strncasecmp(clean, "PreDown=", 8) && j > 8) {
+				append_str_list(pre_down, unstripped_value);
+				unstripped_value = NULL;
+				continue;
+			} else if (!strncasecmp(clean, "PostDown=", 9) && j > 9) {
+				append_str_list(post_down, unstripped_value);
+				unstripped_value = NULL;
+				continue;
 			}
 		}
 		*config = concat_and_free(*config, "", line);
@@ -1279,6 +1357,10 @@ int main(int argc, char *argv[])
 	_cleanup_free_ char *dnses = NULL;
 	_cleanup_free_ char *excluded_applications = NULL;
 	_cleanup_free_ char *included_applications = NULL;
+	DEFINE_STR_LIST(pre_up);
+	DEFINE_STR_LIST(post_up);
+	DEFINE_STR_LIST(pre_down);
+	DEFINE_STR_LIST(post_down);
 	unsigned int mtu;
 	char prop[PROP_VALUE_MAX + 1];
 
@@ -1289,12 +1371,12 @@ int main(int argc, char *argv[])
 		cmd_usage(argv[0]);
 	else if (argc == 3 && !strcmp(argv[1], "up")) {
 		auto_su(argc, argv);
-		parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, argv[2]);
-		cmd_up(iface, config, mtu, addrs, dnses, excluded_applications, included_applications);
+		parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, &pre_up, &post_up, &pre_down, &post_down, argv[2]);
+		cmd_up(iface, config, mtu, addrs, dnses, excluded_applications, included_applications, &pre_up, &post_up);
 	} else if (argc == 3 && !strcmp(argv[1], "down")) {
 		auto_su(argc, argv);
-		parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, argv[2]);
-		cmd_down(iface);
+		parse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, &pre_up, &post_up, &pre_down, &post_down, argv[2]);
+		cmd_down(iface, &pre_down, &post_down);
 	} else {
 		cmd_usage(argv[0]);
 		return 1;
-- 
2.49.0


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH wireguard-tools] wg-quick: android: add support for {Pre,  Post}{Up, Down} hooks
  2025-05-25  8:04 [PATCH wireguard-tools] wg-quick: android: add support for {Pre, Post}{Up, Down} hooks Claire Elaina
@ 2025-05-25 12:45 ` Jason A. Donenfeld
  2025-05-25 22:45   ` Claire
  0 siblings, 1 reply; 3+ messages in thread
From: Jason A. Donenfeld @ 2025-05-25 12:45 UTC (permalink / raw)
  To: Claire Elaina; +Cc: wireguard, adam.irr

On Sun, May 25, 2025 at 06:04:57PM +1000, Claire Elaina wrote:
> +static void execute_hooks(const struct str_list *hooks)
> +{
> +	for (size_t i = 0; i < hooks->len; ++i)
> +		cmd("%s", hooks->items[i]);
> +}

This was also posted here, so copying Adam:
https://lore.kernel.org/wireguard/DM6PR13MB24579CD788EF28E019933C0A92609@DM6PR13MB2457.namprd13.prod.outlook.com/
https://github.com/WireGuard/wireguard-android/pull/23

This feature is appealing, but I've always held off on it because I'm
afraid of the malware potential on client platforms where people are
pretty looseygoosey with loading in random config files. Even on
Windows, it only got added behind a hidden registry setting. If we added
it here, maybe it'd need to be quite gated too. But then how do we
handle cases where a config had it but it was disabled and then it gets
enabled and it's there by surprise? Maybe strip it out on import if it's
disabled? What about the transition from root to non-root and back?
Anyway, many questions.

Wondering, what commands do you want to run?

Jason

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH wireguard-tools] wg-quick: android: add support for {Pre, Post}{Up, Down} hooks
  2025-05-25 12:45 ` Jason A. Donenfeld
@ 2025-05-25 22:45   ` Claire
  0 siblings, 0 replies; 3+ messages in thread
From: Claire @ 2025-05-25 22:45 UTC (permalink / raw)
  To: Jason A. Donenfeld, Claire Elaina; +Cc: wireguard, adam.irr

> Wondering, what commands do you want to run?

PostUp = wg set CelesteWAN fwmark 0

X problem:
I have a Raspberry Pi at home, and I want to have an encrypted link 
between it and client devices. When I'm at home (i.e. connected to 
the Pi's LAN), I want the clients to connect directly to the Pi with 
its LAN IP address. When I'm away from home, I want them to connect 
through a remote server that has access to the Pi.

Y problem:
I cannot do port forwarding on my home internet connection because of 
CGNAT (hence, I cannot have the clients use the Pi's public IP 
address). My cursed idea is to nest Wireguard over Wireguard when not 
on LAN, so the connection would be "Phone -> Server -> Pi". This works 
fine on my laptop, but unfortunately not on my phone (pings to the Pi 
result in no response).

However, when I manually run `wg set CelesteWAN fwmark 0` after the 
tunnel is already set up, the connection works. I have made a patch to 
allow setting FwMark in the config, but it doesn't work when testing. 
Perhaps the `iptables -m mark ...` rules are interfering. I want to 
try only setting the `fwmark` for the interface, but I feel like it's 
too niche to upstream, so I wanted to add generic command execution.

If there's a less cursed way to make Wireguard over Wireguard work, or 
even not having to do WoW, I'd appreciate it.

Sincerely,
Claire Elaina

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-05-25 23:58 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-05-25  8:04 [PATCH wireguard-tools] wg-quick: android: add support for {Pre, Post}{Up, Down} hooks Claire Elaina
2025-05-25 12:45 ` Jason A. Donenfeld
2025-05-25 22:45   ` Claire

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).