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>
This commit is contained in:
Artem Glazychev
2020-08-31 17:12:30 +07:00
committed by Damjan Marion
parent ef80ad6bff
commit edca1325cf
34 changed files with 6226 additions and 1 deletions

View File

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

View File

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

View File

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

74
src/plugins/wireguard/README.md Executable file
View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

Some files were not shown because too many files have changed in this diff Show More