Hello, I have been using my Nvidia Shield with a self-built kernel module as a Wireguard server with the support of this patch to have access to my home network. There is a corresponding change needed in the Android app[1] to make this work. I am not super familiar with C so please take time to scrutinize this code - particularly the memory management. If its easier to provide feedback on a Github pull request, let me know and I can open one. [1] https://github.com/WireGuard/wireguard-android/pull/23 Thank you, Adam ----- From 77e9153510fbcacafe20b2813ca11464768f1fd0 Mon Sep 17 00:00:00 2001 From: Adam Irr Date: Sat, 27 Mar 2021 13:47:09 -0700 Subject: [PATCH] wg-quick: Android: Add up/down hook support This change allows Pre/Post Up/Down commands to be run for Android devices. This provides a mechanism for Android devices to behave like a server and automatically set up routing rules. Signed-off-by: Adam Irr ---  src/wg-quick/android.c | 91 ++++++++++++++++++++++++++++++++++++++----  1 file changed, 84 insertions(+), 7 deletions(-) diff --git a/src/wg-quick/android.c b/src/wg-quick/android.c index 941c7b8..7d3cd03 100644 --- a/src/wg-quick/android.c +++ b/src/wg-quick/android.c @@ -1097,6 +1097,9 @@ static void cmd_usage(const char *program)          "  - DNS: an optional DNS server to use while the device is up.\n"          "  - ExcludedApplications: optional blacklist of applications to exclude from the tunnel.\n\n"          "  - IncludedApplications: optional whitelist of applications to include in the tunnel.\n\n" +        "  - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed\n" +        "    by a root shell at the corresponding phases of the link.\n" +        "    The string \\`%i' is expanded to INTERFACE."          "  See wg-quick(8) for more info and examples.\n");  } @@ -1110,7 +1113,39 @@ 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 run_hooks(const char *cmds, const char *iface) +{ +    if (cmds == NULL) +        return; +    size_t len = strlen(cmds), iface_len = strlen(iface), j = 0, iface_count = 0; +    if (len > (1<<16)) +        return; +    for (size_t i = 0; i < len - 1; ++i) { +        if (cmds[i] == '%' && cmds[++i] == 'i') { +            iface_count++; +        } +    } + +    // Allocate enough space for the full command after replacing %i with the interface name +    _cleanup_free_ char *current_cmd = xmalloc(len + ((iface_len - 2) * iface_count) + 1); + +    for (size_t i = 0; i < len; ++i) { +        if (cmds[i] != '\n') { +            if (cmds[i] == '%' && cmds[++i] == 'i') { +                strcpy(current_cmd + j, iface); +                j += iface_len; +            } else { +                current_cmd[j++] = cmds[i]; +            } +        } else if (j != 0) { +            current_cmd[j] = '\0'; +            j = 0; +            cmd("%s", current_cmd); +        } +    } +} + +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 char *pre_up_cmds, const char *post_up_cmds)  {      DEFINE_CMD(c);      unsigned int netid = 0; @@ -1127,7 +1162,9 @@ static void cmd_up(const char *iface, const char *config, unsigned int mtu, cons      add_if(iface);      set_config(iface, config);      listen_port = determine_listen_port(iface); +    run_hooks(pre_up_cmds, iface);      up_if(&netid, iface, listen_port); +    run_hooks(post_up_cmds, iface);      set_addr(iface, addrs);      set_dnses(netid, dnses);      set_routes(iface, netid); @@ -1140,7 +1177,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, char *pre_down_cmds, char *post_down_cmds)  {      DEFINE_CMD(c);      bool found = false; @@ -1159,12 +1196,32 @@ static void cmd_down(const char *iface)          exit(EMEDIUMTYPE);      } +    run_hooks(pre_down_cmds, iface);      del_if(iface); +    run_hooks(post_down_cmds, iface);      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 clean_hook_cmd(const char *line, const size_t len, char *output) +{ +    size_t j = 0; +    bool found_prefix = false; +    bool found_cmd_start = false; +    for (size_t i = 0; i < len; ++i) { +        if (!found_cmd_start && isspace(line[i])) +            continue; +        if (found_prefix && !isspace(line[i])) +            found_cmd_start = true; +        if (line[i] == '=') +            found_prefix = true; + +        output[j++] = line[i]; +    } +    output[j] = '\0'; +} + +static void parse_options(char **iface, char **config, unsigned int *mtu, char **addrs, char **dnses, char **excluded_applications, char **included_applications, char **pre_up_cmds, char **post_up_cmds, char **pre_down_cmds, char **post_down_cmds, const char *arg)  {      _cleanup_fclose_ FILE *file = NULL;      _cleanup_free_ char *line = NULL; @@ -1254,6 +1311,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 > 4) { +                clean_hook_cmd(line, len, clean); +                *pre_up_cmds = concat_and_free(*pre_up_cmds, "\n", clean + 6); +                continue; +            } else if (!strncasecmp(clean, "PostUp=", 7) && j > 4) { +                clean_hook_cmd(line, len, clean); +                *post_up_cmds = concat_and_free(*post_up_cmds, "\n", clean + 7); +                continue; +            } else if (!strncasecmp(clean, "PreDown=", 8) && j > 4) { +                clean_hook_cmd(line, len, clean); +                *pre_down_cmds = concat_and_free(*pre_down_cmds, "\n", clean + 8); +                continue; +            } else if (!strncasecmp(clean, "PostDown=", 9) && j > 4) { +                clean_hook_cmd(line, len, clean); +                *post_down_cmds = concat_and_free(*post_down_cmds, "\n", clean + 9); +                continue;              }          }          *config = concat_and_free(*config, "", line); @@ -1278,17 +1351,21 @@ int main(int argc, char *argv[])      _cleanup_free_ char *excluded_applications = NULL;      _cleanup_free_ char *included_applications = NULL;      unsigned int mtu; +    _cleanup_free_ char *pre_up_cmds = NULL; +    _cleanup_free_ char *post_up_cmds = NULL; +    _cleanup_free_ char *pre_down_cmds = NULL; +    _cleanup_free_ char *post_down_cmds = NULL;      if (argc == 2 && (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")))          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_cmds, &post_up_cmds, &pre_down_cmds, &post_down_cmds, argv[2]); +        cmd_up(iface, config, mtu, addrs, dnses, excluded_applications, included_applications, pre_up_cmds, post_up_cmds);      } 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_cmds, &post_up_cmds, &pre_down_cmds, &post_down_cmds, argv[2]); +        cmd_down(iface, pre_down_cmds, post_down_cmds);      } else {          cmd_usage(argv[0]);          return 1; -- 2.25.1