Support prefixed bogus endpoints' addresses

Signed-off-by: Iurii Egorov <ye@amnezia.org>
This commit is contained in:
Iurii Egorov
2024-11-12 13:14:53 +03:00
committed by Iurii Egorov
parent e53879f523
commit 7596c5c278
5 changed files with 406 additions and 5 deletions

View File

@@ -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/

View File

@@ -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");

View File

@@ -15,8 +15,20 @@
#include <net/sock.h>
#include <crypto/algapi.h>
#include <linux/random.h>
#include <linux/bitops.h>
#include <linux/inet.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/inetdevice.h>
#include <linux/byteorder/generic.h>
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);
}

View File

@@ -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);

View File

@@ -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 <net/sock.h>
#include <crypto/algapi.h>
#include <linux/random.h>
+#include <linux/bitops.h>
+
+#include <linux/inet.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <linux/inetdevice.h>
+#include <linux/byteorder/generic.h>
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);