From 7596c5c27855a50aa79c0cc923f73f650b32470b Mon Sep 17 00:00:00 2001 From: Iurii Egorov Date: Tue, 12 Nov 2024 13:14:53 +0300 Subject: [PATCH] Support prefixed bogus endpoints' addresses Signed-off-by: Iurii Egorov --- amneziawg-dkms.spec | 2 +- src/main.c | 2 + src/netlink.c | 168 ++++++++++++- src/netlink.h | 2 + .../006-bogus-endpoints-prefixes.patch | 237 ++++++++++++++++++ 5 files changed, 406 insertions(+), 5 deletions(-) create mode 100644 src/patches/006-bogus-endpoints-prefixes.patch diff --git a/amneziawg-dkms.spec b/amneziawg-dkms.spec index 3f75db8..4c70530 100644 --- a/amneziawg-dkms.spec +++ b/amneziawg-dkms.spec @@ -1,7 +1,7 @@ %global debug_package %{nil} Name: amneziawg-dkms -Version: 1.0.20241023 +Version: 1.0.20241112 Release: 1%{?dist} Epoch: 1 URL: https://www.wireguard.com/ diff --git a/src/main.c b/src/main.c index 4c321d4..c9c7057 100644 --- a/src/main.c +++ b/src/main.c @@ -76,6 +76,8 @@ static void __exit wg_mod_exit(void) } module_param(bogus_endpoints, int, 0600); +module_param(bogus_endpoints_prefix, charp, 0600); +module_param(bogus_endpoints_prefix6, charp, 0600); module_init(wg_mod_init); module_exit(wg_mod_exit); MODULE_LICENSE("GPL v2"); diff --git a/src/netlink.c b/src/netlink.c index 7d9f3d1..5043bb3 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -15,8 +15,20 @@ #include #include #include +#include + +#include +#include +#include +#include +#include +#include +#include +#include int bogus_endpoints = 0; +char *bogus_endpoints_prefix = "127.0.0.0/8"; +char *bogus_endpoints_prefix6 = "ff80::/16"; static struct genl_family genl_family; @@ -114,17 +126,159 @@ struct dump_ctx { #define DUMP_CTX(cb) ((struct dump_ctx *)(cb)->args) +struct ipv4_prefix { + __be32 prefix; + int prefix_len; +}; + +struct ipv6_prefix { + u8 prefix[16]; + int prefix_len; +}; + +static inline int parse_ipv4_prefix(const char *prefix_str, struct ipv4_prefix *prefix) +{ + char addr_str[INET_ADDRSTRLEN]; + const char *slash; + int ret; + u8 addr[4]; + + if (!prefix_str || !prefix) + return -EINVAL; + + slash = strchr(prefix_str, '/'); + if (!slash) + return -EINVAL; + + if (slash - prefix_str >= INET_ADDRSTRLEN) + return -EINVAL; + + strncpy(addr_str, prefix_str, slash - prefix_str); + addr_str[slash - prefix_str] = '\0'; + + ret = kstrtoint(slash + 1, 10, &prefix->prefix_len); + if (ret < 0) + return ret; + + if (prefix->prefix_len < 0 || prefix->prefix_len > 32) + return -EINVAL; + + ret = in4_pton(addr_str, -1, addr, '\0', NULL); + if (ret != 1) + return -EINVAL; + + prefix->prefix = *(__be32 *)addr; + + return 0; +} + +static inline int generate_ipv4_address_with_prefix(const struct ipv4_prefix *prefix, __be32 *addr) +{ + u32 prefix_host_order, random_suffix, full_addr_host_order; + u32 suffix_mask; + + if (!prefix || !addr) + return -EINVAL; + + prefix_host_order = ntohl(prefix->prefix); + + if (prefix->prefix_len == 32) { + suffix_mask = 0; + } else { + suffix_mask = (1U << (32 - prefix->prefix_len)) - 1; + } + + get_random_bytes(&random_suffix, sizeof(random_suffix)); + random_suffix &= suffix_mask; + full_addr_host_order = (prefix_host_order & (~suffix_mask)) | random_suffix; + *addr = htonl(full_addr_host_order); + + return 0; +} + +static inline int parse_ipv6_prefix(const char *prefix_str, struct ipv6_prefix *prefix) +{ + char addr_str[INET6_ADDRSTRLEN]; + const char *slash; + int ret; + + if (!prefix_str || !prefix) + return -EINVAL; + + slash = strchr(prefix_str, '/'); + if (!slash) + return -EINVAL; + + if (slash - prefix_str >= INET6_ADDRSTRLEN) + return -EINVAL; + + strncpy(addr_str, prefix_str, slash - prefix_str); + addr_str[slash - prefix_str] = '\0'; + + ret = kstrtoint(slash + 1, 10, &prefix->prefix_len); + if (ret < 0) + return ret; + + if (prefix->prefix_len < 0 || prefix->prefix_len > 128) + return -EINVAL; + + ret = in6_pton(addr_str, -1, prefix->prefix, '\0', NULL); + if (ret != 1) + return -EINVAL; + + return 0; +} + +static inline int generate_ipv6_address_with_prefix(const struct ipv6_prefix *prefix, u8 *addr) +{ + int prefix_bytes, prefix_bits; + u8 mask; + + if (!prefix || !addr) + return -EINVAL; + + memcpy(addr, prefix->prefix, 16); + + prefix_bytes = prefix->prefix_len / 8; + prefix_bits = prefix->prefix_len % 8; + + if (prefix_bytes < 16) { + get_random_bytes(addr + prefix_bytes, 16 - prefix_bytes); + + if (prefix_bits != 0) { + mask = (u8)(0xFF << (8 - prefix_bits)); + addr[prefix_bytes] &= mask; + addr[prefix_bytes] |= get_random_u8() & ~mask; + } + } + + return 0; +} + static int get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx) { struct nlattr *allowedips_nest, *peer_nest = nla_nest_start(skb, 0); struct allowedips_node *allowedips_node = ctx->next_allowedip; + struct ipv4_prefix prefix; + struct ipv6_prefix prefix6; bool fail; + int ret; if (!peer_nest) return -EMSGSIZE; + if (bogus_endpoints) { + ret = parse_ipv4_prefix(bogus_endpoints_prefix, &prefix); + if (ret < 0) + return ret; + + ret = parse_ipv6_prefix(bogus_endpoints_prefix6, &prefix6); + if (ret < 0) + return ret; + } + fail = nla_put_u32(skb, WGPEER_A_FLAGS, WGPEER_F_HAS_ADVANCED_SECURITY); if (fail) goto err; @@ -172,15 +326,21 @@ get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx) if (peer->endpoint.addr.sa_family == AF_INET) { struct sockaddr_in addr4 = peer->endpoint.addr4; - if (bogus_endpoints) - addr4.sin_addr.s_addr = get_random_u32(); + if (bogus_endpoints) { + ret = generate_ipv4_address_with_prefix(&prefix, &addr4.sin_addr.s_addr); + if (ret < 0) + goto err; + } fail = nla_put(skb, WGPEER_A_ENDPOINT, sizeof(addr4), &addr4); } else if (peer->endpoint.addr.sa_family == AF_INET6) { struct sockaddr_in6 addr6 = peer->endpoint.addr6; - if (bogus_endpoints) - get_random_bytes(&addr6.sin6_addr.s6_addr, sizeof(addr6.sin6_addr.s6_addr)); + if (bogus_endpoints) { + ret = generate_ipv6_address_with_prefix(&prefix6, addr6.sin6_addr.s6_addr); + if (ret < 0) + goto err; + } fail = nla_put(skb, WGPEER_A_ENDPOINT, sizeof(addr6), &addr6); } diff --git a/src/netlink.h b/src/netlink.h index 0fcc344..e7fdab7 100644 --- a/src/netlink.h +++ b/src/netlink.h @@ -10,6 +10,8 @@ #include "noise.h" extern int bogus_endpoints; +extern char *bogus_endpoints_prefix; +extern char *bogus_endpoints_prefix6; int wg_genl_mcast_peer_unknown(struct wg_device *wg, const u8 pubkey[NOISE_PUBLIC_KEY_LEN], struct endpoint *endpoint, bool advanced_security); diff --git a/src/patches/006-bogus-endpoints-prefixes.patch b/src/patches/006-bogus-endpoints-prefixes.patch new file mode 100644 index 0000000..ac860f5 --- /dev/null +++ b/src/patches/006-bogus-endpoints-prefixes.patch @@ -0,0 +1,237 @@ +diff --git main.c main.c +index 4c321d4..c9c7057 100644 +--- main.c ++++ main.c +@@ -76,6 +76,8 @@ static void __exit wg_mod_exit(void) + } + + module_param(bogus_endpoints, int, 0600); ++module_param(bogus_endpoints_prefix, charp, 0600); ++module_param(bogus_endpoints_prefix6, charp, 0600); + module_init(wg_mod_init); + module_exit(wg_mod_exit); + MODULE_LICENSE("GPL v2"); +diff --git netlink.c netlink.c +index 7d9f3d1..5043bb3 100644 +--- netlink.c ++++ netlink.c +@@ -15,8 +15,20 @@ + #include + #include + #include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include + + int bogus_endpoints = 0; ++char *bogus_endpoints_prefix = "127.0.0.0/8"; ++char *bogus_endpoints_prefix6 = "ff80::/16"; + + static struct genl_family genl_family; + +@@ -114,17 +126,159 @@ struct dump_ctx { + + #define DUMP_CTX(cb) ((struct dump_ctx *)(cb)->args) + ++struct ipv4_prefix { ++ __be32 prefix; ++ int prefix_len; ++}; ++ ++struct ipv6_prefix { ++ u8 prefix[16]; ++ int prefix_len; ++}; ++ ++static inline int parse_ipv4_prefix(const char *prefix_str, struct ipv4_prefix *prefix) ++{ ++ char addr_str[INET_ADDRSTRLEN]; ++ const char *slash; ++ int ret; ++ u8 addr[4]; ++ ++ if (!prefix_str || !prefix) ++ return -EINVAL; ++ ++ slash = strchr(prefix_str, '/'); ++ if (!slash) ++ return -EINVAL; ++ ++ if (slash - prefix_str >= INET_ADDRSTRLEN) ++ return -EINVAL; ++ ++ strncpy(addr_str, prefix_str, slash - prefix_str); ++ addr_str[slash - prefix_str] = '\0'; ++ ++ ret = kstrtoint(slash + 1, 10, &prefix->prefix_len); ++ if (ret < 0) ++ return ret; ++ ++ if (prefix->prefix_len < 0 || prefix->prefix_len > 32) ++ return -EINVAL; ++ ++ ret = in4_pton(addr_str, -1, addr, '\0', NULL); ++ if (ret != 1) ++ return -EINVAL; ++ ++ prefix->prefix = *(__be32 *)addr; ++ ++ return 0; ++} ++ ++static inline int generate_ipv4_address_with_prefix(const struct ipv4_prefix *prefix, __be32 *addr) ++{ ++ u32 prefix_host_order, random_suffix, full_addr_host_order; ++ u32 suffix_mask; ++ ++ if (!prefix || !addr) ++ return -EINVAL; ++ ++ prefix_host_order = ntohl(prefix->prefix); ++ ++ if (prefix->prefix_len == 32) { ++ suffix_mask = 0; ++ } else { ++ suffix_mask = (1U << (32 - prefix->prefix_len)) - 1; ++ } ++ ++ get_random_bytes(&random_suffix, sizeof(random_suffix)); ++ random_suffix &= suffix_mask; ++ full_addr_host_order = (prefix_host_order & (~suffix_mask)) | random_suffix; ++ *addr = htonl(full_addr_host_order); ++ ++ return 0; ++} ++ ++static inline int parse_ipv6_prefix(const char *prefix_str, struct ipv6_prefix *prefix) ++{ ++ char addr_str[INET6_ADDRSTRLEN]; ++ const char *slash; ++ int ret; ++ ++ if (!prefix_str || !prefix) ++ return -EINVAL; ++ ++ slash = strchr(prefix_str, '/'); ++ if (!slash) ++ return -EINVAL; ++ ++ if (slash - prefix_str >= INET6_ADDRSTRLEN) ++ return -EINVAL; ++ ++ strncpy(addr_str, prefix_str, slash - prefix_str); ++ addr_str[slash - prefix_str] = '\0'; ++ ++ ret = kstrtoint(slash + 1, 10, &prefix->prefix_len); ++ if (ret < 0) ++ return ret; ++ ++ if (prefix->prefix_len < 0 || prefix->prefix_len > 128) ++ return -EINVAL; ++ ++ ret = in6_pton(addr_str, -1, prefix->prefix, '\0', NULL); ++ if (ret != 1) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static inline int generate_ipv6_address_with_prefix(const struct ipv6_prefix *prefix, u8 *addr) ++{ ++ int prefix_bytes, prefix_bits; ++ u8 mask; ++ ++ if (!prefix || !addr) ++ return -EINVAL; ++ ++ memcpy(addr, prefix->prefix, 16); ++ ++ prefix_bytes = prefix->prefix_len / 8; ++ prefix_bits = prefix->prefix_len % 8; ++ ++ if (prefix_bytes < 16) { ++ get_random_bytes(addr + prefix_bytes, 16 - prefix_bytes); ++ ++ if (prefix_bits != 0) { ++ mask = (u8)(0xFF << (8 - prefix_bits)); ++ addr[prefix_bytes] &= mask; ++ addr[prefix_bytes] |= get_random_u8() & ~mask; ++ } ++ } ++ ++ return 0; ++} ++ + static int + get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx) + { + + struct nlattr *allowedips_nest, *peer_nest = nla_nest_start(skb, 0); + struct allowedips_node *allowedips_node = ctx->next_allowedip; ++ struct ipv4_prefix prefix; ++ struct ipv6_prefix prefix6; + bool fail; ++ int ret; + + if (!peer_nest) + return -EMSGSIZE; + ++ if (bogus_endpoints) { ++ ret = parse_ipv4_prefix(bogus_endpoints_prefix, &prefix); ++ if (ret < 0) ++ return ret; ++ ++ ret = parse_ipv6_prefix(bogus_endpoints_prefix6, &prefix6); ++ if (ret < 0) ++ return ret; ++ } ++ + fail = nla_put_u32(skb, WGPEER_A_FLAGS, WGPEER_F_HAS_ADVANCED_SECURITY); + if (fail) + goto err; +@@ -172,15 +326,21 @@ get_peer(struct wg_peer *peer, struct sk_buff *skb, struct dump_ctx *ctx) + if (peer->endpoint.addr.sa_family == AF_INET) { + struct sockaddr_in addr4 = peer->endpoint.addr4; + +- if (bogus_endpoints) +- addr4.sin_addr.s_addr = get_random_u32(); ++ if (bogus_endpoints) { ++ ret = generate_ipv4_address_with_prefix(&prefix, &addr4.sin_addr.s_addr); ++ if (ret < 0) ++ goto err; ++ } + + fail = nla_put(skb, WGPEER_A_ENDPOINT, sizeof(addr4), &addr4); + } else if (peer->endpoint.addr.sa_family == AF_INET6) { + struct sockaddr_in6 addr6 = peer->endpoint.addr6; + +- if (bogus_endpoints) +- get_random_bytes(&addr6.sin6_addr.s6_addr, sizeof(addr6.sin6_addr.s6_addr)); ++ if (bogus_endpoints) { ++ ret = generate_ipv6_address_with_prefix(&prefix6, addr6.sin6_addr.s6_addr); ++ if (ret < 0) ++ goto err; ++ } + + fail = nla_put(skb, WGPEER_A_ENDPOINT, sizeof(addr6), &addr6); + } +diff --git netlink.h netlink.h +index 0fcc344..e7fdab7 100644 +--- netlink.h ++++ netlink.h +@@ -10,6 +10,8 @@ + #include "noise.h" + + extern int bogus_endpoints; ++extern char *bogus_endpoints_prefix; ++extern char *bogus_endpoints_prefix6; + + int wg_genl_mcast_peer_unknown(struct wg_device *wg, const u8 pubkey[NOISE_PUBLIC_KEY_LEN], + struct endpoint *endpoint, bool advanced_security);