aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/src/peer.c
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2015-06-05 15:58:00 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2016-06-25 16:48:39 +0200
commit99d303ac2739e65a02fbbc325b74ad6fcac63cc2 (patch)
tree6f4095f42d3d298cdd5ab8bc6f8ed89d9673b38b /src/peer.c
downloadwireguard-monolithic-historical-99d303ac2739e65a02fbbc325b74ad6fcac63cc2.tar.xz
wireguard-monolithic-historical-99d303ac2739e65a02fbbc325b74ad6fcac63cc2.zip
Initial commit
Diffstat (limited to 'src/peer.c')
-rw-r--r--src/peer.c144
1 files changed, 144 insertions, 0 deletions
diff --git a/src/peer.c b/src/peer.c
new file mode 100644
index 0000000..ad48a4e
--- /dev/null
+++ b/src/peer.c
@@ -0,0 +1,144 @@
+/* Copyright 2015-2016 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */
+
+#include "wireguard.h"
+#include "peer.h"
+#include "packets.h"
+#include "timers.h"
+#include "hashtables.h"
+#include "noise.h"
+#include <linux/kref.h>
+#include <linux/lockdep.h>
+#include <linux/rcupdate.h>
+#include <linux/list.h>
+
+static atomic64_t peer_counter = ATOMIC64_INIT(0);
+
+struct wireguard_peer *peer_create(struct wireguard_device *wg, const u8 public_key[NOISE_PUBLIC_KEY_LEN])
+{
+ struct wireguard_peer *peer;
+ lockdep_assert_held(&wg->device_update_lock);
+
+ if (peer_total_count(wg) >= MAX_PEERS_PER_DEVICE)
+ return NULL;
+
+ peer = kzalloc(sizeof(struct wireguard_peer), GFP_KERNEL);
+ if (!peer)
+ return NULL;
+
+ peer->internal_id = atomic64_inc_return(&peer_counter);
+ peer->device = wg;
+ cookie_init(&peer->latest_cookie);
+ noise_handshake_init(&peer->handshake, &wg->static_identity, public_key, peer);
+ mutex_init(&peer->keypairs.keypair_update_lock);
+ INIT_WORK(&peer->transmit_handshake_work, packet_send_queued_handshakes);
+ rwlock_init(&peer->endpoint_lock);
+ skb_queue_head_init(&peer->tx_packet_queue);
+ kref_init(&peer->refcount);
+ pubkey_hashtable_add(&wg->peer_hashtable, peer);
+ list_add_tail(&peer->peer_list, &wg->peer_list);
+ pr_debug("Peer %Lu created\n", peer->internal_id);
+ return peer;
+}
+
+struct wireguard_peer *peer_get(struct wireguard_peer *peer)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
+ RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "Calling peer_get without holding the RCU read lock.");
+#else
+ rcu_lockdep_assert(rcu_read_lock_held(), "Calling peer_get without holding the RCU read lock.");
+#endif
+ if (!peer)
+ return NULL;
+ if (!kref_get_unless_zero(&peer->refcount))
+ return NULL;
+ return peer;
+}
+
+void peer_remove(struct wireguard_peer *peer)
+{
+ if (!peer)
+ return;
+ lockdep_assert_held(&peer->device->device_update_lock);
+
+ list_del(&peer->peer_list);
+ noise_handshake_clear(&peer->handshake);
+ noise_keypairs_clear(&peer->keypairs);
+ routing_table_remove_by_peer(&peer->device->peer_routing_table, peer);
+ pubkey_hashtable_remove(&peer->device->peer_hashtable, peer);
+ if (peer->device->workqueue)
+ flush_workqueue(peer->device->workqueue);
+ skb_queue_purge(&peer->tx_packet_queue);
+ peer_put(peer);
+}
+
+static void rcu_release(struct rcu_head *rcu)
+{
+ struct wireguard_peer *peer = container_of(rcu, struct wireguard_peer, rcu);
+ pr_debug("Peer %Lu (%pISpfsc) destroyed\n", peer->internal_id, &peer->endpoint_addr);
+ timers_uninit_peer(peer);
+ skb_queue_purge(&peer->tx_packet_queue);
+ if (peer->endpoint_dst)
+ dst_release(peer->endpoint_dst);
+ memzero_explicit(peer, sizeof(struct wireguard_peer));
+ kfree(peer);
+}
+
+static void kref_release(struct kref *refcount)
+{
+ struct wireguard_peer *peer = container_of(refcount, struct wireguard_peer, refcount);
+ call_rcu(&peer->rcu, rcu_release);
+}
+
+void peer_put(struct wireguard_peer *peer)
+{
+ if (!peer)
+ return;
+ kref_put(&peer->refcount, kref_release);
+}
+
+int peer_for_each_unlocked(struct wireguard_device *wg, int (*fn)(struct wireguard_peer *peer, void *ctx), void *data)
+{
+ struct wireguard_peer *peer, *temp;
+ int ret = 0;
+
+ lockdep_assert_held(&wg->device_update_lock);
+ list_for_each_entry_safe(peer, temp, &wg->peer_list, peer_list) {
+ rcu_read_lock();
+ peer = peer_get(peer);
+ rcu_read_unlock();
+ if (unlikely(!peer))
+ continue;
+ ret = fn(peer, data);
+ peer_put(peer);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+int peer_for_each(struct wireguard_device *wg, int (*fn)(struct wireguard_peer *peer, void *ctx), void *data)
+{
+ int ret;
+ mutex_lock(&wg->device_update_lock);
+ ret = peer_for_each_unlocked(wg, fn, data);
+ mutex_unlock(&wg->device_update_lock);
+ return ret;
+}
+
+void peer_remove_all(struct wireguard_device *wg)
+{
+ struct wireguard_peer *peer, *temp;
+ lockdep_assert_held(&wg->device_update_lock);
+ list_for_each_entry_safe(peer, temp, &wg->peer_list, peer_list)
+ peer_remove(peer);
+}
+
+unsigned int peer_total_count(struct wireguard_device *wg)
+{
+ unsigned int i = 0;
+ struct wireguard_peer *peer;
+ lockdep_assert_held(&wg->device_update_lock);
+ list_for_each_entry(peer, &wg->peer_list, peer_list)
+ ++i;
+ return i;
+}