mirror of
https://github.com/amnezia-vpn/amneziawg-linux-kernel-module.git
synced 2026-05-17 00:16:14 +03:00
chore: sync mainstream
This commit is contained in:
committed by
Yaroslav Gurov
parent
87a06ff146
commit
b66f1a2891
102
src/allowedips.c
102
src/allowedips.c
@@ -249,6 +249,52 @@ static int add(struct allowedips_node __rcu **trie, u8 bits, const u8 *key,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void remove_node(struct allowedips_node *node, struct mutex *lock)
|
||||
{
|
||||
struct allowedips_node *child, **parent_bit, *parent;
|
||||
bool free_parent;
|
||||
|
||||
list_del_init(&node->peer_list);
|
||||
RCU_INIT_POINTER(node->peer, NULL);
|
||||
if (node->bit[0] && node->bit[1])
|
||||
return;
|
||||
child = rcu_dereference_protected(node->bit[!rcu_access_pointer(node->bit[0])],
|
||||
lockdep_is_held(lock));
|
||||
if (child)
|
||||
child->parent_bit_packed = node->parent_bit_packed;
|
||||
parent_bit = (struct allowedips_node **)(node->parent_bit_packed & ~3UL);
|
||||
*parent_bit = child;
|
||||
parent = (void *)parent_bit -
|
||||
offsetof(struct allowedips_node, bit[node->parent_bit_packed & 1]);
|
||||
free_parent = !rcu_access_pointer(node->bit[0]) && !rcu_access_pointer(node->bit[1]) &&
|
||||
(node->parent_bit_packed & 3) <= 1 && !rcu_access_pointer(parent->peer);
|
||||
if (free_parent)
|
||||
child = rcu_dereference_protected(parent->bit[!(node->parent_bit_packed & 1)],
|
||||
lockdep_is_held(lock));
|
||||
call_rcu(&node->rcu, node_free_rcu);
|
||||
if (!free_parent)
|
||||
return;
|
||||
if (child)
|
||||
child->parent_bit_packed = parent->parent_bit_packed;
|
||||
*(struct allowedips_node **)(parent->parent_bit_packed & ~3UL) = child;
|
||||
call_rcu(&parent->rcu, node_free_rcu);
|
||||
}
|
||||
|
||||
static int remove(struct allowedips_node __rcu **trie, u8 bits, const u8 *key,
|
||||
u8 cidr, struct wg_peer *peer, struct mutex *lock)
|
||||
{
|
||||
struct allowedips_node *node;
|
||||
|
||||
if (unlikely(cidr > bits))
|
||||
return -EINVAL;
|
||||
if (!rcu_access_pointer(*trie) || !node_placement(*trie, key, cidr, bits, &node, lock) ||
|
||||
peer != rcu_access_pointer(node->peer))
|
||||
return 0;
|
||||
|
||||
remove_node(node, lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wg_allowedips_init(struct allowedips *table)
|
||||
{
|
||||
table->root4 = table->root6 = NULL;
|
||||
@@ -300,44 +346,38 @@ int wg_allowedips_insert_v6(struct allowedips *table, const struct in6_addr *ip,
|
||||
return add(&table->root6, 128, key, cidr, peer, lock);
|
||||
}
|
||||
|
||||
int wg_allowedips_remove_v4(struct allowedips *table, const struct in_addr *ip,
|
||||
u8 cidr, struct wg_peer *peer, struct mutex *lock)
|
||||
{
|
||||
/* Aligned so it can be passed to fls */
|
||||
u8 key[4] __aligned(__alignof(u32));
|
||||
|
||||
++table->seq;
|
||||
swap_endian(key, (const u8 *)ip, 32);
|
||||
return remove(&table->root4, 32, key, cidr, peer, lock);
|
||||
}
|
||||
|
||||
int wg_allowedips_remove_v6(struct allowedips *table, const struct in6_addr *ip,
|
||||
u8 cidr, struct wg_peer *peer, struct mutex *lock)
|
||||
{
|
||||
/* Aligned so it can be passed to fls64 */
|
||||
u8 key[16] __aligned(__alignof(u64));
|
||||
|
||||
++table->seq;
|
||||
swap_endian(key, (const u8 *)ip, 128);
|
||||
return remove(&table->root6, 128, key, cidr, peer, lock);
|
||||
}
|
||||
|
||||
void wg_allowedips_remove_by_peer(struct allowedips *table,
|
||||
struct wg_peer *peer, struct mutex *lock)
|
||||
{
|
||||
struct allowedips_node *node, *child, **parent_bit, *parent, *tmp;
|
||||
bool free_parent;
|
||||
struct allowedips_node *node, *tmp;
|
||||
|
||||
if (list_empty(&peer->allowedips_list))
|
||||
return;
|
||||
++table->seq;
|
||||
list_for_each_entry_safe(node, tmp, &peer->allowedips_list, peer_list) {
|
||||
list_del_init(&node->peer_list);
|
||||
RCU_INIT_POINTER(node->peer, NULL);
|
||||
if (node->bit[0] && node->bit[1])
|
||||
continue;
|
||||
child = rcu_dereference_protected(node->bit[!rcu_access_pointer(node->bit[0])],
|
||||
lockdep_is_held(lock));
|
||||
if (child)
|
||||
child->parent_bit_packed = node->parent_bit_packed;
|
||||
parent_bit = (struct allowedips_node **)(node->parent_bit_packed & ~3UL);
|
||||
*parent_bit = child;
|
||||
parent = (void *)parent_bit -
|
||||
offsetof(struct allowedips_node, bit[node->parent_bit_packed & 1]);
|
||||
free_parent = !rcu_access_pointer(node->bit[0]) &&
|
||||
!rcu_access_pointer(node->bit[1]) &&
|
||||
(node->parent_bit_packed & 3) <= 1 &&
|
||||
!rcu_access_pointer(parent->peer);
|
||||
if (free_parent)
|
||||
child = rcu_dereference_protected(
|
||||
parent->bit[!(node->parent_bit_packed & 1)],
|
||||
lockdep_is_held(lock));
|
||||
call_rcu(&node->rcu, node_free_rcu);
|
||||
if (!free_parent)
|
||||
continue;
|
||||
if (child)
|
||||
child->parent_bit_packed = parent->parent_bit_packed;
|
||||
*(struct allowedips_node **)(parent->parent_bit_packed & ~3UL) = child;
|
||||
call_rcu(&parent->rcu, node_free_rcu);
|
||||
}
|
||||
list_for_each_entry_safe(node, tmp, &peer->allowedips_list, peer_list)
|
||||
remove_node(node, lock);
|
||||
}
|
||||
|
||||
int wg_allowedips_read_node(struct allowedips_node *node, u8 ip[16], u8 *cidr)
|
||||
|
||||
@@ -38,6 +38,10 @@ int wg_allowedips_insert_v4(struct allowedips *table, const struct in_addr *ip,
|
||||
u8 cidr, struct wg_peer *peer, struct mutex *lock);
|
||||
int wg_allowedips_insert_v6(struct allowedips *table, const struct in6_addr *ip,
|
||||
u8 cidr, struct wg_peer *peer, struct mutex *lock);
|
||||
int wg_allowedips_remove_v4(struct allowedips *table, const struct in_addr *ip,
|
||||
u8 cidr, struct wg_peer *peer, struct mutex *lock);
|
||||
int wg_allowedips_remove_v6(struct allowedips *table, const struct in6_addr *ip,
|
||||
u8 cidr, struct wg_peer *peer, struct mutex *lock);
|
||||
void wg_allowedips_remove_by_peer(struct allowedips *table,
|
||||
struct wg_peer *peer, struct mutex *lock);
|
||||
/* The ip input pointer should be __aligned(__alignof(u64))) */
|
||||
|
||||
@@ -1273,4 +1273,71 @@ static inline void dev_sw_netstats_rx_add(struct net_device *dev, unsigned int l
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 91)
|
||||
#include <linux/timer.h>
|
||||
static inline int timer_delete(struct timer_list *timer)
|
||||
{
|
||||
return del_timer(timer);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 16, 0)
|
||||
#define timer_container_of from_timer
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0)
|
||||
#include <net/udp_tunnel.h>
|
||||
#define udp_tunnel_xmit_skb(a, b, c, d, e, f, g, h, i, j, k, l, m) udp_tunnel_xmit_skb(a, b, c, d, e, f, g, h, i, j, k, l)
|
||||
#define udp_tunnel6_xmit_skb(a, b, c, d, e, f, g, h, i, j, k, l, m) udp_tunnel6_xmit_skb(a, b, c, d, e, f, g, h, i, j, k, l)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0)
|
||||
#include <linux/in6.h>
|
||||
struct sockaddr_inet {
|
||||
unsigned short sa_family;
|
||||
char sa_data[sizeof(struct sockaddr_in6) -
|
||||
sizeof(unsigned short)];
|
||||
};
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 17, 0)
|
||||
#include <linux/netdevice.h>
|
||||
static inline void netif_threaded_enable(struct net_device *dev) { }
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 0, 0)
|
||||
#undef CONFIG_PM_USERSPACE_AUTOSLEEP
|
||||
#define CONFIG_PM_USERSPACE_AUTOSLEEP CONFIG_ANDROID
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)
|
||||
#define COMPAT_CANNOT_USE_PCPU_STAT_TYPE
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 15, 0)
|
||||
#define COMPAT_CANNOT_USE_RTNL_NEWLINK_PARAMS
|
||||
struct rtnl_newlink_params {
|
||||
struct net *src_net;
|
||||
struct net *link_net;
|
||||
struct net *peer_net;
|
||||
struct nlattr **tb;
|
||||
struct nlattr **data;
|
||||
};
|
||||
static inline struct net *rtnl_newlink_link_net(struct rtnl_newlink_params *p)
|
||||
{
|
||||
return p->link_net ? : p->src_net;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0)
|
||||
#include <linux/notifier.h>
|
||||
static inline int register_random_vmfork_notifier(struct notifier_block *nb) { return 0; }
|
||||
static inline int unregister_random_vmfork_notifier(struct notifier_block *nb) { return 0; }
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0)
|
||||
#include <linux/netdevice.h>
|
||||
static inline void netif_set_tso_max_size(struct net_device *dev, unsigned int size) {}
|
||||
#endif
|
||||
|
||||
#endif /* _WG_COMPAT_H */
|
||||
|
||||
@@ -26,8 +26,8 @@ void wg_cookie_checker_init(struct cookie_checker *checker,
|
||||
}
|
||||
|
||||
enum { COOKIE_KEY_LABEL_LEN = 8 };
|
||||
static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] = "mac1----";
|
||||
static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] = "cookie--";
|
||||
static const u8 mac1_key_label[COOKIE_KEY_LABEL_LEN] __nonstring = "mac1----";
|
||||
static const u8 cookie_key_label[COOKIE_KEY_LABEL_LEN] __nonstring = "cookie--";
|
||||
|
||||
static void precompute_key(u8 key[NOISE_SYMMETRIC_KEY_LEN],
|
||||
const u8 pubkey[NOISE_PUBLIC_KEY_LEN],
|
||||
|
||||
84
src/device.c
84
src/device.c
@@ -70,9 +70,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int wg_pm_notification(struct notifier_block *nb, unsigned long action,
|
||||
void *data)
|
||||
static int wg_pm_notification(struct notifier_block *nb, unsigned long action, void *data)
|
||||
{
|
||||
struct wg_device *wg;
|
||||
struct wg_peer *peer;
|
||||
@@ -81,7 +79,8 @@ static int wg_pm_notification(struct notifier_block *nb, unsigned long action,
|
||||
* its normal operation rather than as a somewhat rare event, then we
|
||||
* don't actually want to clear keys.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PM_AUTOSLEEP) || IS_ENABLED(CONFIG_ANDROID))
|
||||
if (IS_ENABLED(CONFIG_PM_AUTOSLEEP) ||
|
||||
IS_ENABLED(CONFIG_PM_USERSPACE_AUTOSLEEP))
|
||||
return 0;
|
||||
|
||||
if (action != PM_HIBERNATION_PREPARE && action != PM_SUSPEND_PREPARE)
|
||||
@@ -91,7 +90,7 @@ static int wg_pm_notification(struct notifier_block *nb, unsigned long action,
|
||||
list_for_each_entry(wg, &device_list, device_list) {
|
||||
mutex_lock(&wg->device_update_lock);
|
||||
list_for_each_entry(peer, &wg->peer_list, peer_list) {
|
||||
del_timer(&peer->timer_zero_key_material);
|
||||
timer_delete(&peer->timer_zero_key_material);
|
||||
wg_noise_handshake_clear(&peer->handshake);
|
||||
wg_noise_keypairs_clear(&peer->keypairs);
|
||||
}
|
||||
@@ -103,7 +102,24 @@ static int wg_pm_notification(struct notifier_block *nb, unsigned long action,
|
||||
}
|
||||
|
||||
static struct notifier_block pm_notifier = { .notifier_call = wg_pm_notification };
|
||||
#endif
|
||||
|
||||
static int wg_vm_notification(struct notifier_block *nb, unsigned long action, void *data)
|
||||
{
|
||||
struct wg_device *wg;
|
||||
struct wg_peer *peer;
|
||||
|
||||
rtnl_lock();
|
||||
list_for_each_entry(wg, &device_list, device_list) {
|
||||
mutex_lock(&wg->device_update_lock);
|
||||
list_for_each_entry(peer, &wg->peer_list, peer_list)
|
||||
wg_noise_expire_current_peer_keypairs(peer);
|
||||
mutex_unlock(&wg->device_update_lock);
|
||||
}
|
||||
rtnl_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct notifier_block vm_notifier = { .notifier_call = wg_vm_notification };
|
||||
|
||||
static int wg_stop(struct net_device *dev)
|
||||
{
|
||||
@@ -230,7 +246,9 @@ static const struct net_device_ops netdev_ops = {
|
||||
.ndo_open = wg_open,
|
||||
.ndo_stop = wg_stop,
|
||||
.ndo_start_xmit = wg_xmit,
|
||||
#ifndef COMPAT_CANNOT_USE_PCPU_STAT_TYPE
|
||||
.ndo_get_stats64 = dev_get_tstats64
|
||||
#endif
|
||||
};
|
||||
|
||||
static void wg_destruct(struct net_device *dev)
|
||||
@@ -259,7 +277,9 @@ static void wg_destruct(struct net_device *dev)
|
||||
rcu_barrier(); /* Wait for all the peers to be actually freed. */
|
||||
wg_ratelimiter_uninit();
|
||||
memzero_explicit(&wg->static_identity, sizeof(wg->static_identity));
|
||||
#ifdef COMPAT_CANNOT_USE_PCPU_STAT_TYPE
|
||||
free_percpu(dev->tstats);
|
||||
#endif
|
||||
kvfree(wg->index_hashtable);
|
||||
kvfree(wg->peer_hashtable);
|
||||
mutex_unlock(&wg->device_update_lock);
|
||||
@@ -304,12 +324,17 @@ static void wg_setup(struct net_device *dev)
|
||||
#ifndef COMPAT_CANNOT_USE_MAX_MTU
|
||||
dev->max_mtu = round_down(INT_MAX, MESSAGE_PADDING_MULTIPLE) - overhead;
|
||||
#endif
|
||||
#ifndef COMPAT_CANNOT_USE_PCPU_STAT_TYPE
|
||||
dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS;
|
||||
#endif
|
||||
|
||||
SET_NETDEV_DEVTYPE(dev, &device_type);
|
||||
|
||||
/* We need to keep the dst around in case of icmp replies. */
|
||||
netif_keep_dst(dev);
|
||||
|
||||
netif_set_tso_max_size(dev, GSO_MAX_SIZE);
|
||||
|
||||
memset(wg, 0, sizeof(*wg));
|
||||
wg->dev = dev;
|
||||
|
||||
@@ -331,14 +356,15 @@ static void wg_setup(struct net_device *dev)
|
||||
};
|
||||
}
|
||||
|
||||
static int wg_newlink(struct net *src_net, struct net_device *dev,
|
||||
struct nlattr *tb[], struct nlattr *data[],
|
||||
static int wg_newlink(struct net_device *dev,
|
||||
struct rtnl_newlink_params *params,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct net *link_net = rtnl_newlink_link_net(params);
|
||||
struct wg_device *wg = netdev_priv(dev);
|
||||
int ret = -ENOMEM;
|
||||
|
||||
rcu_assign_pointer(wg->creating_net, src_net);
|
||||
rcu_assign_pointer(wg->creating_net, link_net);
|
||||
init_rwsem(&wg->static_identity.lock);
|
||||
mutex_init(&wg->socket_update_lock);
|
||||
mutex_init(&wg->device_update_lock);
|
||||
@@ -355,9 +381,11 @@ static int wg_newlink(struct net *src_net, struct net_device *dev,
|
||||
if (!wg->index_hashtable)
|
||||
goto err_free_peer_hashtable;
|
||||
|
||||
#ifdef COMPAT_CANNOT_USE_PCPU_STAT_TYPE
|
||||
dev->tstats = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats);
|
||||
if (!dev->tstats)
|
||||
goto err_free_index_hashtable;
|
||||
#endif
|
||||
|
||||
wg->handshake_receive_wq = alloc_workqueue("wg-kex-%s",
|
||||
WQ_CPU_INTENSIVE | WQ_FREEZABLE, 0, dev->name);
|
||||
@@ -393,6 +421,7 @@ static int wg_newlink(struct net *src_net, struct net_device *dev,
|
||||
if (ret < 0)
|
||||
goto err_free_handshake_queue;
|
||||
|
||||
netif_threaded_enable(dev);
|
||||
ret = register_netdevice(dev);
|
||||
if (ret < 0)
|
||||
goto err_uninit_ratelimiter;
|
||||
@@ -422,19 +451,41 @@ err_destroy_handshake_send:
|
||||
err_destroy_handshake_receive:
|
||||
destroy_workqueue(wg->handshake_receive_wq);
|
||||
err_free_tstats:
|
||||
#ifdef COMPAT_CANNOT_USE_PCPU_STAT_TYPE
|
||||
free_percpu(dev->tstats);
|
||||
err_free_index_hashtable:
|
||||
#endif
|
||||
kvfree(wg->index_hashtable);
|
||||
err_free_peer_hashtable:
|
||||
kvfree(wg->peer_hashtable);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef COMPAT_CANNOT_USE_RTNL_NEWLINK_PARAMS
|
||||
static int wg_newlink_old(struct net *src_net, struct net_device *dev,
|
||||
struct nlattr *tb[], struct nlattr *data[],
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct rtnl_newlink_params params = {
|
||||
.src_net = src_net,
|
||||
.link_net = NULL,
|
||||
.peer_net = NULL,
|
||||
.tb = tb,
|
||||
.data = data,
|
||||
};
|
||||
return wg_newlink(dev, ¶ms, extack);
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct rtnl_link_ops link_ops __read_mostly = {
|
||||
.kind = KBUILD_MODNAME,
|
||||
.priv_size = sizeof(struct wg_device),
|
||||
.setup = wg_setup,
|
||||
#ifndef COMPAT_CANNOT_USE_RTNL_NEWLINK_PARAMS
|
||||
.newlink = wg_newlink,
|
||||
#else
|
||||
.newlink = wg_newlink_old,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void wg_netns_pre_exit(struct net *net)
|
||||
@@ -466,15 +517,17 @@ int __init wg_device_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
ret = register_pm_notifier(&pm_notifier);
|
||||
if (ret)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
ret = register_random_vmfork_notifier(&vm_notifier);
|
||||
if (ret)
|
||||
goto error_pm;
|
||||
|
||||
ret = register_pernet_device(&pernet_ops);
|
||||
if (ret)
|
||||
goto error_pm;
|
||||
goto error_vm;
|
||||
|
||||
ret = rtnl_link_register(&link_ops);
|
||||
if (ret)
|
||||
@@ -484,10 +537,10 @@ int __init wg_device_init(void)
|
||||
|
||||
error_pernet:
|
||||
unregister_pernet_device(&pernet_ops);
|
||||
error_vm:
|
||||
unregister_random_vmfork_notifier(&vm_notifier);
|
||||
error_pm:
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
unregister_pm_notifier(&pm_notifier);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -495,9 +548,8 @@ void wg_device_uninit(void)
|
||||
{
|
||||
rtnl_link_unregister(&link_ops);
|
||||
unregister_pernet_device(&pernet_ops);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
unregister_random_vmfork_notifier(&vm_notifier);
|
||||
unregister_pm_notifier(&pm_notifier);
|
||||
#endif
|
||||
rcu_barrier();
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = {
|
||||
[WGDEVICE_A_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ - 1 },
|
||||
[WGDEVICE_A_PRIVATE_KEY] = NLA_POLICY_EXACT_LEN(NOISE_PUBLIC_KEY_LEN),
|
||||
[WGDEVICE_A_PUBLIC_KEY] = NLA_POLICY_EXACT_LEN(NOISE_PUBLIC_KEY_LEN),
|
||||
[WGDEVICE_A_FLAGS] = { .type = NLA_U32 },
|
||||
[WGDEVICE_A_FLAGS] = NLA_POLICY_MASK(NLA_U32, __WGDEVICE_F_ALL),
|
||||
[WGDEVICE_A_LISTEN_PORT] = { .type = NLA_U16 },
|
||||
[WGDEVICE_A_FWMARK] = { .type = NLA_U32 },
|
||||
[WGDEVICE_A_PEERS] = { .type = NLA_NESTED },
|
||||
@@ -64,7 +64,7 @@ static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = {
|
||||
static const struct nla_policy peer_policy[WGPEER_A_MAX + 1] = {
|
||||
[WGPEER_A_PUBLIC_KEY] = NLA_POLICY_EXACT_LEN(NOISE_PUBLIC_KEY_LEN),
|
||||
[WGPEER_A_PRESHARED_KEY] = NLA_POLICY_EXACT_LEN(NOISE_SYMMETRIC_KEY_LEN),
|
||||
[WGPEER_A_FLAGS] = { .type = NLA_U32 },
|
||||
[WGPEER_A_FLAGS] = NLA_POLICY_MASK(NLA_U32, __WGPEER_F_ALL),
|
||||
[WGPEER_A_ENDPOINT] = NLA_POLICY_MIN_LEN(sizeof(struct sockaddr)),
|
||||
[WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL] = { .type = NLA_U16 },
|
||||
[WGPEER_A_LAST_HANDSHAKE_TIME] = NLA_POLICY_EXACT_LEN(sizeof(struct __kernel_timespec)),
|
||||
@@ -78,7 +78,8 @@ static const struct nla_policy peer_policy[WGPEER_A_MAX + 1] = {
|
||||
static const struct nla_policy allowedip_policy[WGALLOWEDIP_A_MAX + 1] = {
|
||||
[WGALLOWEDIP_A_FAMILY] = { .type = NLA_U16 },
|
||||
[WGALLOWEDIP_A_IPADDR] = NLA_POLICY_MIN_LEN(sizeof(struct in_addr)),
|
||||
[WGALLOWEDIP_A_CIDR_MASK] = { .type = NLA_U8 }
|
||||
[WGALLOWEDIP_A_CIDR_MASK] = { .type = NLA_U8 },
|
||||
[WGALLOWEDIP_A_FLAGS] = NLA_POLICY_MASK(NLA_U32, __WGALLOWEDIP_F_ALL),
|
||||
};
|
||||
|
||||
static struct wg_device *lookup_interface(struct nlattr **attrs,
|
||||
@@ -481,17 +482,17 @@ static int wg_get_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
|
||||
if (!peers_nest)
|
||||
goto out;
|
||||
ret = 0;
|
||||
/* If the last cursor was removed via list_del_init in peer_remove, then
|
||||
lockdep_assert_held(&wg->device_update_lock);
|
||||
/* If the last cursor was removed in peer_remove or peer_remove_all, then
|
||||
* we just treat this the same as there being no more peers left. The
|
||||
* reason is that seq_nr should indicate to userspace that this isn't a
|
||||
* coherent dump anyway, so they'll try again.
|
||||
*/
|
||||
if (list_empty(&wg->peer_list) ||
|
||||
(ctx->next_peer && list_empty(&ctx->next_peer->peer_list))) {
|
||||
(ctx->next_peer && ctx->next_peer->is_dead)) {
|
||||
nla_nest_cancel(skb, peers_nest);
|
||||
goto out;
|
||||
}
|
||||
lockdep_assert_held(&wg->device_update_lock);
|
||||
peer = list_prepare_entry(ctx->next_peer, &wg->peer_list, peer_list);
|
||||
list_for_each_entry_continue(peer, &wg->peer_list, peer_list) {
|
||||
if (get_peer(peer, skb, ctx)) {
|
||||
@@ -555,6 +556,7 @@ static int set_port(struct wg_device *wg, u16 port)
|
||||
static int set_allowedip(struct wg_peer *peer, struct nlattr **attrs)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
u32 flags = 0;
|
||||
u16 family;
|
||||
u8 cidr;
|
||||
|
||||
@@ -563,19 +565,30 @@ static int set_allowedip(struct wg_peer *peer, struct nlattr **attrs)
|
||||
return ret;
|
||||
family = nla_get_u16(attrs[WGALLOWEDIP_A_FAMILY]);
|
||||
cidr = nla_get_u8(attrs[WGALLOWEDIP_A_CIDR_MASK]);
|
||||
if (attrs[WGALLOWEDIP_A_FLAGS])
|
||||
flags = nla_get_u32(attrs[WGALLOWEDIP_A_FLAGS]);
|
||||
|
||||
if (family == AF_INET && cidr <= 32 &&
|
||||
nla_len(attrs[WGALLOWEDIP_A_IPADDR]) == sizeof(struct in_addr))
|
||||
ret = wg_allowedips_insert_v4(
|
||||
&peer->device->peer_allowedips,
|
||||
nla_data(attrs[WGALLOWEDIP_A_IPADDR]), cidr, peer,
|
||||
&peer->device->device_update_lock);
|
||||
else if (family == AF_INET6 && cidr <= 128 &&
|
||||
nla_len(attrs[WGALLOWEDIP_A_IPADDR]) == sizeof(struct in6_addr))
|
||||
ret = wg_allowedips_insert_v6(
|
||||
&peer->device->peer_allowedips,
|
||||
nla_data(attrs[WGALLOWEDIP_A_IPADDR]), cidr, peer,
|
||||
&peer->device->device_update_lock);
|
||||
nla_len(attrs[WGALLOWEDIP_A_IPADDR]) == sizeof(struct in_addr)) {
|
||||
if (flags & WGALLOWEDIP_F_REMOVE_ME)
|
||||
ret = wg_allowedips_remove_v4(&peer->device->peer_allowedips,
|
||||
nla_data(attrs[WGALLOWEDIP_A_IPADDR]), cidr,
|
||||
peer, &peer->device->device_update_lock);
|
||||
else
|
||||
ret = wg_allowedips_insert_v4(&peer->device->peer_allowedips,
|
||||
nla_data(attrs[WGALLOWEDIP_A_IPADDR]), cidr,
|
||||
peer, &peer->device->device_update_lock);
|
||||
} else if (family == AF_INET6 && cidr <= 128 &&
|
||||
nla_len(attrs[WGALLOWEDIP_A_IPADDR]) == sizeof(struct in6_addr)) {
|
||||
if (flags & WGALLOWEDIP_F_REMOVE_ME)
|
||||
ret = wg_allowedips_remove_v6(&peer->device->peer_allowedips,
|
||||
nla_data(attrs[WGALLOWEDIP_A_IPADDR]), cidr,
|
||||
peer, &peer->device->device_update_lock);
|
||||
else
|
||||
ret = wg_allowedips_insert_v6(&peer->device->peer_allowedips,
|
||||
nla_data(attrs[WGALLOWEDIP_A_IPADDR]), cidr,
|
||||
peer, &peer->device->device_update_lock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -599,9 +612,6 @@ static int set_peer(struct wg_device *wg, struct nlattr **attrs)
|
||||
|
||||
if (attrs[WGPEER_A_FLAGS])
|
||||
flags = nla_get_u32(attrs[WGPEER_A_FLAGS]);
|
||||
ret = -EOPNOTSUPP;
|
||||
if (flags & ~__WGPEER_F_ALL)
|
||||
goto out;
|
||||
|
||||
ret = -EPFNOSUPPORT;
|
||||
if (attrs[WGPEER_A_PROTOCOL_VERSION]) {
|
||||
@@ -662,14 +672,13 @@ static int set_peer(struct wg_device *wg, struct nlattr **attrs)
|
||||
if (attrs[WGPEER_A_ENDPOINT]) {
|
||||
struct sockaddr *addr = nla_data(attrs[WGPEER_A_ENDPOINT]);
|
||||
size_t len = nla_len(attrs[WGPEER_A_ENDPOINT]);
|
||||
struct endpoint endpoint = { { { 0 } } };
|
||||
|
||||
if ((len == sizeof(struct sockaddr_in) &&
|
||||
addr->sa_family == AF_INET) ||
|
||||
(len == sizeof(struct sockaddr_in6) &&
|
||||
addr->sa_family == AF_INET6)) {
|
||||
struct endpoint endpoint = { { { 0 } } };
|
||||
|
||||
memcpy(&endpoint.addr, addr, len);
|
||||
if (len == sizeof(struct sockaddr_in) && addr->sa_family == AF_INET) {
|
||||
endpoint.addr4 = *(struct sockaddr_in *)addr;
|
||||
wg_socket_set_peer_endpoint(peer, &endpoint);
|
||||
} else if (len == sizeof(struct sockaddr_in6) && addr->sa_family == AF_INET6) {
|
||||
endpoint.addr6 = *(struct sockaddr_in6 *)addr;
|
||||
wg_socket_set_peer_endpoint(peer, &endpoint);
|
||||
}
|
||||
}
|
||||
@@ -739,9 +748,6 @@ static int wg_set_device(struct sk_buff *skb, struct genl_info *info)
|
||||
|
||||
if (info->attrs[WGDEVICE_A_FLAGS])
|
||||
flags = nla_get_u32(info->attrs[WGDEVICE_A_FLAGS]);
|
||||
ret = -EOPNOTSUPP;
|
||||
if (flags & ~__WGDEVICE_F_ALL)
|
||||
goto out;
|
||||
|
||||
if (info->attrs[WGDEVICE_A_LISTEN_PORT] || info->attrs[WGDEVICE_A_FWMARK]) {
|
||||
struct net *net;
|
||||
@@ -923,6 +929,7 @@ skip_set_private_key:
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&wg->device_update_lock);
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
* <- e, ee, se, psk, {}
|
||||
*/
|
||||
|
||||
static const u8 handshake_name[37] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
|
||||
static const u8 identifier_name[34] = "WireGuard v1 zx2c4 Jason@zx2c4.com";
|
||||
static const u8 handshake_name[37] __nonstring = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s";
|
||||
static const u8 identifier_name[34] __nonstring = "WireGuard v1 zx2c4 Jason@zx2c4.com";
|
||||
static u8 handshake_init_hash[NOISE_HASH_LEN] __ro_after_init;
|
||||
static u8 handshake_init_chaining_key[NOISE_HASH_LEN] __ro_after_init;
|
||||
static atomic64_t keypair_counter = ATOMIC64_INIT(0);
|
||||
@@ -361,7 +361,7 @@ static void kdf(u8 *first_dst, u8 *second_dst, u8 *third_dst, const u8 *data,
|
||||
|
||||
/* Extract entropy from data into secret */
|
||||
hmac(secret, data, chaining_key, data_len, NOISE_HASH_LEN);
|
||||
|
||||
|
||||
if (!first_dst || !first_len)
|
||||
goto out;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ struct wg_device;
|
||||
|
||||
struct endpoint {
|
||||
union {
|
||||
struct sockaddr addr;
|
||||
struct sockaddr_inet addr; /* Large enough for both address families */
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
};
|
||||
|
||||
@@ -300,9 +300,9 @@ static bool decrypt_packet(struct sk_buff *skb, struct noise_keypair *keypair,
|
||||
return false;
|
||||
|
||||
if (!chacha20poly1305_decrypt_sg_inplace(sg, skb->len, NULL, 0,
|
||||
PACKET_CB(skb)->nonce,
|
||||
keypair->receiving.key
|
||||
COMPAT_MAYBE_SIMD_CONTEXT(simd_context)))
|
||||
PACKET_CB(skb)->nonce,
|
||||
keypair->receiving.key
|
||||
COMPAT_MAYBE_SIMD_CONTEXT(simd_context)))
|
||||
return false;
|
||||
|
||||
/* Another ugly situation of pushing and pulling the header so as to
|
||||
@@ -532,6 +532,8 @@ void wg_packet_decrypt_worker(struct work_struct *work)
|
||||
PACKET_STATE_CRYPTED : PACKET_STATE_DEAD;
|
||||
wg_queue_enqueue_per_peer_rx(skb, state);
|
||||
simd_relax(&simd_context);
|
||||
if (need_resched())
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
simd_put(&simd_context);
|
||||
@@ -590,16 +592,16 @@ void wg_packet_receive(struct wg_device *wg, struct sk_buff *skb)
|
||||
} else
|
||||
ret = ptr_ring_produce_bh(&wg->handshake_queue.ring, skb);
|
||||
if (ret) {
|
||||
drop:
|
||||
drop:
|
||||
net_dbg_skb_ratelimited("%s: Dropping handshake packet from %pISpfsc\n",
|
||||
wg->dev->name, skb);
|
||||
wg->dev->name, skb);
|
||||
goto err;
|
||||
}
|
||||
atomic_inc(&wg->handshake_queue_len);
|
||||
cpu = wg_cpumask_next_online(&wg->handshake_queue.last_cpu);
|
||||
/* Queues up a call to packet_process_queued_handshake_packets(skb): */
|
||||
queue_work_on(cpu, wg->handshake_receive_wq,
|
||||
&per_cpu_ptr(wg->handshake_queue.worker, cpu)->work);
|
||||
&per_cpu_ptr(wg->handshake_queue.worker, cpu)->work);
|
||||
} else if (mh_validate(SKB_TYPE_LE32(skb), &wg->headers[MSGIDX_TRANSPORT])) {
|
||||
PACKET_CB(skb)->ds = ip_tunnel_get_dsfield(ip_hdr(skb), skb);
|
||||
wg_packet_consume_data(wg, skb);
|
||||
|
||||
18
src/send.c
18
src/send.c
@@ -80,8 +80,8 @@ static void wg_packet_send_handshake_initiation(struct wg_peer *peer)
|
||||
wg_timers_any_authenticated_packet_sent(peer);
|
||||
atomic64_set(&peer->last_sent_handshake,
|
||||
ktime_get_coarse_boottime_ns());
|
||||
|
||||
wg_socket_send_buffer_to_peer(peer, &packet, sizeof(packet), HANDSHAKE_DSCP, wg->junk_size[MSGIDX_HANDSHAKE_INIT]);
|
||||
wg_socket_send_buffer_to_peer(peer, &packet, sizeof(packet),
|
||||
HANDSHAKE_DSCP, wg->junk_size[MSGIDX_HANDSHAKE_INIT]);
|
||||
wg_timers_handshake_initiated(peer);
|
||||
}
|
||||
}
|
||||
@@ -144,7 +144,10 @@ void wg_packet_send_handshake_response(struct wg_peer *peer)
|
||||
wg_timers_any_authenticated_packet_sent(peer);
|
||||
atomic64_set(&peer->last_sent_handshake,
|
||||
ktime_get_coarse_boottime_ns());
|
||||
wg_socket_send_buffer_to_peer(peer, &packet, sizeof(packet), HANDSHAKE_DSCP, wg->junk_size[MSGIDX_HANDSHAKE_RESPONSE]);
|
||||
wg_socket_send_buffer_to_peer(peer, &packet,
|
||||
sizeof(packet),
|
||||
HANDSHAKE_DSCP,
|
||||
wg->junk_size[MSGIDX_HANDSHAKE_RESPONSE]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,9 +160,12 @@ void wg_packet_send_handshake_cookie(struct wg_device *wg,
|
||||
|
||||
net_dbg_skb_ratelimited("%s: Sending cookie response for denied handshake message for %pISpfsc\n",
|
||||
wg->dev->name, initiating_skb);
|
||||
|
||||
wg_cookie_message_create(&packet, initiating_skb, sender_index, &wg->cookie_checker, mh_genheader(&wg->headers[MSGIDX_HANDSHAKE_COOKIE]));
|
||||
wg_socket_send_buffer_as_reply_to_skb(wg, initiating_skb, &packet, sizeof(packet), wg->junk_size[MSGIDX_HANDSHAKE_COOKIE]);
|
||||
wg_cookie_message_create(&packet, initiating_skb, sender_index,
|
||||
&wg->cookie_checker,
|
||||
mh_genheader(&wg->headers[MSGIDX_HANDSHAKE_COOKIE]));
|
||||
wg_socket_send_buffer_as_reply_to_skb(wg, initiating_skb, &packet,
|
||||
sizeof(packet),
|
||||
wg->junk_size[MSGIDX_HANDSHAKE_COOKIE]);
|
||||
}
|
||||
|
||||
static void keep_key_fresh(struct wg_peer *peer)
|
||||
|
||||
@@ -84,7 +84,7 @@ static int send4(struct wg_device *wg, struct sk_buff *skb,
|
||||
skb->ignore_df = 1;
|
||||
udp_tunnel_xmit_skb(rt, sock, skb, fl.saddr, fl.daddr, ds,
|
||||
ip4_dst_hoplimit(&rt->dst), 0, fl.fl4_sport,
|
||||
fl.fl4_dport, false, false);
|
||||
fl.fl4_dport, false, false, 0);
|
||||
goto out;
|
||||
|
||||
err:
|
||||
@@ -151,7 +151,7 @@ static int send6(struct wg_device *wg, struct sk_buff *skb,
|
||||
skb->ignore_df = 1;
|
||||
udp_tunnel6_xmit_skb(dst, sock, skb, skb->dev, &fl.saddr, &fl.daddr, ds,
|
||||
ip6_dst_hoplimit(dst), 0, fl.fl6_sport,
|
||||
fl.fl6_dport, false);
|
||||
fl.fl6_dport, false, 0);
|
||||
goto out;
|
||||
|
||||
err:
|
||||
|
||||
25
src/timers.c
25
src/timers.c
@@ -40,15 +40,15 @@ static inline void mod_peer_timer(struct wg_peer *peer,
|
||||
|
||||
static void wg_expired_retransmit_handshake(struct timer_list *timer)
|
||||
{
|
||||
struct wg_peer *peer = from_timer(peer, timer,
|
||||
timer_retransmit_handshake);
|
||||
struct wg_peer *peer = timer_container_of(peer, timer,
|
||||
timer_retransmit_handshake);
|
||||
|
||||
if (peer->timer_handshake_attempts > MAX_TIMER_HANDSHAKES) {
|
||||
pr_debug("%s: Handshake for peer %llu (%pISpfsc) did not complete after %d attempts, giving up\n",
|
||||
peer->device->dev->name, peer->internal_id,
|
||||
&peer->endpoint.addr, (int)MAX_TIMER_HANDSHAKES + 2);
|
||||
|
||||
del_timer(&peer->timer_send_keepalive);
|
||||
timer_delete(&peer->timer_send_keepalive);
|
||||
/* We drop all packets without a keypair and don't try again,
|
||||
* if we try unsuccessfully for too long to make a handshake.
|
||||
*/
|
||||
@@ -78,7 +78,8 @@ static void wg_expired_retransmit_handshake(struct timer_list *timer)
|
||||
|
||||
static void wg_expired_send_keepalive(struct timer_list *timer)
|
||||
{
|
||||
struct wg_peer *peer = from_timer(peer, timer, timer_send_keepalive);
|
||||
struct wg_peer *peer = timer_container_of(peer, timer,
|
||||
timer_send_keepalive);
|
||||
|
||||
wg_packet_send_keepalive(peer);
|
||||
if (peer->timer_need_another_keepalive) {
|
||||
@@ -90,7 +91,8 @@ static void wg_expired_send_keepalive(struct timer_list *timer)
|
||||
|
||||
static void wg_expired_new_handshake(struct timer_list *timer)
|
||||
{
|
||||
struct wg_peer *peer = from_timer(peer, timer, timer_new_handshake);
|
||||
struct wg_peer *peer = timer_container_of(peer, timer,
|
||||
timer_new_handshake);
|
||||
|
||||
pr_debug("%s: Retrying handshake with peer %llu (%pISpfsc) because we stopped hearing back after %d seconds\n",
|
||||
peer->device->dev->name, peer->internal_id,
|
||||
@@ -104,7 +106,8 @@ static void wg_expired_new_handshake(struct timer_list *timer)
|
||||
|
||||
static void wg_expired_zero_key_material(struct timer_list *timer)
|
||||
{
|
||||
struct wg_peer *peer = from_timer(peer, timer, timer_zero_key_material);
|
||||
struct wg_peer *peer = timer_container_of(peer, timer,
|
||||
timer_zero_key_material);
|
||||
|
||||
rcu_read_lock_bh();
|
||||
if (!READ_ONCE(peer->is_dead)) {
|
||||
@@ -134,8 +137,8 @@ static void wg_queued_expired_zero_key_material(struct work_struct *work)
|
||||
|
||||
static void wg_expired_send_persistent_keepalive(struct timer_list *timer)
|
||||
{
|
||||
struct wg_peer *peer = from_timer(peer, timer,
|
||||
timer_persistent_keepalive);
|
||||
struct wg_peer *peer = timer_container_of(peer, timer,
|
||||
timer_persistent_keepalive);
|
||||
|
||||
if (likely(peer->persistent_keepalive_interval))
|
||||
wg_packet_send_keepalive(peer);
|
||||
@@ -167,7 +170,7 @@ void wg_timers_data_received(struct wg_peer *peer)
|
||||
*/
|
||||
void wg_timers_any_authenticated_packet_sent(struct wg_peer *peer)
|
||||
{
|
||||
del_timer(&peer->timer_send_keepalive);
|
||||
timer_delete(&peer->timer_send_keepalive);
|
||||
}
|
||||
|
||||
/* Should be called after any type of authenticated packet is received, whether
|
||||
@@ -175,7 +178,7 @@ void wg_timers_any_authenticated_packet_sent(struct wg_peer *peer)
|
||||
*/
|
||||
void wg_timers_any_authenticated_packet_received(struct wg_peer *peer)
|
||||
{
|
||||
del_timer(&peer->timer_new_handshake);
|
||||
timer_delete(&peer->timer_new_handshake);
|
||||
}
|
||||
|
||||
/* Should be called after a handshake initiation message is sent. */
|
||||
@@ -191,7 +194,7 @@ void wg_timers_handshake_initiated(struct wg_peer *peer)
|
||||
*/
|
||||
void wg_timers_handshake_complete(struct wg_peer *peer)
|
||||
{
|
||||
del_timer(&peer->timer_retransmit_handshake);
|
||||
timer_delete(&peer->timer_retransmit_handshake);
|
||||
peer->timer_handshake_attempts = 0;
|
||||
peer->sent_lastminute_handshake = false;
|
||||
ktime_get_real_ts64(&peer->walltime_last_handshake);
|
||||
|
||||
@@ -101,6 +101,10 @@
|
||||
* WGALLOWEDIP_A_FAMILY: NLA_U16
|
||||
* WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr
|
||||
* WGALLOWEDIP_A_CIDR_MASK: NLA_U8
|
||||
* WGALLOWEDIP_A_FLAGS: NLA_U32, WGALLOWEDIP_F_REMOVE_ME if
|
||||
* the specified IP should be removed;
|
||||
* otherwise, this IP will be added if
|
||||
* it is not already present.
|
||||
* 0: NLA_NESTED
|
||||
* ...
|
||||
* 0: NLA_NESTED
|
||||
@@ -228,11 +232,16 @@ enum wgpeer_attribute {
|
||||
};
|
||||
#define WGPEER_A_MAX (__WGPEER_A_LAST - 1)
|
||||
|
||||
enum wgallowedip_flag {
|
||||
WGALLOWEDIP_F_REMOVE_ME = 1U << 0,
|
||||
__WGALLOWEDIP_F_ALL = WGALLOWEDIP_F_REMOVE_ME
|
||||
};
|
||||
enum wgallowedip_attribute {
|
||||
WGALLOWEDIP_A_UNSPEC,
|
||||
WGALLOWEDIP_A_FAMILY,
|
||||
WGALLOWEDIP_A_IPADDR,
|
||||
WGALLOWEDIP_A_CIDR_MASK,
|
||||
WGALLOWEDIP_A_FLAGS,
|
||||
__WGALLOWEDIP_A_LAST
|
||||
};
|
||||
#define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1)
|
||||
|
||||
Reference in New Issue
Block a user