aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtem Glazychev <artem.glazychev@xored.com>2020-08-31 17:12:30 +0700
committerDamjan Marion <dmarion@me.com>2020-09-09 11:57:48 +0000
commitedca1325cf296bd0f5ff422fc12de2ce7a7bad88 (patch)
treefb12d12bd4193c5b2c7559d98aba9dc5d2f14e85
parentef80ad6bff03e3cc35950de0e15e4821ef3f7c04 (diff)
wireguard: initial implementation of wireguard protocol
Type: feature The main information about plugin you can see in README.md vpp# wireguard ? wireguard create wireguard create listen-port <port> private-key <key> src <IP> [generate-key] wireguard delete wireguard delete <interface> wireguard peer add wireguard peer add <wg_int> public-key <pub_key_other>endpoint <ip4_dst> allowed-ip <prefix>dst-port [port_dst] persistent-keepalive [keepalive_interval] wireguard peer remove wireguard peer remove <index> Change-Id: I85eb0bfc033ccfb2045696398d8a108b1c64b8d9 Signed-off-by: Artem Glazychev <artem.glazychev@xored.com> Signed-off-by: Damjan Marion <damarion@cisco.com> Signed-off-by: Jim Thompson <jim@netgate.com> Signed-off-by: Neale Ranns <nranns@cisco.com> Signed-off-by: Damjan Marion <damarion@cisco.com>
-rw-r--r--MAINTAINERS5
-rwxr-xr-xsrc/plugins/wireguard/CMakeLists.txt54
-rw-r--r--src/plugins/wireguard/FEATURE.yaml12
-rwxr-xr-xsrc/plugins/wireguard/README.md74
-rwxr-xr-xsrc/plugins/wireguard/blake/blake2-impl.h187
-rwxr-xr-xsrc/plugins/wireguard/blake/blake2s.c335
-rwxr-xr-xsrc/plugins/wireguard/blake/blake2s.h90
-rwxr-xr-xsrc/plugins/wireguard/test/test_wireguard.py292
-rwxr-xr-xsrc/plugins/wireguard/wireguard.api162
-rwxr-xr-xsrc/plugins/wireguard/wireguard.c63
-rwxr-xr-xsrc/plugins/wireguard/wireguard.h50
-rwxr-xr-xsrc/plugins/wireguard/wireguard_api.c278
-rwxr-xr-xsrc/plugins/wireguard/wireguard_cli.c358
-rwxr-xr-xsrc/plugins/wireguard/wireguard_cookie.c169
-rwxr-xr-xsrc/plugins/wireguard/wireguard_cookie.h101
-rw-r--r--src/plugins/wireguard/wireguard_if.c422
-rw-r--r--src/plugins/wireguard/wireguard_if.h94
-rwxr-xr-xsrc/plugins/wireguard/wireguard_index_table.c62
-rwxr-xr-xsrc/plugins/wireguard/wireguard_index_table.h39
-rwxr-xr-xsrc/plugins/wireguard/wireguard_input.c451
-rwxr-xr-xsrc/plugins/wireguard/wireguard_key.c126
-rwxr-xr-xsrc/plugins/wireguard/wireguard_key.h46
-rwxr-xr-xsrc/plugins/wireguard/wireguard_messages.h104
-rwxr-xr-xsrc/plugins/wireguard/wireguard_noise.c985
-rwxr-xr-xsrc/plugins/wireguard/wireguard_noise.h202
-rwxr-xr-xsrc/plugins/wireguard/wireguard_output_tun.c257
-rwxr-xr-xsrc/plugins/wireguard/wireguard_peer.c418
-rwxr-xr-xsrc/plugins/wireguard/wireguard_peer.h122
-rwxr-xr-xsrc/plugins/wireguard/wireguard_send.c225
-rwxr-xr-xsrc/plugins/wireguard/wireguard_send.h45
-rwxr-xr-xsrc/plugins/wireguard/wireguard_timer.c324
-rwxr-xr-xsrc/plugins/wireguard/wireguard_timer.h69
-rw-r--r--test/requirements-3.txt5
-rw-r--r--test/requirements.txt1
34 files changed, 6226 insertions, 1 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 03cd4791b34..e929020fe78 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -688,6 +688,11 @@ M: Nathan Skrzypczak <nathan.skrzypczak@gmail.com>
M: Neale Ranns <nranns@cisco.com>
F: src/plugins/cnat
+Plugin - Wireguard
+I: wireguard
+M: Artem Glazychev <artem.glazychev@xored.com>
+F: src/plugins/wireguard
+
VPP Config Tooling
I: vpp_config
M: John DeNisco <jdenisco@cisco.com>
diff --git a/src/plugins/wireguard/CMakeLists.txt b/src/plugins/wireguard/CMakeLists.txt
new file mode 100755
index 00000000000..db5bb2d8910
--- /dev/null
+++ b/src/plugins/wireguard/CMakeLists.txt
@@ -0,0 +1,54 @@
+
+# Copyright (c) 2020 Doc.ai and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+if (OPENSSL_VERSION VERSION_LESS 1.1.0)
+ return()
+endif()
+
+list(APPEND WG_BLAKE_SOURCES
+ blake/blake2s.h
+ blake/blake2s.c
+)
+
+add_vpp_plugin(wireguard
+ SOURCES
+ ${WG_BLAKE_SOURCES}
+ wireguard.c
+ wireguard.h
+ wireguard_if.c
+ wireguard_if.h
+ wireguard_input.c
+ wireguard_output_tun.c
+ wireguard_key.c
+ wireguard_key.h
+ wireguard_cli.c
+ wireguard_messages.h
+ wireguard_noise.c
+ wireguard_noise.h
+ wireguard_send.c
+ wireguard_send.h
+ wireguard_cookie.c
+ wireguard_cookie.h
+ wireguard_peer.c
+ wireguard_peer.h
+ wireguard_timer.c
+ wireguard_timer.h
+ wireguard_index_table.c
+ wireguard_index_table.h
+ wireguard_api.c
+
+ API_FILES
+ wireguard.api
+
+)
diff --git a/src/plugins/wireguard/FEATURE.yaml b/src/plugins/wireguard/FEATURE.yaml
new file mode 100644
index 00000000000..cf8b6d7f3c4
--- /dev/null
+++ b/src/plugins/wireguard/FEATURE.yaml
@@ -0,0 +1,12 @@
+---
+name: Wireguard protocol
+maintainer: Artem Glazychev <artem.glazychev@xored.com>
+features:
+ - "based on wireguard-openbsd implementation: https://git.zx2c4.com/wireguard-openbsd"
+ - creating secure VPN-tunnel
+description: "Wireguard protocol implementation"
+state: development
+properties: [API, CLI]
+missing:
+ - IPv6 support
+ - DoS protection as in the original protocol
diff --git a/src/plugins/wireguard/README.md b/src/plugins/wireguard/README.md
new file mode 100755
index 00000000000..a11356cfde2
--- /dev/null
+++ b/src/plugins/wireguard/README.md
@@ -0,0 +1,74 @@
+# Wireguard vpp-plugin
+
+## Overview
+This plugin is an implementation of [wireguard protocol](https://www.wireguard.com/) for VPP. It allows one to create secure VPN tunnels.
+This implementation is based on [wireguard-openbsd](https://git.zx2c4.com/wireguard-openbsd/), using the implementaiton of *ipip-tunnel*.
+
+## Crypto
+
+The crypto protocols:
+
+- blake2s [[Source]](https://github.com/BLAKE2/BLAKE2)
+
+OpenSSL:
+
+- curve25519
+- chachapoly1305
+
+## Plugin usage example
+Usage is very similar to other wireguard implementations.
+
+### Create connection
+Create keys:
+
+```
+> vpp# wg genkey
+> *my_private_key*
+> vpp# wg pubkey <my_private_key>
+> *my_pub_key*
+```
+
+Create tunnel:
+```
+> vpp# create ipip tunnel src <ip4_src> dst <ip4_dst>
+> *tun_name*
+> vpp# set int state <tun_name> up
+> vpp# set int ip address <tun_name> <tun_ip4>
+```
+
+After this we can create wg-device. The UDP port is opened automatically.
+```
+> vpp# wg set device private-key <my_private_key> src-port <my_port>
+```
+
+Now, we can add a peer configuration:
+```
+> vpp# wg set peer public-key <peer_pub_key> endpoint <peer_ip4> allowed-ip <peer_tun_ip4> dst-port <peer_port> tunnel <tun_name> persistent-keepalive <keepalive_interval>
+```
+If you need to add more peers, don't forget to first create another ipip-tunnel.
+Ping.
+```
+> vpp# ping <peer_tun_ip4>
+```
+### Show config
+To show device and all peer configurations:
+```
+> vpp# show wg
+```
+
+### Remove peer
+Peer can be removed by its public-key.
+```
+> vpp# wg remove peer <peer_pub_key>
+```
+This removes the associated ipip tunnel as well
+
+### Clear all connections
+```
+> vpp# wg remove device
+```
+
+## main next steps for improving this implementation
+1. Use all benefits of VPP-engine.
+2. Add IP6 support (currently only supports IPv4))
+3. Add DoS protection as in original protocol (using cookie)
diff --git a/src/plugins/wireguard/blake/blake2-impl.h b/src/plugins/wireguard/blake/blake2-impl.h
new file mode 100755
index 00000000000..ad60b4a5775
--- /dev/null
+++ b/src/plugins/wireguard/blake/blake2-impl.h
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Copyright (c) 2012 Samuel Neves <sneves@dei.uc.pt>.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ More information about the BLAKE2 hash function can be found at
+ https://blake2.net.
+*/
+#ifndef __included_crypto_blake2_impl_h__
+#define __included_crypto_blake2_impl_h__
+
+#include <stdint.h>
+#include <string.h>
+#include <vppinfra/byte_order.h>
+
+#if defined(CLIB_ARCH_IS_LITTLE_ENDIAN)
+#define NATIVE_LITTLE_ENDIAN
+#endif
+
+#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L)
+#if defined(_MSC_VER)
+#define BLAKE2_INLINE __inline
+#elif defined(__GNUC__)
+#define BLAKE2_INLINE __inline__
+#else
+#define BLAKE2_INLINE
+#endif
+#else
+#define BLAKE2_INLINE inline
+#endif
+
+static BLAKE2_INLINE uint32_t
+load32 (const void *src)
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint32_t w;
+ memcpy (&w, src, sizeof w);
+ return w;
+#else
+ const uint8_t *p = (const uint8_t *) src;
+ return ((uint32_t) (p[0]) << 0) |
+ ((uint32_t) (p[1]) << 8) |
+ ((uint32_t) (p[2]) << 16) | ((uint32_t) (p[3]) << 24);
+#endif
+}
+
+static BLAKE2_INLINE uint64_t
+load64 (const void *src)
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint64_t w;
+ memcpy (&w, src, sizeof w);
+ return w;
+#else
+ const uint8_t *p = (const uint8_t *) src;
+ return ((uint64_t) (p[0]) << 0) |
+ ((uint64_t) (p[1]) << 8) |
+ ((uint64_t) (p[2]) << 16) |
+ ((uint64_t) (p[3]) << 24) |
+ ((uint64_t) (p[4]) << 32) |
+ ((uint64_t) (p[5]) << 40) |
+ ((uint64_t) (p[6]) << 48) | ((uint64_t) (p[7]) << 56);
+#endif
+}
+
+static BLAKE2_INLINE uint16_t
+load16 (const void *src)
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ uint16_t w;
+ memcpy (&w, src, sizeof w);
+ return w;
+#else
+ const uint8_t *p = (const uint8_t *) src;
+ return (uint16_t) (((uint32_t) (p[0]) << 0) | ((uint32_t) (p[1]) << 8));
+#endif
+}
+
+static BLAKE2_INLINE void
+store16 (void *dst, uint16_t w)
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ memcpy (dst, &w, sizeof w);
+#else
+ uint8_t *p = (uint8_t *) dst;
+ *p++ = (uint8_t) w;
+ w >>= 8;
+ *p++ = (uint8_t) w;
+#endif
+}
+
+static BLAKE2_INLINE void
+store32 (void *dst, uint32_t w)
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ memcpy (dst, &w, sizeof w);
+#else
+ uint8_t *p = (uint8_t *) dst;
+ p[0] = (uint8_t) (w >> 0);
+ p[1] = (uint8_t) (w >> 8);
+ p[2] = (uint8_t) (w >> 16);
+ p[3] = (uint8_t) (w >> 24);
+#endif
+}
+
+static BLAKE2_INLINE void
+store64 (void *dst, uint64_t w)
+{
+#if defined(NATIVE_LITTLE_ENDIAN)
+ memcpy (dst, &w, sizeof w);
+#else
+ uint8_t *p = (uint8_t *) dst;
+ p[0] = (uint8_t) (w >> 0);
+ p[1] = (uint8_t) (w >> 8);
+ p[2] = (uint8_t) (w >> 16);
+ p[3] = (uint8_t) (w >> 24);
+ p[4] = (uint8_t) (w >> 32);
+ p[5] = (uint8_t) (w >> 40);
+ p[6] = (uint8_t) (w >> 48);
+ p[7] = (uint8_t) (w >> 56);
+#endif
+}
+
+static BLAKE2_INLINE uint64_t
+load48 (const void *src)
+{
+ const uint8_t *p = (const uint8_t *) src;
+ return ((uint64_t) (p[0]) << 0) |
+ ((uint64_t) (p[1]) << 8) |
+ ((uint64_t) (p[2]) << 16) |
+ ((uint64_t) (p[3]) << 24) |
+ ((uint64_t) (p[4]) << 32) | ((uint64_t) (p[5]) << 40);
+}
+
+static BLAKE2_INLINE void
+store48 (void *dst, uint64_t w)
+{
+ uint8_t *p = (uint8_t *) dst;
+ p[0] = (uint8_t) (w >> 0);
+ p[1] = (uint8_t) (w >> 8);
+ p[2] = (uint8_t) (w >> 16);
+ p[3] = (uint8_t) (w >> 24);
+ p[4] = (uint8_t) (w >> 32);
+ p[5] = (uint8_t) (w >> 40);
+}
+
+static BLAKE2_INLINE uint32_t
+rotr32 (const uint32_t w, const unsigned c)
+{
+ return (w >> c) | (w << (32 - c));
+}
+
+static BLAKE2_INLINE uint64_t
+rotr64 (const uint64_t w, const unsigned c)
+{
+ return (w >> c) | (w << (64 - c));
+}
+
+/* prevents compiler optimizing out memset() */
+static BLAKE2_INLINE void
+secure_zero_memory (void *v, size_t n)
+{
+ static void *(*const volatile memset_v) (void *, int, size_t) = &memset;
+ memset_v (v, 0, n);
+}
+
+#endif //__included_crypto_blake2_impl_h__
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/blake/blake2s.c b/src/plugins/wireguard/blake/blake2s.c
new file mode 100755
index 00000000000..3ff312a1322
--- /dev/null
+++ b/src/plugins/wireguard/blake/blake2s.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Copyright (c) 2012 Samuel Neves <sneves@dei.uc.pt>.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ More information about the BLAKE2 hash function can be found at
+ https://blake2.net.
+*/
+
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <wireguard/blake/blake2s.h>
+#include "blake2-impl.h"
+
+static const uint32_t blake2s_IV[8] = {
+ 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+ 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const uint8_t blake2s_sigma[10][16] = {
+ {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+ {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
+ {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
+ {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
+ {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
+ {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
+ {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
+ {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
+ {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
+ {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0},
+};
+
+static void
+blake2s_set_lastnode (blake2s_state_t * S)
+{
+ S->f[1] = (uint32_t) - 1;
+}
+
+/* Some helper functions, not necessarily useful */
+static int
+blake2s_is_lastblock (const blake2s_state_t * S)
+{
+ return S->f[0] != 0;
+}
+
+static void
+blake2s_set_lastblock (blake2s_state_t * S)
+{
+ if (S->last_node)
+ blake2s_set_lastnode (S);
+
+ S->f[0] = (uint32_t) - 1;
+}
+
+static void
+blake2s_increment_counter (blake2s_state_t * S, const uint32_t inc)
+{
+ S->t[0] += inc;
+ S->t[1] += (S->t[0] < inc);
+}
+
+static void
+blake2s_init0 (blake2s_state_t * S)
+{
+ size_t i;
+ memset (S, 0, sizeof (blake2s_state_t));
+
+ for (i = 0; i < 8; ++i)
+ S->h[i] = blake2s_IV[i];
+}
+
+/* init2 xors IV with input parameter block */
+int
+blake2s_init_param (blake2s_state_t * S, const blake2s_param_t * P)
+{
+ const unsigned char *p = (const unsigned char *) (P);
+ size_t i;
+
+ blake2s_init0 (S);
+
+ /* IV XOR ParamBlock */
+ for (i = 0; i < 8; ++i)
+ S->h[i] ^= load32 (&p[i * 4]);
+
+ S->outlen = P->digest_length;
+ return 0;
+}
+
+
+/* Sequential blake2s initialization */
+int
+blake2s_init (blake2s_state_t * S, size_t outlen)
+{
+ blake2s_param_t P[1];
+
+ /* Move interval verification here? */
+ if ((!outlen) || (outlen > BLAKE2S_OUT_BYTES))
+ return -1;
+
+ P->digest_length = (uint8_t) outlen;
+ P->key_length = 0;
+ P->fanout = 1;
+ P->depth = 1;
+ store32 (&P->leaf_length, 0);
+ store32 (&P->node_offset, 0);
+ store16 (&P->xof_length, 0);
+ P->node_depth = 0;
+ P->inner_length = 0;
+ /* memset(P->reserved, 0, sizeof(P->reserved) ); */
+ memset (P->salt, 0, sizeof (P->salt));
+ memset (P->personal, 0, sizeof (P->personal));
+ return blake2s_init_param (S, P);
+}
+
+int
+blake2s_init_key (blake2s_state_t * S, size_t outlen, const void *key,
+ size_t keylen)
+{
+ blake2s_param_t P[1];
+
+ if ((!outlen) || (outlen > BLAKE2S_OUT_BYTES))
+ return -1;
+
+ if (!key || !keylen || keylen > BLAKE2S_KEY_BYTES)
+ return -1;
+
+ P->digest_length = (uint8_t) outlen;
+ P->key_length = (uint8_t) keylen;
+ P->fanout = 1;
+ P->depth = 1;
+ store32 (&P->leaf_length, 0);
+ store32 (&P->node_offset, 0);
+ store16 (&P->xof_length, 0);
+ P->node_depth = 0;
+ P->inner_length = 0;
+ /* memset(P->reserved, 0, sizeof(P->reserved) ); */
+ memset (P->salt, 0, sizeof (P->salt));
+ memset (P->personal, 0, sizeof (P->personal));
+
+ if (blake2s_init_param (S, P) < 0)
+ return -1;
+
+ {
+ uint8_t block[BLAKE2S_BLOCK_BYTES];
+ memset (block, 0, BLAKE2S_BLOCK_BYTES);
+ memcpy (block, key, keylen);
+ blake2s_update (S, block, BLAKE2S_BLOCK_BYTES);
+ secure_zero_memory (block, BLAKE2S_BLOCK_BYTES); /* Burn the key from stack */
+ }
+ return 0;
+}
+
+#define G(r,i,a,b,c,d) \
+ do { \
+ a = a + b + m[blake2s_sigma[r][2*i+0]]; \
+ d = rotr32(d ^ a, 16); \
+ c = c + d; \
+ b = rotr32(b ^ c, 12); \
+ a = a + b + m[blake2s_sigma[r][2*i+1]]; \
+ d = rotr32(d ^ a, 8); \
+ c = c + d; \
+ b = rotr32(b ^ c, 7); \
+ } while(0)
+
+#define ROUND(r) \
+ do { \
+ G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+ G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+ G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+ G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+ G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+ G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+ G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+ G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+ } while(0)
+
+static void
+blake2s_compress (blake2s_state_t * S, const uint8_t in[BLAKE2S_BLOCK_BYTES])
+{
+ uint32_t m[16];
+ uint32_t v[16];
+ size_t i;
+
+ for (i = 0; i < 16; ++i)
+ {
+ m[i] = load32 (in + i * sizeof (m[i]));
+ }
+
+ for (i = 0; i < 8; ++i)
+ {
+ v[i] = S->h[i];
+ }
+
+ v[8] = blake2s_IV[0];
+ v[9] = blake2s_IV[1];
+ v[10] = blake2s_IV[2];
+ v[11] = blake2s_IV[3];
+ v[12] = S->t[0] ^ blake2s_IV[4];
+ v[13] = S->t[1] ^ blake2s_IV[5];
+ v[14] = S->f[0] ^ blake2s_IV[6];
+ v[15] = S->f[1] ^ blake2s_IV[7];
+
+ ROUND (0);
+ ROUND (1);
+ ROUND (2);
+ ROUND (3);
+ ROUND (4);
+ ROUND (5);
+ ROUND (6);
+ ROUND (7);
+ ROUND (8);
+ ROUND (9);
+
+ for (i = 0; i < 8; ++i)
+ {
+ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+ }
+}
+
+#undef G
+#undef ROUND
+
+int
+blake2s_update (blake2s_state_t * S, const void *pin, size_t inlen)
+{
+ const unsigned char *in = (const unsigned char *) pin;
+ if (inlen > 0)
+ {
+ size_t left = S->buflen;
+ size_t fill = BLAKE2S_BLOCK_BYTES - left;
+ if (inlen > fill)
+ {
+ S->buflen = 0;
+ memcpy (S->buf + left, in, fill); /* Fill buffer */
+ blake2s_increment_counter (S, BLAKE2S_BLOCK_BYTES);
+ blake2s_compress (S, S->buf); /* Compress */
+ in += fill;
+ inlen -= fill;
+ while (inlen > BLAKE2S_BLOCK_BYTES)
+ {
+ blake2s_increment_counter (S, BLAKE2S_BLOCK_BYTES);
+ blake2s_compress (S, in);
+ in += BLAKE2S_BLOCK_BYTES;
+ inlen -= BLAKE2S_BLOCK_BYTES;
+ }
+ }
+ memcpy (S->buf + S->buflen, in, inlen);
+ S->buflen += inlen;
+ }
+ return 0;
+}
+
+int
+blake2s_final (blake2s_state_t * S, void *out, size_t outlen)
+{
+ uint8_t buffer[BLAKE2S_OUT_BYTES] = { 0 };
+ size_t i;
+
+ if (out == NULL || outlen < S->outlen)
+ return -1;
+
+ if (blake2s_is_lastblock (S))
+ return -1;
+
+ blake2s_increment_counter (S, (uint32_t) S->buflen);
+ blake2s_set_lastblock (S);
+ memset (S->buf + S->buflen, 0, BLAKE2S_BLOCK_BYTES - S->buflen); /* Padding */
+ blake2s_compress (S, S->buf);
+
+ for (i = 0; i < 8; ++i) /* Output full hash to temp buffer */
+ store32 (buffer + sizeof (S->h[i]) * i, S->h[i]);
+
+ memcpy (out, buffer, outlen);
+ secure_zero_memory (buffer, sizeof (buffer));
+ return 0;
+}
+
+int
+blake2s (void *out, size_t outlen, const void *in, size_t inlen,
+ const void *key, size_t keylen)
+{
+ blake2s_state_t S[1];
+
+ /* Verify parameters */
+ if (NULL == in && inlen > 0)
+ return -1;
+
+ if (NULL == out)
+ return -1;
+
+ if (NULL == key && keylen > 0)
+ return -1;
+
+ if (!outlen || outlen > BLAKE2S_OUT_BYTES)
+ return -1;
+
+ if (keylen > BLAKE2S_KEY_BYTES)
+ return -1;
+
+ if (keylen > 0)
+ {
+ if (blake2s_init_key (S, outlen, key, keylen) < 0)
+ return -1;
+ }
+ else
+ {
+ if (blake2s_init (S, outlen) < 0)
+ return -1;
+ }
+
+ blake2s_update (S, (const uint8_t *) in, inlen);
+ blake2s_final (S, out, outlen);
+ return 0;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/blake/blake2s.h b/src/plugins/wireguard/blake/blake2s.h
new file mode 100755
index 00000000000..37da0acf28a
--- /dev/null
+++ b/src/plugins/wireguard/blake/blake2s.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Copyright (c) 2012 Samuel Neves <sneves@dei.uc.pt>.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ More information about the BLAKE2 hash function can be found at
+ https://blake2.net.
+*/
+
+#ifndef __included_crypto_blake2s_h__
+#define __included_crypto_blake2s_h__
+
+#include <vlib/vlib.h>
+
+#if defined(_MSC_VER)
+#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop))
+#else
+#define BLAKE2_PACKED(x) x __attribute__((packed))
+#endif
+
+enum blake2s_constant
+{
+ BLAKE2S_BLOCK_BYTES = 64,
+ BLAKE2S_OUT_BYTES = 32,
+ BLAKE2S_KEY_BYTES = 32,
+ BLAKE2S_HASH_SIZE = BLAKE2S_OUT_BYTES,
+ BLAKE2S_SALT_BYTES = 8,
+ BLAKE2S_PERSONAL_BYTES = 8
+};
+
+typedef struct blake2s_state
+{
+ uint32_t h[8];
+ uint32_t t[2];
+ uint32_t f[2];
+ uint8_t buf[BLAKE2S_BLOCK_BYTES];
+ size_t buflen;
+ size_t outlen;
+ uint8_t last_node;
+} blake2s_state_t;
+
+BLAKE2_PACKED (struct blake2s_param
+ {
+ uint8_t digest_length; /* 1 */
+ uint8_t key_length; /* 2 */
+ uint8_t fanout; /* 3 */
+ uint8_t depth; /* 4 */
+ uint32_t leaf_length; /* 8 */
+ uint32_t node_offset; /* 12 */
+ uint16_t xof_length; /* 14 */
+ uint8_t node_depth; /* 15 */
+ uint8_t inner_length; /* 16 */
+ /* uint8_t reserved[0]; */
+ uint8_t salt[BLAKE2S_SALT_BYTES]; /* 24 */
+ uint8_t personal[BLAKE2S_PERSONAL_BYTES]; /* 32 */
+ });
+
+typedef struct blake2s_param blake2s_param_t;
+
+/* Streaming API */
+int blake2s_init (blake2s_state_t * S, size_t outlen);
+int blake2s_init_key (blake2s_state_t * S, size_t outlen, const void *key,
+ size_t keylen);
+int blake2s_init_param (blake2s_state_t * S, const blake2s_param_t * P);
+int blake2s_update (blake2s_state_t * S, const void *in, size_t inlen);
+int blake2s_final (blake2s_state_t * S, void *out, size_t outlen);
+
+int blake2s (void *out, size_t outlen, const void *in, size_t inlen,
+ const void *key, size_t keylen);
+
+#endif /* __included_crypto_blake2s_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/test/test_wireguard.py b/src/plugins/wireguard/test/test_wireguard.py
new file mode 100755
index 00000000000..cd124f3e246
--- /dev/null
+++ b/src/plugins/wireguard/test/test_wireguard.py
@@ -0,0 +1,292 @@
+#!/usr/bin/env python3
+""" Wg tests """
+
+from scapy.packet import Packet
+from scapy.packet import Raw
+from scapy.layers.l2 import Ether
+from scapy.layers.inet import IP, UDP
+from scapy.contrib.wireguard import Wireguard, WireguardResponse, \
+ WireguardInitiation
+from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
+from cryptography.hazmat.primitives.serialization import Encoding, \
+ PrivateFormat, PublicFormat, NoEncryption
+
+from vpp_ipip_tun_interface import VppIpIpTunInterface
+from vpp_interface import VppInterface
+from vpp_object import VppObject
+from framework import VppTestCase
+from re import compile
+import unittest
+
+""" TestWg is a subclass of VPPTestCase classes.
+
+Wg test.
+
+"""
+
+
+class VppWgInterface(VppInterface):
+ """
+ VPP WireGuard interface
+ """
+
+ def __init__(self, test, src, port, key=None):
+ super(VppWgInterface, self).__init__(test)
+
+ self.key = key
+ if not self.key:
+ self.generate = True
+ else:
+ self.generate = False
+ self.port = port
+ self.src = src
+
+ def add_vpp_config(self):
+ r = self.test.vapi.wireguard_interface_create(interface={
+ 'user_instance': 0xffffffff,
+ 'port': self.port,
+ 'src_ip': self.src,
+ 'private_key': self.key_bytes()
+ })
+ self.set_sw_if_index(r.sw_if_index)
+ self.test.registry.register(self, self.test.logger)
+ return self
+
+ def key_bytes(self):
+ if self.key:
+ return self.key.private_bytes(Encoding.Raw,
+ PrivateFormat.Raw,
+ NoEncryption())
+ else:
+ return bytearray(32)
+
+ def remove_vpp_config(self):
+ self.test.vapi.wireguard_interface_delete(
+ sw_if_index=self._sw_if_index)
+
+ def query_vpp_config(self):
+ ts = self.test.vapi.wireguard_interface_dump(sw_if_index=0xffffffff)
+ for t in ts:
+ if t.interface.sw_if_index == self._sw_if_index and \
+ str(t.interface.src_ip) == self.src and \
+ t.interface.port == self.port and \
+ t.interface.private_key == self.key_bytes():
+ return True
+ return False
+
+ def __str__(self):
+ return self.object_id()
+
+ def object_id(self):
+ return "wireguard-%d" % self._sw_if_index
+
+
+def find_route(test, prefix, table_id=0):
+ routes = test.vapi.ip_route_dump(table_id, False)
+
+ for e in routes:
+ if table_id == e.route.table_id \
+ and str(e.route.prefix) == str(prefix):
+ return True
+ return False
+
+
+class VppWgPeer(VppObject):
+
+ def __init__(self,
+ test,
+ itf,
+ endpoint,
+ port,
+ allowed_ips,
+ persistent_keepalive=15):
+ self._test = test
+ self.itf = itf
+ self.endpoint = endpoint
+ self.port = port
+ self.allowed_ips = allowed_ips
+ self.persistent_keepalive = persistent_keepalive
+ self.private_key = X25519PrivateKey.generate()
+ self.public_key = self.private_key.public_key()
+ self.hash = bytearray(16)
+
+ def validate_routing(self):
+ for a in self.allowed_ips:
+ self._test.assertTrue(find_route(self._test, a))
+
+ def validate_no_routing(self):
+ for a in self.allowed_ips:
+ self._test.assertFalse(find_route(self._test, a))
+
+ def add_vpp_config(self):
+ rv = self._test.vapi.wireguard_peer_add(
+ peer={
+ 'public_key': self.public_key_bytes(),
+ 'port': self.port,
+ 'endpoint': self.endpoint,
+ 'n_allowed_ips': len(self.allowed_ips),
+ 'allowed_ips': self.allowed_ips,
+ 'sw_if_index': self.itf.sw_if_index,
+ 'persistent_keepalive': self.persistent_keepalive})
+ self.index = rv.peer_index
+ self._test.registry.register(self, self._test.logger)
+ self.validate_routing()
+ return self
+
+ def remove_vpp_config(self):
+ self._test.vapi.wireguard_peer_remove(peer_index=self.index)
+ self.validate_no_routing()
+
+ def object_id(self):
+ return ("wireguard-peer-%s" % self.index)
+
+ def public_key_bytes(self):
+ return self.public_key.public_bytes(Encoding.Raw,
+ PublicFormat.Raw)
+
+ def private_key_bytes(self):
+ return self.private_key.private_bytes(Encoding.Raw,
+ PrivateFormat.Raw,
+ NoEncryption())
+
+ def query_vpp_config(self):
+ peers = self._test.vapi.wireguard_peers_dump()
+
+ for p in peers:
+ if p.peer.public_key == self.public_key_bytes() and \
+ p.peer.port == self.port and \
+ str(p.peer.endpoint) == self.endpoint and \
+ p.peer.sw_if_index == self.itf.sw_if_index and \
+ len(self.allowed_ips) == p.peer.n_allowed_ips:
+ self.allowed_ips.sort()
+ p.peer.allowed_ips.sort()
+
+ for (a1, a2) in zip(self.allowed_ips, p.peer.allowed_ips):
+ if str(a1) != str(a2):
+ return False
+ return True
+ return False
+
+
+class TestWg(VppTestCase):
+ """ Wireguard Test Case """
+
+ error_str = compile(r"Error")
+
+ @classmethod
+ def setUpClass(cls):
+ super(TestWg, cls).setUpClass()
+ try:
+ cls.create_pg_interfaces(range(3))
+ for i in cls.pg_interfaces:
+ i.admin_up()
+ i.config_ip4()
+ i.resolve_arp()
+
+ except Exception:
+ super(TestWg, cls).tearDownClass()
+ raise
+
+ @classmethod
+ def tearDownClass(cls):
+ super(TestWg, cls).tearDownClass()
+
+ def test_wg_interface(self):
+ port = 12312
+
+ # Create interface
+ wg0 = VppWgInterface(self,
+ self.pg1.local_ip4,
+ port).add_vpp_config()
+
+ self.logger.info(self.vapi.cli("sh int"))
+
+ # delete interface
+ wg0.remove_vpp_config()
+
+ def test_wg_peer(self):
+ wg_output_node_name = '/err/wg-output-tun/'
+ wg_input_node_name = '/err/wg-input/'
+
+ port = 12323
+
+ # Create interfaces
+ wg0 = VppWgInterface(self,
+ self.pg1.local_ip4,
+ port,
+ key=X25519PrivateKey.generate()).add_vpp_config()
+ wg1 = VppWgInterface(self,
+ self.pg2.local_ip4,
+ port+1).add_vpp_config()
+ wg0.admin_up()
+ wg1.admin_up()
+
+ # Check peer counter
+ self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
+
+ self.pg_enable_capture(self.pg_interfaces)
+ self.pg_start()
+
+ peer_1 = VppWgPeer(self,
+ wg0,
+ self.pg1.remote_ip4,
+ port+1,
+ ["10.11.2.0/24",
+ "10.11.3.0/24"]).add_vpp_config()
+ self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
+
+ # wait for the peer to send a handshake
+ capture = self.pg1.get_capture(1, timeout=2)
+ handshake = capture[0]
+
+ self.assertEqual(handshake[IP].src, wg0.src)
+ self.assertEqual(handshake[IP].dst, peer_1.endpoint)
+ self.assertEqual(handshake[UDP].sport, wg0.port)
+ self.assertEqual(handshake[UDP].dport, peer_1.port)
+ handshake = Wireguard(handshake[Raw])
+ self.assertEqual(handshake.message_type, 1) # "initiate")
+ init = handshake[WireguardInitiation]
+
+ # route a packet into the wg interface
+ # use the allowed-ip prefix
+ p = (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
+ IP(src=self.pg0.remote_ip4, dst="10.11.3.2") /
+ UDP(sport=555, dport=556) /
+ Raw())
+ # rx = self.send_and_expect(self.pg0, [p], self.pg1)
+ rx = self.send_and_assert_no_replies(self.pg0, [p])
+
+ self.logger.info(self.vapi.cli("sh error"))
+ init_sent = wg_output_node_name + "Keypair error"
+ self.assertEqual(1, self.statistics.get_err_counter(init_sent))
+
+ # Create many peers on sencond interface
+ NUM_PEERS = 16
+ self.pg2.generate_remote_hosts(NUM_PEERS)
+ self.pg2.configure_ipv4_neighbors()
+
+ peers = []
+ for i in range(NUM_PEERS):
+ peers.append(VppWgPeer(self,
+ wg1,
+ self.pg2.remote_hosts[i].ip4,
+ port+1+i,
+ ["10.10.%d.4/32" % i]).add_vpp_config())
+ self.assertEqual(len(self.vapi.wireguard_peers_dump()), i+2)
+
+ self.logger.info(self.vapi.cli("show wireguard peer"))
+ self.logger.info(self.vapi.cli("show wireguard interface"))
+ self.logger.info(self.vapi.cli("show adj 37"))
+ self.logger.info(self.vapi.cli("sh ip fib 172.16.3.17"))
+ self.logger.info(self.vapi.cli("sh ip fib 10.11.3.0"))
+
+ # remove peers
+ for p in peers:
+ self.assertTrue(p.query_vpp_config())
+ p.remove_vpp_config()
+ self.assertEqual(len(self.vapi.wireguard_peers_dump()), 1)
+ peer_1.remove_vpp_config()
+ self.assertEqual(len(self.vapi.wireguard_peers_dump()), 0)
+
+ wg0.remove_vpp_config()
+ # wg1.remove_vpp_config()
diff --git a/src/plugins/wireguard/wireguard.api b/src/plugins/wireguard/wireguard.api
new file mode 100755
index 00000000000..195755e5c43
--- /dev/null
+++ b/src/plugins/wireguard/wireguard.api
@@ -0,0 +1,162 @@
+/* Hey Emacs use -*- mode: C -*- */
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+option version = "0.1.0";
+
+import "vnet/interface_types.api";
+import "vnet/ip/ip_types.api";
+
+/** \brief Create wireguard interface
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param private_key - private key in binary format of this device
+ @param port - port of this device
+ @param src_ip - packet sent through this interface us this
+ address as the IP source.
+*/
+typedef wireguard_interface
+{
+ u32 user_instance [default=0xffffffff];
+ vl_api_interface_index_t sw_if_index;
+ u8 private_key[32];
+ u16 port;
+ vl_api_address_t src_ip;
+};
+
+/** \brief Create an Wireguard interface
+ */
+define wireguard_interface_create {
+ u32 client_index;
+ u32 context;
+ vl_api_wireguard_interface_t interface;
+ bool generate_key;
+};
+
+/** \brief Add Wireguard interface interface response
+ @param context - sender context, to match reply w/ request
+ @param retval - return status
+ @param sw_if_index - sw_if_index of new interface (for successful add)
+*/
+define wireguard_interface_create_reply
+{
+ u32 context;
+ i32 retval;
+ vl_api_interface_index_t sw_if_index;
+};
+
+autoreply define wireguard_interface_delete
+{
+ u32 client_index;
+ u32 context;
+ vl_api_interface_index_t sw_if_index;
+};
+
+define wireguard_interface_dump
+{
+ u32 client_index;
+ u32 context;
+ bool show_private_key;
+ vl_api_interface_index_t sw_if_index;
+};
+
+define wireguard_interface_details
+{
+ u32 context;
+ vl_api_wireguard_interface_t interface;
+};
+
+enum wireguard_peer_flags : u8
+{
+ WIREGUARD_PEER_STATUS_DEAD = 0x1,
+};
+
+/** \brief Create new peer
+ @param public_key - public key (in binary format) of destination peer
+ @param port - destination port
+ @param table_id - The IP table in which 'endpoint' is reachable
+ @param endpoint - destination ip
+ @param allowed_ip - allowed incoming ip tunnel
+ @param tun_sw_if_index - tunnel interface
+ @param persistent_keepalive - keepalive packet timeout
+*/
+typedef wireguard_peer
+{
+ u8 public_key[32];
+ u16 port;
+ u16 persistent_keepalive;
+ u32 table_id;
+ vl_api_address_t endpoint;
+ vl_api_interface_index_t sw_if_index;
+ vl_api_wireguard_peer_flags_t flags;
+ u8 n_allowed_ips;
+ vl_api_prefix_t allowed_ips[n_allowed_ips];
+};
+
+/** \brief Create new peer
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param peer - peer to create
+*/
+define wireguard_peer_add
+{
+ u32 client_index;
+ u32 context;
+ vl_api_wireguard_peer_t peer;
+};
+define wireguard_peer_add_reply
+{
+ u32 context;
+ i32 retval;
+ u32 peer_index;
+};
+
+/** \brief Remove peer by public_key
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+ @param public_key
+*/
+autoreply define wireguard_peer_remove
+{
+ u32 client_index;
+ u32 context;
+ u32 peer_index;
+};
+
+/** \brief Dump all peers
+ @param client_index - opaque cookie to identify the sender
+ @param context - sender context, to match reply w/ request
+*/
+define wireguard_peers_dump {
+ u32 client_index;
+ u32 context;
+};
+
+/** \brief Dump peers response
+ @param context - sender context, to match reply w/ request
+ @param is_dead - is peer valid yet
+ @param public_key - peer public_key
+ @param ip4_address - ip4 endpoint address
+*/
+define wireguard_peers_details {
+ u32 context;
+ vl_api_wireguard_peer_t peer;
+};
+
+/*
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard.c b/src/plugins/wireguard/wireguard.c
new file mode 100755
index 00000000000..00921811e4a
--- /dev/null
+++ b/src/plugins/wireguard/wireguard.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/plugin/plugin.h>
+#include <vnet/ipip/ipip.h>
+#include <vpp/app/version.h>
+#include <vnet/udp/udp.h>
+
+#include <wireguard/wireguard_send.h>
+#include <wireguard/wireguard_key.h>
+#include <wireguard/wireguard_if.h>
+#include <wireguard/wireguard.h>
+
+wg_main_t wg_main;
+
+static clib_error_t *
+wg_init (vlib_main_t * vm)
+{
+ wg_main_t *wmp = &wg_main;
+
+ wmp->vlib_main = vm;
+ wmp->peers = 0;
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (wg_init);
+
+/* *INDENT-OFF* */
+
+VNET_FEATURE_INIT (wg_output_tun, static) =
+{
+ .arc_name = "ip4-output",
+ .node_name = "wg-output-tun",
+};
+
+VLIB_PLUGIN_REGISTER () =
+{
+ .version = VPP_BUILD_VER,
+ .description = "Wireguard Protocol",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard.h b/src/plugins/wireguard/wireguard.h
new file mode 100755
index 00000000000..70a692e602f
--- /dev/null
+++ b/src/plugins/wireguard/wireguard.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __included_wg_h__
+#define __included_wg_h__
+
+#include <wireguard/wireguard_index_table.h>
+#include <wireguard/wireguard_messages.h>
+#include <wireguard/wireguard_peer.h>
+
+extern vlib_node_registration_t wg_input_node;
+extern vlib_node_registration_t wg_output_tun_node;
+
+
+
+typedef struct
+{
+ /* convenience */
+ vlib_main_t *vlib_main;
+
+ u16 msg_id_base;
+
+ // Peers pool
+ wg_peer_t *peers;
+ wg_index_table_t index_table;
+
+} wg_main_t;
+
+extern wg_main_t wg_main;
+
+#endif /* __included_wg_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_api.c b/src/plugins/wireguard/wireguard_api.c
new file mode 100755
index 00000000000..e107cb56b4b
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_api.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vlibmemory/api.h>
+
+#include <vnet/format_fns.h>
+#include <vnet/ip/ip_types_api.h>
+#include <vlibapi/api.h>
+
+#include <wireguard/wireguard.api_enum.h>
+#include <wireguard/wireguard.api_types.h>
+
+#include <wireguard/wireguard_key.h>
+#include <wireguard/wireguard.h>
+#include <wireguard/wireguard_if.h>
+#include <wireguard/wireguard_peer.h>
+
+#define REPLY_MSG_ID_BASE wmp->msg_id_base
+#include <vlibapi/api_helper_macros.h>
+
+static void
+ vl_api_wireguard_interface_create_t_handler
+ (vl_api_wireguard_interface_create_t * mp)
+{
+ vl_api_wireguard_interface_create_reply_t *rmp;
+ wg_main_t *wmp = &wg_main;
+ u8 private_key[NOISE_PUBLIC_KEY_LEN];
+ ip_address_t src;
+ u32 sw_if_index;
+ int rv = 0;
+
+ ip_address_decode2 (&mp->interface.src_ip, &src);
+
+ if (AF_IP6 == ip_addr_version (&src))
+ rv = VNET_API_ERROR_INVALID_PROTOCOL;
+ else
+ {
+ if (mp->generate_key)
+ curve25519_gen_secret (private_key);
+ else
+ clib_memcpy (private_key, mp->interface.private_key,
+ NOISE_PUBLIC_KEY_LEN);
+
+ rv = wg_if_create (ntohl (mp->interface.user_instance), private_key,
+ ntohs (mp->interface.port), &src, &sw_if_index);
+ }
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO2(VL_API_WIREGUARD_INTERFACE_CREATE_REPLY,
+ {
+ rmp->sw_if_index = htonl(sw_if_index);
+ });
+ /* *INDENT-ON* */
+}
+
+static void
+ vl_api_wireguard_interface_delete_t_handler
+ (vl_api_wireguard_interface_delete_t * mp)
+{
+ vl_api_wireguard_interface_delete_reply_t *rmp;
+ wg_main_t *wmp = &wg_main;
+ int rv = 0;
+
+ VALIDATE_SW_IF_INDEX (mp);
+
+ rv = wg_if_delete (ntohl (mp->sw_if_index));
+
+ BAD_SW_IF_INDEX_LABEL;
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO(VL_API_WIREGUARD_INTERFACE_DELETE_REPLY);
+ /* *INDENT-ON* */
+}
+
+typedef struct wg_deatils_walk_t_
+{
+ vl_api_registration_t *reg;
+ u32 context;
+} wg_deatils_walk_t;
+
+static walk_rc_t
+wireguard_if_send_details (index_t wgii, void *data)
+{
+ vl_api_wireguard_interface_details_t *rmp;
+ wg_deatils_walk_t *ctx = data;
+ const wg_if_t *wgi;
+
+ wgi = wg_if_get (wgii);
+
+ rmp = vl_msg_api_alloc_zero (sizeof (*rmp));
+ rmp->_vl_msg_id = htons (VL_API_WIREGUARD_INTERFACE_DETAILS +
+ wg_main.msg_id_base);
+
+ clib_memcpy (rmp->interface.private_key,
+ wgi->local.l_private, NOISE_PUBLIC_KEY_LEN);
+ rmp->interface.sw_if_index = htonl (wgi->sw_if_index);
+ rmp->interface.port = htons (wgi->port);
+ ip_address_encode2 (&wgi->src_ip, &rmp->interface.src_ip);
+
+ rmp->context = ctx->context;
+
+ vl_api_send_msg (ctx->reg, (u8 *) rmp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_wireguard_interface_dump_t_handler (vl_api_wireguard_interface_dump_t *
+ mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (reg == 0)
+ return;
+
+ wg_deatils_walk_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ wg_if_walk (wireguard_if_send_details, &ctx);
+}
+
+static void
+vl_api_wireguard_peer_add_t_handler (vl_api_wireguard_peer_add_t * mp)
+{
+ vl_api_wireguard_peer_add_reply_t *rmp;
+ wg_main_t *wmp = &wg_main;
+ index_t peeri;
+ int ii, rv = 0;
+
+ ip_address_t endpoint;
+ fib_prefix_t *allowed_ips = NULL;
+
+ VALIDATE_SW_IF_INDEX (&(mp->peer));
+
+ if (0 == mp->peer.n_allowed_ips)
+ {
+ rv = VNET_API_ERROR_INVALID_VALUE;
+ goto done;
+ }
+
+ vec_validate (allowed_ips, mp->peer.n_allowed_ips - 1);
+ ip_address_decode2 (&mp->peer.endpoint, &endpoint);
+
+ for (ii = 0; ii < mp->peer.n_allowed_ips; ii++)
+ ip_prefix_decode (&mp->peer.allowed_ips[ii], &allowed_ips[ii]);
+
+ if (AF_IP6 == ip_addr_version (&endpoint) ||
+ FIB_PROTOCOL_IP6 == allowed_ips[0].fp_proto)
+ /* ip6 currently not supported, but the API needs to support it
+ * else we'll need to change it later, and that's a PITA */
+ rv = VNET_API_ERROR_INVALID_PROTOCOL;
+ else
+ rv = wg_peer_add (ntohl (mp->peer.sw_if_index),
+ mp->peer.public_key,
+ ntohl (mp->peer.table_id),
+ &ip_addr_46 (&endpoint),
+ allowed_ips,
+ ntohs (mp->peer.port),
+ ntohs (mp->peer.persistent_keepalive), &peeri);
+
+ vec_free (allowed_ips);
+done:
+ BAD_SW_IF_INDEX_LABEL;
+ /* *INDENT-OFF* */
+ REPLY_MACRO2(VL_API_WIREGUARD_PEER_ADD_REPLY,
+ {
+ rmp->peer_index = ntohl (peeri);
+ });
+ /* *INDENT-ON* */
+}
+
+static void
+vl_api_wireguard_peer_remove_t_handler (vl_api_wireguard_peer_remove_t * mp)
+{
+ vl_api_wireguard_peer_remove_reply_t *rmp;
+ wg_main_t *wmp = &wg_main;
+ int rv = 0;
+
+ rv = wg_peer_remove (ntohl (mp->peer_index));
+
+ /* *INDENT-OFF* */
+ REPLY_MACRO(VL_API_WIREGUARD_PEER_REMOVE_REPLY);
+ /* *INDENT-ON* */
+}
+
+static walk_rc_t
+send_wg_peers_details (index_t peeri, void *data)
+{
+ vl_api_wireguard_peers_details_t *rmp;
+ wg_deatils_walk_t *ctx = data;
+ const wg_peer_t *peer;
+ u8 n_allowed_ips;
+ size_t ss;
+
+ peer = wg_peer_get (peeri);
+ n_allowed_ips = vec_len (peer->allowed_ips);
+
+ ss = (sizeof (*rmp) + (n_allowed_ips * sizeof (rmp->peer.allowed_ips[0])));
+
+ rmp = vl_msg_api_alloc_zero (ss);
+
+ rmp->_vl_msg_id = htons (VL_API_WIREGUARD_PEERS_DETAILS +
+ wg_main.msg_id_base);
+
+ if (peer->is_dead)
+ rmp->peer.flags = WIREGUARD_PEER_STATUS_DEAD;
+ clib_memcpy (rmp->peer.public_key,
+ peer->remote.r_public, NOISE_PUBLIC_KEY_LEN);
+
+ ip_address_encode (&peer->dst.addr, IP46_TYPE_ANY, &rmp->peer.endpoint);
+ rmp->peer.port = htons (peer->dst.port);
+ rmp->peer.n_allowed_ips = n_allowed_ips;
+ rmp->peer.sw_if_index = htonl (peer->wg_sw_if_index);
+
+ int ii;
+ for (ii = 0; ii < n_allowed_ips; ii++)
+ ip_prefix_encode (&peer->allowed_ips[ii].prefix,
+ &rmp->peer.allowed_ips[ii]);
+
+ rmp->context = ctx->context;
+
+ vl_api_send_msg (ctx->reg, (u8 *) rmp);
+
+ return (WALK_CONTINUE);
+}
+
+static void
+vl_api_wireguard_peers_dump_t_handler (vl_api_wireguard_peers_dump_t * mp)
+{
+ vl_api_registration_t *reg;
+
+ reg = vl_api_client_index_to_registration (mp->client_index);
+ if (reg == NULL)
+ return;
+
+ wg_deatils_walk_t ctx = {
+ .reg = reg,
+ .context = mp->context,
+ };
+
+ wg_peer_walk (send_wg_peers_details, &ctx);
+}
+
+/* set tup the API message handling tables */
+#include <wireguard/wireguard.api.c>
+static clib_error_t *
+wg_api_hookup (vlib_main_t * vm)
+{
+ wg_main_t *wmp = &wg_main;
+ wmp->msg_id_base = setup_message_id_table ();
+ return 0;
+}
+
+VLIB_API_INIT_FUNCTION (wg_api_hookup);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_cli.c b/src/plugins/wireguard/wireguard_cli.c
new file mode 100755
index 00000000000..7fdccdc64b3
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_cli.c
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <wireguard/wireguard.h>
+#include <wireguard/wireguard_key.h>
+#include <wireguard/wireguard_peer.h>
+#include <wireguard/wireguard_if.h>
+
+static clib_error_t *
+wg_if_create_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ unformat_input_t _line_input, *line_input = &_line_input;
+ u8 private_key[NOISE_PUBLIC_KEY_LEN];
+ u32 instance, sw_if_index;
+ ip_address_t src_ip;
+ clib_error_t *error;
+ u8 *private_key_64;
+ u32 port, generate_key = 0;
+ int rv;
+
+ error = NULL;
+ instance = sw_if_index = ~0;
+ private_key_64 = 0;
+ port = 0;
+
+ if (unformat_user (input, unformat_line_input, line_input))
+ {
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "instance %d", &instance))
+ ;
+ else if (unformat (line_input, "private-key %s", &private_key_64))
+ {
+ if (!(key_from_base64 (private_key_64,
+ NOISE_KEY_LEN_BASE64, private_key)))
+ {
+ error = clib_error_return (0, "Error parsing private key");
+ break;
+ }
+ }
+ else if (unformat (line_input, "listen-port %d", &port))
+ ;
+ else if (unformat (line_input, "port %d", &port))
+ ;
+ else if (unformat (line_input, "generate-key"))
+ generate_key = 1;
+ else
+ if (unformat (line_input, "src %U", unformat_ip_address, &src_ip))
+ ;
+ else
+ {
+ error = clib_error_return (0, "unknown input: %U",
+ format_unformat_error, line_input);
+ break;
+ }
+ }
+
+ unformat_free (line_input);
+
+ if (error)
+ return error;
+ }
+
+ if (generate_key)
+ curve25519_gen_secret (private_key);
+
+ rv = wg_if_create (instance, private_key, port, &src_ip, &sw_if_index);
+
+ if (rv)
+ return clib_error_return (0, "wireguard interface create failed");
+
+ vlib_cli_output (vm, "%U\n", format_vnet_sw_if_index_name, vnet_get_main (),
+ sw_if_index);
+ return 0;
+}
+
+/*?
+ * Create a Wireguard interface.
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (wg_if_create_command, static) = {
+ .path = "wireguard create",
+ .short_help = "wireguard create listen-port <port> "
+ "private-key <key> src <IP> [generate-key]",
+ .function = wg_if_create_cli,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+wg_if_delete_cli (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm;
+ u32 sw_if_index;
+ int rv;
+
+ vnm = vnet_get_main ();
+ sw_if_index = ~0;
+
+ while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat
+ (input, "%U", unformat_vnet_sw_interface, vnm, &sw_if_index))
+ ;
+ else
+ break;
+ }
+
+ if (~0 != sw_if_index)
+ {
+ rv = wg_if_delete (sw_if_index);
+
+ if (rv)
+ return clib_error_return (0, "wireguard interface delete failed");
+ }
+ else
+ return clib_error_return (0, "no such interface: %U",
+ format_unformat_error, input);
+
+ return 0;
+}
+
+/*?
+ * Delete a Wireguard interface.
+?*/
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (wg_if_delete_command, static) = {
+ .path = "wireguard delete",
+ .short_help = "wireguard delete <interface>",
+ .function = wg_if_delete_cli,
+};
+/* *INDENT-ON* */
+
+
+static clib_error_t *
+wg_peer_add_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ clib_error_t *error = NULL;
+ unformat_input_t _line_input, *line_input = &_line_input;
+
+ u8 *public_key_64 = 0;
+ u8 public_key[NOISE_PUBLIC_KEY_LEN];
+ fib_prefix_t allowed_ip, *allowed_ips = NULL;
+ ip_prefix_t pfx;
+ ip_address_t ip;
+ u32 portDst = 0, table_id = 0;
+ u32 persistent_keepalive = 0;
+ u32 tun_sw_if_index = ~0;
+ u32 peer_index;
+ int rv;
+
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
+ {
+ if (unformat (line_input, "public-key %s", &public_key_64))
+ {
+ if (!(key_from_base64 (public_key_64,
+ NOISE_KEY_LEN_BASE64, public_key)))
+ {
+ error = clib_error_return (0, "Error parsing private key");
+ goto done;
+ }
+ }
+ else if (unformat (line_input, "endpoint %U", unformat_ip_address, &ip))
+ ;
+ else if (unformat (line_input, "table-id %d", &table_id))
+ ;
+ else if (unformat (line_input, "port %d", &portDst))
+ ;
+ else if (unformat (line_input, "persistent-keepalive %d",
+ &persistent_keepalive))
+ ;
+ else if (unformat (line_input, "allowed-ip %U",
+ unformat_ip_prefix, &pfx))
+ {
+ ip_prefix_to_fib_prefix (&pfx, &allowed_ip);
+ vec_add1 (allowed_ips, allowed_ip);
+ }
+ else if (unformat (line_input, "%U",
+ unformat_vnet_sw_interface, vnm, &tun_sw_if_index))
+ ;
+ else
+ {
+ error = clib_error_return (0, "Input error");
+ goto done;
+ }
+ }
+
+ if (AF_IP6 == ip_addr_version (&ip) ||
+ FIB_PROTOCOL_IP6 == allowed_ip.fp_proto)
+ rv = VNET_API_ERROR_INVALID_PROTOCOL;
+ else
+ rv = wg_peer_add (tun_sw_if_index,
+ public_key,
+ table_id,
+ &ip_addr_46 (&ip),
+ allowed_ips,
+ portDst, persistent_keepalive, &peer_index);
+
+ switch (rv)
+ {
+ case VNET_API_ERROR_KEY_LENGTH:
+ error = clib_error_return (0, "Error parsing public key");
+ break;
+ case VNET_API_ERROR_ENTRY_ALREADY_EXISTS:
+ error = clib_error_return (0, "Peer already exist");
+ break;
+ case VNET_API_ERROR_INVALID_SW_IF_INDEX:
+ error = clib_error_return (0, "Tunnel is not specified");
+ break;
+ case VNET_API_ERROR_LIMIT_EXCEEDED:
+ error = clib_error_return (0, "Max peers limit");
+ break;
+ case VNET_API_ERROR_INIT_FAILED:
+ error = clib_error_return (0, "wireguard device parameters is not set");
+ break;
+ case VNET_API_ERROR_INVALID_PROTOCOL:
+ error = clib_error_return (0, "ipv6 not supported yet");
+ break;
+ }
+
+done:
+ vec_free (public_key_64);
+ vec_free (allowed_ips);
+ unformat_free (line_input);
+ return error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (wg_peer_add_command, static) =
+{
+ .path = "wireguard peer add",
+ .short_help = "wireguard peer add <wg_int> public-key <pub_key_other>"
+ "endpoint <ip4_dst> allowed-ip <prefix>"
+ "dst-port [port_dst] persistent-keepalive [keepalive_interval]",
+ .function = wg_peer_add_command_fn,
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+wg_peer_remove_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ clib_error_t *error = NULL;
+ u32 peer_index;
+ int rv;
+
+ unformat_input_t _line_input, *line_input = &_line_input;
+ if (!unformat_user (input, unformat_line_input, line_input))
+ return 0;
+
+ if (unformat (line_input, "%d", &peer_index))
+ ;
+ else
+ {
+ error = clib_error_return (0, "Input error");
+ goto done;
+ }
+
+ rv = wg_peer_remove (peer_index);
+
+ switch (rv)
+ {
+ case VNET_API_ERROR_KEY_LENGTH:
+ error = clib_error_return (0, "Error parsing public key");
+ break;
+ }
+
+done:
+ unformat_free (line_input);
+ return error;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (wg_peer_remove_command, static) =
+{
+ .path = "wireguard peer remove",
+ .short_help = "wireguard peer remove <index>",
+ .function = wg_peer_remove_command_fn,
+};
+/* *INDENT-ON* */
+
+static walk_rc_t
+wg_peer_show_one (index_t peeri, void *arg)
+{
+ vlib_cli_output (arg, "%U", format_wg_peer, peeri);
+
+ return (WALK_CONTINUE);
+}
+
+static clib_error_t *
+wg_show_peer_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ wg_peer_walk (wg_peer_show_one, vm);
+
+ return NULL;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (wg_show_peers_command, static) =
+{
+ .path = "show wireguard peer",
+ .short_help = "show wireguard peer",
+ .function = wg_show_peer_command_fn,
+};
+/* *INDENT-ON* */
+
+static walk_rc_t
+wg_if_show_one (index_t itfi, void *arg)
+{
+ vlib_cli_output (arg, "%U", format_wg_if, itfi);
+
+ return (WALK_CONTINUE);
+}
+
+static clib_error_t *
+wg_show_if_command_fn (vlib_main_t * vm,
+ unformat_input_t * input, vlib_cli_command_t * cmd)
+{
+ wg_if_walk (wg_if_show_one, vm);
+
+ return NULL;
+}
+
+/* *INDENT-OFF* */
+VLIB_CLI_COMMAND (wg_show_itfs_command, static) =
+{
+ .path = "show wireguard interface",
+ .short_help = "show wireguard",
+ .function = wg_show_if_command_fn,
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_cookie.c b/src/plugins/wireguard/wireguard_cookie.c
new file mode 100755
index 00000000000..aa476f792a1
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_cookie.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Copyright (c) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>.
+ * Copyright (c) 2019-2020 Matt Dunwoodie <ncon@noconroy.net>.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stddef.h>
+#include <openssl/rand.h>
+#include <vlib/vlib.h>
+
+#include <wireguard/wireguard_cookie.h>
+#include <wireguard/wireguard.h>
+
+static void cookie_precompute_key (uint8_t *,
+ const uint8_t[COOKIE_INPUT_SIZE],
+ const char *);
+static void cookie_macs_mac1 (message_macs_t *, const void *, size_t,
+ const uint8_t[COOKIE_KEY_SIZE]);
+static void cookie_macs_mac2 (message_macs_t *, const void *, size_t,
+ const uint8_t[COOKIE_COOKIE_SIZE]);
+static void cookie_checker_make_cookie (vlib_main_t * vm, cookie_checker_t *,
+ uint8_t[COOKIE_COOKIE_SIZE],
+ ip4_address_t ip4, u16 udp_port);
+
+/* Public Functions */
+void
+cookie_maker_init (cookie_maker_t * cp, const uint8_t key[COOKIE_INPUT_SIZE])
+{
+ clib_memset (cp, 0, sizeof (*cp));
+ cookie_precompute_key (cp->cp_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
+ cookie_precompute_key (cp->cp_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
+}
+
+void
+cookie_checker_update (cookie_checker_t * cc, uint8_t key[COOKIE_INPUT_SIZE])
+{
+ if (key)
+ {
+ cookie_precompute_key (cc->cc_mac1_key, key, COOKIE_MAC1_KEY_LABEL);
+ cookie_precompute_key (cc->cc_cookie_key, key, COOKIE_COOKIE_KEY_LABEL);
+ }
+ else
+ {
+ clib_memset (cc->cc_mac1_key, 0, sizeof (cc->cc_mac1_key));
+ clib_memset (cc->cc_cookie_key, 0, sizeof (cc->cc_cookie_key));
+ }
+}
+
+void
+cookie_maker_mac (cookie_maker_t * cp, message_macs_t * cm, void *buf,
+ size_t len)
+{
+ len = len - sizeof (message_macs_t);
+ cookie_macs_mac1 (cm, buf, len, cp->cp_mac1_key);
+
+ clib_memcpy (cp->cp_mac1_last, cm->mac1, COOKIE_MAC_SIZE);
+ cp->cp_mac1_valid = 1;
+
+ if (!wg_birthdate_has_expired (cp->cp_birthdate,
+ COOKIE_SECRET_MAX_AGE -
+ COOKIE_SECRET_LATENCY))
+ cookie_macs_mac2 (cm, buf, len, cp->cp_cookie);
+ else
+ clib_memset (cm->mac2, 0, COOKIE_MAC_SIZE);
+}
+
+enum cookie_mac_state
+cookie_checker_validate_macs (vlib_main_t * vm, cookie_checker_t * cc,
+ message_macs_t * cm, void *buf, size_t len,
+ bool busy, ip4_address_t ip4, u16 udp_port)
+{
+ message_macs_t our_cm;
+ uint8_t cookie[COOKIE_COOKIE_SIZE];
+
+ len = len - sizeof (message_macs_t);
+ cookie_macs_mac1 (&our_cm, buf, len, cc->cc_mac1_key);
+
+ /* If mac1 is invald, we want to drop the packet */
+ if (clib_memcmp (our_cm.mac1, cm->mac1, COOKIE_MAC_SIZE) != 0)
+ return INVALID_MAC;
+
+ if (!busy)
+ return VALID_MAC_BUT_NO_COOKIE;
+
+ cookie_checker_make_cookie (vm, cc, cookie, ip4, udp_port);
+ cookie_macs_mac2 (&our_cm, buf, len, cookie);
+
+ /* If the mac2 is invalid, we want to send a cookie response */
+ if (clib_memcmp (our_cm.mac2, cm->mac2, COOKIE_MAC_SIZE) != 0)
+ return VALID_MAC_BUT_NO_COOKIE;
+
+ return VALID_MAC_WITH_COOKIE;
+}
+
+/* Private functions */
+static void
+cookie_precompute_key (uint8_t * key, const uint8_t input[COOKIE_INPUT_SIZE],
+ const char *label)
+{
+ blake2s_state_t blake;
+
+ blake2s_init (&blake, COOKIE_KEY_SIZE);
+ blake2s_update (&blake, (const uint8_t *) label, strlen (label));
+ blake2s_update (&blake, input, COOKIE_INPUT_SIZE);
+ blake2s_final (&blake, key, COOKIE_KEY_SIZE);
+}
+
+static void
+cookie_macs_mac1 (message_macs_t * cm, const void *buf, size_t len,
+ const uint8_t key[COOKIE_KEY_SIZE])
+{
+ blake2s_state_t state;
+ blake2s_init_key (&state, COOKIE_MAC_SIZE, key, COOKIE_KEY_SIZE);
+ blake2s_update (&state, buf, len);
+ blake2s_final (&state, cm->mac1, COOKIE_MAC_SIZE);
+
+}
+
+static void
+cookie_macs_mac2 (message_macs_t * cm, const void *buf, size_t len,
+ const uint8_t key[COOKIE_COOKIE_SIZE])
+{
+ blake2s_state_t state;
+ blake2s_init_key (&state, COOKIE_MAC_SIZE, key, COOKIE_COOKIE_SIZE);
+ blake2s_update (&state, buf, len);
+ blake2s_update (&state, cm->mac1, COOKIE_MAC_SIZE);
+ blake2s_final (&state, cm->mac2, COOKIE_MAC_SIZE);
+}
+
+static void
+cookie_checker_make_cookie (vlib_main_t * vm, cookie_checker_t * cc,
+ uint8_t cookie[COOKIE_COOKIE_SIZE],
+ ip4_address_t ip4, u16 udp_port)
+{
+ blake2s_state_t state;
+
+ if (wg_birthdate_has_expired (cc->cc_secret_birthdate,
+ COOKIE_SECRET_MAX_AGE))
+ {
+ cc->cc_secret_birthdate = vlib_time_now (vm);
+ RAND_bytes (cc->cc_secret, COOKIE_SECRET_SIZE);
+ }
+
+ blake2s_init_key (&state, COOKIE_COOKIE_SIZE, cc->cc_secret,
+ COOKIE_SECRET_SIZE);
+
+ blake2s_update (&state, ip4.as_u8, sizeof (ip4_address_t)); //TODO: IP6
+ blake2s_update (&state, (u8 *) & udp_port, sizeof (u16));
+ blake2s_final (&state, cookie, COOKIE_COOKIE_SIZE);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_cookie.h b/src/plugins/wireguard/wireguard_cookie.h
new file mode 100755
index 00000000000..489cce81325
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_cookie.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Copyright (c) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>.
+ * Copyright (c) 2019-2020 Matt Dunwoodie <ncon@noconroy.net>.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_wg_cookie_h__
+#define __included_wg_cookie_h__
+
+#include <vnet/ip/ip4_packet.h>
+#include <wireguard/wireguard_noise.h>
+
+enum cookie_mac_state
+{
+ INVALID_MAC,
+ VALID_MAC_BUT_NO_COOKIE,
+ VALID_MAC_WITH_COOKIE
+};
+
+#define COOKIE_MAC_SIZE 16
+#define COOKIE_KEY_SIZE 32
+#define COOKIE_NONCE_SIZE 24
+#define COOKIE_COOKIE_SIZE 16
+#define COOKIE_SECRET_SIZE 32
+#define COOKIE_INPUT_SIZE 32
+#define COOKIE_ENCRYPTED_SIZE (COOKIE_COOKIE_SIZE + COOKIE_MAC_SIZE)
+
+#define COOKIE_MAC1_KEY_LABEL "mac1----"
+#define COOKIE_COOKIE_KEY_LABEL "cookie--"
+#define COOKIE_SECRET_MAX_AGE 120
+#define COOKIE_SECRET_LATENCY 5
+
+/* Constants for initiation rate limiting */
+#define RATELIMIT_SIZE (1 << 13)
+#define RATELIMIT_SIZE_MAX (RATELIMIT_SIZE * 8)
+#define NSEC_PER_SEC 1000000000LL
+#define INITIATIONS_PER_SECOND 20
+#define INITIATIONS_BURSTABLE 5
+#define INITIATION_COST (NSEC_PER_SEC / INITIATIONS_PER_SECOND)
+#define TOKEN_MAX (INITIATION_COST * INITIATIONS_BURSTABLE)
+#define ELEMENT_TIMEOUT 1
+#define IPV4_MASK_SIZE 4 /* Use all 4 bytes of IPv4 address */
+#define IPV6_MASK_SIZE 8 /* Use top 8 bytes (/64) of IPv6 address */
+
+typedef struct cookie_macs
+{
+ uint8_t mac1[COOKIE_MAC_SIZE];
+ uint8_t mac2[COOKIE_MAC_SIZE];
+} message_macs_t;
+
+typedef struct cookie_maker
+{
+ uint8_t cp_mac1_key[COOKIE_KEY_SIZE];
+ uint8_t cp_cookie_key[COOKIE_KEY_SIZE];
+
+ uint8_t cp_cookie[COOKIE_COOKIE_SIZE];
+ f64 cp_birthdate;
+ int cp_mac1_valid;
+ uint8_t cp_mac1_last[COOKIE_MAC_SIZE];
+} cookie_maker_t;
+
+typedef struct cookie_checker
+{
+ uint8_t cc_mac1_key[COOKIE_KEY_SIZE];
+ uint8_t cc_cookie_key[COOKIE_KEY_SIZE];
+
+ f64 cc_secret_birthdate;
+ uint8_t cc_secret[COOKIE_SECRET_SIZE];
+} cookie_checker_t;
+
+
+void cookie_maker_init (cookie_maker_t *, const uint8_t[COOKIE_INPUT_SIZE]);
+void cookie_checker_update (cookie_checker_t *, uint8_t[COOKIE_INPUT_SIZE]);
+void cookie_maker_mac (cookie_maker_t *, message_macs_t *, void *, size_t);
+enum cookie_mac_state cookie_checker_validate_macs (vlib_main_t * vm,
+ cookie_checker_t *,
+ message_macs_t *, void *,
+ size_t, bool,
+ ip4_address_t ip4,
+ u16 udp_port);
+
+#endif /* __included_wg_cookie_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_if.c b/src/plugins/wireguard/wireguard_if.c
new file mode 100644
index 00000000000..ff8ed35477e
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_if.c
@@ -0,0 +1,422 @@
+
+#include <vnet/adj/adj_midchain.h>
+#include <vnet/udp/udp.h>
+
+#include <wireguard/wireguard_messages.h>
+#include <wireguard/wireguard_if.h>
+#include <wireguard/wireguard.h>
+
+/* pool of interfaces */
+wg_if_t *wg_if_pool;
+
+/* bitmap of Allocated WG_ITF instances */
+static uword *wg_if_instances;
+
+/* vector of interfaces key'd on their sw_if_index */
+static index_t *wg_if_index_by_sw_if_index;
+
+/* vector of interfaces key'd on their UDP port (in network order) */
+index_t *wg_if_index_by_port;
+
+static u8 *
+format_wg_if_name (u8 * s, va_list * args)
+{
+ u32 dev_instance = va_arg (*args, u32);
+ return format (s, "wg%d", dev_instance);
+}
+
+u8 *
+format_wg_if (u8 * s, va_list * args)
+{
+ index_t wgii = va_arg (*args, u32);
+ wg_if_t *wgi = wg_if_get (wgii);
+ u8 key[NOISE_KEY_LEN_BASE64];
+
+ key_to_base64 (wgi->local.l_private, NOISE_PUBLIC_KEY_LEN, key);
+
+ s = format (s, "[%d] %U src:%U port:%d",
+ wgii,
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ wgi->sw_if_index, format_ip_address, &wgi->src_ip, wgi->port);
+
+ key_to_base64 (wgi->local.l_private, NOISE_PUBLIC_KEY_LEN, key);
+
+ s = format (s, " private-key:%s", key);
+
+ key_to_base64 (wgi->local.l_public, NOISE_PUBLIC_KEY_LEN, key);
+
+ s = format (s, " public-key:%s", key);
+
+ return (s);
+}
+
+index_t
+wg_if_find_by_sw_if_index (u32 sw_if_index)
+{
+ if (vec_len (wg_if_index_by_sw_if_index) <= sw_if_index)
+ return INDEX_INVALID;
+ u32 ti = wg_if_index_by_sw_if_index[sw_if_index];
+ if (ti == ~0)
+ return INDEX_INVALID;
+
+ return (ti);
+}
+
+static noise_remote_t *
+wg_remote_get (uint8_t public[NOISE_PUBLIC_KEY_LEN])
+{
+ wg_main_t *wmp = &wg_main;
+ wg_peer_t *peer = NULL;
+ wg_peer_t *peer_iter;
+ /* *INDENT-OFF* */
+ pool_foreach (peer_iter, wmp->peers,
+ ({
+ if (!memcmp (peer_iter->remote.r_public, public, NOISE_PUBLIC_KEY_LEN))
+ {
+ peer = peer_iter;
+ break;
+ }
+ }));
+ /* *INDENT-ON* */
+ return peer ? &peer->remote : NULL;
+}
+
+static uint32_t
+wg_index_set (noise_remote_t * remote)
+{
+ wg_main_t *wmp = &wg_main;
+ u32 rnd_seed = (u32) (vlib_time_now (wmp->vlib_main) * 1e6);
+ u32 ret =
+ wg_index_table_add (&wmp->index_table, remote->r_peer_idx, rnd_seed);
+ return ret;
+}
+
+static void
+wg_index_drop (uint32_t key)
+{
+ wg_main_t *wmp = &wg_main;
+ wg_index_table_del (&wmp->index_table, key);
+}
+
+static clib_error_t *
+wg_if_admin_up_down (vnet_main_t * vnm, u32 hw_if_index, u32 flags)
+{
+ vnet_hw_interface_t *hi;
+ index_t wgii;
+ u32 hw_flags;
+
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+ hw_flags = (flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP ?
+ VNET_HW_INTERFACE_FLAG_LINK_UP : 0);
+ vnet_hw_interface_set_flags (vnm, hw_if_index, hw_flags);
+
+ wgii = wg_if_find_by_sw_if_index (hi->sw_if_index);
+
+ wg_if_peer_walk (wg_if_get (wgii), wg_peer_if_admin_state_change, NULL);
+
+ return (NULL);
+}
+
+void
+wg_if_update_adj (vnet_main_t * vnm, u32 sw_if_index, adj_index_t ai)
+{
+ /* The peers manage the adjacencies */
+}
+
+
+/* *INDENT-OFF* */
+VNET_DEVICE_CLASS (wg_if_device_class) = {
+ .name = "Wireguard Tunnel",
+ .format_device_name = format_wg_if_name,
+ .admin_up_down_function = wg_if_admin_up_down,
+};
+
+VNET_HW_INTERFACE_CLASS(wg_hw_interface_class) = {
+ .name = "Wireguard",
+ .update_adjacency = wg_if_update_adj,
+ .flags = VNET_HW_INTERFACE_CLASS_FLAG_NBMA,
+};
+/* *INDENT-ON* */
+
+/*
+ * Maintain a bitmap of allocated wg_if instance numbers.
+ */
+#define WG_ITF_MAX_INSTANCE (16 * 1024)
+
+static u32
+wg_if_instance_alloc (u32 want)
+{
+ /*
+ * Check for dynamically allocated instance number.
+ */
+ if (~0 == want)
+ {
+ u32 bit;
+
+ bit = clib_bitmap_first_clear (wg_if_instances);
+ if (bit >= WG_ITF_MAX_INSTANCE)
+ {
+ return ~0;
+ }
+ wg_if_instances = clib_bitmap_set (wg_if_instances, bit, 1);
+ return bit;
+ }
+
+ /*
+ * In range?
+ */
+ if (want >= WG_ITF_MAX_INSTANCE)
+ {
+ return ~0;
+ }
+
+ /*
+ * Already in use?
+ */
+ if (clib_bitmap_get (wg_if_instances, want))
+ {
+ return ~0;
+ }
+
+ /*
+ * Grant allocation request.
+ */
+ wg_if_instances = clib_bitmap_set (wg_if_instances, want, 1);
+
+ return want;
+}
+
+static int
+wg_if_instance_free (u32 instance)
+{
+ if (instance >= WG_ITF_MAX_INSTANCE)
+ {
+ return -1;
+ }
+
+ if (clib_bitmap_get (wg_if_instances, instance) == 0)
+ {
+ return -1;
+ }
+
+ wg_if_instances = clib_bitmap_set (wg_if_instances, instance, 0);
+ return 0;
+}
+
+
+int
+wg_if_create (u32 user_instance,
+ const u8 private_key[NOISE_PUBLIC_KEY_LEN],
+ u16 port, const ip_address_t * src_ip, u32 * sw_if_indexp)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+ u32 instance, hw_if_index;
+ vnet_hw_interface_t *hi;
+ wg_if_t *wg_if;
+
+ ASSERT (sw_if_indexp);
+
+ *sw_if_indexp = (u32) ~ 0;
+
+ /*
+ * Allocate a wg_if instance. Either select on dynamically
+ * or try to use the desired user_instance number.
+ */
+ instance = wg_if_instance_alloc (user_instance);
+ if (instance == ~0)
+ return VNET_API_ERROR_INVALID_REGISTRATION;
+
+ pool_get (wg_if_pool, wg_if);
+
+ /* tunnel index (or instance) */
+ u32 t_idx = wg_if - wg_if_pool;
+
+ wg_if->user_instance = instance;
+ if (~0 == wg_if->user_instance)
+ wg_if->user_instance = t_idx;
+
+ udp_dst_port_info_t *pi = udp_get_dst_port_info (&udp_main, port, UDP_IP4);
+ if (pi)
+ return (VNET_API_ERROR_VALUE_EXIST);
+ udp_register_dst_port (vlib_get_main (), port, wg_input_node.index, 1);
+
+ vec_validate_init_empty (wg_if_index_by_port, port, INDEX_INVALID);
+ wg_if_index_by_port[port] = wg_if - wg_if_pool;
+
+ wg_if->port = port;
+ struct noise_upcall upcall;
+ upcall.u_remote_get = wg_remote_get;
+ upcall.u_index_set = wg_index_set;
+ upcall.u_index_drop = wg_index_drop;
+
+ noise_local_init (&wg_if->local, &upcall);
+ noise_local_set_private (&wg_if->local, private_key);
+ cookie_checker_update (&wg_if->cookie_checker, wg_if->local.l_public);
+
+ hw_if_index = vnet_register_interface (vnm,
+ wg_if_device_class.index,
+ t_idx,
+ wg_hw_interface_class.index, t_idx);
+
+ hi = vnet_get_hw_interface (vnm, hw_if_index);
+
+ vec_validate_init_empty (wg_if_index_by_sw_if_index, hi->sw_if_index,
+ INDEX_INVALID);
+ wg_if_index_by_sw_if_index[hi->sw_if_index] = t_idx;
+
+ ip_address_copy (&wg_if->src_ip, src_ip);
+ wg_if->sw_if_index = *sw_if_indexp = hi->sw_if_index;
+
+ return 0;
+}
+
+int
+wg_if_delete (u32 sw_if_index)
+{
+ vnet_main_t *vnm = vnet_get_main ();
+
+ if (pool_is_free_index (vnm->interface_main.sw_interfaces, sw_if_index))
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ vnet_hw_interface_t *hw = vnet_get_sup_hw_interface (vnm, sw_if_index);
+ if (hw == 0 || hw->dev_class_index != wg_if_device_class.index)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ wg_if_t *wg_if;
+ wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
+ if (NULL == wg_if)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ if (wg_if_instance_free (hw->dev_instance) < 0)
+ return VNET_API_ERROR_INVALID_SW_IF_INDEX;
+
+ wg_if_index_by_port[wg_if->port] = INDEX_INVALID;
+ vnet_delete_hw_interface (vnm, hw->hw_if_index);
+ pool_put (wg_if_pool, wg_if);
+
+ return 0;
+}
+
+void
+wg_if_peer_add (wg_if_t * wgi, index_t peeri)
+{
+ hash_set (wgi->peers, peeri, peeri);
+
+ if (1 == hash_elts (wgi->peers))
+ vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
+ wgi->sw_if_index, 1, 0, 0);
+}
+
+void
+wg_if_peer_remove (wg_if_t * wgi, index_t peeri)
+{
+ hash_unset (wgi->peers, peeri);
+
+ if (0 == hash_elts (wgi->peers))
+ vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
+ wgi->sw_if_index, 0, 0, 0);
+}
+
+void
+wg_if_walk (wg_if_walk_cb_t fn, void *data)
+{
+ index_t wgii;
+
+ /* *INDENT-OFF* */
+ pool_foreach_index (wgii, wg_if_pool,
+ {
+ if (WALK_STOP == fn(wgii, data))
+ break;
+ });
+ /* *INDENT-ON* */
+}
+
+void
+wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data)
+{
+ index_t peeri, val;
+
+ /* *INDENT-OFF* */
+ hash_foreach (peeri, val, wgi->peers,
+ {
+ if (WALK_STOP == fn(wgi, peeri, data))
+ break;
+ });
+ /* *INDENT-ON* */
+}
+
+
+static void
+wg_if_table_bind_v4 (ip4_main_t * im,
+ uword opaque,
+ u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
+{
+ wg_if_t *wg_if;
+
+ wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
+ if (NULL == wg_if)
+ return;
+
+ wg_peer_table_bind_ctx_t ctx = {
+ .af = AF_IP4,
+ .old_fib_index = old_fib_index,
+ .new_fib_index = new_fib_index,
+ };
+
+ wg_if_peer_walk (wg_if, wg_peer_if_table_change, &ctx);
+}
+
+static void
+wg_if_table_bind_v6 (ip6_main_t * im,
+ uword opaque,
+ u32 sw_if_index, u32 new_fib_index, u32 old_fib_index)
+{
+ wg_if_t *wg_if;
+
+ wg_if = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
+ if (NULL == wg_if)
+ return;
+
+ wg_peer_table_bind_ctx_t ctx = {
+ .af = AF_IP6,
+ .old_fib_index = old_fib_index,
+ .new_fib_index = new_fib_index,
+ };
+
+ wg_if_peer_walk (wg_if, wg_peer_if_table_change, &ctx);
+}
+
+static clib_error_t *
+wg_if_module_init (vlib_main_t * vm)
+{
+ {
+ ip4_table_bind_callback_t cb = {
+ .function = wg_if_table_bind_v4,
+ };
+ vec_add1 (ip4_main.table_bind_callbacks, cb);
+ }
+ {
+ ip6_table_bind_callback_t cb = {
+ .function = wg_if_table_bind_v6,
+ };
+ vec_add1 (ip6_main.table_bind_callbacks, cb);
+ }
+
+ return (NULL);
+}
+
+/* *INDENT-OFF* */
+VLIB_INIT_FUNCTION (wg_if_module_init) =
+{
+ .runs_after = VLIB_INITS("ip_main_init"),
+};
+/* *INDENT-ON* */
+
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_if.h b/src/plugins/wireguard/wireguard_if.h
new file mode 100644
index 00000000000..9e6b6190e0e
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_if.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __WG_ITF_H__
+#define __WG_ITF_H__
+
+#include <wireguard/wireguard_index_table.h>
+#include <wireguard/wireguard_messages.h>
+
+typedef struct wg_if_t_
+{
+ int user_instance;
+ u32 sw_if_index;
+
+ // Interface params
+ noise_local_t local;
+ cookie_checker_t cookie_checker;
+ u16 port;
+
+ wg_index_table_t index_table;
+
+ /* Source IP address for originated packets */
+ ip_address_t src_ip;
+
+ /* hash table of peers on this link */
+ uword *peers;
+} wg_if_t;
+
+
+int wg_if_create (u32 user_instance,
+ const u8 private_key_64[NOISE_PUBLIC_KEY_LEN],
+ u16 port, const ip_address_t * src_ip, u32 * sw_if_indexp);
+int wg_if_delete (u32 sw_if_index);
+index_t wg_if_find_by_sw_if_index (u32 sw_if_index);
+
+u8 *format_wg_if (u8 * s, va_list * va);
+
+typedef walk_rc_t (*wg_if_walk_cb_t) (index_t wgi, void *data);
+void wg_if_walk (wg_if_walk_cb_t fn, void *data);
+
+typedef walk_rc_t (*wg_if_peer_walk_cb_t) (wg_if_t * wgi, index_t peeri,
+ void *data);
+void wg_if_peer_walk (wg_if_t * wgi, wg_if_peer_walk_cb_t fn, void *data);
+
+void wg_if_peer_add (wg_if_t * wgi, index_t peeri);
+void wg_if_peer_remove (wg_if_t * wgi, index_t peeri);
+
+/**
+ * Data-plane exposed functions
+ */
+extern wg_if_t *wg_if_pool;
+
+static_always_inline wg_if_t *
+wg_if_get (index_t wgii)
+{
+ if (INDEX_INVALID == wgii)
+ return (NULL);
+ return (pool_elt_at_index (wg_if_pool, wgii));
+}
+
+extern index_t *wg_if_index_by_port;
+
+static_always_inline wg_if_t *
+wg_if_get_by_port (u16 port)
+{
+ if (vec_len (wg_if_index_by_port) < port)
+ return (NULL);
+ if (INDEX_INVALID == wg_if_index_by_port[port])
+ return (NULL);
+ return (wg_if_get (wg_if_index_by_port[port]));
+}
+
+
+#endif
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_index_table.c b/src/plugins/wireguard/wireguard_index_table.c
new file mode 100755
index 00000000000..5f81204b4c0
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_index_table.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vppinfra/hash.h>
+#include <vppinfra/pool.h>
+#include <vppinfra/random.h>
+#include <wireguard/wireguard_index_table.h>
+
+u32
+wg_index_table_add (wg_index_table_t * table, u32 peer_pool_idx, u32 rnd_seed)
+{
+ u32 key;
+
+ while (1)
+ {
+ key = random_u32 (&rnd_seed);
+ if (hash_get (table->hash, key))
+ continue;
+
+ hash_set (table->hash, key, peer_pool_idx);
+ break;
+ }
+ return key;
+}
+
+void
+wg_index_table_del (wg_index_table_t * table, u32 key)
+{
+ uword *p;
+ p = hash_get (table->hash, key);
+ if (p)
+ hash_unset (table->hash, key);
+}
+
+u32 *
+wg_index_table_lookup (const wg_index_table_t * table, u32 key)
+{
+ uword *p;
+
+ p = hash_get (table->hash, key);
+ return (u32 *) p;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_index_table.h b/src/plugins/wireguard/wireguard_index_table.h
new file mode 100755
index 00000000000..67cae1f49d5
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_index_table.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_wg_index_table_h__
+#define __included_wg_index_table_h__
+
+#include <vppinfra/types.h>
+
+typedef struct
+{
+ uword *hash;
+} wg_index_table_t;
+
+u32 wg_index_table_add (wg_index_table_t * table, u32 peer_pool_idx,
+ u32 rnd_seed);
+void wg_index_table_del (wg_index_table_t * table, u32 key);
+u32 *wg_index_table_lookup (const wg_index_table_t * table, u32 key);
+
+#endif //__included_wg_index_table_h__
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_input.c b/src/plugins/wireguard/wireguard_input.c
new file mode 100755
index 00000000000..832ad031daa
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_input.c
@@ -0,0 +1,451 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vppinfra/error.h>
+#include <wireguard/wireguard.h>
+
+#include <wireguard/wireguard_send.h>
+#include <wireguard/wireguard_if.h>
+
+#define foreach_wg_input_error \
+ _(NONE, "No error") \
+ _(HANDSHAKE_MAC, "Invalid MAC handshake") \
+ _(PEER, "Peer error") \
+ _(INTERFACE, "Interface error") \
+ _(DECRYPTION, "Failed during decryption") \
+ _(KEEPALIVE_SEND, "Failed while sending Keepalive") \
+ _(HANDSHAKE_SEND, "Failed while sending Handshake") \
+ _(UNDEFINED, "Undefined error")
+
+typedef enum
+{
+#define _(sym,str) WG_INPUT_ERROR_##sym,
+ foreach_wg_input_error
+#undef _
+ WG_INPUT_N_ERROR,
+} wg_input_error_t;
+
+static char *wg_input_error_strings[] = {
+#define _(sym,string) string,
+ foreach_wg_input_error
+#undef _
+};
+
+typedef struct
+{
+ message_type_t type;
+ u16 current_length;
+ bool is_keepalive;
+
+} wg_input_trace_t;
+
+u8 *
+format_wg_message_type (u8 * s, va_list * args)
+{
+ message_type_t type = va_arg (*args, message_type_t);
+
+ switch (type)
+ {
+#define _(v,a) case MESSAGE_##v: return (format (s, "%s", a));
+ foreach_wg_message_type
+#undef _
+ }
+ return (format (s, "unknown"));
+}
+
+/* packet trace format function */
+static u8 *
+format_wg_input_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+
+ wg_input_trace_t *t = va_arg (*args, wg_input_trace_t *);
+
+ s = format (s, "WG input: \n");
+ s = format (s, " Type: %U\n", format_wg_message_type, t->type);
+ s = format (s, " Length: %d\n", t->current_length);
+ s = format (s, " Keepalive: %s", t->is_keepalive ? "true" : "false");
+
+ return s;
+}
+
+typedef enum
+{
+ WG_INPUT_NEXT_IP4_INPUT,
+ WG_INPUT_NEXT_PUNT,
+ WG_INPUT_NEXT_ERROR,
+ WG_INPUT_N_NEXT,
+} wg_input_next_t;
+
+/* static void */
+/* set_peer_address (wg_peer_t * peer, ip4_address_t ip4, u16 udp_port) */
+/* { */
+/* if (peer) */
+/* { */
+/* ip46_address_set_ip4 (&peer->dst.addr, &ip4); */
+/* peer->dst.port = udp_port; */
+/* } */
+/* } */
+
+static wg_input_error_t
+wg_handshake_process (vlib_main_t * vm, wg_main_t * wmp, vlib_buffer_t * b)
+{
+ enum cookie_mac_state mac_state;
+ bool packet_needs_cookie;
+ bool under_load;
+ wg_if_t *wg_if;
+ wg_peer_t *peer = NULL;
+
+ void *current_b_data = vlib_buffer_get_current (b);
+
+ udp_header_t *uhd = current_b_data - sizeof (udp_header_t);
+ ip4_header_t *iph =
+ current_b_data - sizeof (udp_header_t) - sizeof (ip4_header_t);
+ ip4_address_t ip4_src = iph->src_address;
+ u16 udp_src_port = clib_host_to_net_u16 (uhd->src_port);;
+ u16 udp_dst_port = clib_host_to_net_u16 (uhd->dst_port);;
+
+ message_header_t *header = current_b_data;
+ under_load = false;
+
+ wg_if = wg_if_get_by_port (udp_dst_port);
+
+ if (NULL == wg_if)
+ return WG_INPUT_ERROR_INTERFACE;
+
+ if (header->type == MESSAGE_HANDSHAKE_COOKIE)
+ {
+ message_handshake_cookie_t *packet =
+ (message_handshake_cookie_t *) current_b_data;
+ u32 *entry =
+ wg_index_table_lookup (&wmp->index_table, packet->receiver_index);
+ if (entry)
+ {
+ peer = pool_elt_at_index (wmp->peers, *entry);
+ }
+ if (!peer)
+ return WG_INPUT_ERROR_PEER;
+
+ // TODO: Implement cookie_maker_consume_payload
+
+ return WG_INPUT_ERROR_NONE;
+ }
+
+ u32 len = (header->type == MESSAGE_HANDSHAKE_INITIATION ?
+ sizeof (message_handshake_initiation_t) :
+ sizeof (message_handshake_response_t));
+
+ message_macs_t *macs = (message_macs_t *)
+ ((u8 *) current_b_data + len - sizeof (*macs));
+
+ mac_state =
+ cookie_checker_validate_macs (vm, &wg_if->cookie_checker, macs,
+ current_b_data, len, under_load, ip4_src,
+ udp_src_port);
+
+ if ((under_load && mac_state == VALID_MAC_WITH_COOKIE)
+ || (!under_load && mac_state == VALID_MAC_BUT_NO_COOKIE))
+ packet_needs_cookie = false;
+ else if (under_load && mac_state == VALID_MAC_BUT_NO_COOKIE)
+ packet_needs_cookie = true;
+ else
+ return WG_INPUT_ERROR_HANDSHAKE_MAC;
+
+ switch (header->type)
+ {
+ case MESSAGE_HANDSHAKE_INITIATION:
+ {
+ message_handshake_initiation_t *message = current_b_data;
+
+ if (packet_needs_cookie)
+ {
+ // TODO: Add processing
+ }
+ noise_remote_t *rp;
+
+ if (noise_consume_initiation
+ (wmp->vlib_main, &wg_if->local, &rp, message->sender_index,
+ message->unencrypted_ephemeral, message->encrypted_static,
+ message->encrypted_timestamp))
+ {
+ peer = pool_elt_at_index (wmp->peers, rp->r_peer_idx);
+ }
+
+ if (!peer)
+ return WG_INPUT_ERROR_PEER;
+
+ // set_peer_address (peer, ip4_src, udp_src_port);
+ if (PREDICT_FALSE (!wg_send_handshake_response (vm, peer)))
+ {
+ vlib_node_increment_counter (vm, wg_input_node.index,
+ WG_INPUT_ERROR_HANDSHAKE_SEND, 1);
+ }
+ break;
+ }
+ case MESSAGE_HANDSHAKE_RESPONSE:
+ {
+ message_handshake_response_t *resp = current_b_data;
+ u32 *entry =
+ wg_index_table_lookup (&wmp->index_table, resp->receiver_index);
+ if (entry)
+ {
+ peer = pool_elt_at_index (wmp->peers, *entry);
+ if (!peer || peer->is_dead)
+ return WG_INPUT_ERROR_PEER;
+ }
+
+ if (!noise_consume_response
+ (wmp->vlib_main, &peer->remote, resp->sender_index,
+ resp->receiver_index, resp->unencrypted_ephemeral,
+ resp->encrypted_nothing))
+ {
+ return WG_INPUT_ERROR_PEER;
+ }
+ if (packet_needs_cookie)
+ {
+ // TODO: Add processing
+ }
+
+ // set_peer_address (peer, ip4_src, udp_src_port);
+ if (noise_remote_begin_session (wmp->vlib_main, &peer->remote))
+ {
+ wg_timers_session_derived (peer);
+ wg_timers_handshake_complete (peer);
+ if (PREDICT_FALSE (!wg_send_keepalive (vm, peer)))
+ {
+ vlib_node_increment_counter (vm, wg_input_node.index,
+ WG_INPUT_ERROR_KEEPALIVE_SEND,
+ 1);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ wg_timers_any_authenticated_packet_received (peer);
+ wg_timers_any_authenticated_packet_traversal (peer);
+ return WG_INPUT_ERROR_NONE;
+}
+
+static_always_inline bool
+fib_prefix_is_cover_addr_4 (const fib_prefix_t * p1,
+ const ip4_address_t * ip4)
+{
+ switch (p1->fp_proto)
+ {
+ case FIB_PROTOCOL_IP4:
+ return (ip4_destination_matches_route (&ip4_main,
+ &p1->fp_addr.ip4,
+ ip4, p1->fp_len) != 0);
+ case FIB_PROTOCOL_IP6:
+ return (false);
+ case FIB_PROTOCOL_MPLS:
+ break;
+ }
+ return (false);
+}
+
+VLIB_NODE_FN (wg_input_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ message_type_t header_type;
+ u32 n_left_from;
+ u32 *from;
+ vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
+ u16 nexts[VLIB_FRAME_SIZE], *next;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ b = bufs;
+ next = nexts;
+
+ vlib_get_buffers (vm, from, bufs, n_left_from);
+
+ wg_main_t *wmp = &wg_main;
+ wg_peer_t *peer = NULL;
+
+ while (n_left_from > 0)
+ {
+ bool is_keepalive = false;
+ next[0] = WG_INPUT_NEXT_PUNT;
+ header_type =
+ ((message_header_t *) vlib_buffer_get_current (b[0]))->type;
+
+ switch (header_type)
+ {
+ case MESSAGE_HANDSHAKE_INITIATION:
+ case MESSAGE_HANDSHAKE_RESPONSE:
+ case MESSAGE_HANDSHAKE_COOKIE:
+ {
+ wg_input_error_t ret = wg_handshake_process (vm, wmp, b[0]);
+ if (ret != WG_INPUT_ERROR_NONE)
+ {
+ next[0] = WG_INPUT_NEXT_ERROR;
+ b[0]->error = node->errors[ret];
+ }
+ break;
+ }
+ case MESSAGE_DATA:
+ {
+ message_data_t *data = vlib_buffer_get_current (b[0]);
+ u32 *entry =
+ wg_index_table_lookup (&wmp->index_table, data->receiver_index);
+
+ if (entry)
+ {
+ peer = pool_elt_at_index (wmp->peers, *entry);
+ if (!peer)
+ {
+ next[0] = WG_INPUT_NEXT_ERROR;
+ b[0]->error = node->errors[WG_INPUT_ERROR_PEER];
+ goto out;
+ }
+ }
+
+ u16 encr_len = b[0]->current_length - sizeof (message_data_t);
+ u16 decr_len = encr_len - NOISE_AUTHTAG_LEN;
+ u8 *decr_data = clib_mem_alloc (decr_len);
+
+ enum noise_state_crypt state_cr =
+ noise_remote_decrypt (wmp->vlib_main,
+ &peer->remote,
+ data->receiver_index,
+ data->counter,
+ data->encrypted_data,
+ encr_len,
+ decr_data);
+
+ switch (state_cr)
+ {
+ case SC_OK:
+ break;
+ case SC_CONN_RESET:
+ wg_timers_handshake_complete (peer);
+ break;
+ case SC_KEEP_KEY_FRESH:
+ if (PREDICT_FALSE (!wg_send_handshake (vm, peer, false)))
+ {
+ vlib_node_increment_counter (vm, wg_input_node.index,
+ WG_INPUT_ERROR_HANDSHAKE_SEND,
+ 1);
+ }
+ break;
+ case SC_FAILED:
+ next[0] = WG_INPUT_NEXT_ERROR;
+ b[0]->error = node->errors[WG_INPUT_ERROR_DECRYPTION];
+ goto out;
+ default:
+ break;
+ }
+
+ clib_memcpy (vlib_buffer_get_current (b[0]), decr_data, decr_len);
+ b[0]->current_length = decr_len;
+ b[0]->flags &= ~VNET_BUFFER_F_OFFLOAD_UDP_CKSUM;
+
+ clib_mem_free (decr_data);
+
+ wg_timers_any_authenticated_packet_received (peer);
+ wg_timers_any_authenticated_packet_traversal (peer);
+
+ if (decr_len == 0)
+ {
+ is_keepalive = true;
+ goto out;
+ }
+
+ wg_timers_data_received (peer);
+
+ ip4_header_t *iph = vlib_buffer_get_current (b[0]);
+
+ const wg_peer_allowed_ip_t *allowed_ip;
+ bool allowed = false;
+
+ /*
+ * we could make this into an ACL, but the expectation
+ * is that there aren't many allowed IPs and thus a linear
+ * walk is fater than an ACL
+ */
+ vec_foreach (allowed_ip, peer->allowed_ips)
+ {
+ if (fib_prefix_is_cover_addr_4 (&allowed_ip->prefix,
+ &iph->src_address))
+ {
+ allowed = true;
+ break;
+ }
+ }
+ if (allowed)
+ {
+ vnet_buffer (b[0])->sw_if_index[VLIB_RX] =
+ peer->wg_sw_if_index;
+ next[0] = WG_INPUT_NEXT_IP4_INPUT;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ out:
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ wg_input_trace_t *t = vlib_add_trace (vm, node, b[0], sizeof (*t));
+ t->type = header_type;
+ t->current_length = b[0]->current_length;
+ t->is_keepalive = is_keepalive;
+ }
+ n_left_from -= 1;
+ next += 1;
+ b += 1;
+ }
+ vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
+
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (wg_input_node) =
+{
+ .name = "wg-input",
+ .vector_size = sizeof (u32),
+ .format_trace = format_wg_input_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (wg_input_error_strings),
+ .error_strings = wg_input_error_strings,
+ .n_next_nodes = WG_INPUT_N_NEXT,
+ /* edit / add dispositions here */
+ .next_nodes = {
+ [WG_INPUT_NEXT_IP4_INPUT] = "ip4-input-no-checksum",
+ [WG_INPUT_NEXT_PUNT] = "error-punt",
+ [WG_INPUT_NEXT_ERROR] = "error-drop",
+ },
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_key.c b/src/plugins/wireguard/wireguard_key.c
new file mode 100755
index 00000000000..db8c4864492
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_key.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Copyright (c) 2005-2011 Jouni Malinen <j@w1.fi>.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <wireguard/wireguard_key.h>
+#include <openssl/evp.h>
+
+bool
+curve25519_gen_shared (u8 shared_key[CURVE25519_KEY_SIZE],
+ const u8 secret_key[CURVE25519_KEY_SIZE],
+ const u8 basepoint[CURVE25519_KEY_SIZE])
+{
+
+ bool ret;
+ EVP_PKEY_CTX *ctx;
+ size_t key_len;
+
+ EVP_PKEY *peerkey = NULL;
+ EVP_PKEY *pkey =
+ EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, secret_key,
+ CURVE25519_KEY_SIZE);
+
+ ret = true;
+
+ ctx = EVP_PKEY_CTX_new (pkey, NULL);
+ if (EVP_PKEY_derive_init (ctx) <= 0)
+ {
+ ret = false;
+ goto out;
+ }
+
+ peerkey =
+ EVP_PKEY_new_raw_public_key (EVP_PKEY_X25519, NULL, basepoint,
+ CURVE25519_KEY_SIZE);
+ if (EVP_PKEY_derive_set_peer (ctx, peerkey) <= 0)
+ {
+ ret = false;
+ goto out;
+ }
+
+ key_len = CURVE25519_KEY_SIZE;
+ if (EVP_PKEY_derive (ctx, shared_key, &key_len) <= 0)
+ {
+ ret = false;
+ }
+
+out:
+ EVP_PKEY_CTX_free (ctx);
+ EVP_PKEY_free (pkey);
+ EVP_PKEY_free (peerkey);
+ return ret;
+}
+
+bool
+curve25519_gen_public (u8 public_key[CURVE25519_KEY_SIZE],
+ const u8 secret_key[CURVE25519_KEY_SIZE])
+{
+ size_t pub_len;
+ EVP_PKEY *pkey =
+ EVP_PKEY_new_raw_private_key (EVP_PKEY_X25519, NULL, secret_key,
+ CURVE25519_KEY_SIZE);
+ pub_len = CURVE25519_KEY_SIZE;
+ if (!EVP_PKEY_get_raw_public_key (pkey, public_key, &pub_len))
+ {
+ EVP_PKEY_free (pkey);
+ return false;
+ }
+ EVP_PKEY_free (pkey);
+ return true;
+}
+
+bool
+curve25519_gen_secret (u8 secret_key[CURVE25519_KEY_SIZE])
+{
+ size_t secret_len;
+ EVP_PKEY *pkey = NULL;
+ EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id (EVP_PKEY_X25519, NULL);
+ EVP_PKEY_keygen_init (pctx);
+ EVP_PKEY_keygen (pctx, &pkey);
+ EVP_PKEY_CTX_free (pctx);
+
+ secret_len = CURVE25519_KEY_SIZE;
+ if (!EVP_PKEY_get_raw_private_key (pkey, secret_key, &secret_len))
+ {
+ EVP_PKEY_free (pkey);
+ return false;
+ }
+ EVP_PKEY_free (pkey);
+ return true;
+}
+
+bool
+key_to_base64 (const u8 * src, size_t src_len, u8 * out)
+{
+ if (!EVP_EncodeBlock (out, src, src_len))
+ return false;
+ return true;
+}
+
+bool
+key_from_base64 (const u8 * src, size_t src_len, u8 * out)
+{
+ if (EVP_DecodeBlock (out, src, src_len - 1) <= 0)
+ return false;
+ return true;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_key.h b/src/plugins/wireguard/wireguard_key.h
new file mode 100755
index 00000000000..6decca67205
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_key.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Copyright (c) 2005 Jouni Malinen <j@w1.fi>.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_wg_convert_h__
+#define __included_wg_convert_h__
+
+#include <stdbool.h>
+#include <vlib/vlib.h>
+
+enum curve25519_lengths
+{
+ CURVE25519_KEY_SIZE = 32
+};
+
+bool curve25519_gen_shared (u8 shared_key[CURVE25519_KEY_SIZE],
+ const u8 secret_key[CURVE25519_KEY_SIZE],
+ const u8 basepoint[CURVE25519_KEY_SIZE]);
+bool curve25519_gen_secret (u8 secret[CURVE25519_KEY_SIZE]);
+bool curve25519_gen_public (u8 public_key[CURVE25519_KEY_SIZE],
+ const u8 secret_key[CURVE25519_KEY_SIZE]);
+
+bool key_to_base64 (const u8 * src, size_t src_len, u8 * out);
+bool key_from_base64 (const u8 * src, size_t src_len, u8 * out);
+
+#endif /* __included_wg_convert_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_messages.h b/src/plugins/wireguard/wireguard_messages.h
new file mode 100755
index 00000000000..3587c5c8a45
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_messages.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_wg_messages_h__
+#define __included_wg_messages_h__
+
+#include <stdint.h>
+#include <wireguard/wireguard_noise.h>
+#include <wireguard/wireguard_cookie.h>
+
+#define WG_TICK 0.01 /**< WG tick period (s) */
+#define WHZ (u32) (1/WG_TICK) /**< WG tick frequency */
+
+#define NOISE_KEY_LEN_BASE64 ((((NOISE_PUBLIC_KEY_LEN) + 2) / 3) * 4 + 1)
+#define noise_encrypted_len(plain_len) ((plain_len) + NOISE_AUTHTAG_LEN)
+
+enum limits
+{
+ REKEY_TIMEOUT = 5,
+ REKEY_TIMEOUT_JITTER = WHZ / 3,
+ KEEPALIVE_TIMEOUT = 10,
+ MAX_TIMER_HANDSHAKES = 90 / REKEY_TIMEOUT,
+ MAX_PEERS = 1U << 20
+};
+
+#define foreach_wg_message_type \
+ _(INVALID, "Invalid") \
+ _(HANDSHAKE_INITIATION, "Handshake initiation") \
+ _(HANDSHAKE_RESPONSE, "Handshake response") \
+ _(HANDSHAKE_COOKIE, "Handshake cookie") \
+ _(DATA, "Data") \
+
+typedef enum message_type
+{
+#define _(v,s) MESSAGE_##v,
+ foreach_wg_message_type
+#undef _
+} message_type_t;
+
+typedef struct message_header
+{
+ message_type_t type;
+} message_header_t;
+
+typedef struct message_handshake_initiation
+{
+ message_header_t header;
+ u32 sender_index;
+ u8 unencrypted_ephemeral[NOISE_PUBLIC_KEY_LEN];
+ u8 encrypted_static[noise_encrypted_len (NOISE_PUBLIC_KEY_LEN)];
+ u8 encrypted_timestamp[noise_encrypted_len (NOISE_TIMESTAMP_LEN)];
+ message_macs_t macs;
+} message_handshake_initiation_t;
+
+typedef struct message_handshake_response
+{
+ message_header_t header;
+ u32 sender_index;
+ u32 receiver_index;
+ u8 unencrypted_ephemeral[NOISE_PUBLIC_KEY_LEN];
+ u8 encrypted_nothing[noise_encrypted_len (0)];
+ message_macs_t macs;
+} message_handshake_response_t;
+
+typedef struct message_handshake_cookie
+{
+ message_header_t header;
+ u32 receiver_index;
+ u8 nonce[COOKIE_NONCE_SIZE];
+ u8 encrypted_cookie[noise_encrypted_len (COOKIE_MAC_SIZE)];
+} message_handshake_cookie_t;
+
+typedef struct message_data
+{
+ message_header_t header;
+ u32 receiver_index;
+ u64 counter;
+ u8 encrypted_data[];
+} message_data_t;
+
+#define message_data_len(plain_len) \
+ (noise_encrypted_len(plain_len) + sizeof(message_data_t))
+
+#endif /* __included_wg_messages_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_noise.c b/src/plugins/wireguard/wireguard_noise.c
new file mode 100755
index 00000000000..666618a2a51
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_noise.c
@@ -0,0 +1,985 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Copyright (c) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>.
+ * Copyright (c) 2019-2020 Matt Dunwoodie <ncon@noconroy.net>.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <openssl/hmac.h>
+#include <wireguard/wireguard.h>
+
+/* This implements Noise_IKpsk2:
+ *
+ * <- s
+ * ******
+ * -> e, es, s, ss, {t}
+ * <- e, ee, se, psk, {}
+ */
+
+/* Private functions */
+static noise_keypair_t *noise_remote_keypair_allocate (noise_remote_t *);
+static void noise_remote_keypair_free (vlib_main_t * vm, noise_remote_t *,
+ noise_keypair_t **);
+static uint32_t noise_remote_handshake_index_get (noise_remote_t *);
+static void noise_remote_handshake_index_drop (noise_remote_t *);
+
+static uint64_t noise_counter_send (noise_counter_t *);
+static bool noise_counter_recv (noise_counter_t *, uint64_t);
+
+static void noise_kdf (uint8_t *, uint8_t *, uint8_t *, const uint8_t *,
+ size_t, size_t, size_t, size_t,
+ const uint8_t[NOISE_HASH_LEN]);
+static bool noise_mix_dh (uint8_t[NOISE_HASH_LEN],
+ uint8_t[NOISE_SYMMETRIC_KEY_LEN],
+ const uint8_t[NOISE_PUBLIC_KEY_LEN],
+ const uint8_t[NOISE_PUBLIC_KEY_LEN]);
+static bool noise_mix_ss (uint8_t ck[NOISE_HASH_LEN],
+ uint8_t key[NOISE_SYMMETRIC_KEY_LEN],
+ const uint8_t ss[NOISE_PUBLIC_KEY_LEN]);
+static void noise_mix_hash (uint8_t[NOISE_HASH_LEN], const uint8_t *, size_t);
+static void noise_mix_psk (uint8_t[NOISE_HASH_LEN],
+ uint8_t[NOISE_HASH_LEN],
+ uint8_t[NOISE_SYMMETRIC_KEY_LEN],
+ const uint8_t[NOISE_SYMMETRIC_KEY_LEN]);
+static void noise_param_init (uint8_t[NOISE_HASH_LEN],
+ uint8_t[NOISE_HASH_LEN],
+ const uint8_t[NOISE_PUBLIC_KEY_LEN]);
+
+static void noise_msg_encrypt (vlib_main_t * vm, uint8_t *, uint8_t *, size_t,
+ uint32_t key_idx, uint8_t[NOISE_HASH_LEN]);
+static bool noise_msg_decrypt (vlib_main_t * vm, uint8_t *, uint8_t *, size_t,
+ uint32_t key_idx, uint8_t[NOISE_HASH_LEN]);
+static void noise_msg_ephemeral (uint8_t[NOISE_HASH_LEN],
+ uint8_t[NOISE_HASH_LEN],
+ const uint8_t src[NOISE_PUBLIC_KEY_LEN]);
+
+static void noise_tai64n_now (uint8_t[NOISE_TIMESTAMP_LEN]);
+
+static void secure_zero_memory (void *v, size_t n);
+
+/* Set/Get noise parameters */
+void
+noise_local_init (noise_local_t * l, struct noise_upcall *upcall)
+{
+ clib_memset (l, 0, sizeof (*l));
+ l->l_upcall = *upcall;
+}
+
+bool
+noise_local_set_private (noise_local_t * l,
+ const uint8_t private[NOISE_PUBLIC_KEY_LEN])
+{
+ clib_memcpy (l->l_private, private, NOISE_PUBLIC_KEY_LEN);
+ l->l_has_identity = curve25519_gen_public (l->l_public, private);
+
+ return l->l_has_identity;
+}
+
+bool
+noise_local_keys (noise_local_t * l, uint8_t public[NOISE_PUBLIC_KEY_LEN],
+ uint8_t private[NOISE_PUBLIC_KEY_LEN])
+{
+ if (l->l_has_identity)
+ {
+ if (public != NULL)
+ clib_memcpy (public, l->l_public, NOISE_PUBLIC_KEY_LEN);
+ if (private != NULL)
+ clib_memcpy (private, l->l_private, NOISE_PUBLIC_KEY_LEN);
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+}
+
+void
+noise_remote_init (noise_remote_t * r, uint32_t peer_pool_idx,
+ const uint8_t public[NOISE_PUBLIC_KEY_LEN],
+ noise_local_t * l)
+{
+ clib_memset (r, 0, sizeof (*r));
+ clib_memcpy (r->r_public, public, NOISE_PUBLIC_KEY_LEN);
+ r->r_peer_idx = peer_pool_idx;
+
+ ASSERT (l != NULL);
+ r->r_local = l;
+ r->r_handshake.hs_state = HS_ZEROED;
+ noise_remote_precompute (r);
+}
+
+bool
+noise_remote_set_psk (noise_remote_t * r,
+ uint8_t psk[NOISE_SYMMETRIC_KEY_LEN])
+{
+ int same;
+ same = !clib_memcmp (r->r_psk, psk, NOISE_SYMMETRIC_KEY_LEN);
+ if (!same)
+ {
+ clib_memcpy (r->r_psk, psk, NOISE_SYMMETRIC_KEY_LEN);
+ }
+ return same == 0;
+}
+
+bool
+noise_remote_keys (noise_remote_t * r, uint8_t public[NOISE_PUBLIC_KEY_LEN],
+ uint8_t psk[NOISE_SYMMETRIC_KEY_LEN])
+{
+ static uint8_t null_psk[NOISE_SYMMETRIC_KEY_LEN];
+ int ret;
+
+ if (public != NULL)
+ clib_memcpy (public, r->r_public, NOISE_PUBLIC_KEY_LEN);
+
+ if (psk != NULL)
+ clib_memcpy (psk, r->r_psk, NOISE_SYMMETRIC_KEY_LEN);
+ ret = clib_memcmp (r->r_psk, null_psk, NOISE_SYMMETRIC_KEY_LEN);
+
+ return ret;
+}
+
+void
+noise_remote_precompute (noise_remote_t * r)
+{
+ noise_local_t *l = r->r_local;
+ if (!l->l_has_identity)
+ clib_memset (r->r_ss, 0, NOISE_PUBLIC_KEY_LEN);
+ else if (!curve25519_gen_shared (r->r_ss, l->l_private, r->r_public))
+ clib_memset (r->r_ss, 0, NOISE_PUBLIC_KEY_LEN);
+
+ noise_remote_handshake_index_drop (r);
+ secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake));
+}
+
+/* Handshake functions */
+bool
+noise_create_initiation (vlib_main_t * vm, noise_remote_t * r,
+ uint32_t * s_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN],
+ uint8_t es[NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN],
+ uint8_t ets[NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN])
+{
+ noise_handshake_t *hs = &r->r_handshake;
+ noise_local_t *l = r->r_local;
+ uint8_t _key[NOISE_SYMMETRIC_KEY_LEN];
+ uint32_t key_idx;
+ uint8_t *key;
+ int ret = false;
+
+ key_idx =
+ vnet_crypto_key_add (vm, VNET_CRYPTO_ALG_CHACHA20_POLY1305, _key,
+ NOISE_SYMMETRIC_KEY_LEN);
+ key = vnet_crypto_get_key (key_idx)->data;
+
+ if (!l->l_has_identity)
+ goto error;
+ noise_param_init (hs->hs_ck, hs->hs_hash, r->r_public);
+
+ /* e */
+ curve25519_gen_secret (hs->hs_e);
+ if (!curve25519_gen_public (ue, hs->hs_e))
+ goto error;
+ noise_msg_ephemeral (hs->hs_ck, hs->hs_hash, ue);
+
+ /* es */
+ if (!noise_mix_dh (hs->hs_ck, key, hs->hs_e, r->r_public))
+ goto error;
+
+ /* s */
+ noise_msg_encrypt (vm, es, l->l_public, NOISE_PUBLIC_KEY_LEN, key_idx,
+ hs->hs_hash);
+
+ /* ss */
+ if (!noise_mix_ss (hs->hs_ck, key, r->r_ss))
+ goto error;
+
+ /* {t} */
+ noise_tai64n_now (ets);
+ noise_msg_encrypt (vm, ets, ets, NOISE_TIMESTAMP_LEN, key_idx, hs->hs_hash);
+ noise_remote_handshake_index_drop (r);
+ hs->hs_state = CREATED_INITIATION;
+ hs->hs_local_index = noise_remote_handshake_index_get (r);
+ *s_idx = hs->hs_local_index;
+ ret = true;
+error:
+ vnet_crypto_key_del (vm, key_idx);
+ secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN);
+ return ret;
+}
+
+bool
+noise_consume_initiation (vlib_main_t * vm, noise_local_t * l,
+ noise_remote_t ** rp, uint32_t s_idx,
+ uint8_t ue[NOISE_PUBLIC_KEY_LEN],
+ uint8_t es[NOISE_PUBLIC_KEY_LEN +
+ NOISE_AUTHTAG_LEN],
+ uint8_t ets[NOISE_TIMESTAMP_LEN +
+ NOISE_AUTHTAG_LEN])
+{
+ noise_remote_t *r;
+ noise_handshake_t hs;
+ uint8_t _key[NOISE_SYMMETRIC_KEY_LEN];
+ uint8_t r_public[NOISE_PUBLIC_KEY_LEN];
+ uint8_t timestamp[NOISE_TIMESTAMP_LEN];
+ u32 key_idx;
+ uint8_t *key;
+ int ret = false;
+
+ key_idx =
+ vnet_crypto_key_add (vm, VNET_CRYPTO_ALG_CHACHA20_POLY1305, _key,
+ NOISE_SYMMETRIC_KEY_LEN);
+ key = vnet_crypto_get_key (key_idx)->data;
+
+ if (!l->l_has_identity)
+ goto error;
+ noise_param_init (hs.hs_ck, hs.hs_hash, l->l_public);
+
+ /* e */
+ noise_msg_ephemeral (hs.hs_ck, hs.hs_hash, ue);
+
+ /* es */
+ if (!noise_mix_dh (hs.hs_ck, key, l->l_private, ue))
+ goto error;
+
+ /* s */
+
+ if (!noise_msg_decrypt (vm, r_public, es,
+ NOISE_PUBLIC_KEY_LEN + NOISE_AUTHTAG_LEN, key_idx,
+ hs.hs_hash))
+ goto error;
+
+ /* Lookup the remote we received from */
+ if ((r = l->l_upcall.u_remote_get (r_public)) == NULL)
+ goto error;
+
+ /* ss */
+ if (!noise_mix_ss (hs.hs_ck, key, r->r_ss))
+ goto error;
+
+ /* {t} */
+ if (!noise_msg_decrypt (vm, timestamp, ets,
+ NOISE_TIMESTAMP_LEN + NOISE_AUTHTAG_LEN, key_idx,
+ hs.hs_hash))
+ goto error;
+ ;
+
+ hs.hs_state = CONSUMED_INITIATION;
+ hs.hs_local_index = 0;
+ hs.hs_remote_index = s_idx;
+ clib_memcpy (hs.hs_e, ue, NOISE_PUBLIC_KEY_LEN);
+
+ /* Replay */
+ if (clib_memcmp (timestamp, r->r_timestamp, NOISE_TIMESTAMP_LEN) > 0)
+ clib_memcpy (r->r_timestamp, timestamp, NOISE_TIMESTAMP_LEN);
+ else
+ goto error;
+
+ /* Flood attack */
+ if (wg_birthdate_has_expired (r->r_last_init, REJECT_INTERVAL))
+ r->r_last_init = vlib_time_now (vm);
+ else
+ goto error;
+
+ /* Ok, we're happy to accept this initiation now */
+ noise_remote_handshake_index_drop (r);
+ r->r_handshake = hs;
+ *rp = r;
+ ret = true;
+error:
+ vnet_crypto_key_del (vm, key_idx);
+ secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN);
+ secure_zero_memory (&hs, sizeof (hs));
+ return ret;
+}
+
+bool
+noise_create_response (vlib_main_t * vm, noise_remote_t * r, uint32_t * s_idx,
+ uint32_t * r_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN],
+ uint8_t en[0 + NOISE_AUTHTAG_LEN])
+{
+ noise_handshake_t *hs = &r->r_handshake;
+ uint8_t _key[NOISE_SYMMETRIC_KEY_LEN];
+ uint8_t e[NOISE_PUBLIC_KEY_LEN];
+ uint32_t key_idx;
+ uint8_t *key;
+ int ret = false;
+
+ key_idx =
+ vnet_crypto_key_add (vm, VNET_CRYPTO_ALG_CHACHA20_POLY1305, _key,
+ NOISE_SYMMETRIC_KEY_LEN);
+ key = vnet_crypto_get_key (key_idx)->data;
+
+ if (hs->hs_state != CONSUMED_INITIATION)
+ goto error;
+
+ /* e */
+ curve25519_gen_secret (e);
+ if (!curve25519_gen_public (ue, e))
+ goto error;
+ noise_msg_ephemeral (hs->hs_ck, hs->hs_hash, ue);
+
+ /* ee */
+ if (!noise_mix_dh (hs->hs_ck, NULL, e, hs->hs_e))
+ goto error;
+
+ /* se */
+ if (!noise_mix_dh (hs->hs_ck, NULL, e, r->r_public))
+ goto error;
+
+ /* psk */
+ noise_mix_psk (hs->hs_ck, hs->hs_hash, key, r->r_psk);
+
+ /* {} */
+ noise_msg_encrypt (vm, en, NULL, 0, key_idx, hs->hs_hash);
+
+
+ hs->hs_state = CREATED_RESPONSE;
+ hs->hs_local_index = noise_remote_handshake_index_get (r);
+ *r_idx = hs->hs_remote_index;
+ *s_idx = hs->hs_local_index;
+ ret = true;
+error:
+ vnet_crypto_key_del (vm, key_idx);
+ secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN);
+ secure_zero_memory (e, NOISE_PUBLIC_KEY_LEN);
+ return ret;
+}
+
+bool
+noise_consume_response (vlib_main_t * vm, noise_remote_t * r, uint32_t s_idx,
+ uint32_t r_idx, uint8_t ue[NOISE_PUBLIC_KEY_LEN],
+ uint8_t en[0 + NOISE_AUTHTAG_LEN])
+{
+ noise_local_t *l = r->r_local;
+ noise_handshake_t hs;
+ uint8_t _key[NOISE_SYMMETRIC_KEY_LEN];
+ uint8_t preshared_key[NOISE_PUBLIC_KEY_LEN];
+ uint32_t key_idx;
+ uint8_t *key;
+ int ret = false;
+
+ key_idx =
+ vnet_crypto_key_add (vm, VNET_CRYPTO_ALG_CHACHA20_POLY1305, _key,
+ NOISE_SYMMETRIC_KEY_LEN);
+ key = vnet_crypto_get_key (key_idx)->data;
+
+ if (!l->l_has_identity)
+ goto error;
+
+ hs = r->r_handshake;
+ clib_memcpy (preshared_key, r->r_psk, NOISE_SYMMETRIC_KEY_LEN);
+
+ if (hs.hs_state != CREATED_INITIATION || hs.hs_local_index != r_idx)
+ goto error;
+
+ /* e */
+ noise_msg_ephemeral (hs.hs_ck, hs.hs_hash, ue);
+
+ /* ee */
+ if (!noise_mix_dh (hs.hs_ck, NULL, hs.hs_e, ue))
+ goto error;
+
+ /* se */
+ if (!noise_mix_dh (hs.hs_ck, NULL, l->l_private, ue))
+ goto error;
+
+ /* psk */
+ noise_mix_psk (hs.hs_ck, hs.hs_hash, key, preshared_key);
+
+ /* {} */
+
+ if (!noise_msg_decrypt
+ (vm, NULL, en, 0 + NOISE_AUTHTAG_LEN, key_idx, hs.hs_hash))
+ goto error;
+
+
+ hs.hs_remote_index = s_idx;
+
+ if (r->r_handshake.hs_state == hs.hs_state &&
+ r->r_handshake.hs_local_index == hs.hs_local_index)
+ {
+ r->r_handshake = hs;
+ r->r_handshake.hs_state = CONSUMED_RESPONSE;
+ ret = true;
+ }
+error:
+ vnet_crypto_key_del (vm, key_idx);
+ secure_zero_memory (&hs, sizeof (hs));
+ secure_zero_memory (key, NOISE_SYMMETRIC_KEY_LEN);
+ return ret;
+}
+
+bool
+noise_remote_begin_session (vlib_main_t * vm, noise_remote_t * r)
+{
+ noise_handshake_t *hs = &r->r_handshake;
+ noise_keypair_t kp, *next, *current, *previous;
+
+ uint8_t key_send[NOISE_SYMMETRIC_KEY_LEN];
+ uint8_t key_recv[NOISE_SYMMETRIC_KEY_LEN];
+
+ /* We now derive the keypair from the handshake */
+ if (hs->hs_state == CONSUMED_RESPONSE)
+ {
+ kp.kp_is_initiator = 1;
+ noise_kdf (key_send, key_recv, NULL, NULL,
+ NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0,
+ hs->hs_ck);
+ }
+ else if (hs->hs_state == CREATED_RESPONSE)
+ {
+ kp.kp_is_initiator = 0;
+ noise_kdf (key_recv, key_send, NULL, NULL,
+ NOISE_SYMMETRIC_KEY_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, 0,
+ hs->hs_ck);
+ }
+ else
+ {
+ return false;
+ }
+
+ kp.kp_valid = 1;
+ kp.kp_send_index = vnet_crypto_key_add (vm,
+ VNET_CRYPTO_ALG_CHACHA20_POLY1305,
+ key_send, NOISE_SYMMETRIC_KEY_LEN);
+ kp.kp_recv_index = vnet_crypto_key_add (vm,
+ VNET_CRYPTO_ALG_CHACHA20_POLY1305,
+ key_recv, NOISE_SYMMETRIC_KEY_LEN);
+ kp.kp_local_index = hs->hs_local_index;
+ kp.kp_remote_index = hs->hs_remote_index;
+ kp.kp_birthdate = vlib_time_now (vm);
+ clib_memset (&kp.kp_ctr, 0, sizeof (kp.kp_ctr));
+
+ /* Now we need to add_new_keypair */
+ next = r->r_next;
+ current = r->r_current;
+ previous = r->r_previous;
+
+ if (kp.kp_is_initiator)
+ {
+ if (next != NULL)
+ {
+ r->r_next = NULL;
+ r->r_previous = next;
+ noise_remote_keypair_free (vm, r, &current);
+ }
+ else
+ {
+ r->r_previous = current;
+ }
+
+ noise_remote_keypair_free (vm, r, &previous);
+
+ r->r_current = noise_remote_keypair_allocate (r);
+ *r->r_current = kp;
+ }
+ else
+ {
+ noise_remote_keypair_free (vm, r, &next);
+ r->r_previous = NULL;
+ noise_remote_keypair_free (vm, r, &previous);
+
+ r->r_next = noise_remote_keypair_allocate (r);
+ *r->r_next = kp;
+ }
+ secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake));
+ secure_zero_memory (&kp, sizeof (kp));
+ return true;
+}
+
+void
+noise_remote_clear (vlib_main_t * vm, noise_remote_t * r)
+{
+ noise_remote_handshake_index_drop (r);
+ secure_zero_memory (&r->r_handshake, sizeof (r->r_handshake));
+
+ noise_remote_keypair_free (vm, r, &r->r_next);
+ noise_remote_keypair_free (vm, r, &r->r_current);
+ noise_remote_keypair_free (vm, r, &r->r_previous);
+ r->r_next = NULL;
+ r->r_current = NULL;
+ r->r_previous = NULL;
+}
+
+void
+noise_remote_expire_current (noise_remote_t * r)
+{
+ if (r->r_next != NULL)
+ r->r_next->kp_valid = 0;
+ if (r->r_current != NULL)
+ r->r_current->kp_valid = 0;
+}
+
+bool
+noise_remote_ready (noise_remote_t * r)
+{
+ noise_keypair_t *kp;
+ int ret;
+
+ if ((kp = r->r_current) == NULL ||
+ !kp->kp_valid ||
+ wg_birthdate_has_expired (kp->kp_birthdate, REJECT_AFTER_TIME) ||
+ kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES ||
+ kp->kp_ctr.c_send >= REJECT_AFTER_MESSAGES)
+ ret = false;
+ else
+ ret = true;
+ return ret;
+}
+
+static void
+chacha20poly1305_calc (vlib_main_t * vm,
+ u8 * src,
+ u32 src_len,
+ u8 * dst,
+ u8 * aad,
+ u32 aad_len,
+ u64 nonce,
+ vnet_crypto_op_id_t op_id,
+ vnet_crypto_key_index_t key_index)
+{
+ u8 iv[12];
+ clib_memset (iv, 0, 12);
+ clib_memcpy (iv + 4, &nonce, sizeof (nonce));
+
+ vnet_crypto_op_t _op, *op = &_op;
+
+ u8 _tag[16] = { };
+ if (op_id == VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC)
+ {
+ clib_memcpy (_tag, src + src_len - NOISE_AUTHTAG_LEN,
+ NOISE_AUTHTAG_LEN);
+ src_len -= NOISE_AUTHTAG_LEN;
+ }
+ vnet_crypto_op_init (op, op_id);
+ op->key_index = key_index;
+ op->src = src;
+ op->dst = dst;
+ op->len = src_len;
+ op->aad = aad;
+ op->aad_len = aad_len;
+ op->iv = iv;
+ op->tag_len = NOISE_AUTHTAG_LEN;
+ op->tag = _tag;
+ vnet_crypto_process_ops (vm, op, 1);
+ if (op_id == VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC)
+ {
+ clib_memcpy (dst + src_len, op->tag, NOISE_AUTHTAG_LEN);
+ }
+}
+
+enum noise_state_crypt
+noise_remote_encrypt (vlib_main_t * vm, noise_remote_t * r, uint32_t * r_idx,
+ uint64_t * nonce, uint8_t * src, size_t srclen,
+ uint8_t * dst)
+{
+ noise_keypair_t *kp;
+ enum noise_state_crypt ret = SC_FAILED;
+
+ if ((kp = r->r_current) == NULL)
+ goto error;
+
+ /* We confirm that our values are within our tolerances. We want:
+ * - a valid keypair
+ * - our keypair to be less than REJECT_AFTER_TIME seconds old
+ * - our receive counter to be less than REJECT_AFTER_MESSAGES
+ * - our send counter to be less than REJECT_AFTER_MESSAGES
+ */
+ if (!kp->kp_valid ||
+ wg_birthdate_has_expired (kp->kp_birthdate, REJECT_AFTER_TIME) ||
+ kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES ||
+ ((*nonce = noise_counter_send (&kp->kp_ctr)) > REJECT_AFTER_MESSAGES))
+ goto error;
+
+ /* We encrypt into the same buffer, so the caller must ensure that buf
+ * has NOISE_AUTHTAG_LEN bytes to store the MAC. The nonce and index
+ * are passed back out to the caller through the provided data pointer. */
+ *r_idx = kp->kp_remote_index;
+
+ chacha20poly1305_calc (vm, src, srclen, dst, NULL, 0, *nonce,
+ VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC,
+ kp->kp_send_index);
+
+ /* If our values are still within tolerances, but we are approaching
+ * the tolerances, we notify the caller with ESTALE that they should
+ * establish a new keypair. The current keypair can continue to be used
+ * until the tolerances are hit. We notify if:
+ * - our send counter is valid and not less than REKEY_AFTER_MESSAGES
+ * - we're the initiator and our keypair is older than
+ * REKEY_AFTER_TIME seconds */
+ ret = SC_KEEP_KEY_FRESH;
+ if ((kp->kp_valid && *nonce >= REKEY_AFTER_MESSAGES) ||
+ (kp->kp_is_initiator &&
+ wg_birthdate_has_expired (kp->kp_birthdate, REKEY_AFTER_TIME)))
+ goto error;
+
+ ret = SC_OK;
+error:
+ return ret;
+}
+
+enum noise_state_crypt
+noise_remote_decrypt (vlib_main_t * vm, noise_remote_t * r, uint32_t r_idx,
+ uint64_t nonce, uint8_t * src, size_t srclen,
+ uint8_t * dst)
+{
+ noise_keypair_t *kp;
+ enum noise_state_crypt ret = SC_FAILED;
+
+ if (r->r_current != NULL && r->r_current->kp_local_index == r_idx)
+ {
+ kp = r->r_current;
+ }
+ else if (r->r_previous != NULL && r->r_previous->kp_local_index == r_idx)
+ {
+ kp = r->r_previous;
+ }
+ else if (r->r_next != NULL && r->r_next->kp_local_index == r_idx)
+ {
+ kp = r->r_next;
+ }
+ else
+ {
+ goto error;
+ }
+
+ /* We confirm that our values are within our tolerances. These values
+ * are the same as the encrypt routine.
+ *
+ * kp_ctr isn't locked here, we're happy to accept a racy read. */
+ if (wg_birthdate_has_expired (kp->kp_birthdate, REJECT_AFTER_TIME) ||
+ kp->kp_ctr.c_recv >= REJECT_AFTER_MESSAGES)
+ goto error;
+
+ /* Decrypt, then validate the counter. We don't want to validate the
+ * counter before decrypting as we do not know the message is authentic
+ * prior to decryption. */
+ chacha20poly1305_calc (vm, src, srclen, dst, NULL, 0, nonce,
+ VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC,
+ kp->kp_recv_index);
+
+ if (!noise_counter_recv (&kp->kp_ctr, nonce))
+ goto error;
+
+ /* If we've received the handshake confirming data packet then move the
+ * next keypair into current. If we do slide the next keypair in, then
+ * we skip the REKEY_AFTER_TIME_RECV check. This is safe to do as a
+ * data packet can't confirm a session that we are an INITIATOR of. */
+ if (kp == r->r_next && kp->kp_local_index == r_idx)
+ {
+ noise_remote_keypair_free (vm, r, &r->r_previous);
+ r->r_previous = r->r_current;
+ r->r_current = r->r_next;
+ r->r_next = NULL;
+
+ ret = SC_CONN_RESET;
+ goto error;
+ }
+
+
+ /* Similar to when we encrypt, we want to notify the caller when we
+ * are approaching our tolerances. We notify if:
+ * - we're the initiator and the current keypair is older than
+ * REKEY_AFTER_TIME_RECV seconds. */
+ ret = SC_KEEP_KEY_FRESH;
+ kp = r->r_current;
+ if (kp != NULL &&
+ kp->kp_valid &&
+ kp->kp_is_initiator &&
+ wg_birthdate_has_expired (kp->kp_birthdate, REKEY_AFTER_TIME_RECV))
+ goto error;
+
+ ret = SC_OK;
+error:
+ return ret;
+}
+
+/* Private functions - these should not be called outside this file under any
+ * circumstances. */
+static noise_keypair_t *
+noise_remote_keypair_allocate (noise_remote_t * r)
+{
+ noise_keypair_t *kp;
+ kp = clib_mem_alloc (sizeof (*kp));
+ return kp;
+}
+
+static void
+noise_remote_keypair_free (vlib_main_t * vm, noise_remote_t * r,
+ noise_keypair_t ** kp)
+{
+ struct noise_upcall *u = &r->r_local->l_upcall;
+ if (*kp)
+ {
+ u->u_index_drop ((*kp)->kp_local_index);
+ vnet_crypto_key_del (vm, (*kp)->kp_send_index);
+ vnet_crypto_key_del (vm, (*kp)->kp_recv_index);
+ clib_mem_free (*kp);
+ }
+}
+
+static uint32_t
+noise_remote_handshake_index_get (noise_remote_t * r)
+{
+ struct noise_upcall *u = &r->r_local->l_upcall;
+ return u->u_index_set (r);
+}
+
+static void
+noise_remote_handshake_index_drop (noise_remote_t * r)
+{
+ noise_handshake_t *hs = &r->r_handshake;
+ struct noise_upcall *u = &r->r_local->l_upcall;
+ if (hs->hs_state != HS_ZEROED)
+ u->u_index_drop (hs->hs_local_index);
+}
+
+static uint64_t
+noise_counter_send (noise_counter_t * ctr)
+{
+ uint64_t ret = ctr->c_send++;
+ return ret;
+}
+
+static bool
+noise_counter_recv (noise_counter_t * ctr, uint64_t recv)
+{
+ uint64_t i, top, index_recv, index_ctr;
+ unsigned long bit;
+ bool ret = false;
+
+
+ /* Check that the recv counter is valid */
+ if (ctr->c_recv >= REJECT_AFTER_MESSAGES || recv >= REJECT_AFTER_MESSAGES)
+ goto error;
+
+ /* If the packet is out of the window, invalid */
+ if (recv + COUNTER_WINDOW_SIZE < ctr->c_recv)
+ goto error;
+
+ /* If the new counter is ahead of the current counter, we'll need to
+ * zero out the bitmap that has previously been used */
+ index_recv = recv / COUNTER_BITS;
+ index_ctr = ctr->c_recv / COUNTER_BITS;
+
+ if (recv > ctr->c_recv)
+ {
+ top = clib_min (index_recv - index_ctr, COUNTER_NUM);
+ for (i = 1; i <= top; i++)
+ ctr->c_backtrack[(i + index_ctr) & (COUNTER_NUM - 1)] = 0;
+ ctr->c_recv = recv;
+ }
+
+ index_recv %= COUNTER_NUM;
+ bit = 1ul << (recv % COUNTER_BITS);
+
+ if (ctr->c_backtrack[index_recv] & bit)
+ goto error;
+
+ ctr->c_backtrack[index_recv] |= bit;
+
+ ret = true;
+error:
+ return ret;
+}
+
+static void
+noise_kdf (uint8_t * a, uint8_t * b, uint8_t * c, const uint8_t * x,
+ size_t a_len, size_t b_len, size_t c_len, size_t x_len,
+ const uint8_t ck[NOISE_HASH_LEN])
+{
+ uint8_t out[BLAKE2S_HASH_SIZE + 1];
+ uint8_t sec[BLAKE2S_HASH_SIZE];
+
+ /* Extract entropy from "x" into sec */
+ u32 l = 0;
+ HMAC (EVP_blake2s256 (), ck, NOISE_HASH_LEN, x, x_len, sec, &l);
+ ASSERT (l == BLAKE2S_HASH_SIZE);
+ if (a == NULL || a_len == 0)
+ goto out;
+
+ /* Expand first key: key = sec, data = 0x1 */
+ out[0] = 1;
+ HMAC (EVP_blake2s256 (), sec, BLAKE2S_HASH_SIZE, out, 1, out, &l);
+ ASSERT (l == BLAKE2S_HASH_SIZE);
+ clib_memcpy (a, out, a_len);
+
+ if (b == NULL || b_len == 0)
+ goto out;
+
+ /* Expand second key: key = sec, data = "a" || 0x2 */
+ out[BLAKE2S_HASH_SIZE] = 2;
+ HMAC (EVP_blake2s256 (), sec, BLAKE2S_HASH_SIZE, out, BLAKE2S_HASH_SIZE + 1,
+ out, &l);
+ ASSERT (l == BLAKE2S_HASH_SIZE);
+ clib_memcpy (b, out, b_len);
+
+ if (c == NULL || c_len == 0)
+ goto out;
+
+ /* Expand third key: key = sec, data = "b" || 0x3 */
+ out[BLAKE2S_HASH_SIZE] = 3;
+ HMAC (EVP_blake2s256 (), sec, BLAKE2S_HASH_SIZE, out, BLAKE2S_HASH_SIZE + 1,
+ out, &l);
+ ASSERT (l == BLAKE2S_HASH_SIZE);
+
+ clib_memcpy (c, out, c_len);
+
+out:
+ /* Clear sensitive data from stack */
+ secure_zero_memory (sec, BLAKE2S_HASH_SIZE);
+ secure_zero_memory (out, BLAKE2S_HASH_SIZE + 1);
+}
+
+static bool
+noise_mix_dh (uint8_t ck[NOISE_HASH_LEN],
+ uint8_t key[NOISE_SYMMETRIC_KEY_LEN],
+ const uint8_t private[NOISE_PUBLIC_KEY_LEN],
+ const uint8_t public[NOISE_PUBLIC_KEY_LEN])
+{
+ uint8_t dh[NOISE_PUBLIC_KEY_LEN];
+ if (!curve25519_gen_shared (dh, private, public))
+ return false;
+ noise_kdf (ck, key, NULL, dh,
+ NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN,
+ ck);
+ secure_zero_memory (dh, NOISE_PUBLIC_KEY_LEN);
+ return true;
+}
+
+static bool
+noise_mix_ss (uint8_t ck[NOISE_HASH_LEN],
+ uint8_t key[NOISE_SYMMETRIC_KEY_LEN],
+ const uint8_t ss[NOISE_PUBLIC_KEY_LEN])
+{
+ static uint8_t null_point[NOISE_PUBLIC_KEY_LEN];
+ if (clib_memcmp (ss, null_point, NOISE_PUBLIC_KEY_LEN) == 0)
+ return false;
+ noise_kdf (ck, key, NULL, ss,
+ NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN, 0, NOISE_PUBLIC_KEY_LEN,
+ ck);
+ return true;
+}
+
+static void
+noise_mix_hash (uint8_t hash[NOISE_HASH_LEN], const uint8_t * src,
+ size_t src_len)
+{
+ blake2s_state_t blake;
+
+ blake2s_init (&blake, NOISE_HASH_LEN);
+ blake2s_update (&blake, hash, NOISE_HASH_LEN);
+ blake2s_update (&blake, src, src_len);
+ blake2s_final (&blake, hash, NOISE_HASH_LEN);
+}
+
+static void
+noise_mix_psk (uint8_t ck[NOISE_HASH_LEN], uint8_t hash[NOISE_HASH_LEN],
+ uint8_t key[NOISE_SYMMETRIC_KEY_LEN],
+ const uint8_t psk[NOISE_SYMMETRIC_KEY_LEN])
+{
+ uint8_t tmp[NOISE_HASH_LEN];
+
+ noise_kdf (ck, tmp, key, psk,
+ NOISE_HASH_LEN, NOISE_HASH_LEN, NOISE_SYMMETRIC_KEY_LEN,
+ NOISE_SYMMETRIC_KEY_LEN, ck);
+ noise_mix_hash (hash, tmp, NOISE_HASH_LEN);
+ secure_zero_memory (tmp, NOISE_HASH_LEN);
+}
+
+static void
+noise_param_init (uint8_t ck[NOISE_HASH_LEN], uint8_t hash[NOISE_HASH_LEN],
+ const uint8_t s[NOISE_PUBLIC_KEY_LEN])
+{
+ blake2s_state_t blake;
+
+ blake2s (ck, NOISE_HASH_LEN, (uint8_t *) NOISE_HANDSHAKE_NAME,
+ strlen (NOISE_HANDSHAKE_NAME), NULL, 0);
+
+ blake2s_init (&blake, NOISE_HASH_LEN);
+ blake2s_update (&blake, ck, NOISE_HASH_LEN);
+ blake2s_update (&blake, (uint8_t *) NOISE_IDENTIFIER_NAME,
+ strlen (NOISE_IDENTIFIER_NAME));
+ blake2s_final (&blake, hash, NOISE_HASH_LEN);
+
+ noise_mix_hash (hash, s, NOISE_PUBLIC_KEY_LEN);
+}
+
+static void
+noise_msg_encrypt (vlib_main_t * vm, uint8_t * dst, uint8_t * src,
+ size_t src_len, uint32_t key_idx,
+ uint8_t hash[NOISE_HASH_LEN])
+{
+ /* Nonce always zero for Noise_IK */
+ chacha20poly1305_calc (vm, src, src_len, dst, hash, NOISE_HASH_LEN, 0,
+ VNET_CRYPTO_OP_CHACHA20_POLY1305_ENC, key_idx);
+ noise_mix_hash (hash, dst, src_len + NOISE_AUTHTAG_LEN);
+}
+
+static bool
+noise_msg_decrypt (vlib_main_t * vm, uint8_t * dst, uint8_t * src,
+ size_t src_len, uint32_t key_idx,
+ uint8_t hash[NOISE_HASH_LEN])
+{
+ /* Nonce always zero for Noise_IK */
+ chacha20poly1305_calc (vm, src, src_len, dst, hash, NOISE_HASH_LEN, 0,
+ VNET_CRYPTO_OP_CHACHA20_POLY1305_DEC, key_idx);
+ noise_mix_hash (hash, src, src_len);
+ return true;
+}
+
+static void
+noise_msg_ephemeral (uint8_t ck[NOISE_HASH_LEN], uint8_t hash[NOISE_HASH_LEN],
+ const uint8_t src[NOISE_PUBLIC_KEY_LEN])
+{
+ noise_mix_hash (hash, src, NOISE_PUBLIC_KEY_LEN);
+ noise_kdf (ck, NULL, NULL, src, NOISE_HASH_LEN, 0, 0,
+ NOISE_PUBLIC_KEY_LEN, ck);
+}
+
+static void
+noise_tai64n_now (uint8_t output[NOISE_TIMESTAMP_LEN])
+{
+ uint32_t unix_sec;
+ uint32_t unix_nanosec;
+
+ uint64_t sec;
+ uint32_t nsec;
+
+ unix_time_now_nsec_fraction (&unix_sec, &unix_nanosec);
+
+ /* Round down the nsec counter to limit precise timing leak. */
+ unix_nanosec &= REJECT_INTERVAL_MASK;
+
+ /* https://cr.yp.to/libtai/tai64.html */
+ sec = htobe64 (0x400000000000000aULL + unix_sec);
+ nsec = htobe32 (unix_nanosec);
+
+ /* memcpy to output buffer, assuming output could be unaligned. */
+ clib_memcpy (output, &sec, sizeof (sec));
+ clib_memcpy (output + sizeof (sec), &nsec, sizeof (nsec));
+}
+
+static void
+secure_zero_memory (void *v, size_t n)
+{
+ static void *(*const volatile memset_v) (void *, int, size_t) = &memset;
+ memset_v (v, 0, n);
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_noise.h b/src/plugins/wireguard/wireguard_noise.h
new file mode 100755
index 00000000000..1f6804c59ca
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_noise.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Copyright (c) 2015-2020 Jason A. Donenfeld <Jason@zx2c4.com>.
+ * Copyright (c) 2019-2020 Matt Dunwoodie <ncon@noconroy.net>.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_wg_noise_h__
+#define __included_wg_noise_h__
+
+#include <vlib/vlib.h>
+#include <vnet/crypto/crypto.h>
+#include <wireguard/blake/blake2s.h>
+#include <wireguard/wireguard_key.h>
+
+#define NOISE_PUBLIC_KEY_LEN CURVE25519_KEY_SIZE
+#define NOISE_SYMMETRIC_KEY_LEN 32 // CHACHA20POLY1305_KEY_SIZE
+#define NOISE_TIMESTAMP_LEN (sizeof(uint64_t) + sizeof(uint32_t))
+#define NOISE_AUTHTAG_LEN 16 //CHACHA20POLY1305_AUTHTAG_SIZE
+#define NOISE_HASH_LEN BLAKE2S_HASH_SIZE
+
+/* Protocol string constants */
+#define NOISE_HANDSHAKE_NAME "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"
+#define NOISE_IDENTIFIER_NAME "WireGuard v1 zx2c4 Jason@zx2c4.com"
+
+/* Constants for the counter */
+#define COUNTER_BITS_TOTAL 8192
+#define COUNTER_BITS (sizeof(unsigned long) * 8)
+#define COUNTER_NUM (COUNTER_BITS_TOTAL / COUNTER_BITS)
+#define COUNTER_WINDOW_SIZE (COUNTER_BITS_TOTAL - COUNTER_BITS)
+
+/* Constants for the keypair */
+#define REKEY_AFTER_MESSAGES (1ull << 60)
+#define REJECT_AFTER_MESSAGES (UINT64_MAX - COUNTER_WINDOW_SIZE - 1)
+#define REKEY_AFTER_TIME 120
+#define REKEY_AFTER_TIME_RECV 165
+#define REJECT_AFTER_TIME 180
+#define REJECT_INTERVAL (0.02) /* fifty times per sec */
+/* 24 = floor(log2(REJECT_INTERVAL)) */
+#define REJECT_INTERVAL_MASK (~((1ull<<24)-1))
+
+enum noise_state_crypt
+{
+ SC_OK = 0,
+ SC_CONN_RESET,
+ SC_KEEP_KEY_FRESH,
+ SC_FAILED,
+};
+
+enum noise_state_hs
+{
+ HS_ZEROED = 0,
+ CREATED_INITIATION,
+ CONSUMED_INITIATION,
+ CREATED_RESPONSE,
+ CONSUMED_RESPONSE,
+};
+
+typedef struct noise_handshake
+{
+ enum noise_state_hs hs_state;
+ uint32_t hs_local_index;
+ uint32_t hs_remote_index;
+ uint8_t hs_e[NOISE_PUBLIC_KEY_LEN];
+ uint8_t hs_hash[NOISE_HASH_LEN];
+ uint8_t hs_ck[NOISE_HASH_LEN];
+} noise_handshake_t;
+
+typedef struct noise_counter
+{
+ uint64_t c_send;
+ uint64_t c_recv;
+ unsigned long c_backtrack[COUNTER_NUM];
+} noise_counter_t;
+
+typedef struct noise_keypair
+{
+ int kp_valid;
+ int kp_is_initiator;
+ uint32_t kp_local_index;
+ uint32_t kp_remote_index;
+ vnet_crypto_key_index_t kp_send_index;
+ vnet_crypto_key_index_t kp_recv_index;
+ f64 kp_birthdate;
+ noise_counter_t kp_ctr;
+} noise_keypair_t;
+
+typedef struct noise_local noise_local_t;
+typedef struct noise_remote
+{
+ uint32_t r_peer_idx;
+ uint8_t r_public[NOISE_PUBLIC_KEY_LEN];
+ noise_local_t *r_local;
+ uint8_t r_ss[NOISE_PUBLIC_KEY_LEN];
+
+ noise_handshake_t r_handshake;
+ uint8_t r_psk[NOISE_SYMMETRIC_KEY_LEN];
+ uint8_t r_timestamp[NOISE_TIMESTAMP_LEN];
+ f64 r_last_init;
+
+ noise_keypair_t *r_next, *r_current, *r_previous;
+} noise_remote_t;
+
+typedef struct noise_local
+{
+ bool l_has_identity;
+ uint8_t l_public[NOISE_PUBLIC_KEY_LEN];
+ uint8_t l_private[NOISE_PUBLIC_KEY_LEN];
+
+ struct noise_upcall
+ {
+ void *u_arg;
+ noise_remote_t *(*u_remote_get) (uint8_t[NOISE_PUBLIC_KEY_LEN]);
+ uint32_t (*u_index_set) (noise_remote_t *);
+ void (*u_index_drop) (uint32_t);
+ } l_upcall;
+} noise_local_t;
+
+/* Set/Get noise parameters */
+void noise_local_init (noise_local_t *, struct noise_upcall *);
+bool noise_local_set_private (noise_local_t *,
+ const uint8_t[NOISE_PUBLIC_KEY_LEN]);
+bool noise_local_keys (noise_local_t *, uint8_t[NOISE_PUBLIC_KEY_LEN],
+ uint8_t[NOISE_PUBLIC_KEY_LEN]);
+
+void noise_remote_init (noise_remote_t *, uint32_t,
+ const uint8_t[NOISE_PUBLIC_KEY_LEN], noise_local_t *);
+bool noise_remote_set_psk (noise_remote_t *,
+ uint8_t[NOISE_SYMMETRIC_KEY_LEN]);
+bool noise_remote_keys (noise_remote_t *, uint8_t[NOISE_PUBLIC_KEY_LEN],
+ uint8_t[NOISE_SYMMETRIC_KEY_LEN]);
+
+/* Should be called anytime noise_local_set_private is called */
+void noise_remote_precompute (noise_remote_t *);
+
+/* Cryptographic functions */
+bool noise_create_initiation (vlib_main_t * vm, noise_remote_t *,
+ uint32_t * s_idx,
+ uint8_t ue[NOISE_PUBLIC_KEY_LEN],
+ uint8_t es[NOISE_PUBLIC_KEY_LEN +
+ NOISE_AUTHTAG_LEN],
+ uint8_t ets[NOISE_TIMESTAMP_LEN +
+ NOISE_AUTHTAG_LEN]);
+
+bool noise_consume_initiation (vlib_main_t * vm, noise_local_t *,
+ noise_remote_t **,
+ uint32_t s_idx,
+ uint8_t ue[NOISE_PUBLIC_KEY_LEN],
+ uint8_t es[NOISE_PUBLIC_KEY_LEN +
+ NOISE_AUTHTAG_LEN],
+ uint8_t ets[NOISE_TIMESTAMP_LEN +
+ NOISE_AUTHTAG_LEN]);
+
+bool noise_create_response (vlib_main_t * vm, noise_remote_t *,
+ uint32_t * s_idx,
+ uint32_t * r_idx,
+ uint8_t ue[NOISE_PUBLIC_KEY_LEN],
+ uint8_t en[0 + NOISE_AUTHTAG_LEN]);
+
+bool noise_consume_response (vlib_main_t * vm, noise_remote_t *,
+ uint32_t s_idx,
+ uint32_t r_idx,
+ uint8_t ue[NOISE_PUBLIC_KEY_LEN],
+ uint8_t en[0 + NOISE_AUTHTAG_LEN]);
+
+bool noise_remote_begin_session (vlib_main_t * vm, noise_remote_t * r);
+void noise_remote_clear (vlib_main_t * vm, noise_remote_t * r);
+void noise_remote_expire_current (noise_remote_t * r);
+
+bool noise_remote_ready (noise_remote_t *);
+
+enum noise_state_crypt
+noise_remote_encrypt (vlib_main_t * vm, noise_remote_t *,
+ uint32_t * r_idx,
+ uint64_t * nonce,
+ uint8_t * src, size_t srclen, uint8_t * dst);
+enum noise_state_crypt
+noise_remote_decrypt (vlib_main_t * vm, noise_remote_t *,
+ uint32_t r_idx,
+ uint64_t nonce,
+ uint8_t * src, size_t srclen, uint8_t * dst);
+
+
+#endif /* __included_wg_noise_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_output_tun.c b/src/plugins/wireguard/wireguard_output_tun.c
new file mode 100755
index 00000000000..daec7a4a2f1
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_output_tun.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vlib/vlib.h>
+#include <vnet/vnet.h>
+#include <vnet/pg/pg.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_entry.h>
+#include <vppinfra/error.h>
+
+#include <wireguard/wireguard.h>
+#include <wireguard/wireguard_send.h>
+
+#define foreach_wg_output_error \
+ _(NONE, "No error") \
+ _(PEER, "Peer error") \
+ _(KEYPAIR, "Keypair error") \
+ _(HANDSHAKE_SEND, "Handshake sending failed") \
+ _(TOO_BIG, "packet too big") \
+
+#define WG_OUTPUT_SCRATCH_SIZE 2048
+
+typedef struct wg_output_scratch_t_
+{
+ u8 scratch[WG_OUTPUT_SCRATCH_SIZE];
+} wg_output_scratch_t;
+
+/* Cache line aligned per-thread scratch space */
+static wg_output_scratch_t *wg_output_scratchs;
+
+typedef enum
+{
+#define _(sym,str) WG_OUTPUT_ERROR_##sym,
+ foreach_wg_output_error
+#undef _
+ WG_OUTPUT_N_ERROR,
+} wg_output_error_t;
+
+static char *wg_output_error_strings[] = {
+#define _(sym,string) string,
+ foreach_wg_output_error
+#undef _
+};
+
+typedef enum
+{
+ WG_OUTPUT_NEXT_ERROR,
+ WG_OUTPUT_NEXT_INTERFACE_OUTPUT,
+ WG_OUTPUT_N_NEXT,
+} wg_output_next_t;
+
+typedef struct
+{
+ ip4_udp_header_t hdr;
+} wg_output_tun_trace_t;
+
+u8 *
+format_ip4_udp_header (u8 * s, va_list * args)
+{
+ ip4_udp_header_t *hdr = va_arg (*args, ip4_udp_header_t *);
+
+ s = format (s, "%U:$U",
+ format_ip4_header, &hdr->ip4, format_udp_header, &hdr->udp);
+
+ return (s);
+}
+
+/* packet trace format function */
+static u8 *
+format_wg_output_tun_trace (u8 * s, va_list * args)
+{
+ CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+ CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+
+ wg_output_tun_trace_t *t = va_arg (*args, wg_output_tun_trace_t *);
+
+ s = format (s, "Encrypted packet: %U\n", format_ip4_udp_header, &t->hdr);
+ return s;
+}
+
+VLIB_NODE_FN (wg_output_tun_node) (vlib_main_t * vm,
+ vlib_node_runtime_t * node,
+ vlib_frame_t * frame)
+{
+ u32 n_left_from;
+ u32 *from;
+ vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b;
+ u16 nexts[VLIB_FRAME_SIZE], *next;
+ u32 thread_index = vm->thread_index;
+
+ from = vlib_frame_vector_args (frame);
+ n_left_from = frame->n_vectors;
+ b = bufs;
+ next = nexts;
+
+ vlib_get_buffers (vm, from, bufs, n_left_from);
+
+ wg_main_t *wmp = &wg_main;
+ u32 handsh_fails = 0;
+ wg_peer_t *peer = NULL;
+
+ while (n_left_from > 0)
+ {
+ ip4_udp_header_t *hdr = vlib_buffer_get_current (b[0]);
+ u8 *plain_data = vlib_buffer_get_current (b[0]) + sizeof (ip4_header_t);
+ u16 plain_data_len =
+ clib_net_to_host_u16 (((ip4_header_t *) plain_data)->length);
+
+ next[0] = WG_OUTPUT_NEXT_ERROR;
+
+ peer =
+ wg_peer_get_by_adj_index (vnet_buffer (b[0])->ip.adj_index[VLIB_TX]);
+
+ if (!peer || peer->is_dead)
+ {
+ b[0]->error = node->errors[WG_OUTPUT_ERROR_PEER];
+ goto out;
+ }
+
+ if (PREDICT_FALSE (!peer->remote.r_current))
+ {
+ if (PREDICT_FALSE (!wg_send_handshake (vm, peer, false)))
+ handsh_fails++;
+ b[0]->error = node->errors[WG_OUTPUT_ERROR_KEYPAIR];
+ goto out;
+ }
+
+ size_t encrypted_packet_len = message_data_len (plain_data_len);
+
+ /*
+ * Ensure there is enough space to write the encrypted data
+ * into the packet
+ */
+ if (PREDICT_FALSE (encrypted_packet_len > WG_OUTPUT_SCRATCH_SIZE) ||
+ PREDICT_FALSE ((b[0]->current_data + encrypted_packet_len) <
+ vlib_buffer_get_default_data_size (vm)))
+ {
+ b[0]->error = node->errors[WG_OUTPUT_ERROR_TOO_BIG];
+ goto out;
+ }
+
+ message_data_t *encrypted_packet =
+ (message_data_t *) wg_output_scratchs[thread_index].scratch;
+
+ enum noise_state_crypt state;
+ state =
+ noise_remote_encrypt (wmp->vlib_main,
+ &peer->remote,
+ &encrypted_packet->receiver_index,
+ &encrypted_packet->counter, plain_data,
+ plain_data_len,
+ encrypted_packet->encrypted_data);
+ switch (state)
+ {
+ case SC_OK:
+ break;
+ case SC_KEEP_KEY_FRESH:
+ if (PREDICT_FALSE (!wg_send_handshake (vm, peer, false)))
+ handsh_fails++;
+ break;
+ case SC_FAILED:
+ //TODO: Maybe wrong
+ if (PREDICT_FALSE (!wg_send_handshake (vm, peer, false)))
+ handsh_fails++;
+ clib_mem_free (encrypted_packet);
+ goto out;
+ default:
+ break;
+ }
+
+ // Here we are sure that can send packet to next node.
+ next[0] = WG_OUTPUT_NEXT_INTERFACE_OUTPUT;
+ encrypted_packet->header.type = MESSAGE_DATA;
+
+ clib_memcpy (plain_data, (u8 *) encrypted_packet, encrypted_packet_len);
+
+ hdr->udp.length = clib_host_to_net_u16 (encrypted_packet_len +
+ sizeof (udp_header_t));
+ b[0]->current_length = (encrypted_packet_len +
+ sizeof (ip4_header_t) + sizeof (udp_header_t));
+ ip4_header_set_len_w_chksum
+ (&hdr->ip4, clib_host_to_net_u16 (b[0]->current_length));
+
+ wg_timers_any_authenticated_packet_traversal (peer);
+ wg_timers_any_authenticated_packet_sent (peer);
+ wg_timers_data_sent (peer);
+
+ out:
+ if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
+ && (b[0]->flags & VLIB_BUFFER_IS_TRACED)))
+ {
+ wg_output_tun_trace_t *t =
+ vlib_add_trace (vm, node, b[0], sizeof (*t));
+ t->hdr = *hdr;
+ }
+ n_left_from -= 1;
+ next += 1;
+ b += 1;
+ }
+
+ vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
+
+ vlib_node_increment_counter (vm, node->node_index,
+ WG_OUTPUT_ERROR_HANDSHAKE_SEND, handsh_fails);
+
+ return frame->n_vectors;
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (wg_output_tun_node) =
+{
+ .name = "wg-output-tun",
+ .vector_size = sizeof (u32),
+ .format_trace = format_wg_output_tun_trace,
+ .type = VLIB_NODE_TYPE_INTERNAL,
+ .n_errors = ARRAY_LEN (wg_output_error_strings),
+ .error_strings = wg_output_error_strings,
+ .n_next_nodes = WG_OUTPUT_N_NEXT,
+ .next_nodes = {
+ [WG_OUTPUT_NEXT_INTERFACE_OUTPUT] = "adj-midchain-tx",
+ [WG_OUTPUT_NEXT_ERROR] = "error-drop",
+ },
+};
+/* *INDENT-ON* */
+
+static clib_error_t *
+wireguard_output_module_init (vlib_main_t * vm)
+{
+ vlib_thread_main_t *tm = vlib_get_thread_main ();
+
+ vec_validate_aligned (wg_output_scratchs, tm->n_vlib_mains,
+ CLIB_CACHE_LINE_BYTES);
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (wireguard_output_module_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_peer.c b/src/plugins/wireguard/wireguard_peer.c
new file mode 100755
index 00000000000..0dcc4e20e41
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_peer.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/adj/adj_midchain.h>
+#include <vnet/fib/fib_table.h>
+#include <wireguard/wireguard_peer.h>
+#include <wireguard/wireguard_if.h>
+#include <wireguard/wireguard_messages.h>
+#include <wireguard/wireguard_key.h>
+#include <wireguard/wireguard_send.h>
+#include <wireguard/wireguard.h>
+
+static fib_source_t wg_fib_source;
+
+index_t *wg_peer_by_adj_index;
+
+wg_peer_t *
+wg_peer_get (index_t peeri)
+{
+ return (pool_elt_at_index (wg_main.peers, peeri));
+}
+
+static void
+wg_peer_endpoint_reset (wg_peer_endpoint_t * ep)
+{
+ ip46_address_reset (&ep->addr);
+ ep->port = 0;
+}
+
+static void
+wg_peer_endpoint_init (wg_peer_endpoint_t * ep,
+ const ip46_address_t * addr, u16 port)
+{
+ ip46_address_copy (&ep->addr, addr);
+ ep->port = port;
+}
+
+static void
+wg_peer_fib_flush (wg_peer_t * peer)
+{
+ wg_peer_allowed_ip_t *allowed_ip;
+
+ vec_foreach (allowed_ip, peer->allowed_ips)
+ {
+ fib_table_entry_delete_index (allowed_ip->fib_entry_index, wg_fib_source);
+ allowed_ip->fib_entry_index = FIB_NODE_INDEX_INVALID;
+ }
+}
+
+static void
+wg_peer_fib_populate (wg_peer_t * peer, u32 fib_index)
+{
+ wg_peer_allowed_ip_t *allowed_ip;
+
+ vec_foreach (allowed_ip, peer->allowed_ips)
+ {
+ allowed_ip->fib_entry_index =
+ fib_table_entry_path_add (fib_index,
+ &allowed_ip->prefix,
+ wg_fib_source,
+ FIB_ENTRY_FLAG_NONE,
+ fib_proto_to_dpo (allowed_ip->
+ prefix.fp_proto),
+ &peer->dst.addr, peer->wg_sw_if_index, ~0, 1,
+ NULL, FIB_ROUTE_PATH_FLAG_NONE);
+ }
+}
+
+static void
+wg_peer_clear (vlib_main_t * vm, wg_peer_t * peer)
+{
+ wg_timers_stop (peer);
+ noise_remote_clear (vm, &peer->remote);
+ peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1);
+
+ clib_memset (&peer->cookie_maker, 0, sizeof (peer->cookie_maker));
+
+ wg_peer_endpoint_reset (&peer->src);
+ wg_peer_endpoint_reset (&peer->dst);
+
+ if (INDEX_INVALID != peer->adj_index)
+ {
+ adj_unlock (peer->adj_index);
+ wg_peer_by_adj_index[peer->adj_index] = INDEX_INVALID;
+ }
+ wg_peer_fib_flush (peer);
+
+ peer->adj_index = INDEX_INVALID;
+ peer->persistent_keepalive_interval = 0;
+ peer->timer_handshake_attempts = 0;
+ peer->timer_need_another_keepalive = false;
+ peer->is_dead = true;
+ vec_free (peer->allowed_ips);
+}
+
+static void
+wg_peer_init (vlib_main_t * vm, wg_peer_t * peer)
+{
+ wg_timers_init (peer, vlib_time_now (vm));
+ wg_peer_clear (vm, peer);
+}
+
+static u8 *
+wg_peer_build_rewrite (const wg_peer_t * peer)
+{
+ // v4 only for now
+ ip4_udp_header_t *hdr;
+ u8 *rewrite = NULL;
+
+ vec_validate (rewrite, sizeof (*hdr) - 1);
+ hdr = (ip4_udp_header_t *) rewrite;
+
+ hdr->ip4.ip_version_and_header_length = 0x45;
+ hdr->ip4.ttl = 64;
+ hdr->ip4.src_address = peer->src.addr.ip4;
+ hdr->ip4.dst_address = peer->dst.addr.ip4;
+ hdr->ip4.protocol = IP_PROTOCOL_UDP;
+ hdr->ip4.checksum = ip4_header_checksum (&hdr->ip4);
+
+ hdr->udp.src_port = clib_host_to_net_u16 (peer->src.port);
+ hdr->udp.dst_port = clib_host_to_net_u16 (peer->dst.port);
+ hdr->udp.checksum = 0;
+
+ return (rewrite);
+}
+
+static void
+wg_peer_adj_stack (wg_peer_t * peer)
+{
+ ip_adjacency_t *adj;
+ u32 sw_if_index;
+ wg_if_t *wgi;
+
+ adj = adj_get (peer->adj_index);
+ sw_if_index = adj->rewrite_header.sw_if_index;
+
+ wgi = wg_if_get (wg_if_find_by_sw_if_index (sw_if_index));
+
+ if (!wgi)
+ return;
+
+ if (!vnet_sw_interface_is_admin_up (vnet_get_main (), wgi->sw_if_index))
+ {
+ adj_midchain_delegate_unstack (peer->adj_index);
+ }
+ else
+ {
+ /* *INDENT-OFF* */
+ fib_prefix_t dst = {
+ .fp_len = 32,
+ .fp_proto = FIB_PROTOCOL_IP4,
+ .fp_addr = peer->dst.addr,
+ };
+ /* *INDENT-ON* */
+ u32 fib_index;
+
+ fib_index = fib_table_find (FIB_PROTOCOL_IP4, peer->table_id);
+
+ adj_midchain_delegate_stack (peer->adj_index, fib_index, &dst);
+ }
+}
+
+walk_rc_t
+wg_peer_if_admin_state_change (wg_if_t * wgi, index_t peeri, void *data)
+{
+ wg_peer_adj_stack (wg_peer_get (peeri));
+
+ return (WALK_CONTINUE);
+}
+
+walk_rc_t
+wg_peer_if_table_change (wg_if_t * wgi, index_t peeri, void *data)
+{
+ wg_peer_table_bind_ctx_t *ctx = data;
+ wg_peer_t *peer;
+
+ peer = wg_peer_get (peeri);
+
+ wg_peer_fib_flush (peer);
+ wg_peer_fib_populate (peer, ctx->new_fib_index);
+
+ return (WALK_CONTINUE);
+}
+
+static int
+wg_peer_fill (vlib_main_t * vm, wg_peer_t * peer,
+ u32 table_id,
+ const ip46_address_t * dst,
+ u16 port,
+ u16 persistent_keepalive_interval,
+ const fib_prefix_t * allowed_ips, u32 wg_sw_if_index)
+{
+ wg_peer_endpoint_init (&peer->dst, dst, port);
+
+ peer->table_id = table_id;
+ peer->persistent_keepalive_interval = persistent_keepalive_interval;
+ peer->wg_sw_if_index = wg_sw_if_index;
+ peer->last_sent_handshake = vlib_time_now (vm) - (REKEY_TIMEOUT + 1);
+ peer->is_dead = false;
+
+ const wg_if_t *wgi = wg_if_get (wg_if_find_by_sw_if_index (wg_sw_if_index));
+
+ if (NULL == wgi)
+ return (VNET_API_ERROR_INVALID_INTERFACE);
+
+ ip_address_to_46 (&wgi->src_ip, &peer->src.addr);
+ peer->src.port = wgi->port;
+
+ /*
+ * and an adjacency for the endpoint address in the overlay
+ * on the wg interface
+ */
+ peer->rewrite = wg_peer_build_rewrite (peer);
+
+ peer->adj_index = adj_nbr_add_or_lock (FIB_PROTOCOL_IP4,
+ VNET_LINK_IP4,
+ &peer->dst.addr, wgi->sw_if_index);
+
+ vec_validate_init_empty (wg_peer_by_adj_index,
+ peer->adj_index, INDEX_INVALID);
+ wg_peer_by_adj_index[peer->adj_index] = peer - wg_main.peers;
+
+ adj_nbr_midchain_update_rewrite (peer->adj_index,
+ NULL,
+ NULL,
+ ADJ_FLAG_MIDCHAIN_IP_STACK,
+ vec_dup (peer->rewrite));
+ wg_peer_adj_stack (peer);
+
+ /*
+ * add a route in the overlay to each of the allowed-ips
+ */
+ u32 ii;
+
+ vec_validate (peer->allowed_ips, vec_len (allowed_ips) - 1);
+
+ vec_foreach_index (ii, allowed_ips)
+ {
+ peer->allowed_ips[ii].prefix = allowed_ips[ii];
+ }
+
+ wg_peer_fib_populate (peer,
+ fib_table_get_index_for_sw_if_index
+ (FIB_PROTOCOL_IP4, peer->wg_sw_if_index));
+
+ return (0);
+}
+
+int
+wg_peer_add (u32 tun_sw_if_index,
+ const u8 public_key[NOISE_PUBLIC_KEY_LEN],
+ u32 table_id,
+ const ip46_address_t * endpoint,
+ const fib_prefix_t * allowed_ips,
+ u16 port, u16 persistent_keepalive, u32 * peer_index)
+{
+ wg_if_t *wg_if;
+ wg_peer_t *peer;
+ int rv;
+
+ vlib_main_t *vm = vlib_get_main ();
+
+ if (tun_sw_if_index == ~0)
+ return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
+
+ wg_if = wg_if_get (wg_if_find_by_sw_if_index (tun_sw_if_index));
+ if (!wg_if)
+ return (VNET_API_ERROR_INVALID_SW_IF_INDEX);
+
+ /* *INDENT-OFF* */
+ pool_foreach (peer, wg_main.peers,
+ ({
+ if (!memcmp (peer->remote.r_public, public_key, NOISE_PUBLIC_KEY_LEN))
+ {
+ return (VNET_API_ERROR_ENTRY_ALREADY_EXISTS);
+ }
+ }));
+ /* *INDENT-ON* */
+
+ if (pool_elts (wg_main.peers) > MAX_PEERS)
+ return (VNET_API_ERROR_LIMIT_EXCEEDED);
+
+ pool_get (wg_main.peers, peer);
+
+ wg_peer_init (vm, peer);
+
+ rv = wg_peer_fill (vm, peer, table_id, endpoint, (u16) port,
+ persistent_keepalive, allowed_ips, tun_sw_if_index);
+
+ if (rv)
+ {
+ wg_peer_clear (vm, peer);
+ pool_put (wg_main.peers, peer);
+ return (rv);
+ }
+
+ noise_remote_init (&peer->remote, peer - wg_main.peers, public_key,
+ &wg_if->local);
+ cookie_maker_init (&peer->cookie_maker, public_key);
+
+ if (peer->persistent_keepalive_interval != 0)
+ {
+ wg_send_keepalive (vm, peer);
+ }
+
+ *peer_index = peer - wg_main.peers;
+ wg_if_peer_add (wg_if, *peer_index);
+
+ return (0);
+}
+
+int
+wg_peer_remove (index_t peeri)
+{
+ wg_main_t *wmp = &wg_main;
+ wg_peer_t *peer = NULL;
+ wg_if_t *wgi;
+
+ if (pool_is_free_index (wmp->peers, peeri))
+ return VNET_API_ERROR_NO_SUCH_ENTRY;
+
+ peer = pool_elt_at_index (wmp->peers, peeri);
+
+ wgi = wg_if_get (wg_if_find_by_sw_if_index (peer->wg_sw_if_index));
+ wg_if_peer_remove (wgi, peeri);
+
+ vnet_feature_enable_disable ("ip4-output", "wg-output-tun",
+ peer->wg_sw_if_index, 0, 0, 0);
+ wg_peer_clear (wmp->vlib_main, peer);
+ pool_put (wmp->peers, peer);
+
+ return (0);
+}
+
+void
+wg_peer_walk (wg_peer_walk_cb_t fn, void *data)
+{
+ index_t peeri;
+
+ /* *INDENT-OFF* */
+ pool_foreach_index(peeri, wg_main.peers,
+ {
+ if (WALK_STOP == fn(peeri, data))
+ break;
+ });
+ /* *INDENT-ON* */
+}
+
+static u8 *
+format_wg_peer_endpoint (u8 * s, va_list * args)
+{
+ wg_peer_endpoint_t *ep = va_arg (*args, wg_peer_endpoint_t *);
+
+ s = format (s, "%U:%d",
+ format_ip46_address, &ep->addr, IP46_TYPE_ANY, ep->port);
+
+ return (s);
+}
+
+u8 *
+format_wg_peer (u8 * s, va_list * va)
+{
+ index_t peeri = va_arg (*va, index_t);
+ wg_peer_allowed_ip_t *allowed_ip;
+ u8 key[NOISE_KEY_LEN_BASE64];
+ wg_peer_t *peer;
+
+ peer = wg_peer_get (peeri);
+ key_to_base64 (peer->remote.r_public, NOISE_PUBLIC_KEY_LEN, key);
+
+ s = format (s, "[%d] key:%=45s endpoint:[%U->%U] %U keep-alive:%d adj:%d",
+ peeri,
+ key,
+ format_wg_peer_endpoint, &peer->src,
+ format_wg_peer_endpoint, &peer->dst,
+ format_vnet_sw_if_index_name, vnet_get_main (),
+ peer->wg_sw_if_index,
+ peer->persistent_keepalive_interval, peer->adj_index);
+
+ s = format (s, "\n allowed-ips:");
+ vec_foreach (allowed_ip, peer->allowed_ips)
+ {
+ s = format (s, " %U", format_fib_prefix, &allowed_ip->prefix);
+ }
+
+ return s;
+}
+
+static clib_error_t *
+wg_peer_module_init (vlib_main_t * vm)
+{
+ wg_fib_source = fib_source_allocate ("wireguard", 0xb0, //
+ FIB_SOURCE_BH_SIMPLE);
+
+ return (NULL);
+}
+
+VLIB_INIT_FUNCTION (wg_peer_module_init);
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_peer.h b/src/plugins/wireguard/wireguard_peer.h
new file mode 100755
index 00000000000..99c73f3a0ed
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_peer.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_wg_peer_h__
+#define __included_wg_peer_h__
+
+#include <vnet/ip/ip.h>
+
+#include <wireguard/wireguard_cookie.h>
+#include <wireguard/wireguard_timer.h>
+#include <wireguard/wireguard_key.h>
+#include <wireguard/wireguard_messages.h>
+#include <wireguard/wireguard_if.h>
+
+typedef struct ip4_udp_header_t_
+{
+ ip4_header_t ip4;
+ udp_header_t udp;
+} __clib_packed ip4_udp_header_t;
+
+u8 *format_ip4_udp_header (u8 * s, va_list * va);
+
+typedef struct wg_peer_allowed_ip_t_
+{
+ fib_prefix_t prefix;
+ fib_node_index_t fib_entry_index;
+} wg_peer_allowed_ip_t;
+
+typedef struct wg_peer_endpoint_t_
+{
+ ip46_address_t addr;
+ u16 port;
+} wg_peer_endpoint_t;
+
+typedef struct wg_peer
+{
+ noise_remote_t remote;
+ cookie_maker_t cookie_maker;
+
+ /* Peer addresses */
+ wg_peer_endpoint_t dst;
+ wg_peer_endpoint_t src;
+ u32 table_id;
+ adj_index_t adj_index;
+
+ /* rewrite built from address information */
+ u8 *rewrite;
+
+ /* Vector of allowed-ips */
+ wg_peer_allowed_ip_t *allowed_ips;
+
+ /* The WG interface this peer is attached to */
+ u32 wg_sw_if_index;
+
+ /* Timers */
+ tw_timer_wheel_16t_2w_512sl_t timer_wheel;
+ u32 timers[WG_N_TIMERS];
+ u32 timer_handshake_attempts;
+ u16 persistent_keepalive_interval;
+ f64 last_sent_handshake;
+ bool timer_need_another_keepalive;
+
+ bool is_dead;
+} wg_peer_t;
+
+typedef struct wg_peer_table_bind_ctx_t_
+{
+ ip_address_family_t af;
+ u32 new_fib_index;
+ u32 old_fib_index;
+} wg_peer_table_bind_ctx_t;
+
+int wg_peer_add (u32 tun_sw_if_index,
+ const u8 public_key_64[NOISE_PUBLIC_KEY_LEN],
+ u32 table_id,
+ const ip46_address_t * endpoint,
+ const fib_prefix_t * allowed_ips,
+ u16 port, u16 persistent_keepalive, index_t * peer_index);
+int wg_peer_remove (u32 peer_index);
+
+typedef walk_rc_t (*wg_peer_walk_cb_t) (index_t peeri, void *arg);
+void wg_peer_walk (wg_peer_walk_cb_t fn, void *data);
+
+u8 *format_wg_peer (u8 * s, va_list * va);
+wg_peer_t *wg_peer_get (index_t peeri);
+
+walk_rc_t wg_peer_if_admin_state_change (wg_if_t * wgi, index_t peeri,
+ void *data);
+walk_rc_t wg_peer_if_table_change (wg_if_t * wgi, index_t peeri, void *data);
+
+/*
+ * Expoed for the data-plane
+ */
+extern index_t *wg_peer_by_adj_index;
+
+static inline wg_peer_t *
+wg_peer_get_by_adj_index (index_t ai)
+{
+ return wg_peer_get (wg_peer_by_adj_index[ai]);
+}
+
+#endif // __included_wg_peer_h__
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_send.c b/src/plugins/wireguard/wireguard_send.c
new file mode 100755
index 00000000000..a5d8aaf6900
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_send.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vnet/vnet.h>
+#include <vnet/fib/ip6_fib.h>
+#include <vnet/fib/ip4_fib.h>
+#include <vnet/fib/fib_entry.h>
+#include <vnet/ip/ip6_link.h>
+#include <vnet/pg/pg.h>
+#include <vnet/udp/udp.h>
+#include <vppinfra/error.h>
+#include <wireguard/wireguard.h>
+#include <wireguard/wireguard_send.h>
+
+static int
+ip46_enqueue_packet (vlib_main_t * vm, u32 bi0, int is_ip6)
+{
+ vlib_frame_t *f = 0;
+ u32 lookup_node_index =
+ is_ip6 ? ip6_lookup_node.index : ip4_lookup_node.index;
+
+ f = vlib_get_frame_to_node (vm, lookup_node_index);
+ /* f can not be NULL here - frame allocation failure causes panic */
+
+ u32 *to_next = vlib_frame_vector_args (f);
+ f->n_vectors = 1;
+ to_next[0] = bi0;
+
+ vlib_put_frame_to_node (vm, lookup_node_index, f);
+
+ return f->n_vectors;
+}
+
+static void
+wg_buffer_prepend_rewrite (vlib_buffer_t * b0, const wg_peer_t * peer)
+{
+ ip4_udp_header_t *hdr;
+
+ vlib_buffer_advance (b0, -sizeof (*hdr));
+
+ hdr = vlib_buffer_get_current (b0);
+ clib_memcpy (hdr, peer->rewrite, vec_len (peer->rewrite));
+
+ hdr->udp.length =
+ clib_host_to_net_u16 (b0->current_length - sizeof (ip4_header_t));
+ ip4_header_set_len_w_chksum (&hdr->ip4,
+ clib_host_to_net_u16 (b0->current_length));
+}
+
+static bool
+wg_create_buffer (vlib_main_t * vm,
+ const wg_peer_t * peer,
+ const u8 * packet, u32 packet_len, u32 * bi)
+{
+ u32 n_buf0 = 0;
+ vlib_buffer_t *b0;
+
+ n_buf0 = vlib_buffer_alloc (vm, bi, 1);
+ if (!n_buf0)
+ return false;
+
+ b0 = vlib_get_buffer (vm, *bi);
+
+ u8 *payload = vlib_buffer_get_current (b0);
+ clib_memcpy (payload, packet, packet_len);
+
+ b0->current_length = packet_len;
+
+ wg_buffer_prepend_rewrite (b0, peer);
+
+ return true;
+}
+
+bool
+wg_send_handshake (vlib_main_t * vm, wg_peer_t * peer, bool is_retry)
+{
+ wg_main_t *wmp = &wg_main;
+ message_handshake_initiation_t packet;
+
+ if (!is_retry)
+ peer->timer_handshake_attempts = 0;
+
+ if (!wg_birthdate_has_expired (peer->last_sent_handshake,
+ REKEY_TIMEOUT) || peer->is_dead)
+ {
+ return true;
+ }
+ if (noise_create_initiation (wmp->vlib_main,
+ &peer->remote,
+ &packet.sender_index,
+ packet.unencrypted_ephemeral,
+ packet.encrypted_static,
+ packet.encrypted_timestamp))
+ {
+ f64 now = vlib_time_now (vm);
+ packet.header.type = MESSAGE_HANDSHAKE_INITIATION;
+ cookie_maker_mac (&peer->cookie_maker, &packet.macs, &packet,
+ sizeof (packet));
+ wg_timers_any_authenticated_packet_traversal (peer);
+ wg_timers_any_authenticated_packet_sent (peer);
+ peer->last_sent_handshake = now;
+ wg_timers_handshake_initiated (peer);
+ }
+ else
+ return false;
+ u32 bi0 = 0;
+ if (!wg_create_buffer (vm, peer, (u8 *) & packet, sizeof (packet), &bi0))
+ return false;
+ ip46_enqueue_packet (vm, bi0, false);
+
+ return true;
+}
+
+bool
+wg_send_keepalive (vlib_main_t * vm, wg_peer_t * peer)
+{
+ wg_main_t *wmp = &wg_main;
+ u32 size_of_packet = message_data_len (0);
+ message_data_t *packet = clib_mem_alloc (size_of_packet);
+ u32 bi0 = 0;
+ bool ret = true;
+ enum noise_state_crypt state;
+
+ if (!peer->remote.r_current)
+ {
+ wg_send_handshake (vm, peer, false);
+ goto out;
+ }
+
+ state =
+ noise_remote_encrypt (wmp->vlib_main,
+ &peer->remote,
+ &packet->receiver_index,
+ &packet->counter, NULL, 0, packet->encrypted_data);
+ switch (state)
+ {
+ case SC_OK:
+ break;
+ case SC_KEEP_KEY_FRESH:
+ wg_send_handshake (vm, peer, false);
+ break;
+ case SC_FAILED:
+ ret = false;
+ goto out;
+ default:
+ break;
+ }
+ packet->header.type = MESSAGE_DATA;
+
+ if (!wg_create_buffer (vm, peer, (u8 *) packet, size_of_packet, &bi0))
+ {
+ ret = false;
+ goto out;
+ }
+
+ ip46_enqueue_packet (vm, bi0, false);
+ wg_timers_any_authenticated_packet_traversal (peer);
+ wg_timers_any_authenticated_packet_sent (peer);
+
+out:
+ clib_mem_free (packet);
+ return ret;
+}
+
+bool
+wg_send_handshake_response (vlib_main_t * vm, wg_peer_t * peer)
+{
+ wg_main_t *wmp = &wg_main;
+ message_handshake_response_t packet;
+
+ peer->last_sent_handshake = vlib_time_now (vm);
+
+ if (noise_create_response (vm,
+ &peer->remote,
+ &packet.sender_index,
+ &packet.receiver_index,
+ packet.unencrypted_ephemeral,
+ packet.encrypted_nothing))
+ {
+ f64 now = vlib_time_now (vm);
+ packet.header.type = MESSAGE_HANDSHAKE_RESPONSE;
+ cookie_maker_mac (&peer->cookie_maker, &packet.macs, &packet,
+ sizeof (packet));
+
+ if (noise_remote_begin_session (wmp->vlib_main, &peer->remote))
+ {
+ wg_timers_session_derived (peer);
+ wg_timers_any_authenticated_packet_traversal (peer);
+ wg_timers_any_authenticated_packet_sent (peer);
+ peer->last_sent_handshake = now;
+
+ u32 bi0 = 0;
+ if (!wg_create_buffer (vm, peer, (u8 *) & packet,
+ sizeof (packet), &bi0))
+ return false;
+
+ ip46_enqueue_packet (vm, bi0, false);
+ }
+ else
+ return false;
+ }
+ else
+ return false;
+ return true;
+}
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_send.h b/src/plugins/wireguard/wireguard_send.h
new file mode 100755
index 00000000000..8f5e7ab8765
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_send.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_wg_send_h__
+#define __included_wg_send_h__
+
+#include <wireguard/wireguard_peer.h>
+
+bool wg_send_keepalive (vlib_main_t * vm, wg_peer_t * peer);
+bool wg_send_handshake (vlib_main_t * vm, wg_peer_t * peer, bool is_retry);
+bool wg_send_handshake_response (vlib_main_t * vm, wg_peer_t * peer);
+
+always_inline void
+ip4_header_set_len_w_chksum (ip4_header_t * ip4, u16 len)
+{
+ ip_csum_t sum = ip4->checksum;
+ u8 old = ip4->length;
+ u8 new = len;
+
+ sum = ip_csum_update (sum, old, new, ip4_header_t, length);
+ ip4->checksum = ip_csum_fold (sum);
+ ip4->length = new;
+}
+
+#endif /* __included_wg_send_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_timer.c b/src/plugins/wireguard/wireguard_timer.c
new file mode 100755
index 00000000000..e4d4030bb18
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_timer.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <wireguard/wireguard.h>
+#include <wireguard/wireguard_send.h>
+#include <wireguard/wireguard_timer.h>
+
+static u32
+get_random_u32_max (u32 max)
+{
+ vlib_main_t *vm = vlib_get_main ();
+ u32 seed = (u32) (vlib_time_now (vm) * 1e6);
+ return random_u32 (&seed) % max;
+}
+
+static void
+stop_timer (wg_peer_t * peer, u32 timer_id)
+{
+ if (peer->timers[timer_id] != ~0)
+ {
+ tw_timer_stop_16t_2w_512sl (&peer->timer_wheel, peer->timers[timer_id]);
+ peer->timers[timer_id] = ~0;
+ }
+}
+
+static void
+start_or_update_timer (wg_peer_t * peer, u32 timer_id, u32 interval)
+{
+ if (peer->timers[timer_id] == ~0)
+ {
+ wg_main_t *wmp = &wg_main;
+ peer->timers[timer_id] =
+ tw_timer_start_16t_2w_512sl (&peer->timer_wheel, peer - wmp->peers,
+ timer_id, interval);
+ }
+ else
+ {
+ tw_timer_update_16t_2w_512sl (&peer->timer_wheel,
+ peer->timers[timer_id], interval);
+ }
+}
+
+static void
+wg_expired_retransmit_handshake (vlib_main_t * vm, wg_peer_t * peer)
+{
+
+ if (peer->timer_handshake_attempts > MAX_TIMER_HANDSHAKES)
+ {
+ stop_timer (peer, WG_TIMER_SEND_KEEPALIVE);
+
+ /* We set a timer for destroying any residue that might be left
+ * of a partial exchange.
+ */
+
+ if (peer->timers[WG_TIMER_KEY_ZEROING] == ~0)
+ {
+ wg_main_t *wmp = &wg_main;
+
+ peer->timers[WG_TIMER_KEY_ZEROING] =
+ tw_timer_start_16t_2w_512sl (&peer->timer_wheel,
+ peer - wmp->peers,
+ WG_TIMER_KEY_ZEROING,
+ REJECT_AFTER_TIME * 3 * WHZ);
+ }
+ }
+ else
+ {
+ ++peer->timer_handshake_attempts;
+ wg_send_handshake (vm, peer, true);
+ }
+}
+
+static void
+wg_expired_send_keepalive (vlib_main_t * vm, wg_peer_t * peer)
+{
+ wg_send_keepalive (vm, peer);
+
+ if (peer->timer_need_another_keepalive)
+ {
+ peer->timer_need_another_keepalive = false;
+ start_or_update_timer (peer, WG_TIMER_SEND_KEEPALIVE,
+ KEEPALIVE_TIMEOUT * WHZ);
+ }
+}
+
+static void
+wg_expired_send_persistent_keepalive (vlib_main_t * vm, wg_peer_t * peer)
+{
+ if (peer->persistent_keepalive_interval)
+ {
+ wg_send_keepalive (vm, peer);
+ }
+}
+
+static void
+wg_expired_new_handshake (vlib_main_t * vm, wg_peer_t * peer)
+{
+ wg_send_handshake (vm, peer, false);
+}
+
+static void
+wg_expired_zero_key_material (vlib_main_t * vm, wg_peer_t * peer)
+{
+ if (!peer->is_dead)
+ {
+ noise_remote_clear (vm, &peer->remote);
+ }
+}
+
+
+void
+wg_timers_any_authenticated_packet_traversal (wg_peer_t * peer)
+{
+ if (peer->persistent_keepalive_interval)
+ {
+ start_or_update_timer (peer, WG_TIMER_PERSISTENT_KEEPALIVE,
+ peer->persistent_keepalive_interval * WHZ);
+ }
+}
+
+void
+wg_timers_any_authenticated_packet_sent (wg_peer_t * peer)
+{
+ stop_timer (peer, WG_TIMER_SEND_KEEPALIVE);
+}
+
+void
+wg_timers_handshake_initiated (wg_peer_t * peer)
+{
+ u32 interval =
+ REKEY_TIMEOUT * WHZ + get_random_u32_max (REKEY_TIMEOUT_JITTER);
+ start_or_update_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE, interval);
+}
+
+void
+wg_timers_session_derived (wg_peer_t * peer)
+{
+ start_or_update_timer (peer, WG_TIMER_KEY_ZEROING,
+ REJECT_AFTER_TIME * 3 * WHZ);
+}
+
+/* Should be called after an authenticated data packet is sent. */
+void
+wg_timers_data_sent (wg_peer_t * peer)
+{
+ u32 interval = (KEEPALIVE_TIMEOUT + REKEY_TIMEOUT) * WHZ +
+ get_random_u32_max (REKEY_TIMEOUT_JITTER);
+
+ if (peer->timers[WG_TIMER_NEW_HANDSHAKE] == ~0)
+ {
+ wg_main_t *wmp = &wg_main;
+ peer->timers[WG_TIMER_NEW_HANDSHAKE] =
+ tw_timer_start_16t_2w_512sl (&peer->timer_wheel, peer - wmp->peers,
+ WG_TIMER_NEW_HANDSHAKE, interval);
+ }
+}
+
+/* Should be called after an authenticated data packet is received. */
+void
+wg_timers_data_received (wg_peer_t * peer)
+{
+ if (peer->timers[WG_TIMER_SEND_KEEPALIVE] == ~0)
+ {
+ wg_main_t *wmp = &wg_main;
+ peer->timers[WG_TIMER_SEND_KEEPALIVE] =
+ tw_timer_start_16t_2w_512sl (&peer->timer_wheel, peer - wmp->peers,
+ WG_TIMER_SEND_KEEPALIVE,
+ KEEPALIVE_TIMEOUT * WHZ);
+ }
+ else
+ {
+ peer->timer_need_another_keepalive = true;
+ }
+}
+
+/* Should be called after a handshake response message is received and processed
+ * or when getting key confirmation via the first data message.
+ */
+void
+wg_timers_handshake_complete (wg_peer_t * peer)
+{
+ stop_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE);
+
+ peer->timer_handshake_attempts = 0;
+}
+
+void
+wg_timers_any_authenticated_packet_received (wg_peer_t * peer)
+{
+ stop_timer (peer, WG_TIMER_NEW_HANDSHAKE);
+}
+
+static vlib_node_registration_t wg_timer_mngr_node;
+
+static void
+expired_timer_callback (u32 * expired_timers)
+{
+ int i;
+ u32 timer_id;
+ u32 pool_index;
+
+ wg_main_t *wmp = &wg_main;
+ vlib_main_t *vm = wmp->vlib_main;
+
+ wg_peer_t *peer;
+
+ /* Need to invalidate all of them because one can restart other */
+ for (i = 0; i < vec_len (expired_timers); i++)
+ {
+ pool_index = expired_timers[i] & 0x0FFFFFFF;
+ timer_id = expired_timers[i] >> 28;
+
+ peer = pool_elt_at_index (wmp->peers, pool_index);
+ peer->timers[timer_id] = ~0;
+ }
+
+ for (i = 0; i < vec_len (expired_timers); i++)
+ {
+ pool_index = expired_timers[i] & 0x0FFFFFFF;
+ timer_id = expired_timers[i] >> 28;
+
+ peer = pool_elt_at_index (wmp->peers, pool_index);
+ switch (timer_id)
+ {
+ case WG_TIMER_RETRANSMIT_HANDSHAKE:
+ wg_expired_retransmit_handshake (vm, peer);
+ break;
+ case WG_TIMER_PERSISTENT_KEEPALIVE:
+ wg_expired_send_persistent_keepalive (vm, peer);
+ break;
+ case WG_TIMER_SEND_KEEPALIVE:
+ wg_expired_send_keepalive (vm, peer);
+ break;
+ case WG_TIMER_NEW_HANDSHAKE:
+ wg_expired_new_handshake (vm, peer);
+ break;
+ case WG_TIMER_KEY_ZEROING:
+ wg_expired_zero_key_material (vm, peer);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void
+wg_timers_init (wg_peer_t * peer, f64 now)
+{
+ for (int i = 0; i < WG_N_TIMERS; i++)
+ {
+ peer->timers[i] = ~0;
+ }
+ tw_timer_wheel_16t_2w_512sl_t *tw = &peer->timer_wheel;
+ tw_timer_wheel_init_16t_2w_512sl (tw,
+ expired_timer_callback,
+ WG_TICK /* timer period in s */ , ~0);
+ tw->last_run_time = now;
+ peer->adj_index = INDEX_INVALID;
+}
+
+static uword
+wg_timer_mngr_fn (vlib_main_t * vm, vlib_node_runtime_t * rt,
+ vlib_frame_t * f)
+{
+ wg_main_t *wmp = &wg_main;
+ wg_peer_t *peers;
+ wg_peer_t *peer;
+
+ while (1)
+ {
+ vlib_process_wait_for_event_or_clock (vm, WG_TICK);
+ vlib_process_get_events (vm, NULL);
+
+ peers = wmp->peers;
+ /* *INDENT-OFF* */
+ pool_foreach (peer, peers,
+ ({
+ tw_timer_expire_timers_16t_2w_512sl
+ (&peer->timer_wheel, vlib_time_now (vm));
+ }));
+ /* *INDENT-ON* */
+ }
+
+ return 0;
+}
+
+void
+wg_timers_stop (wg_peer_t * peer)
+{
+ stop_timer (peer, WG_TIMER_RETRANSMIT_HANDSHAKE);
+ stop_timer (peer, WG_TIMER_PERSISTENT_KEEPALIVE);
+ stop_timer (peer, WG_TIMER_SEND_KEEPALIVE);
+ stop_timer (peer, WG_TIMER_NEW_HANDSHAKE);
+ stop_timer (peer, WG_TIMER_KEY_ZEROING);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (wg_timer_mngr_node, static) = {
+ .function = wg_timer_mngr_fn,
+ .type = VLIB_NODE_TYPE_PROCESS,
+ .name =
+ "wg-timer-manager",
+};
+/* *INDENT-ON* */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/src/plugins/wireguard/wireguard_timer.h b/src/plugins/wireguard/wireguard_timer.h
new file mode 100755
index 00000000000..457dce28674
--- /dev/null
+++ b/src/plugins/wireguard/wireguard_timer.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020 Doc.ai and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __included_wg_timer_h__
+#define __included_wg_timer_h__
+
+#include <vlib/vlib.h>
+#include <vppinfra/clib.h>
+#include <vppinfra/tw_timer_16t_2w_512sl.h>
+
+/** WG timers */
+#define foreach_wg_timer \
+ _(RETRANSMIT_HANDSHAKE, "RETRANSMIT HANDSHAKE") \
+ _(PERSISTENT_KEEPALIVE, "PERSISTENT KEEPALIVE") \
+ _(SEND_KEEPALIVE, "SEND KEEPALIVE") \
+ _(NEW_HANDSHAKE, "NEW HANDSHAKE") \
+ _(KEY_ZEROING, "KEY ZEROING") \
+
+typedef enum _wg_timers
+{
+#define _(sym, str) WG_TIMER_##sym,
+ foreach_wg_timer
+#undef _
+ WG_N_TIMERS
+} wg_timers_e;
+
+typedef struct wg_peer wg_peer_t;
+
+void wg_timers_init (wg_peer_t * peer, f64 now);
+void wg_timers_stop (wg_peer_t * peer);
+void wg_timers_data_sent (wg_peer_t * peer);
+void wg_timers_data_received (wg_peer_t * peer);
+void wg_timers_any_authenticated_packet_sent (wg_peer_t * peer);
+void wg_timers_any_authenticated_packet_received (wg_peer_t * peer);
+void wg_timers_handshake_initiated (wg_peer_t * peer);
+void wg_timers_handshake_complete (wg_peer_t * peer);
+void wg_timers_session_derived (wg_peer_t * peer);
+void wg_timers_any_authenticated_packet_traversal (wg_peer_t * peer);
+
+
+static inline bool
+wg_birthdate_has_expired (f64 birthday_seconds, f64 expiration_seconds)
+{
+ f64 now_seconds = vlib_time_now (vlib_get_main ());
+ return (birthday_seconds + expiration_seconds) < now_seconds;
+}
+
+
+#endif /* __included_wg_timer_h__ */
+
+/*
+ * fd.io coding-style-patch-verification: ON
+ *
+ * Local Variables:
+ * eval: (c-set-style "gnu")
+ * End:
+ */
diff --git a/test/requirements-3.txt b/test/requirements-3.txt
index a17c49fc3ac..f955f37c471 100644
--- a/test/requirements-3.txt
+++ b/test/requirements-3.txt
@@ -74,7 +74,7 @@ cryptography==2.9.2 \
--hash=sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e \
--hash=sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785 \
--hash=sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0 \
- # via -r requirements.txt
+ # via -r requirements.txt, noiseprotocol
deprecation==2.1.0 \
--hash=sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff \
--hash=sha256:a10811591210e1fb0e768a8c25517cabeabcba6f0bf96564f8ff45189f90b14a \
@@ -149,6 +149,9 @@ mccabe==0.6.1 \
--hash=sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42 \
--hash=sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f \
# via flake8
+noiseprotocol==0.3.1 \
+ --hash=sha256:2e1a603a38439636cf0ffd8b3e8b12cee27d368a28b41be7dbe568b2abb23111 \
+ # via -r requirements.txt
objgraph==3.4.1 \
--hash=sha256:10d50116d1a66934d143a07308a5b3b3edcc021e1457f66ff792584166cae7cd \
--hash=sha256:bf29512d7f8b457b53fa0722ea59f516abb8abc59b78f97f0ef81394a0c615a7 \
diff --git a/test/requirements.txt b/test/requirements.txt
index 5f9f44c2244..e934bc042a2 100644
--- a/test/requirements.txt
+++ b/test/requirements.txt
@@ -19,3 +19,4 @@ objgraph # MIT
pympler # Apache-2.0
sphinx<3.0.0 # BSD, sphinx 3.0.0 crashes with a traceback
sphinx-rtd-theme # MIT
+noiseprotocol # MIT